Skip to content

Commit

Permalink
Reduces number of calls to injectDefaultSchema (#5107)
Browse files Browse the repository at this point in the history
  • Loading branch information
flovilmart authored Oct 8, 2018
1 parent 7fe4030 commit f1bc55b
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 68 deletions.
18 changes: 11 additions & 7 deletions spec/Schema.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});

Expand Down Expand Up @@ -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);
});
});

Expand Down
6 changes: 3 additions & 3 deletions src/Controllers/DatabaseController.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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'
Expand Down
134 changes: 76 additions & 58 deletions src/Controllers/SchemaController.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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> {
Expand All @@ -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;
}
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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;
})
Expand All @@ -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
Expand All @@ -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(
Expand All @@ -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.`
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -1200,16 +1218,16 @@ 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;
}

// 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]);
}
}

Expand Down

0 comments on commit f1bc55b

Please sign in to comment.