From 87198202767c677c61dfd7249559b1e299a9e6e5 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 10:11:09 -0700 Subject: [PATCH 01/17] Break dependency on MongoCollection for updateMany --- src/Adapters/Storage/Mongo/MongoStorageAdapter.js | 5 +++++ src/Controllers/DatabaseController.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index c9bb6d6706..e5daba8ed5 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -197,6 +197,11 @@ export class MongoStorageAdapter { }); } + updateObjectsByQuery(className, query, schema, mongoWhere, mongoUpdate) { + return this.adaptiveCollection(className) + .then(collection => collection.updateMany(mongoWhere, mongoUpdate)); + } + // Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }. find(className, query, schema, { skip, limit, sort }) { let mongoWhere = this.transform.transformWhere(className, query, schema); diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 76fc5589cc..e063805508 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -218,7 +218,7 @@ DatabaseController.prototype.update = function(className, query, update, { {validate: !this.skipValidation} ); if (many) { - return collection.updateMany(mongoWhere, mongoUpdate); + return this.adapter.updateObjectsByQuery(className, query, parseFormatSchema, mongoWhere, mongoUpdate); } else if (upsert) { return collection.upsertOne(mongoWhere, mongoUpdate); } else { From 41f2434a9d8967d395ef64f8222f08018003677c Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 10:44:28 -0700 Subject: [PATCH 02/17] Move transformWhere usage into MongoTransform --- src/Adapters/Storage/Mongo/MongoStorageAdapter.js | 8 ++++---- src/Controllers/DatabaseController.js | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index e5daba8ed5..2f2438117a 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -176,11 +176,9 @@ export class MongoStorageAdapter { }); } - // Remove all objects that match the given parse query. Parse Query should be in Parse Format. + // Remove all objects that match the given Parse Query. // If no objects match, reject with OBJECT_NOT_FOUND. If objects are found and deleted, resolve with undefined. // If there is some other error, reject with INTERNAL_SERVER_ERROR. - - // Currently accepts the schema, that may not actually be necessary. deleteObjectsByQuery(className, query, schema) { return this.adaptiveCollection(className) .then(collection => { @@ -197,7 +195,9 @@ export class MongoStorageAdapter { }); } - updateObjectsByQuery(className, query, schema, mongoWhere, mongoUpdate) { + // Apply the update to all objects that match the given Parse Query. + updateObjectsByQuery(className, query, schema, mongoUpdate) { + const mongoWhere = transform.transformWhere(className, query, schema); return this.adaptiveCollection(className) .then(collection => collection.updateMany(mongoWhere, mongoUpdate)); } diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index e063805508..fa0e4e0c13 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -210,7 +210,6 @@ DatabaseController.prototype.update = function(className, query, update, { throw error; }) .then(parseFormatSchema => { - var mongoWhere = this.transform.transformWhere(className, query, parseFormatSchema); mongoUpdate = this.transform.transformUpdate( schemaController, className, @@ -218,10 +217,12 @@ DatabaseController.prototype.update = function(className, query, update, { {validate: !this.skipValidation} ); if (many) { - return this.adapter.updateObjectsByQuery(className, query, parseFormatSchema, mongoWhere, mongoUpdate); + return this.adapter.updateObjectsByQuery(className, query, parseFormatSchema, mongoUpdate); } else if (upsert) { + var mongoWhere = this.transform.transformWhere(className, query, parseFormatSchema); return collection.upsertOne(mongoWhere, mongoUpdate); } else { + var mongoWhere = this.transform.transformWhere(className, query, parseFormatSchema); return collection.findOneAndUpdate(mongoWhere, mongoUpdate); } }); From a6130efd83b8553fccbf7b20e2cdca78e494edbb Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 10:49:15 -0700 Subject: [PATCH 03/17] Pass parse schema into transformUpdate --- src/Adapters/Storage/Mongo/MongoTransform.js | 6 +++--- src/Controllers/DatabaseController.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 5b7cc1e3d2..973957f254 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -19,7 +19,7 @@ const transformKey = (className, fieldName, schema) => { return fieldName; } -const transformKeyValueForUpdate = (schema, className, restKey, restValue) => { +const transformKeyValueForUpdate = (schema, className, restKey, restValue, parseFormatSchema) => { // Check if the schema is known since it's a built-in field. var key = restKey; var timeField = false; @@ -326,7 +326,7 @@ function parseObjectToMongoObjectForCreate(schema, className, restCreate, parseF } // Main exposed method to help update old objects. -function transformUpdate(schema, className, restUpdate) { +function transformUpdate(schema, className, restUpdate, parseFormatSchema) { if (!restUpdate) { throw 'got empty restUpdate'; } @@ -350,7 +350,7 @@ function transformUpdate(schema, className, restUpdate) { } for (var restKey in restUpdate) { - var out = transformKeyValueForUpdate(schema, className, restKey, restUpdate[restKey]); + var out = transformKeyValueForUpdate(schema, className, restKey, restUpdate[restKey], parseFormatSchema); // If the output value is an object with any $ keys, it's an // operator that needs to be lifted onto the top level update diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index fa0e4e0c13..2264dc04a6 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -214,7 +214,7 @@ DatabaseController.prototype.update = function(className, query, update, { schemaController, className, update, - {validate: !this.skipValidation} + parseFormatSchema ); if (many) { return this.adapter.updateObjectsByQuery(className, query, parseFormatSchema, mongoUpdate); From f98085664bb336153f8e5666fb51cc1ac238745b Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 11:31:41 -0700 Subject: [PATCH 04/17] break dependency on schemaController --- src/Adapters/Storage/Mongo/MongoTransform.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 973957f254..76a709577c 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -69,14 +69,7 @@ const transformKeyValueForUpdate = (schema, className, restKey, restValue, parse } } - // Handle special schema key changes - // TODO: it seems like this is likely to have edge cases where - // pointer types are missed - var expected = undefined; - if (schema && schema.getExpectedType) { - expected = schema.getExpectedType(className, key); - } - if ((expected && expected.type == 'Pointer') || (!expected && restValue && restValue.__type == 'Pointer')) { + if ((parseFormatSchema.fields[key] && parseFormatSchema.fields[key].type === 'Pointer') || (!parseFormatSchema.fields[key] && restValue && restValue.__type == 'Pointer')) { key = '_p_' + key; } From e95cd2f78fc4f56e4ebd1eba9d9df3f03c17144b Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 11:40:02 -0700 Subject: [PATCH 05/17] remove schema parameter --- src/Adapters/Storage/Mongo/MongoTransform.js | 6 +++--- src/Controllers/DatabaseController.js | 7 +------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 76a709577c..e395d2b4db 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -19,7 +19,7 @@ const transformKey = (className, fieldName, schema) => { return fieldName; } -const transformKeyValueForUpdate = (schema, className, restKey, restValue, parseFormatSchema) => { +const transformKeyValueForUpdate = (className, restKey, restValue, parseFormatSchema) => { // Check if the schema is known since it's a built-in field. var key = restKey; var timeField = false; @@ -319,7 +319,7 @@ function parseObjectToMongoObjectForCreate(schema, className, restCreate, parseF } // Main exposed method to help update old objects. -function transformUpdate(schema, className, restUpdate, parseFormatSchema) { +const transformUpdate = (className, restUpdate, parseFormatSchema) => { if (!restUpdate) { throw 'got empty restUpdate'; } @@ -343,7 +343,7 @@ function transformUpdate(schema, className, restUpdate, parseFormatSchema) { } for (var restKey in restUpdate) { - var out = transformKeyValueForUpdate(schema, className, restKey, restUpdate[restKey], parseFormatSchema); + var out = transformKeyValueForUpdate(className, restKey, restUpdate[restKey], parseFormatSchema); // If the output value is an object with any $ keys, it's an // operator that needs to be lifted onto the top level update diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 2264dc04a6..7e01df23da 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -210,12 +210,7 @@ DatabaseController.prototype.update = function(className, query, update, { throw error; }) .then(parseFormatSchema => { - mongoUpdate = this.transform.transformUpdate( - schemaController, - className, - update, - parseFormatSchema - ); + mongoUpdate = this.transform.transformUpdate(className, update, parseFormatSchema); if (many) { return this.adapter.updateObjectsByQuery(className, query, parseFormatSchema, mongoUpdate); } else if (upsert) { From d166801afb9dfe84d7ce5f0f8008f566b0bad961 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 11:53:07 -0700 Subject: [PATCH 06/17] move key name validation up one level --- src/Adapters/Storage/Mongo/MongoTransform.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index e395d2b4db..701fe285fc 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -94,9 +94,6 @@ const transformKeyValueForUpdate = (className, restKey, restValue, parseFormatSc } // Handle normal objects by recursing - if (Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))) { - throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters"); - } value = _.mapValues(restValue, transformInteriorValue); return {key, value}; } @@ -343,6 +340,9 @@ const transformUpdate = (className, restUpdate, parseFormatSchema) => { } for (var restKey in restUpdate) { + if (Object.keys(restUpdate[restKey]).some(innerKey => innerKey.includes('$') || innerKey.includes('.'))) { + throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters"); + } var out = transformKeyValueForUpdate(className, restKey, restUpdate[restKey], parseFormatSchema); // If the output value is an object with any $ keys, it's an From e70cd8b1383e8c2e2c8c00b7c87772fca41d8e73 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 12:02:32 -0700 Subject: [PATCH 07/17] Move validation out of mongo adapter --- src/Adapters/Storage/Mongo/MongoTransform.js | 7 ------- src/Controllers/DatabaseController.js | 5 +++++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 701fe285fc..d56da5a993 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -317,9 +317,6 @@ function parseObjectToMongoObjectForCreate(schema, className, restCreate, parseF // Main exposed method to help update old objects. const transformUpdate = (className, restUpdate, parseFormatSchema) => { - if (!restUpdate) { - throw 'got empty restUpdate'; - } if (className == '_User') { restUpdate = transformAuthData(restUpdate); } @@ -338,11 +335,7 @@ const transformUpdate = (className, restUpdate, parseFormatSchema) => { mongoUpdate['$set']['_acl'] = acl._acl; } } - for (var restKey in restUpdate) { - if (Object.keys(restUpdate[restKey]).some(innerKey => innerKey.includes('$') || innerKey.includes('.'))) { - throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters"); - } var out = transformKeyValueForUpdate(className, restKey, restUpdate[restKey], parseFormatSchema); // If the output value is an object with any $ keys, it's an diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 7e01df23da..97af266c29 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -210,6 +210,11 @@ DatabaseController.prototype.update = function(className, query, update, { throw error; }) .then(parseFormatSchema => { + for (let updateOperation in update) { + if (Object.keys(updateOperation).some(innerKey => innerKey.includes('$') || innerKey.includes('.'))) { + throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters"); + } + } mongoUpdate = this.transform.transformUpdate(className, update, parseFormatSchema); if (many) { return this.adapter.updateObjectsByQuery(className, query, parseFormatSchema, mongoUpdate); From 8c32c4f0503a28596a11880dc4940d53b9f14e76 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 13:30:19 -0700 Subject: [PATCH 08/17] Move validation into Parse Server and transformUpdate in Mongo Adapter --- .../Storage/Mongo/MongoStorageAdapter.js | 3 ++- src/Adapters/Storage/Mongo/MongoTransform.js | 16 ---------------- src/Controllers/DatabaseController.js | 15 +++++++++++++-- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 2f2438117a..2f468ec8a1 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -196,7 +196,8 @@ export class MongoStorageAdapter { } // Apply the update to all objects that match the given Parse Query. - updateObjectsByQuery(className, query, schema, mongoUpdate) { + updateObjectsByQuery(className, query, schema, update) { + const mongoUpdate = transform.transformUpdate(className, update, schema); const mongoWhere = transform.transformWhere(className, query, schema); return this.adaptiveCollection(className) .then(collection => collection.updateMany(mongoWhere, mongoUpdate)); diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index d56da5a993..75189cf8a7 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -38,12 +38,6 @@ const transformKeyValueForUpdate = (className, restKey, restValue, parseFormatSc key = '_updated_at'; timeField = true; break; - case '_email_verify_token': - key = "_email_verify_token"; - break; - case '_perishable_token': - key = "_perishable_token"; - break; case 'sessionToken': case '_session_token': key = '_session_token'; @@ -57,16 +51,6 @@ const transformKeyValueForUpdate = (className, restKey, restValue, parseFormatSc case '_wperm': return {key: key, value: restValue}; break; - case '$or': - throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'you can only use $or in queries'); - case '$and': - throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'you can only use $and in queries'); - default: - // Other auth data - var authDataMatch = key.match(/^authData\.([a-zA-Z0-9_]+)\.id$/); - if (authDataMatch) { - throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'can only query on ' + key); - } } if ((parseFormatSchema.fields[key] && parseFormatSchema.fields[key].type === 'Pointer') || (!parseFormatSchema.fields[key] && restValue && restValue.__type == 'Pointer')) { diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 97af266c29..11315b4ee5 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -171,6 +171,7 @@ const filterSensitiveData = (isMaster, aclGroup, className, object) => { // acl: a list of strings. If the object to be updated has an ACL, // one of the provided strings must provide the caller with // write permissions. +const specialKeysForUpdate = ['_hashed_password', '_perishable_token', '_email_verify_token']; DatabaseController.prototype.update = function(className, query, update, { acl, many, @@ -210,18 +211,28 @@ DatabaseController.prototype.update = function(className, query, update, { throw error; }) .then(parseFormatSchema => { + Object.keys(update).forEach(fieldName => { + if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) { + throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name for update: ${fieldName}`); + } + fieldName = fieldName.split('.')[0]; + if (!SchemaController.fieldNameIsValid(fieldName) && !specialKeysForUpdate.includes(fieldName)) { + throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name for update: ${fieldName}`); + } + }); for (let updateOperation in update) { if (Object.keys(updateOperation).some(innerKey => innerKey.includes('$') || innerKey.includes('.'))) { throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters"); } } - mongoUpdate = this.transform.transformUpdate(className, update, parseFormatSchema); if (many) { - return this.adapter.updateObjectsByQuery(className, query, parseFormatSchema, mongoUpdate); + return this.adapter.updateObjectsByQuery(className, query, parseFormatSchema, update); } else if (upsert) { + var mongoUpdate = this.transform.transformUpdate(className, update, parseFormatSchema); var mongoWhere = this.transform.transformWhere(className, query, parseFormatSchema); return collection.upsertOne(mongoWhere, mongoUpdate); } else { + var mongoUpdate = this.transform.transformUpdate(className, update, parseFormatSchema); var mongoWhere = this.transform.transformWhere(className, query, parseFormatSchema); return collection.findOneAndUpdate(mongoWhere, mongoUpdate); } From 8e7809f740fc21d8c66e5cf7118c533cd26bf693 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 15:17:51 -0700 Subject: [PATCH 09/17] Update mongo adapter --- .../Storage/Mongo/MongoStorageAdapter.js | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 2f468ec8a1..1c8bdd788d 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -163,8 +163,8 @@ export class MongoStorageAdapter { // schemaController, but MongoTransform still needs it :( maybe shouldn't even need the schema, // and should infer from the type. Or maybe does need the schema for validations. Or maybe needs // the schem only for the legacy mongo format. We'll figure that out later. - createObject(className, object, schemaController, parseFormatSchema) { - const mongoObject = transform.parseObjectToMongoObjectForCreate(schemaController, className, object, parseFormatSchema); + createObject(className, object, schemaController, schema) { + const mongoObject = transform.parseObjectToMongoObjectForCreate(schemaController, className, object, schema); return this.adaptiveCollection(className) .then(collection => collection.insertOne(mongoObject)) .catch(error => { @@ -203,6 +203,22 @@ export class MongoStorageAdapter { .then(collection => collection.updateMany(mongoWhere, mongoUpdate)); } + // Hopefully we can get rid of this in favor of updateObjectsByQuery. + findOneAndUpdate(className, query, schema, update) { + const mongoUpdate = transform.transformUpdate(className, update, schema); + const mongoWhere = transform.transformWhere(className, query, schema); + return this.adaptiveCollection(className) + .then(collection => collection.findOneAndUpdate()); + } + + // Hopefully we can get rid of this. It's only used for config and hooks. + upsertOneObject(className, query, schema, update) { + const mongoUpdate = this.transform.transformUpdate(className, update, schema); + const mongoWhere = this.transform.transformWhere(className, query, schema); + return this.adaptiveCollection(className) + .then(collection => collection.upsertOne(mongoWhere, mongoUpdate)); + } + // Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }. find(className, query, schema, { skip, limit, sort }) { let mongoWhere = this.transform.transformWhere(className, query, schema); From 6d8a47a3b50069c3ee29d7be762eb477e4ea793d Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 15:21:59 -0700 Subject: [PATCH 10/17] Use adapter API --- src/Controllers/DatabaseController.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 11315b4ee5..8923e0309e 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -228,9 +228,7 @@ DatabaseController.prototype.update = function(className, query, update, { if (many) { return this.adapter.updateObjectsByQuery(className, query, parseFormatSchema, update); } else if (upsert) { - var mongoUpdate = this.transform.transformUpdate(className, update, parseFormatSchema); - var mongoWhere = this.transform.transformWhere(className, query, parseFormatSchema); - return collection.upsertOne(mongoWhere, mongoUpdate); + return this.adapter.upsertOneObject(className, query, parseFormatSchema, update); } else { var mongoUpdate = this.transform.transformUpdate(className, update, parseFormatSchema); var mongoWhere = this.transform.transformWhere(className, query, parseFormatSchema); From 2a321e59fa6499cc851729e43170dc5e040bc13c Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 15:24:56 -0700 Subject: [PATCH 11/17] use and fix mongo adapter api --- src/Adapters/Storage/Mongo/MongoStorageAdapter.js | 2 +- src/Controllers/DatabaseController.js | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 1c8bdd788d..55decf13e2 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -208,7 +208,7 @@ export class MongoStorageAdapter { const mongoUpdate = transform.transformUpdate(className, update, schema); const mongoWhere = transform.transformWhere(className, query, schema); return this.adaptiveCollection(className) - .then(collection => collection.findOneAndUpdate()); + .then(collection => collection.findOneAndUpdate(mongoWhere, mongoUpdate)); } // Hopefully we can get rid of this. It's only used for config and hooks. diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 8923e0309e..47be5692e7 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -230,9 +230,7 @@ DatabaseController.prototype.update = function(className, query, update, { } else if (upsert) { return this.adapter.upsertOneObject(className, query, parseFormatSchema, update); } else { - var mongoUpdate = this.transform.transformUpdate(className, update, parseFormatSchema); - var mongoWhere = this.transform.transformWhere(className, query, parseFormatSchema); - return collection.findOneAndUpdate(mongoWhere, mongoUpdate); + return this.adapter.findOneAndUpdate(className, query, parseFormatSchema, update); } }); }) From 4108486db5b70086039f6156258b231bc59f0b60 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 15:27:42 -0700 Subject: [PATCH 12/17] Remove/rename stuff --- src/Controllers/DatabaseController.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 47be5692e7..a6acb69807 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -189,8 +189,7 @@ DatabaseController.prototype.update = function(className, query, update, { .then(schemaController => { return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, 'update')) .then(() => this.handleRelationUpdates(className, query.objectId, update)) - .then(() => this.adapter.adaptiveCollection(className)) - .then(collection => { + .then(() => { if (!isMaster) { query = this.addPointerPermissions(schemaController, className, 'update', query, aclGroup); } @@ -210,7 +209,7 @@ DatabaseController.prototype.update = function(className, query, update, { } throw error; }) - .then(parseFormatSchema => { + .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, `Invalid field name for update: ${fieldName}`); @@ -226,11 +225,11 @@ DatabaseController.prototype.update = function(className, query, update, { } } if (many) { - return this.adapter.updateObjectsByQuery(className, query, parseFormatSchema, update); + return this.adapter.updateObjectsByQuery(className, query, schema, update); } else if (upsert) { - return this.adapter.upsertOneObject(className, query, parseFormatSchema, update); + return this.adapter.upsertOneObject(className, query, schema, update); } else { - return this.adapter.findOneAndUpdate(className, query, parseFormatSchema, update); + return this.adapter.findOneAndUpdate(className, query, schema, update); } }); }) From 7af2ada9a624d1b53f7a11345406add584bb093f Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 15:41:55 -0700 Subject: [PATCH 13/17] Kill transform in DBController --- src/Adapters/Storage/Mongo/MongoStorageAdapter.js | 10 +++------- src/Controllers/DatabaseController.js | 6 ------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 55decf13e2..f0043cde4c 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -213,15 +213,15 @@ export class MongoStorageAdapter { // Hopefully we can get rid of this. It's only used for config and hooks. upsertOneObject(className, query, schema, update) { - const mongoUpdate = this.transform.transformUpdate(className, update, schema); - const mongoWhere = this.transform.transformWhere(className, query, schema); + const mongoUpdate = transform.transformUpdate(className, update, schema); + const mongoWhere = transform.transformWhere(className, query, schema); return this.adaptiveCollection(className) .then(collection => collection.upsertOne(mongoWhere, mongoUpdate)); } // Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }. find(className, query, schema, { skip, limit, sort }) { - let mongoWhere = this.transform.transformWhere(className, query, schema); + let mongoWhere = transform.transformWhere(className, query, schema); let mongoSort = _.mapKeys(sort, (value, fieldName) => transform.transformKey(className, fieldName, schema)); return this.adaptiveCollection(className) .then(collection => collection.find(mongoWhere, { skip, limit, sort: mongoSort })) @@ -233,10 +233,6 @@ export class MongoStorageAdapter { return this.adaptiveCollection(className) .then(collection => collection.count(transform.transformWhere(className, query, schema))); } - - get transform() { - return transform; - } } export default MongoStorageAdapter; diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index a6acb69807..2a0049fef6 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -62,12 +62,6 @@ function DatabaseController(adapter, { skipValidation } = {}) { this.schemaPromise = null; this.skipValidation = !!skipValidation; this.connect(); - - Object.defineProperty(this, 'transform', { - get: function() { - return adapter.transform; - } - }) } DatabaseController.prototype.WithoutValidation = function() { From 015fb608ec9c5bb84809ac5f281974918df03364 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 15:49:04 -0700 Subject: [PATCH 14/17] better imports for transform --- .../Storage/Mongo/MongoStorageAdapter.js | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index f0043cde4c..694ce283fc 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -1,8 +1,17 @@ -import MongoCollection from './MongoCollection'; -import MongoSchemaCollection from './MongoSchemaCollection'; -import {parse as parseUrl, format as formatUrl} from '../../../vendor/mongodbUrl'; -import * as transform from './MongoTransform'; -import _ from 'lodash'; +import MongoCollection from './MongoCollection'; +import MongoSchemaCollection from './MongoSchemaCollection'; +import { + parse as parseUrl, + format as formatUrl, +} from '../../../vendor/mongodbUrl'; +import { + parseObjectToMongoObjectForCreate, + mongoObjectToParseObject, + transformKey, + transformWhere, + transformUpdate, +} from './MongoTransform'; +import _ from 'lodash'; let mongodb = require('mongodb'); let MongoClient = mongodb.MongoClient; @@ -164,7 +173,7 @@ export class MongoStorageAdapter { // and should infer from the type. Or maybe does need the schema for validations. Or maybe needs // the schem only for the legacy mongo format. We'll figure that out later. createObject(className, object, schemaController, schema) { - const mongoObject = transform.parseObjectToMongoObjectForCreate(schemaController, className, object, schema); + const mongoObject = parseObjectToMongoObjectForCreate(schemaController, className, object, schema); return this.adaptiveCollection(className) .then(collection => collection.insertOne(mongoObject)) .catch(error => { @@ -182,7 +191,7 @@ export class MongoStorageAdapter { deleteObjectsByQuery(className, query, schema) { return this.adaptiveCollection(className) .then(collection => { - let mongoWhere = transform.transformWhere(className, query, schema); + let mongoWhere = transformWhere(className, query, schema); return collection.deleteMany(mongoWhere) }) .then(({ result }) => { @@ -197,41 +206,41 @@ export class MongoStorageAdapter { // Apply the update to all objects that match the given Parse Query. updateObjectsByQuery(className, query, schema, update) { - const mongoUpdate = transform.transformUpdate(className, update, schema); - const mongoWhere = transform.transformWhere(className, query, schema); + const mongoUpdate = transformUpdate(className, update, schema); + const mongoWhere = transformWhere(className, query, schema); return this.adaptiveCollection(className) .then(collection => collection.updateMany(mongoWhere, mongoUpdate)); } // Hopefully we can get rid of this in favor of updateObjectsByQuery. findOneAndUpdate(className, query, schema, update) { - const mongoUpdate = transform.transformUpdate(className, update, schema); - const mongoWhere = transform.transformWhere(className, query, schema); + const mongoUpdate = transformUpdate(className, update, schema); + const mongoWhere = transformWhere(className, query, schema); return this.adaptiveCollection(className) .then(collection => collection.findOneAndUpdate(mongoWhere, mongoUpdate)); } // Hopefully we can get rid of this. It's only used for config and hooks. upsertOneObject(className, query, schema, update) { - const mongoUpdate = transform.transformUpdate(className, update, schema); - const mongoWhere = transform.transformWhere(className, query, schema); + const mongoUpdate = transformUpdate(className, update, schema); + const mongoWhere = transformWhere(className, query, schema); return this.adaptiveCollection(className) .then(collection => collection.upsertOne(mongoWhere, mongoUpdate)); } // Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }. find(className, query, schema, { skip, limit, sort }) { - let mongoWhere = transform.transformWhere(className, query, schema); - let mongoSort = _.mapKeys(sort, (value, fieldName) => transform.transformKey(className, fieldName, schema)); + let mongoWhere = transformWhere(className, query, schema); + let mongoSort = _.mapKeys(sort, (value, fieldName) => transformKey(className, fieldName, schema)); return this.adaptiveCollection(className) .then(collection => collection.find(mongoWhere, { skip, limit, sort: mongoSort })) - .then(objects => objects.map(object => transform.mongoObjectToParseObject(className, object, schema))); + .then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema))); } // Executs a count. count(className, query, schema) { return this.adaptiveCollection(className) - .then(collection => collection.count(transform.transformWhere(className, query, schema))); + .then(collection => collection.count(transformWhere(className, query, schema))); } } From 20e261a1dcc51bdd95b34f207d45a9ca482ad6b3 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 16:03:03 -0700 Subject: [PATCH 15/17] Tidy ConfigRouter --- src/Routers/GlobalConfigRouter.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Routers/GlobalConfigRouter.js b/src/Routers/GlobalConfigRouter.js index 5ab89b0b61..f876ab0bbf 100644 --- a/src/Routers/GlobalConfigRouter.js +++ b/src/Routers/GlobalConfigRouter.js @@ -24,9 +24,7 @@ export class GlobalConfigRouter extends PromiseRouter { return acc; }, {}); let database = req.config.database.WithoutValidation(); - return database.update('_GlobalConfig', {objectId: 1}, update, {upsert: true}).then(() => { - return Promise.resolve({ response: { result: true } }); - }); + return database.update('_GlobalConfig', {objectId: 1}, update, {upsert: true}).then(() => ({ response: { result: true } })); } mountRoutes() { From 56143943e762b6f1aede2ca0a859ff401a3c9be5 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 16:21:27 -0700 Subject: [PATCH 16/17] Remove schemaController in more places --- spec/MongoTransform.spec.js | 39 ++++++------------- .../Storage/Mongo/MongoStorageAdapter.js | 7 ++-- src/Adapters/Storage/Mongo/MongoTransform.js | 15 ++----- src/Controllers/DatabaseController.js | 2 +- src/Routers/GlobalConfigRouter.js | 7 ++++ 5 files changed, 27 insertions(+), 43 deletions(-) diff --git a/spec/MongoTransform.spec.js b/spec/MongoTransform.spec.js index f667a2eee4..cb5baf7758 100644 --- a/spec/MongoTransform.spec.js +++ b/spec/MongoTransform.spec.js @@ -5,26 +5,11 @@ let transform = require('../src/Adapters/Storage/Mongo/MongoTransform'); let dd = require('deep-diff'); let mongodb = require('mongodb'); -var dummySchema = { - data: {}, - getExpectedType: function(className, key) { - if (key == 'userPointer') { - return { type: 'Pointer', targetClass: '_User' }; - } else if (key == 'picture') { - return { type: 'File' }; - } else if (key == 'location') { - return { type: 'GeoPoint' }; - } - return; - }, -}; - - describe('parseObjectToMongoObjectForCreate', () => { it('a basic number', (done) => { var input = {five: 5}; - var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { + var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {five: {type: 'Number'}} }); jequal(input, output); @@ -36,7 +21,7 @@ describe('parseObjectToMongoObjectForCreate', () => { createdAt: "2015-10-06T21:24:50.332Z", updatedAt: "2015-10-06T21:24:50.332Z" }; - var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} }); + var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} }); expect(output._created_at instanceof Date).toBe(true); expect(output._updated_at instanceof Date).toBe(true); done(); @@ -48,7 +33,7 @@ describe('parseObjectToMongoObjectForCreate', () => { objectId: 'myId', className: 'Blah', }; - var out = transform.parseObjectToMongoObjectForCreate(dummySchema, null, {pointers: [pointer]},{ + var out = transform.parseObjectToMongoObjectForCreate(null, {pointers: [pointer]},{ fields: {pointers: {type: 'Array'}} }); jequal([pointer], out.pointers); @@ -59,14 +44,14 @@ describe('parseObjectToMongoObjectForCreate', () => { //have __op delete in a new object. Figure out what this should actually be testing. notWorking('a delete op', (done) => { var input = {deleteMe: {__op: 'Delete'}}; - var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} }); + var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} }); jequal(output, {}); done(); }); it('basic ACL', (done) => { var input = {ACL: {'0123': {'read': true, 'write': true}}}; - var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} }); + var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} }); // This just checks that it doesn't crash, but it should check format. done(); }); @@ -74,7 +59,7 @@ describe('parseObjectToMongoObjectForCreate', () => { describe('GeoPoints', () => { it('plain', (done) => { var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180}; - var out = transform.parseObjectToMongoObjectForCreate(dummySchema, null, {location: geoPoint},{ + var out = transform.parseObjectToMongoObjectForCreate(null, {location: geoPoint},{ fields: {location: {type: 'GeoPoint'}} }); expect(out.location).toEqual([180, -180]); @@ -83,7 +68,7 @@ describe('parseObjectToMongoObjectForCreate', () => { it('in array', (done) => { var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180}; - var out = transform.parseObjectToMongoObjectForCreate(dummySchema, null, {locations: [geoPoint, geoPoint]},{ + var out = transform.parseObjectToMongoObjectForCreate(null, {locations: [geoPoint, geoPoint]},{ fields: {locations: {type: 'Array'}} }); expect(out.locations).toEqual([geoPoint, geoPoint]); @@ -92,7 +77,7 @@ describe('parseObjectToMongoObjectForCreate', () => { it('in sub-object', (done) => { var geoPoint = {__type: 'GeoPoint', longitude: 180, latitude: -180}; - var out = transform.parseObjectToMongoObjectForCreate(dummySchema, null, { locations: { start: geoPoint }},{ + var out = transform.parseObjectToMongoObjectForCreate(null, { locations: { start: geoPoint }},{ fields: {locations: {type: 'Object'}} }); expect(out).toEqual({ locations: { start: geoPoint } }); @@ -206,7 +191,7 @@ describe('transform schema key changes', () => { var input = { somePointer: {__type: 'Pointer', className: 'Micro', objectId: 'oft'} }; - var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { + var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {somePointer: {type: 'Pointer'}} }); expect(typeof output._p_somePointer).toEqual('string'); @@ -218,7 +203,7 @@ describe('transform schema key changes', () => { var input = { userPointer: {__type: 'Pointer', className: '_User', objectId: 'qwerty'} }; - var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { + var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {userPointer: {type: 'Pointer'}} }); expect(typeof output._p_userPointer).toEqual('string'); @@ -233,7 +218,7 @@ describe('transform schema key changes', () => { "Kevin": { "write": true } } }; - var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} }); + var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} }); expect(typeof output._rperm).toEqual('object'); expect(typeof output._wperm).toEqual('object'); expect(output.ACL).toBeUndefined(); @@ -250,7 +235,7 @@ describe('transform schema key changes', () => { } }; - var output = transform.parseObjectToMongoObjectForCreate(dummySchema, null, input, { fields: {} }); + var output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} }); expect(typeof output._acl).toEqual('object'); expect(output._acl["Kevin"].w).toBeTruthy(); expect(output._acl["Kevin"].r).toBeUndefined(); diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 694ce283fc..e62e9e30ed 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -168,12 +168,11 @@ export class MongoStorageAdapter { .then(schemasCollection => schemasCollection._fechOneSchemaFrom_SCHEMA(className)); } - // TODO: As yet not particularly well specified. Creates an object. Shouldn't need the - // schemaController, but MongoTransform still needs it :( maybe shouldn't even need the schema, + // TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema, // and should infer from the type. Or maybe does need the schema for validations. Or maybe needs // the schem only for the legacy mongo format. We'll figure that out later. - createObject(className, object, schemaController, schema) { - const mongoObject = parseObjectToMongoObjectForCreate(schemaController, className, object, schema); + createObject(className, object, schema) { + const mongoObject = parseObjectToMongoObjectForCreate(className, object, schema); return this.adaptiveCollection(className) .then(collection => collection.insertOne(mongoObject)) .catch(error => { diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 75189cf8a7..898c62e00f 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -197,13 +197,7 @@ function transformWhere(className, restWhere, schema) { return mongoWhere; } -const parseObjectKeyValueToMongoObjectKeyValue = ( - schema, - className, - restKey, - restValue, - parseFormatSchema -) => { +const parseObjectKeyValueToMongoObjectKeyValue = (className, restKey, restValue, schema) => { // Check if the schema is known since it's a built-in field. let transformedValue; let coercedToDate; @@ -241,7 +235,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = ( if (restValue && restValue.__type !== 'Bytes') { //Note: We may not know the type of a field here, as the user could be saving (null) to a field //That never existed before, meaning we can't infer the type. - if (parseFormatSchema.fields[restKey] && parseFormatSchema.fields[restKey].type == 'Pointer' || restValue.__type == 'Pointer') { + if (schema.fields[restKey] && schema.fields[restKey].type == 'Pointer' || restValue.__type == 'Pointer') { restKey = '_p_' + restKey; } } @@ -279,18 +273,17 @@ const parseObjectKeyValueToMongoObjectKeyValue = ( // Main exposed method to create new objects. // restCreate is the "create" clause in REST API form. -function parseObjectToMongoObjectForCreate(schema, className, restCreate, parseFormatSchema) { +function parseObjectToMongoObjectForCreate(className, restCreate, schema) { if (className == '_User') { restCreate = transformAuthData(restCreate); } var mongoCreate = transformACL(restCreate); for (let restKey in restCreate) { let { key, value } = parseObjectKeyValueToMongoObjectKeyValue( - schema, className, restKey, restCreate[restKey], - parseFormatSchema + schema ); if (value !== undefined) { mongoCreate[key] = value; diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 2a0049fef6..8fbf45dc4a 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -394,7 +394,7 @@ DatabaseController.prototype.create = function(className, object, { acl } = {}) .then(() => this.handleRelationUpdates(className, null, object)) .then(() => schemaController.enforceClassExists(className)) .then(() => schemaController.getOneSchema(className, true)) - .then(schema => this.adapter.createObject(className, object, schemaController, schema)) + .then(schema => this.adapter.createObject(className, object, schema)) .then(result => sanitizeDatabaseResult(originalObject, result.ops[0])); }) }; diff --git a/src/Routers/GlobalConfigRouter.js b/src/Routers/GlobalConfigRouter.js index f876ab0bbf..bb9e3f480b 100644 --- a/src/Routers/GlobalConfigRouter.js +++ b/src/Routers/GlobalConfigRouter.js @@ -24,6 +24,13 @@ export class GlobalConfigRouter extends PromiseRouter { return acc; }, {}); let database = req.config.database.WithoutValidation(); + // TODO: We don't want to require db adapters to support upsert, so we create + // and then update whether the object already existed or not. The result + // is that simultaneous changes of _GlobalConfig might not work, but I + // think given the low write load of _GlobalConfig, thats probably fine. + + // However, we could allow db adapters to optionally support upsert, and + // use upsert if its there. That will come later though. return database.update('_GlobalConfig', {objectId: 1}, update, {upsert: true}).then(() => ({ response: { result: true } })); } From 64ed2018f78de9e93b679864eb36493daf9f5f0b Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 24 May 2016 16:42:19 -0700 Subject: [PATCH 17/17] Remove comment --- src/Routers/GlobalConfigRouter.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Routers/GlobalConfigRouter.js b/src/Routers/GlobalConfigRouter.js index bb9e3f480b..f876ab0bbf 100644 --- a/src/Routers/GlobalConfigRouter.js +++ b/src/Routers/GlobalConfigRouter.js @@ -24,13 +24,6 @@ export class GlobalConfigRouter extends PromiseRouter { return acc; }, {}); let database = req.config.database.WithoutValidation(); - // TODO: We don't want to require db adapters to support upsert, so we create - // and then update whether the object already existed or not. The result - // is that simultaneous changes of _GlobalConfig might not work, but I - // think given the low write load of _GlobalConfig, thats probably fine. - - // However, we could allow db adapters to optionally support upsert, and - // use upsert if its there. That will come later though. return database.update('_GlobalConfig', {objectId: 1}, update, {upsert: true}).then(() => ({ response: { result: true } })); }