Silences warnings from mongodb client (#5025)

* Silences warnings from mongodb client

* Update count, delete and finds to recommended implementations

* With new parser, readPref will be null by default

* Update flaky specs wih async/await style

* Adds gridstore adapter spec

* Use GridFSBucketStorage adapter
This commit is contained in:
Florent Vilmart
2018-09-04 16:15:09 -04:00
committed by GitHub
parent d83a0b6808
commit a42101531a
14 changed files with 265 additions and 109 deletions

View File

@@ -9,7 +9,7 @@
// * getFileData(filename)
// * getFileLocation(config, filename)
//
// Default is GridStoreAdapter, which requires mongo
// Default is GridFSBucketAdapter, which requires mongo
// and for the API server to be using the DatabaseController with Mongo
// database adapter.

View File

@@ -0,0 +1,95 @@
/**
GridFSBucketAdapter
Stores files in Mongo using GridStore
Requires the database adapter to be based on mongoclient
@flow weak
*/
// @flow-disable-next
import { MongoClient, GridFSBucket, Db } from 'mongodb';
import { FilesAdapter } from './FilesAdapter';
import defaults from '../../defaults';
export class GridFSBucketAdapter extends FilesAdapter {
_databaseURI: string;
_connectionPromise: Promise<Db>;
constructor(mongoDatabaseURI = defaults.DefaultMongoURI) {
super();
this._databaseURI = mongoDatabaseURI;
}
_connect() {
if (!this._connectionPromise) {
this._connectionPromise = MongoClient.connect(this._databaseURI).then(
client => client.db(client.s.options.dbName)
);
}
return this._connectionPromise;
}
_getBucket() {
return this._connect().then(database => new GridFSBucket(database));
}
// For a given config object, filename, and data, store a file
// Returns a promise
async createFile(filename: string, data) {
const bucket = await this._getBucket();
const stream = await bucket.openUploadStream(filename);
await stream.write(data);
stream.end();
return new Promise((resolve, reject) => {
stream.on('finish', resolve);
stream.on('error', reject);
});
}
async deleteFile(filename: string) {
const bucket = await this._getBucket();
const documents = await bucket.find({ filename: filename }).toArray();
if (documents.length === 0) {
throw new Error('FileNotFound');
}
return Promise.all(
documents.map(doc => {
return bucket.delete(doc._id);
})
);
}
async getFileData(filename: string) {
const stream = await this.getDownloadStream(filename);
stream.read();
return new Promise((resolve, reject) => {
const chunks = [];
stream.on('data', data => {
chunks.push(data);
});
stream.on('end', () => {
resolve(Buffer.concat(chunks));
});
stream.on('error', err => {
reject(err);
});
});
}
getFileLocation(config, filename) {
return (
config.mount +
'/files/' +
config.applicationId +
'/' +
encodeURIComponent(filename)
);
}
async getDownloadStream(filename: string) {
const bucket = await this._getBucket();
return bucket.openDownloadStreamByName(filename);
}
}
export default GridFSBucketAdapter;

View File

@@ -80,7 +80,7 @@ export default class MongoCollection {
}
count(query, { skip, limit, sort, maxTimeMS, readPreference } = {}) {
const countOperation = this._mongoCollection.count(query, {
const countOperation = this._mongoCollection.countDocuments(query, {
skip,
limit,
sort,
@@ -109,7 +109,7 @@ export default class MongoCollection {
// If there is nothing that matches the query - does insert
// Postgres Note: `INSERT ... ON CONFLICT UPDATE` that is available since 9.5.
upsertOne(query, update) {
return this._mongoCollection.update(query, update, { upsert: true });
return this._mongoCollection.updateOne(query, update, { upsert: true });
}
updateOne(query, update) {
@@ -126,7 +126,7 @@ export default class MongoCollection {
_ensureSparseUniqueIndexInBackground(indexRequest) {
return new Promise((resolve, reject) => {
this._mongoCollection.ensureIndex(
this._mongoCollection.createIndex(
indexRequest,
{ unique: true, background: true, sparse: true },
error => {

View File

@@ -135,6 +135,7 @@ export class MongoStorageAdapter implements StorageAdapter {
this._uri = uri;
this._collectionPrefix = collectionPrefix;
this._mongoOptions = mongoOptions;
this._mongoOptions.useNewUrlParser = true;
// MaxTimeMS is not a global MongoDB client option, it is applied per operation.
this._maxTimeMS = mongoOptions.maxTimeMS;
@@ -385,7 +386,7 @@ export class MongoStorageAdapter implements StorageAdapter {
return storageAdapterAllCollections(this).then(collections =>
Promise.all(
collections.map(
collection => (fast ? collection.remove({}) : collection.drop())
collection => (fast ? collection.deleteMany({}) : collection.drop())
)
)
);
@@ -557,8 +558,8 @@ export class MongoStorageAdapter implements StorageAdapter {
const mongoWhere = transformWhere(className, query, schema);
return this._adaptiveCollection(className)
.then(collection =>
collection._mongoCollection.findAndModify(mongoWhere, [], mongoUpdate, {
new: true,
collection._mongoCollection.findOneAndUpdate(mongoWhere, mongoUpdate, {
returnOriginal: false,
})
)
.then(result => mongoObjectToParseObject(className, result.value, schema))

View File

@@ -18,7 +18,7 @@ import DatabaseController from './DatabaseController';
import SchemaCache from './SchemaCache';
// Adapters
import { GridStoreAdapter } from '../Adapters/Files/GridStoreAdapter';
import { GridFSBucketAdapter } from '../Adapters/Files/GridFSBucketAdapter';
import { WinstonLoggerAdapter } from '../Adapters/Logger/WinstonLoggerAdapter';
import { InMemoryCacheAdapter } from '../Adapters/Cache/InMemoryCacheAdapter';
import { AnalyticsAdapter } from '../Adapters/Analytics/AnalyticsAdapter';
@@ -96,7 +96,7 @@ export function getFilesController(
throw 'When using an explicit database adapter, you must also use an explicit filesAdapter.';
}
const filesControllerAdapter = loadAdapter(filesAdapter, () => {
return new GridStoreAdapter(databaseURI);
return new GridFSBucketAdapter(databaseURI);
});
return new FilesController(filesControllerAdapter, appId, {
preserveFileName,

View File

@@ -43,7 +43,7 @@ addParseCloud();
// ParseServer works like a constructor of an express app.
// The args that we understand are:
// "analyticsAdapter": an adapter class for analytics
// "filesAdapter": a class like GridStoreAdapter providing create, get,
// "filesAdapter": a class like GridFSBucketAdapter providing create, get,
// and delete
// "loggerAdapter": a class like WinstonLoggerAdapter providing info, error,
// and query