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' },
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);
});
});

View File

@@ -848,12 +848,12 @@ class DatabaseController {
object: any,
aclGroup: string[]
): Promise<void> {
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'

View File

@@ -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<any>;
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<any> {
@@ -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<SchemaController> {
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]);
}
}