Reduces number of calls to injectDefaultSchema (#5107)
This commit is contained in:
@@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user