From f1bc55bf890edc0dbd35154f31f9c9b5f56a46ac Mon Sep 17 00:00:00 2001 From: Florent Vilmart <364568+flovilmart@users.noreply.github.com> Date: Mon, 8 Oct 2018 14:16:29 -0400 Subject: [PATCH] Reduces number of calls to injectDefaultSchema (#5107) --- spec/Schema.spec.js | 18 ++-- src/Controllers/DatabaseController.js | 6 +- src/Controllers/SchemaController.js | 134 +++++++++++++++----------- 3 files changed, 90 insertions(+), 68 deletions(-) diff --git a/spec/Schema.spec.js b/spec/Schema.spec.js index 2d2eabc782..b19448b6ef 100644 --- a/spec/Schema.spec.js +++ b/spec/Schema.spec.js @@ -1032,9 +1032,12 @@ describe('SchemaController', () => { createdAt: { type: 'Date' }, ACL: { type: 'ACL' }, }; - expect(dd(schema.data.NewClass, expectedSchema)).toEqual(undefined); - done(); - }); + expect(dd(schema.schemaData.NewClass.fields, expectedSchema)).toEqual( + undefined + ); + }) + .then(done) + .catch(done.fail); }); }); @@ -1268,14 +1271,15 @@ describe('SchemaController', () => { }) .then(userSchema => { validateSchemaStructure(userSchema); - validateSchemaDataStructure(schema.data); + validateSchemaDataStructure(schema.schemaData); return schema.getOneSchema('_PushStatus', true); }) .then(pushStatusSchema => { validateSchemaStructure(pushStatusSchema); - validateSchemaDataStructure(schema.data); - done(); - }); + validateSchemaDataStructure(schema.schemaData); + }) + .then(done) + .catch(done.fail); }); }); diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index fb6d6b9525..3b28126133 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -848,12 +848,12 @@ class DatabaseController { object: any, aclGroup: string[] ): Promise { - const classSchema = schema.data[className]; + const classSchema = schema.schemaData[className]; if (!classSchema) { return Promise.resolve(); } const fields = Object.keys(object); - const schemaFields = Object.keys(classSchema); + const schemaFields = Object.keys(classSchema.fields); const newKeys = fields.filter(field => { // Skip fields that are unset if ( @@ -1346,7 +1346,7 @@ class DatabaseController { if (schema.testBaseCLP(className, aclGroup, operation)) { return query; } - const perms = schema.perms[className]; + const perms = schema.schemaData[className].classLevelPermissions; const field = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index a7252700ac..b82295f4ab 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -381,6 +381,48 @@ const convertAdapterSchemaToParseSchema = ({ ...schema }) => { return schema; }; +class SchemaData { + __data: any; + constructor(allSchemas = []) { + this.__data = {}; + allSchemas.forEach(schema => { + Object.defineProperty(this, schema.className, { + get: () => { + if (!this.__data[schema.className]) { + const data = {}; + data.fields = injectDefaultSchema(schema).fields; + data.classLevelPermissions = schema.classLevelPermissions; + data.indexes = schema.indexes; + this.__data[schema.className] = data; + } + return this.__data[schema.className]; + }, + }); + }); + + // Inject the in-memory classes + volatileClasses.forEach(className => { + Object.defineProperty(this, className, { + get: () => { + if (!this.__data[className]) { + const schema = injectDefaultSchema({ + className, + fields: {}, + classLevelPermissions: {}, + }); + const data = {}; + data.fields = schema.fields; + data.classLevelPermissions = schema.classLevelPermissions; + data.indexes = schema.indexes; + this.__data[className] = data; + } + return this.__data[className]; + }, + }); + }); + } +} + const injectDefaultSchema = ({ className, fields, @@ -469,21 +511,14 @@ const typeToString = (type: SchemaField | string): string => { // the mongo format and the Parse format. Soon, this will all be Parse format. export default class SchemaController { _dbAdapter: StorageAdapter; - data: any; - perms: any; - indexes: any; + schemaData: { [string]: Schema }; _cache: any; reloadDataPromise: Promise; constructor(databaseAdapter: StorageAdapter, schemaCache: any) { this._dbAdapter = databaseAdapter; this._cache = schemaCache; - // this.data[className][fieldName] tells you the type of that field, in mongo format - this.data = {}; - // this.perms[className][operation] tells you the acl-style permissions - this.perms = {}; - // this.indexes[className][operation] tells you the indexes - this.indexes = {}; + this.schemaData = new SchemaData(); } reloadData(options: LoadSchemaOptions = { clearCache: false }): Promise { @@ -500,35 +535,11 @@ export default class SchemaController { .then(() => { return this.getAllClasses(options).then( allSchemas => { - const data = {}; - const perms = {}; - const indexes = {}; - allSchemas.forEach(schema => { - data[schema.className] = injectDefaultSchema(schema).fields; - perms[schema.className] = schema.classLevelPermissions; - indexes[schema.className] = schema.indexes; - }); - - // Inject the in-memory classes - volatileClasses.forEach(className => { - const schema = injectDefaultSchema({ - className, - fields: {}, - classLevelPermissions: {}, - }); - data[className] = schema.fields; - perms[className] = schema.classLevelPermissions; - indexes[className] = schema.indexes; - }); - this.data = data; - this.perms = perms; - this.indexes = indexes; + this.schemaData = new SchemaData(allSchemas); delete this.reloadDataPromise; }, err => { - this.data = {}; - this.perms = {}; - this.indexes = {}; + this.schemaData = new SchemaData(); delete this.reloadDataPromise; throw err; } @@ -575,11 +586,12 @@ export default class SchemaController { } return promise.then(() => { if (allowVolatileClasses && volatileClasses.indexOf(className) > -1) { + const data = this.schemaData[className]; return Promise.resolve({ className, - fields: this.data[className], - classLevelPermissions: this.perms[className], - indexes: this.indexes[className], + fields: data.fields, + classLevelPermissions: data.classLevelPermissions, + indexes: data.indexes, }); } return this._cache.getOneSchema(className).then(cached => { @@ -730,16 +742,14 @@ export default class SchemaController { .then(() => this.reloadData({ clearCache: true })) //TODO: Move this logic into the database adapter .then(() => { + const schema = this.schemaData[className]; const reloadedSchema: Schema = { className: className, - fields: this.data[className], - classLevelPermissions: this.perms[className], + fields: schema.fields, + classLevelPermissions: schema.classLevelPermissions, }; - if ( - this.indexes[className] && - Object.keys(this.indexes[className]).length !== 0 - ) { - reloadedSchema.indexes = this.indexes[className]; + if (schema.indexes && Object.keys(schema.indexes).length !== 0) { + reloadedSchema.indexes = schema.indexes; } return reloadedSchema; }) @@ -760,7 +770,7 @@ export default class SchemaController { // Returns a promise that resolves successfully to the new schema // object or fails with a reason. enforceClassExists(className: string): Promise { - if (this.data[className]) { + if (this.schemaData[className]) { return Promise.resolve(this); } // We don't have this class. Update the schema @@ -777,7 +787,7 @@ export default class SchemaController { }) .then(() => { // Ensure that the schema now validates - if (this.data[className]) { + if (this.schemaData[className]) { return this; } else { throw new Parse.Error( @@ -801,7 +811,7 @@ export default class SchemaController { fields: SchemaFields = {}, classLevelPermissions: any ): any { - if (this.data[className]) { + if (this.schemaData[className]) { throw new Parse.Error( Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.` @@ -1114,11 +1124,15 @@ export default class SchemaController { // Validates the base CLP for an operation testBaseCLP(className: string, aclGroup: string[], operation: string) { - if (!this.perms[className] || !this.perms[className][operation]) { + const classSchema = this.schemaData[className]; + if ( + !classSchema || + !classSchema.classLevelPermissions || + !classSchema.classLevelPermissions[operation] + ) { return true; } - const classPerms = this.perms[className]; - const perms = classPerms[operation]; + const perms = classSchema.classLevelPermissions[operation]; // Handle the public scenario quickly if (perms['*']) { return true; @@ -1139,12 +1153,16 @@ export default class SchemaController { if (this.testBaseCLP(className, aclGroup, operation)) { return Promise.resolve(); } - - if (!this.perms[className] || !this.perms[className][operation]) { + const classSchema = this.schemaData[className]; + if ( + !classSchema || + !classSchema.classLevelPermissions || + !classSchema.classLevelPermissions[operation] + ) { return true; } - const classPerms = this.perms[className]; - const perms = classPerms[operation]; + const classPerms = classSchema.classLevelPermissions; + const perms = classSchema.classLevelPermissions[operation]; // If only for authenticated users // make sure we have an aclGroup @@ -1200,8 +1218,8 @@ export default class SchemaController { className: string, fieldName: string ): ?(SchemaField | string) { - if (this.data && this.data[className]) { - const expectedType = this.data[className][fieldName]; + if (this.schemaData[className]) { + const expectedType = this.schemaData[className].fields[fieldName]; return expectedType === 'map' ? 'Object' : expectedType; } return undefined; @@ -1209,7 +1227,7 @@ export default class SchemaController { // Checks if a given class is in the schema. hasClass(className: string) { - return this.reloadData().then(() => !!this.data[className]); + return this.reloadData().then(() => !!this.schemaData[className]); } }