Reduces number of calls to injectDefaultSchema (#5107)

This commit is contained in:
Florent Vilmart
2018-10-08 14:16:29 -04:00
committed by GitHub
parent 7fe4030453
commit f1bc55bf89
3 changed files with 90 additions and 68 deletions

View File

@@ -1032,9 +1032,12 @@ describe('SchemaController', () => {
createdAt: { type: 'Date' }, createdAt: { type: 'Date' },
ACL: { type: 'ACL' }, ACL: { type: 'ACL' },
}; };
expect(dd(schema.data.NewClass, expectedSchema)).toEqual(undefined); expect(dd(schema.schemaData.NewClass.fields, expectedSchema)).toEqual(
done(); undefined
}); );
})
.then(done)
.catch(done.fail);
}); });
}); });
@@ -1268,14 +1271,15 @@ describe('SchemaController', () => {
}) })
.then(userSchema => { .then(userSchema => {
validateSchemaStructure(userSchema); validateSchemaStructure(userSchema);
validateSchemaDataStructure(schema.data); validateSchemaDataStructure(schema.schemaData);
return schema.getOneSchema('_PushStatus', true); return schema.getOneSchema('_PushStatus', true);
}) })
.then(pushStatusSchema => { .then(pushStatusSchema => {
validateSchemaStructure(pushStatusSchema); validateSchemaStructure(pushStatusSchema);
validateSchemaDataStructure(schema.data); validateSchemaDataStructure(schema.schemaData);
done(); })
}); .then(done)
.catch(done.fail);
}); });
}); });

View File

