Centralizes list of system classes into Schema

This commit is contained in:
Florent Vilmart
2016-03-12 13:40:59 -05:00
parent 49531e7efe
commit 308fe1498a
3 changed files with 39 additions and 26 deletions

View File

@@ -1,6 +1,7 @@
// An object that encapsulates everything we need to run a 'find' // An object that encapsulates everything we need to run a 'find'
// operation, encoded in the REST API format. // operation, encoded in the REST API format.
var Schema = require('./Schema');
var Parse = require('parse/node').Parse; var Parse = require('parse/node').Parse;
import { default as FilesController } from './Controllers/FilesController'; import { default as FilesController } from './Controllers/FilesController';
@@ -171,7 +172,7 @@ RestQuery.prototype.redirectClassNameForKey = function() {
// Validates this operation against the allowClientClassCreation config. // Validates this operation against the allowClientClassCreation config.
RestQuery.prototype.validateClientClassCreation = function() { RestQuery.prototype.validateClientClassCreation = function() {
let sysClass = ['_User', '_Installation', '_Role', '_Session', '_Product']; let sysClass = Schema.systemClasses;
if (this.config.allowClientClassCreation === false && !this.auth.isMaster if (this.config.allowClientClassCreation === false && !this.auth.isMaster
&& sysClass.indexOf(this.className) === -1) { && sysClass.indexOf(this.className) === -1) {
return this.config.database.collectionExists(this.className).then((hasClass) => { return this.config.database.collectionExists(this.className).then((hasClass) => {
@@ -423,7 +424,7 @@ RestQuery.prototype.handleInclude = function() {
this.include = this.include.slice(1); this.include = this.include.slice(1);
return this.handleInclude(); return this.handleInclude();
} }
return pathResponse; return pathResponse;
}; };

View File

@@ -3,6 +3,7 @@
// This could be either a "create" or an "update". // This could be either a "create" or an "update".
import cache from './cache'; import cache from './cache';
var Schema = require('./Schema');
var deepcopy = require('deepcopy'); var deepcopy = require('deepcopy');
var Auth = require('./Auth'); var Auth = require('./Auth');
@@ -32,7 +33,7 @@ function RestWrite(config, auth, className, query, data, originalData) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'objectId ' + throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'objectId ' +
'is an invalid field name.'); 'is an invalid field name.');
} }
// When the operation is complete, this.response may have several // When the operation is complete, this.response may have several
// fields. // fields.
// response: the actual data to be returned // response: the actual data to be returned
@@ -108,7 +109,7 @@ RestWrite.prototype.getUserAndRoleACL = function() {
// Validates this operation against the allowClientClassCreation config. // Validates this operation against the allowClientClassCreation config.
RestWrite.prototype.validateClientClassCreation = function() { RestWrite.prototype.validateClientClassCreation = function() {
let sysClass = ['_User', '_Installation', '_Role', '_Session', '_Product']; let sysClass = Schema.systemClasses;
if (this.config.allowClientClassCreation === false && !this.auth.isMaster if (this.config.allowClientClassCreation === false && !this.auth.isMaster
&& sysClass.indexOf(this.className) === -1) { && sysClass.indexOf(this.className) === -1) {
return this.config.database.collectionExists(this.className).then((hasClass) => { return this.config.database.collectionExists(this.className).then((hasClass) => {
@@ -136,7 +137,7 @@ RestWrite.prototype.runBeforeTrigger = function() {
if (this.response) { if (this.response) {
return; return;
} }
// Avoid doing any setup for triggers if there is no 'beforeSave' trigger for this class. // Avoid doing any setup for triggers if there is no 'beforeSave' trigger for this class.
if (!triggers.triggerExists(this.className, triggers.Types.beforeSave, this.config.applicationId)) { if (!triggers.triggerExists(this.className, triggers.Types.beforeSave, this.config.applicationId)) {
return Promise.resolve(); return Promise.resolve();
@@ -254,14 +255,14 @@ RestWrite.prototype.findUsersWithAuthData = function(authData) {
}, []).filter((q) => { }, []).filter((q) => {
return typeof q !== undefined; return typeof q !== undefined;
}); });
let findPromise = Promise.resolve([]); let findPromise = Promise.resolve([]);
if (query.length > 0) { if (query.length > 0) {
findPromise = this.config.database.find( findPromise = this.config.database.find(
this.className, this.className,
{'$or': query}, {}) {'$or': query}, {})
} }
return findPromise; return findPromise;
} }
@@ -276,9 +277,9 @@ RestWrite.prototype.handleAuthData = function(authData) {
throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED,
'this auth is already used'); 'this auth is already used');
} }
this.storage['authProvider'] = Object.keys(authData).join(','); this.storage['authProvider'] = Object.keys(authData).join(',');
if (results.length == 0) { if (results.length == 0) {
this.data.username = cryptoUtils.newToken(); this.data.username = cryptoUtils.newToken();
} else if (!this.query) { } else if (!this.query) {
@@ -404,7 +405,7 @@ RestWrite.prototype.transformUser = function() {
// Handles any followup logic // Handles any followup logic
RestWrite.prototype.handleFollowup = function() { RestWrite.prototype.handleFollowup = function() {
if (this.storage && this.storage['clearSessions']) { if (this.storage && this.storage['clearSessions']) {
var sessionQuery = { var sessionQuery = {
user: { user: {
@@ -417,7 +418,7 @@ RestWrite.prototype.handleFollowup = function() {
this.config.database.destroy('_Session', sessionQuery) this.config.database.destroy('_Session', sessionQuery)
.then(this.handleFollowup.bind(this)); .then(this.handleFollowup.bind(this));
} }
if (this.storage && this.storage['sendVerificationEmail']) { if (this.storage && this.storage['sendVerificationEmail']) {
delete this.storage['sendVerificationEmail']; delete this.storage['sendVerificationEmail'];
// Fire and forget! // Fire and forget!
@@ -695,7 +696,7 @@ RestWrite.prototype.runDatabaseOperation = function() {
throw new Parse.Error(Parse.Error.SESSION_MISSING, throw new Parse.Error(Parse.Error.SESSION_MISSING,
'cannot modify user ' + this.query.objectId); 'cannot modify user ' + this.query.objectId);
} }
if (this.className === '_Product' && this.data.download) { if (this.className === '_Product' && this.data.download) {
this.data.downloadName = this.data.download.name; this.data.downloadName = this.data.download.name;
} }
@@ -722,7 +723,7 @@ RestWrite.prototype.runDatabaseOperation = function() {
ACL[this.data.objectId] = { read: true, write: true }; ACL[this.data.objectId] = { read: true, write: true };
ACL['*'] = { read: true, write: false }; ACL['*'] = { read: true, write: false };
this.data.ACL = ACL; this.data.ACL = ACL;
} }
// Run a create // Run a create
return this.config.database.create(this.className, this.data, this.runOptions) return this.config.database.create(this.className, this.data, this.runOptions)
.then(() => { .then(() => {

View File

@@ -67,7 +67,20 @@ var defaultColumns = {
"icon": {type:'File'}, "icon": {type:'File'},
"order": {type:'Number'}, "order": {type:'Number'},
"title": {type:'String'}, "title": {type:'String'},
"subtitle": {type:'String'}, "subtitle": {type:'String'},
},
_PushStatus: {
"pushTime": {type:'String'},
"source": {type:'String'}, // rest or web
"query": {type:'String'}, // the stringified JSON query
"payload": {type:'Object'}, // the JSON payload,
"title": {type:'String'},
"expiry": {type:'Number'},
"status": {type:'String'},
"numSent": {type:'Number'},
"pushHash": {type:'String'},
"errorMessage": {type:'Object'},
"sentPerType": {type:'Object'}
} }
}; };
@@ -76,6 +89,8 @@ var requiredColumns = {
_Role: ["name", "ACL"] _Role: ["name", "ACL"]
} }
const systemClasses = ['_User', '_Installation', '_Role', '_Session', '_Product', '_PushStatus'];
// 10 alpha numberic chars + uppercase // 10 alpha numberic chars + uppercase
const userIdRegex = /^[a-zA-Z0-9]{10}$/; const userIdRegex = /^[a-zA-Z0-9]{10}$/;
// Anything that start with role // Anything that start with role
@@ -127,13 +142,8 @@ function validateCLP(perms) {
var joinClassRegex = /^_Join:[A-Za-z0-9_]+:[A-Za-z0-9_]+/; var joinClassRegex = /^_Join:[A-Za-z0-9_]+:[A-Za-z0-9_]+/;
var classAndFieldRegex = /^[A-Za-z][A-Za-z0-9_]*$/; var classAndFieldRegex = /^[A-Za-z][A-Za-z0-9_]*$/;
function classNameIsValid(className) { function classNameIsValid(className) {
return ( return (systemClasses.indexOf(className) > -1 ||
className === '_User' ||
className === '_Installation' ||
className === '_Session' ||
className === '_SCHEMA' || //TODO: remove this, as _SCHEMA is not a valid class name for storing Parse Objects. className === '_SCHEMA' || //TODO: remove this, as _SCHEMA is not a valid class name for storing Parse Objects.
className === '_Role' ||
className === '_Product' ||
joinClassRegex.test(className) || joinClassRegex.test(className) ||
//Class names have the same constraints as field names, but also allow the previous additional names. //Class names have the same constraints as field names, but also allow the previous additional names.
fieldNameIsValid(className) fieldNameIsValid(className)
@@ -284,7 +294,7 @@ class Schema {
return Promise.reject(error); return Promise.reject(error);
}); });
} }
updateClass(className, submittedFields, classLevelPermissions, database) { updateClass(className, submittedFields, classLevelPermissions, database) {
if (!this.data[className]) { if (!this.data[className]) {
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} does not exist.`); throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} does not exist.`);
@@ -299,7 +309,7 @@ class Schema {
throw new Parse.Error(255, `Field ${name} does not exist, cannot delete.`); throw new Parse.Error(255, `Field ${name} does not exist, cannot delete.`);
} }
}); });
let newSchema = buildMergedSchemaObject(existingFields, submittedFields); let newSchema = buildMergedSchemaObject(existingFields, submittedFields);
let mongoObject = mongoSchemaFromFieldsAndClassNameAndCLP(newSchema, className, classLevelPermissions); let mongoObject = mongoSchemaFromFieldsAndClassNameAndCLP(newSchema, className, classLevelPermissions);
if (!mongoObject.result) { if (!mongoObject.result) {
@@ -327,7 +337,7 @@ class Schema {
}); });
return Promise.all(promises); return Promise.all(promises);
}) })
.then(() => { .then(() => {
return this.setPermissions(className, classLevelPermissions) return this.setPermissions(className, classLevelPermissions)
}) })
.then(() => { return mongoSchemaToSchemaAPIResponse(mongoObject.result) }); .then(() => { return mongoSchemaToSchemaAPIResponse(mongoObject.result) });
@@ -697,7 +707,7 @@ function mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPe
error: 'currently, only one GeoPoint field may exist in an object. Adding ' + geoPoints[1] + ' when ' + geoPoints[0] + ' already exists.', error: 'currently, only one GeoPoint field may exist in an object. Adding ' + geoPoints[1] + ' when ' + geoPoints[0] + ' already exists.',
}; };
} }
validateCLP(classLevelPermissions); validateCLP(classLevelPermissions);
if (typeof classLevelPermissions !== 'undefined') { if (typeof classLevelPermissions !== 'undefined') {
mongoObject._metadata = mongoObject._metadata || {}; mongoObject._metadata = mongoObject._metadata || {};
@@ -886,11 +896,11 @@ function mongoSchemaToSchemaAPIResponse(schema) {
className: schema._id, className: schema._id,
fields: mongoSchemaAPIResponseFields(schema), fields: mongoSchemaAPIResponseFields(schema),
}; };
let classLevelPermissions = DefaultClassLevelPermissions; let classLevelPermissions = DefaultClassLevelPermissions;
if (schema._metadata && schema._metadata.class_permissions) { if (schema._metadata && schema._metadata.class_permissions) {
classLevelPermissions = Object.assign(classLevelPermissions, schema._metadata.class_permissions); classLevelPermissions = Object.assign(classLevelPermissions, schema._metadata.class_permissions);
} }
result.classLevelPermissions = classLevelPermissions; result.classLevelPermissions = classLevelPermissions;
return result; return result;
} }
@@ -903,4 +913,5 @@ module.exports = {
buildMergedSchemaObject: buildMergedSchemaObject, buildMergedSchemaObject: buildMergedSchemaObject,
mongoFieldTypeToSchemaAPIType: mongoFieldTypeToSchemaAPIType, mongoFieldTypeToSchemaAPIType: mongoFieldTypeToSchemaAPIType,
mongoSchemaToSchemaAPIResponse, mongoSchemaToSchemaAPIResponse,
systemClasses,
}; };