From 308fe1498a30a85299fdc318d19e287d3cc0fa34 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sat, 12 Mar 2016 13:40:59 -0500 Subject: [PATCH] Centralizes list of system classes into Schema --- src/RestQuery.js | 5 +++-- src/RestWrite.js | 23 ++++++++++++----------- src/Schema.js | 37 ++++++++++++++++++++++++------------- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/RestQuery.js b/src/RestQuery.js index 0ddc7b4b..fa7a0298 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -1,6 +1,7 @@ // An object that encapsulates everything we need to run a 'find' // operation, encoded in the REST API format. +var Schema = require('./Schema'); var Parse = require('parse/node').Parse; import { default as FilesController } from './Controllers/FilesController'; @@ -171,7 +172,7 @@ RestQuery.prototype.redirectClassNameForKey = function() { // Validates this operation against the allowClientClassCreation config. RestQuery.prototype.validateClientClassCreation = function() { - let sysClass = ['_User', '_Installation', '_Role', '_Session', '_Product']; + let sysClass = Schema.systemClasses; if (this.config.allowClientClassCreation === false && !this.auth.isMaster && sysClass.indexOf(this.className) === -1) { return this.config.database.collectionExists(this.className).then((hasClass) => { @@ -423,7 +424,7 @@ RestQuery.prototype.handleInclude = function() { this.include = this.include.slice(1); return this.handleInclude(); } - + return pathResponse; }; diff --git a/src/RestWrite.js b/src/RestWrite.js index 0aaa5d5a..51902331 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -3,6 +3,7 @@ // This could be either a "create" or an "update". import cache from './cache'; +var Schema = require('./Schema'); var deepcopy = require('deepcopy'); 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 ' + 'is an invalid field name.'); } - + // When the operation is complete, this.response may have several // fields. // response: the actual data to be returned @@ -108,7 +109,7 @@ RestWrite.prototype.getUserAndRoleACL = function() { // Validates this operation against the allowClientClassCreation config. RestWrite.prototype.validateClientClassCreation = function() { - let sysClass = ['_User', '_Installation', '_Role', '_Session', '_Product']; + let sysClass = Schema.systemClasses; if (this.config.allowClientClassCreation === false && !this.auth.isMaster && sysClass.indexOf(this.className) === -1) { return this.config.database.collectionExists(this.className).then((hasClass) => { @@ -136,7 +137,7 @@ RestWrite.prototype.runBeforeTrigger = function() { if (this.response) { return; } - + // 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)) { return Promise.resolve(); @@ -254,14 +255,14 @@ RestWrite.prototype.findUsersWithAuthData = function(authData) { }, []).filter((q) => { return typeof q !== undefined; }); - + let findPromise = Promise.resolve([]); if (query.length > 0) { findPromise = this.config.database.find( this.className, {'$or': query}, {}) } - + return findPromise; } @@ -276,9 +277,9 @@ RestWrite.prototype.handleAuthData = function(authData) { throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, 'this auth is already used'); } - + this.storage['authProvider'] = Object.keys(authData).join(','); - + if (results.length == 0) { this.data.username = cryptoUtils.newToken(); } else if (!this.query) { @@ -404,7 +405,7 @@ RestWrite.prototype.transformUser = function() { // Handles any followup logic RestWrite.prototype.handleFollowup = function() { - + if (this.storage && this.storage['clearSessions']) { var sessionQuery = { user: { @@ -417,7 +418,7 @@ RestWrite.prototype.handleFollowup = function() { this.config.database.destroy('_Session', sessionQuery) .then(this.handleFollowup.bind(this)); } - + if (this.storage && this.storage['sendVerificationEmail']) { delete this.storage['sendVerificationEmail']; // Fire and forget! @@ -695,7 +696,7 @@ RestWrite.prototype.runDatabaseOperation = function() { throw new Parse.Error(Parse.Error.SESSION_MISSING, 'cannot modify user ' + this.query.objectId); } - + if (this.className === '_Product' && this.data.download) { this.data.downloadName = this.data.download.name; } @@ -722,7 +723,7 @@ RestWrite.prototype.runDatabaseOperation = function() { ACL[this.data.objectId] = { read: true, write: true }; ACL['*'] = { read: true, write: false }; this.data.ACL = ACL; - } + } // Run a create return this.config.database.create(this.className, this.data, this.runOptions) .then(() => { diff --git a/src/Schema.js b/src/Schema.js index ffb7b088..79c49496 100644 --- a/src/Schema.js +++ b/src/Schema.js @@ -67,7 +67,20 @@ var defaultColumns = { "icon": {type:'File'}, "order": {type:'Number'}, "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"] } +const systemClasses = ['_User', '_Installation', '_Role', '_Session', '_Product', '_PushStatus']; + // 10 alpha numberic chars + uppercase const userIdRegex = /^[a-zA-Z0-9]{10}$/; // Anything that start with role @@ -127,13 +142,8 @@ function validateCLP(perms) { var joinClassRegex = /^_Join:[A-Za-z0-9_]+:[A-Za-z0-9_]+/; var classAndFieldRegex = /^[A-Za-z][A-Za-z0-9_]*$/; function classNameIsValid(className) { - return ( - className === '_User' || - className === '_Installation' || - className === '_Session' || + return (systemClasses.indexOf(className) > -1 || className === '_SCHEMA' || //TODO: remove this, as _SCHEMA is not a valid class name for storing Parse Objects. - className === '_Role' || - className === '_Product' || joinClassRegex.test(className) || //Class names have the same constraints as field names, but also allow the previous additional names. fieldNameIsValid(className) @@ -284,7 +294,7 @@ class Schema { return Promise.reject(error); }); } - + updateClass(className, submittedFields, classLevelPermissions, database) { if (!this.data[className]) { 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.`); } }); - + let newSchema = buildMergedSchemaObject(existingFields, submittedFields); let mongoObject = mongoSchemaFromFieldsAndClassNameAndCLP(newSchema, className, classLevelPermissions); if (!mongoObject.result) { @@ -327,7 +337,7 @@ class Schema { }); return Promise.all(promises); }) - .then(() => { + .then(() => { return this.setPermissions(className, classLevelPermissions) }) .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.', }; } - + validateCLP(classLevelPermissions); if (typeof classLevelPermissions !== 'undefined') { mongoObject._metadata = mongoObject._metadata || {}; @@ -886,11 +896,11 @@ function mongoSchemaToSchemaAPIResponse(schema) { className: schema._id, fields: mongoSchemaAPIResponseFields(schema), }; - + let classLevelPermissions = DefaultClassLevelPermissions; if (schema._metadata && schema._metadata.class_permissions) { classLevelPermissions = Object.assign(classLevelPermissions, schema._metadata.class_permissions); - } + } result.classLevelPermissions = classLevelPermissions; return result; } @@ -903,4 +913,5 @@ module.exports = { buildMergedSchemaObject: buildMergedSchemaObject, mongoFieldTypeToSchemaAPIType: mongoFieldTypeToSchemaAPIType, mongoSchemaToSchemaAPIResponse, + systemClasses, };