@@ -848,12 +848,12 @@ class DatabaseController {
object: any, object: any,
aclGroup: string[] aclGroup: string[]
): Promise<void> { ): Promise<void> {
const classSchema = schema.data[className]; const classSchema = schema.schemaData[className];
if (!classSchema) { if (!classSchema) {
return Promise.resolve(); return Promise.resolve();
} }
const fields = Object.keys(object); const fields = Object.keys(object);
const schemaFields = Object.keys(classSchema); const schemaFields = Object.keys(classSchema.fields);
const newKeys = fields.filter(field => { const newKeys = fields.filter(field => {
// Skip fields that are unset // Skip fields that are unset
if ( if (
@@ -1346,7 +1346,7 @@ class DatabaseController {
if (schema.testBaseCLP(className, aclGroup, operation)) { if (schema.testBaseCLP(className, aclGroup, operation)) {
return query; return query;
} }
const perms = schema.perms[className]; const perms = schema.schemaData[className].classLevelPermissions;
const field = const field =
['get', 'find'].indexOf(operation) > -1 ['get', 'find'].indexOf(operation) > -1
? 'readUserFields' ? 'readUserFields'

View File

@@ -381,6 +381,48 @@ const convertAdapterSchemaToParseSchema = ({ ...schema }) => {
return 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 = ({ const injectDefaultSchema = ({
className, className,
fields, 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. // the mongo format and the Parse format. Soon, this will all be Parse format.
export default class SchemaController { export default class SchemaController {
_dbAdapter: StorageAdapter; _dbAdapter: StorageAdapter;
data: any; schemaData: { [string]: Schema };
perms: any;
indexes: any;
_cache: any; _cache: any;
reloadDataPromise: Promise<any>; reloadDataPromise: Promise<any>;
constructor(databaseAdapter: StorageAdapter, schemaCache: any) { constructor(databaseAdapter: StorageAdapter, schemaCache: any) {
this._dbAdapter = databaseAdapter; this._dbAdapter = databaseAdapter;
this._cache = schemaCache; this._cache = schemaCache;
// this.data[className][fieldName] tells you the type of that field, in mongo format this.schemaData = new SchemaData();
this.data = {};
// this.perms[className][operation] tells you the acl-style permissions
this.perms = {};
// this.indexes[className][operation] tells you the indexes
this.indexes = {};
} }
reloadData(options: LoadSchemaOptions = { clearCache: false }): Promise<any> { reloadData(options: LoadSchemaOptions = { clearCache: false }): Promise<any> {
@@ -500,35 +535,11 @@ export default class SchemaController {
.then(() => { .then(() => {
return this.getAllClasses(options).then( return this.getAllClasses(options).then(
allSchemas => { allSchemas => {
const data = {}; this.schemaData = new SchemaData(allSchemas);
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;
delete this.reloadDataPromise; delete this.reloadDataPromise;
}, },
err => { err => {
this.data = {}; this.schemaData = new SchemaData();
this.perms = {};
this.indexes = {};
delete this.reloadDataPromise; delete this.reloadDataPromise;
throw err; throw err;
} }
@@ -575,11 +586,12 @@ export default class SchemaController {
} }
return promise.then(() => { return promise.then(() => {
if (allowVolatileClasses && volatileClasses.indexOf(className) > -1) { if (allowVolatileClasses && volatileClasses.indexOf(className) > -1) {
const data = this.schemaData[className];
return Promise.resolve({ return Promise.resolve({
className, className,
fields: this.data[className], fields: data.fields,
classLevelPermissions: this.perms[className], classLevelPermissions: data.classLevelPermissions,
indexes: this.indexes[className], indexes: data.indexes,
}); });
} }
return this._cache.getOneSchema(className).then(cached => { return this._cache.getOneSchema(className).then(cached => {
@@ -730,16 +742,14 @@ export default class SchemaController {
.then(() => this.reloadData({ clearCache: true })) .then(() => this.reloadData({ clearCache: true }))
//TODO: Move this logic into the database adapter //TODO: Move this logic into the database adapter
.then(() => { .then(() => {
const schema = this.schemaData[className];
const reloadedSchema: Schema = { const reloadedSchema: Schema = {
className: className, className: className,
fields: this.data[className], fields: schema.fields,
classLevelPermissions: this.perms[className], classLevelPermissions: schema.classLevelPermissions,
}; };
if ( if (schema.indexes && Object.keys(schema.indexes).length !== 0) {
this.indexes[className] && reloadedSchema.indexes = schema.indexes;
Object.keys(this.indexes[className]).length !== 0
) {
reloadedSchema.indexes = this.indexes[className];
} }
return reloadedSchema; return reloadedSchema;
}) })
@@ -760,7 +770,7 @@ export default class SchemaController {
// Returns a promise that resolves successfully to the new schema // Returns a promise that resolves successfully to the new schema
// object or fails with a reason. // object or fails with a reason.
enforceClassExists(className: string): Promise<SchemaController> { enforceClassExists(className: string): Promise<SchemaController> {
if (this.data[className]) { if (this.schemaData[className]) {
return Promise.resolve(this); return Promise.resolve(this);
} }
// We don't have this class. Update the schema // We don't have this class. Update the schema
@@ -777,7 +787,7 @@ export default class SchemaController {
}) })
.then(() => { .then(() => {
// Ensure that the schema now validates // Ensure that the schema now validates
if (this.data[className]) { if (this.schemaData[className]) {
return this; return this;
} else { } else {
throw new Parse.Error( throw new Parse.Error(
@@ -801,7 +811,7 @@ export default class SchemaController {
fields: SchemaFields = {}, fields: SchemaFields = {},
classLevelPermissions: any classLevelPermissions: any
): any { ): any {
if (this.data[className]) { if (this.schemaData[className]) {
throw new Parse.Error( throw new Parse.Error(
Parse.Error.INVALID_CLASS_NAME, Parse.Error.INVALID_CLASS_NAME,
`Class ${className} already exists.` `Class ${className} already exists.`
@@ -1114,11 +1124,15 @@ export default class SchemaController {
// Validates the base CLP for an operation // Validates the base CLP for an operation
testBaseCLP(className: string, aclGroup: string[], operation: string) { 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; return true;
} }
const classPerms = this.perms[className]; const perms = classSchema.classLevelPermissions[operation];
const perms = classPerms[operation];
// Handle the public scenario quickly // Handle the public scenario quickly
if (perms['*']) { if (perms['*']) {
return true; return true;
@@ -1139,12 +1153,16 @@ export default class SchemaController {
if (this.testBaseCLP(className, aclGroup, operation)) { if (this.testBaseCLP(className, aclGroup, operation)) {
return Promise.resolve(); return Promise.resolve();
} }
const classSchema = this.schemaData[className];
if (!this.perms[className] || !this.perms[className][operation]) { if (
!classSchema ||
!classSchema.classLevelPermissions ||
!classSchema.classLevelPermissions[operation]
) {
return true; return true;
} }
const classPerms = this.perms[className]; const classPerms = classSchema.classLevelPermissions;
const perms = classPerms[operation]; const perms = classSchema.classLevelPermissions[operation];
// If only for authenticated users // If only for authenticated users
// make sure we have an aclGroup // make sure we have an aclGroup
@@ -1200,8 +1218,8 @@ export default class SchemaController {
className: string, className: string,
fieldName: string fieldName: string
): ?(SchemaField | string) { ): ?(SchemaField | string) {
if (this.data && this.data[className]) { if (this.schemaData[className]) {
const expectedType = this.data[className][fieldName]; const expectedType = this.schemaData[className].fields[fieldName];
return expectedType === 'map' ? 'Object' : expectedType; return expectedType === 'map' ? 'Object' : expectedType;
} }
return undefined; return undefined;
@@ -1209,7 +1227,7 @@ export default class SchemaController {
// Checks if a given class is in the schema. // Checks if a given class is in the schema.
hasClass(className: string) { hasClass(className: string) {
return this.reloadData().then(() => !!this.data[className]); return this.reloadData().then(() => !!this.schemaData[className]);
} }
} }