diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index a5107a906c..7b8d2a3ef3 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -34,12 +34,12 @@ const ReadPreference = mongodb.ReadPreference; const MongoSchemaCollectionName = '_SCHEMA'; -const storageAdapterAllCollections = mongoAdapter => { +const storageAdapterAllCollections = (mongoAdapter) => { return mongoAdapter .connect() .then(() => mongoAdapter.database.collections()) - .then(collections => { - return collections.filter(collection => { + .then((collections) => { + return collections.filter((collection) => { if (collection.namespace.match(/\.system\./)) { return false; } @@ -164,7 +164,7 @@ export class MongoStorageAdapter implements StorageAdapter { const encodedUri = formatUrl(parseUrl(this._uri)); this.connectionPromise = MongoClient.connect(encodedUri, this._mongoOptions) - .then(client => { + .then((client) => { // Starting mongoDB 3.0, the MongoClient.connect don't return a DB anymore but a client // Fortunately, we can get back the options and use them to select the proper DB. // https://github.com/mongodb/node-mongodb-native/blob/2c35d76f08574225b8db02d7bef687123e6bb018/lib/mongo_client.js#L885 @@ -183,7 +183,7 @@ export class MongoStorageAdapter implements StorageAdapter { this.client = client; this.database = database; }) - .catch(err => { + .catch((err) => { delete this.connectionPromise; return Promise.reject(err); }); @@ -212,14 +212,14 @@ export class MongoStorageAdapter implements StorageAdapter { _adaptiveCollection(name: string) { return this.connect() .then(() => this.database.collection(this._collectionPrefix + name)) - .then(rawCollection => new MongoCollection(rawCollection)) - .catch(err => this.handleError(err)); + .then((rawCollection) => new MongoCollection(rawCollection)) + .catch((err) => this.handleError(err)); } _schemaCollection(): Promise { return this.connect() .then(() => this._adaptiveCollection(MongoSchemaCollectionName)) - .then(collection => new MongoSchemaCollection(collection)); + .then((collection) => new MongoSchemaCollection(collection)); } classExists(name: string) { @@ -229,20 +229,20 @@ export class MongoStorageAdapter implements StorageAdapter { .listCollections({ name: this._collectionPrefix + name }) .toArray(); }) - .then(collections => { + .then((collections) => { return collections.length > 0; }) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } setClassLevelPermissions(className: string, CLPs: any): Promise { return this._schemaCollection() - .then(schemaCollection => + .then((schemaCollection) => schemaCollection.updateSchema(className, { $set: { '_metadata.class_permissions': CLPs }, }) ) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } setIndexesWithSchemaFormat( @@ -259,7 +259,7 @@ export class MongoStorageAdapter implements StorageAdapter { } const deletePromises = []; const insertedIndexes = []; - Object.keys(submittedIndexes).forEach(name => { + Object.keys(submittedIndexes).forEach((name) => { const field = submittedIndexes[name]; if (existingIndexes[name] && field.__op !== 'Delete') { throw new Parse.Error( @@ -278,7 +278,7 @@ export class MongoStorageAdapter implements StorageAdapter { deletePromises.push(promise); delete existingIndexes[name]; } else { - Object.keys(field).forEach(key => { + Object.keys(field).forEach((key) => { if (!Object.prototype.hasOwnProperty.call(fields, key)) { throw new Parse.Error( Parse.Error.INVALID_QUERY, @@ -300,17 +300,17 @@ export class MongoStorageAdapter implements StorageAdapter { return Promise.all(deletePromises) .then(() => insertPromise) .then(() => this._schemaCollection()) - .then(schemaCollection => + .then((schemaCollection) => schemaCollection.updateSchema(className, { $set: { '_metadata.indexes': existingIndexes }, }) ) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } setIndexesFromMongo(className: string) { return this.getIndexes(className) - .then(indexes => { + .then((indexes) => { indexes = indexes.reduce((obj, index) => { if (index.key._fts) { delete index.key._fts; @@ -322,13 +322,13 @@ export class MongoStorageAdapter implements StorageAdapter { obj[index.name] = index.key; return obj; }, {}); - return this._schemaCollection().then(schemaCollection => + return this._schemaCollection().then((schemaCollection) => schemaCollection.updateSchema(className, { $set: { '_metadata.indexes': indexes }, }) ); }) - .catch(err => this.handleError(err)) + .catch((err) => this.handleError(err)) .catch(() => { // Ignore if collection not found return Promise.resolve(); @@ -351,8 +351,8 @@ export class MongoStorageAdapter implements StorageAdapter { schema.fields ) .then(() => this._schemaCollection()) - .then(schemaCollection => schemaCollection.insertSchema(mongoObject)) - .catch(err => this.handleError(err)); + .then((schemaCollection) => schemaCollection.insertSchema(mongoObject)) + .catch((err) => this.handleError(err)); } addFieldIfNotExists( @@ -361,11 +361,11 @@ export class MongoStorageAdapter implements StorageAdapter { type: any ): Promise { return this._schemaCollection() - .then(schemaCollection => + .then((schemaCollection) => schemaCollection.addFieldIfNotExists(className, fieldName, type) ) .then(() => this.createIndexesIfNeeded(className, fieldName, type)) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } // Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.) @@ -373,8 +373,8 @@ export class MongoStorageAdapter implements StorageAdapter { deleteClass(className: string) { return ( this._adaptiveCollection(className) - .then(collection => collection.drop()) - .catch(error => { + .then((collection) => collection.drop()) + .catch((error) => { // 'ns not found' means collection was already gone. Ignore deletion attempt. if (error.message == 'ns not found') { return; @@ -383,17 +383,17 @@ export class MongoStorageAdapter implements StorageAdapter { }) // We've dropped the collection, now remove the _SCHEMA document .then(() => this._schemaCollection()) - .then(schemaCollection => + .then((schemaCollection) => schemaCollection.findAndDeleteSchema(className) ) - .catch(err => this.handleError(err)) + .catch((err) => this.handleError(err)) ); } deleteAllClasses(fast: boolean) { - return storageAdapterAllCollections(this).then(collections => + return storageAdapterAllCollections(this).then((collections) => Promise.all( - collections.map(collection => + collections.map((collection) => fast ? collection.deleteMany({}) : collection.drop() ) ) @@ -421,7 +421,7 @@ export class MongoStorageAdapter implements StorageAdapter { // Returns a Promise. deleteFields(className: string, schema: SchemaType, fieldNames: string[]) { - const mongoFormatNames = fieldNames.map(fieldName => { + const mongoFormatNames = fieldNames.map((fieldName) => { if (schema.fields[fieldName].type === 'Pointer') { return `_p_${fieldName}`; } else { @@ -429,23 +429,23 @@ export class MongoStorageAdapter implements StorageAdapter { } }); const collectionUpdate = { $unset: {} }; - mongoFormatNames.forEach(name => { + mongoFormatNames.forEach((name) => { collectionUpdate['$unset'][name] = null; }); const schemaUpdate = { $unset: {} }; - fieldNames.forEach(name => { + fieldNames.forEach((name) => { schemaUpdate['$unset'][name] = null; schemaUpdate['$unset'][`_metadata.fields_options.${name}`] = null; }); return this._adaptiveCollection(className) - .then(collection => collection.updateMany({}, collectionUpdate)) + .then((collection) => collection.updateMany({}, collectionUpdate)) .then(() => this._schemaCollection()) - .then(schemaCollection => + .then((schemaCollection) => schemaCollection.updateSchema(className, schemaUpdate) ) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } // Return a promise for all schemas known to this adapter, in Parse format. In case the @@ -453,10 +453,10 @@ export class MongoStorageAdapter implements StorageAdapter { // rejection reason are TBD. getAllClasses(): Promise { return this._schemaCollection() - .then(schemasCollection => + .then((schemasCollection) => schemasCollection._fetchAllSchemasFrom_SCHEMA() ) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } // Return a promise for the schema with the given name, in Parse format. If @@ -464,10 +464,10 @@ export class MongoStorageAdapter implements StorageAdapter { // undefined as the reason. getClass(className: string): Promise { return this._schemaCollection() - .then(schemasCollection => + .then((schemasCollection) => schemasCollection._fetchOneSchemaFrom_SCHEMA(className) ) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } // TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema, @@ -486,10 +486,10 @@ export class MongoStorageAdapter implements StorageAdapter { schema ); return this._adaptiveCollection(className) - .then(collection => + .then((collection) => collection.insertOne(mongoObject, transactionalSession) ) - .catch(error => { + .catch((error) => { if (error.code === 11000) { // Duplicate value const err = new Parse.Error( @@ -509,7 +509,7 @@ export class MongoStorageAdapter implements StorageAdapter { } throw error; }) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } // Remove all objects that match the given Parse Query. @@ -523,11 +523,11 @@ export class MongoStorageAdapter implements StorageAdapter { ) { schema = convertParseSchemaToMongoSchema(schema); return this._adaptiveCollection(className) - .then(collection => { + .then((collection) => { const mongoWhere = transformWhere(className, query, schema); return collection.deleteMany(mongoWhere, transactionalSession); }) - .catch(err => this.handleError(err)) + .catch((err) => this.handleError(err)) .then( ({ result }) => { if (result.n === 0) { @@ -559,10 +559,10 @@ export class MongoStorageAdapter implements StorageAdapter { const mongoUpdate = transformUpdate(className, update, schema); const mongoWhere = transformWhere(className, query, schema); return this._adaptiveCollection(className) - .then(collection => + .then((collection) => collection.updateMany(mongoWhere, mongoUpdate, transactionalSession) ) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } // Atomically finds and updates an object based on query. @@ -578,14 +578,16 @@ export class MongoStorageAdapter implements StorageAdapter { const mongoUpdate = transformUpdate(className, update, schema); const mongoWhere = transformWhere(className, query, schema); return this._adaptiveCollection(className) - .then(collection => + .then((collection) => collection._mongoCollection.findOneAndUpdate(mongoWhere, mongoUpdate, { returnOriginal: false, session: transactionalSession || undefined, }) ) - .then(result => mongoObjectToParseObject(className, result.value, schema)) - .catch(error => { + .then((result) => + mongoObjectToParseObject(className, result.value, schema) + ) + .catch((error) => { if (error.code === 11000) { throw new Parse.Error( Parse.Error.DUPLICATE_VALUE, @@ -594,7 +596,7 @@ export class MongoStorageAdapter implements StorageAdapter { } throw error; }) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } // Hopefully we can get rid of this. It's only used for config and hooks. @@ -609,10 +611,10 @@ export class MongoStorageAdapter implements StorageAdapter { const mongoUpdate = transformUpdate(className, update, schema); const mongoWhere = transformWhere(className, query, schema); return this._adaptiveCollection(className) - .then(collection => + .then((collection) => collection.upsertOne(mongoWhere, mongoUpdate, transactionalSession) ) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } // Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }. @@ -650,10 +652,17 @@ export class MongoStorageAdapter implements StorageAdapter { {} ); + // If we aren't requesting the `_id` field, we need to explicitly opt out + // of it. Doing so in parse-server is unusual, but it can allow us to + // optimize some queries with covering indexes. + if (keys && !mongoKeys._id) { + mongoKeys._id = 0; + } + readPreference = this._parseReadPreference(readPreference); return this.createTextIndexesIfNeeded(className, query, schema) .then(() => this._adaptiveCollection(className)) - .then(collection => + .then((collection) => collection.find(mongoWhere, { skip, limit, @@ -666,15 +675,15 @@ export class MongoStorageAdapter implements StorageAdapter { explain, }) ) - .then(objects => { + .then((objects) => { if (explain) { return objects; } - return objects.map(object => + return objects.map((object) => mongoObjectToParseObject(className, object, schema) ); }) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } ensureIndex( @@ -687,10 +696,10 @@ export class MongoStorageAdapter implements StorageAdapter { ): Promise { schema = convertParseSchemaToMongoSchema(schema); const indexCreationRequest = {}; - const mongoFieldNames = fieldNames.map(fieldName => + const mongoFieldNames = fieldNames.map((fieldName) => transformKey(className, fieldName, schema) ); - mongoFieldNames.forEach(fieldName => { + mongoFieldNames.forEach((fieldName) => { indexCreationRequest[fieldName] = indexType; }); @@ -707,16 +716,16 @@ export class MongoStorageAdapter implements StorageAdapter { return this._adaptiveCollection(className) .then( - collection => + (collection) => new Promise((resolve, reject) => collection._mongoCollection.createIndex( indexCreationRequest, indexOptions, - error => (error ? reject(error) : resolve()) + (error) => (error ? reject(error) : resolve()) ) ) ) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } // Create a unique index. Unique indexes on nullable fields are not allowed. Since we don't @@ -731,17 +740,17 @@ export class MongoStorageAdapter implements StorageAdapter { ) { schema = convertParseSchemaToMongoSchema(schema); const indexCreationRequest = {}; - const mongoFieldNames = fieldNames.map(fieldName => + const mongoFieldNames = fieldNames.map((fieldName) => transformKey(className, fieldName, schema) ); - mongoFieldNames.forEach(fieldName => { + mongoFieldNames.forEach((fieldName) => { indexCreationRequest[fieldName] = 1; }); return this._adaptiveCollection(className) - .then(collection => + .then((collection) => collection._ensureSparseUniqueIndexInBackground(indexCreationRequest) ) - .catch(error => { + .catch((error) => { if (error.code === 11000) { throw new Parse.Error( Parse.Error.DUPLICATE_VALUE, @@ -750,18 +759,18 @@ export class MongoStorageAdapter implements StorageAdapter { } throw error; }) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } // Used in tests _rawFind(className: string, query: QueryType) { return this._adaptiveCollection(className) - .then(collection => + .then((collection) => collection.find(query, { maxTimeMS: this._maxTimeMS, }) ) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } // Executes a count. @@ -775,14 +784,14 @@ export class MongoStorageAdapter implements StorageAdapter { schema = convertParseSchemaToMongoSchema(schema); readPreference = this._parseReadPreference(readPreference); return this._adaptiveCollection(className) - .then(collection => + .then((collection) => collection.count(transformWhere(className, query, schema, true), { maxTimeMS: this._maxTimeMS, readPreference, hint, }) ) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } distinct( @@ -797,22 +806,22 @@ export class MongoStorageAdapter implements StorageAdapter { const transformField = transformKey(className, fieldName, schema); return this._adaptiveCollection(className) - .then(collection => + .then((collection) => collection.distinct( transformField, transformWhere(className, query, schema) ) ) - .then(objects => { - objects = objects.filter(obj => obj != null); - return objects.map(object => { + .then((objects) => { + objects = objects.filter((obj) => obj != null); + return objects.map((object) => { if (isPointerField) { return transformPointerString(schema, fieldName, object); } return mongoObjectToParseObject(className, object, schema); }); }) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } aggregate( @@ -824,7 +833,7 @@ export class MongoStorageAdapter implements StorageAdapter { explain?: boolean ) { let isPointerField = false; - pipeline = pipeline.map(stage => { + pipeline = pipeline.map((stage) => { if (stage.$group) { stage.$group = this._parseAggregateGroupArgs(schema, stage.$group); if ( @@ -845,13 +854,16 @@ export class MongoStorageAdapter implements StorageAdapter { ); } if (stage.$geoNear) { - stage.$geoNear.query = this._parseAggregateArgs(schema, stage.$geoNear.query); + stage.$geoNear.query = this._parseAggregateArgs( + schema, + stage.$geoNear.query + ); } return stage; }); readPreference = this._parseReadPreference(readPreference); return this._adaptiveCollection(className) - .then(collection => + .then((collection) => collection.aggregate(pipeline, { readPreference, maxTimeMS: this._maxTimeMS, @@ -859,8 +871,8 @@ export class MongoStorageAdapter implements StorageAdapter { explain, }) ) - .then(results => { - results.forEach(result => { + .then((results) => { + results.forEach((result) => { if (Object.prototype.hasOwnProperty.call(result, '_id')) { if (isPointerField && result._id) { result._id = result._id.split('$')[1]; @@ -879,12 +891,12 @@ export class MongoStorageAdapter implements StorageAdapter { }); return results; }) - .then(objects => - objects.map(object => + .then((objects) => + objects.map((object) => mongoObjectToParseObject(className, object, schema) ) ) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } // This function will recursively traverse the pipeline and convert any Pointer or Date columns. @@ -910,7 +922,7 @@ export class MongoStorageAdapter implements StorageAdapter { if (pipeline === null) { return null; } else if (Array.isArray(pipeline)) { - return pipeline.map(value => this._parseAggregateArgs(schema, value)); + return pipeline.map((value) => this._parseAggregateArgs(schema, value)); } else if (typeof pipeline === 'object') { const returnValue = {}; for (const field in pipeline) { @@ -985,7 +997,7 @@ export class MongoStorageAdapter implements StorageAdapter { // updatedAt or objectId and change it accordingly. _parseAggregateGroupArgs(schema: any, pipeline: any): any { if (Array.isArray(pipeline)) { - return pipeline.map(value => + return pipeline.map((value) => this._parseAggregateGroupArgs(schema, value) ); } else if (typeof pipeline === 'object') { @@ -1065,14 +1077,14 @@ export class MongoStorageAdapter implements StorageAdapter { createIndex(className: string, index: any) { return this._adaptiveCollection(className) - .then(collection => collection._mongoCollection.createIndex(index)) - .catch(err => this.handleError(err)); + .then((collection) => collection._mongoCollection.createIndex(index)) + .catch((err) => this.handleError(err)); } createIndexes(className: string, indexes: any) { return this._adaptiveCollection(className) - .then(collection => collection._mongoCollection.createIndexes(indexes)) - .catch(err => this.handleError(err)); + .then((collection) => collection._mongoCollection.createIndexes(indexes)) + .catch((err) => this.handleError(err)); } createIndexesIfNeeded(className: string, fieldName: string, type: any) { @@ -1110,7 +1122,7 @@ export class MongoStorageAdapter implements StorageAdapter { textIndex, existingIndexes, schema.fields - ).catch(error => { + ).catch((error) => { if (error.code === 85) { // Index exist with different options return this.setIndexesFromMongo(className); @@ -1123,31 +1135,31 @@ export class MongoStorageAdapter implements StorageAdapter { getIndexes(className: string) { return this._adaptiveCollection(className) - .then(collection => collection._mongoCollection.indexes()) - .catch(err => this.handleError(err)); + .then((collection) => collection._mongoCollection.indexes()) + .catch((err) => this.handleError(err)); } dropIndex(className: string, index: any) { return this._adaptiveCollection(className) - .then(collection => collection._mongoCollection.dropIndex(index)) - .catch(err => this.handleError(err)); + .then((collection) => collection._mongoCollection.dropIndex(index)) + .catch((err) => this.handleError(err)); } dropAllIndexes(className: string) { return this._adaptiveCollection(className) - .then(collection => collection._mongoCollection.dropIndexes()) - .catch(err => this.handleError(err)); + .then((collection) => collection._mongoCollection.dropIndexes()) + .catch((err) => this.handleError(err)); } updateSchemaWithIndexes(): Promise { return this.getAllClasses() - .then(classes => { - const promises = classes.map(schema => { + .then((classes) => { + const promises = classes.map((schema) => { return this.setIndexesFromMongo(schema.className); }); return Promise.all(promises); }) - .catch(err => this.handleError(err)); + .catch((err) => this.handleError(err)); } createTransactionalSession(): Promise { diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 5c3b8ab342..b56a8d3efc 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -65,7 +65,7 @@ const specialQuerykeys = [ '_failed_login_count', ]; -const isSpecialQueryKey = key => { +const isSpecialQueryKey = (key) => { return specialQuerykeys.indexOf(key) >= 0; }; @@ -107,7 +107,7 @@ const validateQuery = (query: any): void => { } } - Object.keys(query).forEach(key => { + Object.keys(query).forEach((key) => { if (query && query[key] && query[key].$regex) { if (typeof query[key].$options === 'string') { if (!query[key].$options.match(/^[imxs]+$/)) { @@ -149,8 +149,8 @@ const filterSensitiveData = ( if (isReadOperation && perms.protectedFields) { // extract protectedFields added with the pointer-permission prefix const protectedFieldsPointerPerm = Object.keys(perms.protectedFields) - .filter(key => key.startsWith('userField:')) - .map(key => { + .filter((key) => key.startsWith('userField:')) + .map((key) => { return { key: key.substring(10), value: perms.protectedFields[key] }; }); @@ -158,13 +158,13 @@ const filterSensitiveData = ( let overrideProtectedFields = false; // check if the object grants the current user access based on the extracted fields - protectedFieldsPointerPerm.forEach(pointerPerm => { + protectedFieldsPointerPerm.forEach((pointerPerm) => { let pointerPermIncludesUser = false; const readUserFieldValue = object[pointerPerm.key]; if (readUserFieldValue) { if (Array.isArray(readUserFieldValue)) { pointerPermIncludesUser = readUserFieldValue.some( - user => user.objectId && user.objectId === userId + (user) => user.objectId && user.objectId === userId ); } else { pointerPermIncludesUser = @@ -186,14 +186,14 @@ const filterSensitiveData = ( newProtectedFields.push(protectedFields); } // intersect all sets of protectedFields - newProtectedFields.forEach(fields => { + newProtectedFields.forEach((fields) => { if (fields) { // if there're no protctedFields by other criteria ( id / role / auth) // then we must intersect each set (per userField) if (!protectedFields) { protectedFields = fields; } else { - protectedFields = protectedFields.filter(v => fields.includes(v)); + protectedFields = protectedFields.filter((v) => fields.includes(v)); } } }); @@ -205,13 +205,13 @@ const filterSensitiveData = ( /* special treat for the user class: don't filter protectedFields if currently loggedin user is the retrieved user */ if (!(isUserClass && userId && object.objectId === userId)) { - protectedFields && protectedFields.forEach(k => delete object[k]); + protectedFields && protectedFields.forEach((k) => delete object[k]); // fields not requested by client (excluded), //but were needed to apply protecttedFields perms.protectedFields && perms.protectedFields.temporaryKeys && - perms.protectedFields.temporaryKeys.forEach(k => delete object[k]); + perms.protectedFields.temporaryKeys.forEach((k) => delete object[k]); } if (!isUserClass) { @@ -265,7 +265,7 @@ const specialKeysForUpdate = [ '_password_history', ]; -const isSpecialUpdateKey = key => { +const isSpecialUpdateKey = (key) => { return specialKeysForUpdate.indexOf(key) >= 0; }; @@ -291,7 +291,7 @@ function sanitizeDatabaseResult(originalObject, result): Promise { if (!result) { return Promise.resolve(response); } - Object.keys(originalObject).forEach(key => { + Object.keys(originalObject).forEach((key) => { const keyUpdate = originalObject[key]; // determine if that was an op if ( @@ -312,7 +312,7 @@ function joinTableName(className, key) { return `_Join:${key}:${className}`; } -const flattenUpdateOperatorsForCreate = object => { +const flattenUpdateOperatorsForCreate = (object) => { for (const key in object) { if (object[key] && object[key].__op) { switch (object[key].__op) { @@ -367,7 +367,7 @@ const flattenUpdateOperatorsForCreate = object => { const transformAuthData = (className, object, schema) => { if (object.authData && className === '_User') { - Object.keys(object.authData).forEach(provider => { + Object.keys(object.authData).forEach((provider) => { const providerData = object.authData[provider]; const fieldName = `_auth_data_${provider}`; if (providerData == null) { @@ -387,7 +387,7 @@ const untransformObjectACL = ({ _rperm, _wperm, ...output }) => { if (_rperm || _wperm) { output.ACL = {}; - (_rperm || []).forEach(entry => { + (_rperm || []).forEach((entry) => { if (!output.ACL[entry]) { output.ACL[entry] = { read: true }; } else { @@ -395,7 +395,7 @@ const untransformObjectACL = ({ _rperm, _wperm, ...output }) => { } }); - (_wperm || []).forEach(entry => { + (_wperm || []).forEach((entry) => { if (!output.ACL[entry]) { output.ACL[entry] = { write: true }; } else { @@ -442,8 +442,10 @@ class DatabaseController { purgeCollection(className: string): Promise { return this.loadSchema() - .then(schemaController => schemaController.getOneSchema(className)) - .then(schema => this.adapter.deleteObjectsByQuery(className, schema, {})); + .then((schemaController) => schemaController.getOneSchema(className)) + .then((schema) => + this.adapter.deleteObjectsByQuery(className, schema, {}) + ); } validateClassName(className: string): Promise { @@ -490,7 +492,7 @@ class DatabaseController { // classname through the key. // TODO: make this not in the DatabaseController interface redirectClassNameForKey(className: string, key: string): Promise { - return this.loadSchema().then(schema => { + return this.loadSchema().then((schema) => { var t = schema.getExpectedType(className, key); if (t != null && typeof t !== 'string' && t.type === 'Relation') { return t.targetClass; @@ -514,7 +516,7 @@ class DatabaseController { const isMaster = acl === undefined; var aclGroup: string[] = acl || []; return this.loadSchema() - .then(s => { + .then((s) => { schema = s; if (isMaster) { return Promise.resolve(); @@ -550,7 +552,7 @@ class DatabaseController { var aclGroup = acl || []; return this.loadSchemaIfNeeded(validSchemaController).then( - schemaController => { + (schemaController) => { return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, 'update') @@ -594,7 +596,7 @@ class DatabaseController { validateQuery(query); return schemaController .getOneSchema(className, true) - .catch(error => { + .catch((error) => { // If the schema doesn't exist, pretend it exists with no fields. This behavior // will likely need revisiting. if (error === undefined) { @@ -602,8 +604,8 @@ class DatabaseController { } throw error; }) - .then(schema => { - Object.keys(update).forEach(fieldName => { + .then((schema) => { + Object.keys(update).forEach((fieldName) => { if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) { throw new Parse.Error( Parse.Error.INVALID_KEY_NAME, @@ -626,7 +628,7 @@ class DatabaseController { update[updateOperation] && typeof update[updateOperation] === 'object' && Object.keys(update[updateOperation]).some( - innerKey => + (innerKey) => innerKey.includes('$') || innerKey.includes('.') ) ) { @@ -641,7 +643,7 @@ class DatabaseController { if (validateOnly) { return this.adapter .find(className, schema, query, {}) - .then(result => { + .then((result) => { if (!result || !result.length) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, @@ -697,7 +699,7 @@ class DatabaseController { return result; }); }) - .then(result => { + .then((result) => { if (skipSanitization) { return Promise.resolve(result); } @@ -820,7 +822,7 @@ class DatabaseController { doc, this._transactionalSession ) - .catch(error => { + .catch((error) => { // We don't care if they try to delete a non-existent relation. if (error.code == Parse.Error.OBJECT_NOT_FOUND) { return; @@ -846,7 +848,7 @@ class DatabaseController { const aclGroup = acl || []; return this.loadSchemaIfNeeded(validSchemaController).then( - schemaController => { + (schemaController) => { return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, 'delete') @@ -873,7 +875,7 @@ class DatabaseController { validateQuery(query); return schemaController .getOneSchema(className) - .catch(error => { + .catch((error) => { // If the schema doesn't exist, pretend it exists with no fields. This behavior // will likely need revisiting. if (error === undefined) { @@ -881,7 +883,7 @@ class DatabaseController { } throw error; }) - .then(parseFormatSchema => + .then((parseFormatSchema) => this.adapter.deleteObjectsByQuery( className, parseFormatSchema, @@ -889,7 +891,7 @@ class DatabaseController { this._transactionalSession ) ) - .catch(error => { + .catch((error) => { // When deleting sessions while changing passwords, don't throw an error if they don't have any sessions. if ( className === '_Session' && @@ -930,14 +932,14 @@ class DatabaseController { return this.validateClassName(className) .then(() => this.loadSchemaIfNeeded(validSchemaController)) - .then(schemaController => { + .then((schemaController) => { return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, 'create') ) .then(() => schemaController.enforceClassExists(className)) .then(() => schemaController.getOneSchema(className, true)) - .then(schema => { + .then((schema) => { transformAuthData(className, object, schema); flattenUpdateOperatorsForCreate(object); if (validateOnly) { @@ -950,7 +952,7 @@ class DatabaseController { this._transactionalSession ); }) - .then(result => { + .then((result) => { if (validateOnly) { return originalObject; } @@ -979,7 +981,7 @@ class DatabaseController { } const fields = Object.keys(object); const schemaFields = Object.keys(classSchema.fields); - const newKeys = fields.filter(field => { + const newKeys = fields.filter((field) => { // Skip fields that are unset if ( object[field] && @@ -1038,7 +1040,7 @@ class DatabaseController { { owningId }, findOptions ) - .then(results => results.map(result => result.relatedId)); + .then((results) => results.map((result) => result.relatedId)); } // Returns a promise for a list of owning ids given some related ids. @@ -1053,9 +1055,9 @@ class DatabaseController { joinTableName(className, key), relationSchema, { relatedId: { $in: relatedIds } }, - {} + { keys: ['owningId'] } ) - .then(results => results.map(result => result.owningId)); + .then((results) => results.map((result) => result.owningId)); } // Modifies query so that it no longer has $in on relation fields, or @@ -1069,7 +1071,7 @@ class DatabaseController { return Promise.all( ors.map((aQuery, index) => { return this.reduceInRelation(className, aQuery, schema).then( - aQuery => { + (aQuery) => { query['$or'][index] = aQuery; } ); @@ -1079,7 +1081,7 @@ class DatabaseController { }); } - const promises = Object.keys(query).map(key => { + const promises = Object.keys(query).map((key) => { const t = schema.getExpectedType(className, key); if (!t || t.type !== 'Relation') { return Promise.resolve(query); @@ -1093,16 +1095,16 @@ class DatabaseController { query[key].__type == 'Pointer') ) { // Build the list of queries - queries = Object.keys(query[key]).map(constraintKey => { + queries = Object.keys(query[key]).map((constraintKey) => { let relatedIds; let isNegation = false; if (constraintKey === 'objectId') { relatedIds = [query[key].objectId]; } else if (constraintKey == '$in') { - relatedIds = query[key]['$in'].map(r => r.objectId); + relatedIds = query[key]['$in'].map((r) => r.objectId); } else if (constraintKey == '$nin') { isNegation = true; - relatedIds = query[key]['$nin'].map(r => r.objectId); + relatedIds = query[key]['$nin'].map((r) => r.objectId); } else if (constraintKey == '$ne') { isNegation = true; relatedIds = [query[key]['$ne'].objectId]; @@ -1122,11 +1124,11 @@ class DatabaseController { delete query[key]; // execute each query independently to build the list of // $in / $nin - const promises = queries.map(q => { + const promises = queries.map((q) => { if (!q) { return Promise.resolve(); } - return this.owningIds(className, key, q.relatedIds).then(ids => { + return this.owningIds(className, key, q.relatedIds).then((ids) => { if (q.isNegation) { this.addNotInObjectIdsIds(ids, query); } else { @@ -1155,7 +1157,7 @@ class DatabaseController { ): ?Promise { if (query['$or']) { return Promise.all( - query['$or'].map(aQuery => { + query['$or'].map((aQuery) => { return this.reduceRelationKeys(className, aQuery, queryOptions); }) ); @@ -1169,7 +1171,7 @@ class DatabaseController { relatedTo.object.objectId, queryOptions ) - .then(ids => { + .then((ids) => { delete query['$relatedTo']; this.addInObjectIdsIds(ids, query); return this.reduceRelationKeys(className, query, queryOptions); @@ -1192,7 +1194,7 @@ class DatabaseController { idsFromEq, idsFromIn, ids, - ].filter(list => list !== null); + ].filter((list) => list !== null); const totalLength = allIds.reduce((memo, list) => memo + list.length, 0); let idsIntersection = []; @@ -1221,7 +1223,7 @@ class DatabaseController { addNotInObjectIdsIds(ids: string[] = [], query: any) { const idsFromNin = query.objectId && query.objectId['$nin'] ? query.objectId['$nin'] : []; - let allIds = [...idsFromNin, ...ids].filter(list => list !== null); + let allIds = [...idsFromNin, ...ids].filter((list) => list !== null); // make a set and spread to remove duplicates allIds = [...new Set(allIds)]; @@ -1290,13 +1292,13 @@ class DatabaseController { let classExists = true; return this.loadSchemaIfNeeded(validSchemaController).then( - schemaController => { + (schemaController) => { //Allow volatile classes if querying with Master (for _PushStatus) //TODO: Move volatile classes concept into mongo adapter, postgres adapter shouldn't care //that api.parse.com breaks when _PushStatus exists in mongo. return schemaController .getOneSchema(className, isMaster) - .catch(error => { + .catch((error) => { // Behavior for non-existent classes is kinda weird on Parse.com. Probably doesn't matter too much. // For now, pretend the class exists but has no objects, if (error === undefined) { @@ -1305,7 +1307,7 @@ class DatabaseController { } throw error; }) - .then(schema => { + .then((schema) => { // Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt, // so duplicate that behavior here. If both are specified, the correct behavior to match Parse.com is to // use the one that appears first in the sort list. @@ -1327,7 +1329,7 @@ class DatabaseController { caseInsensitive, explain, }; - Object.keys(sort).forEach(fieldName => { + Object.keys(sort).forEach((fieldName) => { if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) { throw new Parse.Error( Parse.Error.INVALID_KEY_NAME, @@ -1439,8 +1441,8 @@ class DatabaseController { } else { return this.adapter .find(className, schema, query, queryOptions) - .then(objects => - objects.map(object => { + .then((objects) => + objects.map((object) => { object = untransformObjectACL(object); return filterSensitiveData( isMaster, @@ -1454,7 +1456,7 @@ class DatabaseController { ); }) ) - .catch(error => { + .catch((error) => { throw new Parse.Error( Parse.Error.INTERNAL_SERVER_ERROR, error @@ -1469,8 +1471,10 @@ class DatabaseController { deleteSchema(className: string): Promise { return this.loadSchema({ clearCache: true }) - .then(schemaController => schemaController.getOneSchema(className, true)) - .catch(error => { + .then((schemaController) => + schemaController.getOneSchema(className, true) + ) + .catch((error) => { if (error === undefined) { return { fields: {} }; } else { @@ -1482,7 +1486,7 @@ class DatabaseController { .then(() => this.adapter.count(className, { fields: {} }, null, '', false) ) - .then(count => { + .then((count) => { if (count > 0) { throw new Parse.Error( 255, @@ -1491,13 +1495,13 @@ class DatabaseController { } return this.adapter.deleteClass(className); }) - .then(wasParseCollection => { + .then((wasParseCollection) => { if (wasParseCollection) { const relationFieldNames = Object.keys(schema.fields).filter( - fieldName => schema.fields[fieldName].type === 'Relation' + (fieldName) => schema.fields[fieldName].type === 'Relation' ); return Promise.all( - relationFieldNames.map(name => + relationFieldNames.map((name) => this.adapter.deleteClass(joinTableName(className, name)) ) ).then(() => { @@ -1529,7 +1533,7 @@ class DatabaseController { } const perms = schema.getClassLevelPermissions(className); - const userACL = aclGroup.filter(acl => { + const userACL = aclGroup.filter((acl) => { return acl.indexOf('role:') != 0 && acl != '*'; }); @@ -1566,7 +1570,7 @@ class DatabaseController { objectId: userId, }; - const ors = permFields.flatMap(key => { + const ors = permFields.flatMap((key) => { // constraint for single pointer setup const q = { [key]: userPointer, @@ -1682,9 +1686,9 @@ class DatabaseController { }, []); // intersect all sets of protectedFields - protectedKeysSets.forEach(fields => { + protectedKeysSets.forEach((fields) => { if (fields) { - protectedKeys = protectedKeys.filter(v => fields.includes(v)); + protectedKeys = protectedKeys.filter((v) => fields.includes(v)); } }); @@ -1694,7 +1698,7 @@ class DatabaseController { createTransactionalSession() { return this.adapter .createTransactionalSession() - .then(transactionalSession => { + .then((transactionalSession) => { this._transactionalSession = transactionalSession; }); } @@ -1737,10 +1741,10 @@ class DatabaseController { }, }; - const userClassPromise = this.loadSchema().then(schema => + const userClassPromise = this.loadSchema().then((schema) => schema.enforceClassExists('_User') ); - const roleClassPromise = this.loadSchema().then(schema => + const roleClassPromise = this.loadSchema().then((schema) => schema.enforceClassExists('_Role') ); @@ -1748,7 +1752,7 @@ class DatabaseController { .then(() => this.adapter.ensureUniqueness('_User', requiredUserFields, ['username']) ) - .catch(error => { + .catch((error) => { logger.warn('Unable to ensure uniqueness for usernames: ', error); throw error; }); @@ -1763,7 +1767,7 @@ class DatabaseController { true ) ) - .catch(error => { + .catch((error) => { logger.warn( 'Unable to create case insensitive username index: ', error @@ -1775,7 +1779,7 @@ class DatabaseController { .then(() => this.adapter.ensureUniqueness('_User', requiredUserFields, ['email']) ) - .catch(error => { + .catch((error) => { logger.warn( 'Unable to ensure uniqueness for user email addresses: ', error @@ -1793,7 +1797,7 @@ class DatabaseController { true ) ) - .catch(error => { + .catch((error) => { logger.warn('Unable to create case insensitive email index: ', error); throw error; }); @@ -1802,7 +1806,7 @@ class DatabaseController { .then(() => this.adapter.ensureUniqueness('_Role', requiredRoleFields, ['name']) ) - .catch(error => { + .catch((error) => { logger.warn('Unable to ensure uniqueness for role name: ', error); throw error; }); @@ -1824,7 +1828,7 @@ class DatabaseController { ]); } - static _validateQuery: any => void; + static _validateQuery: (any) => void; } module.exports = DatabaseController;