From 1e29095dcb789c478776fd1e4d09c116a91aaeaf Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sat, 17 Sep 2016 15:50:04 -0400 Subject: [PATCH 1/5] Removes runtime dependency babel-polyfill (#2692) * Removes runtime dependency babel-polyfill * removes references to polyfilled array includes --- package.json | 1 - src/Adapters/Storage/Mongo/MongoTransform.js | 4 ++-- .../Storage/Postgres/PostgresStorageAdapter.js | 12 ++++++------ src/ParseServer.js | 4 ---- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 5b2f0c7b37..78843e2ed3 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ ], "license": "BSD-3-Clause", "dependencies": { - "babel-polyfill": "6.13.0", "bcryptjs": "2.3.0", "body-parser": "1.15.2", "commander": "2.9.0", diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 5f043252ed..f4ecd32daf 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -99,7 +99,7 @@ const transformKeyValueForUpdate = (className, restKey, restValue, parseFormatSc } const transformInteriorValue = restValue => { - if (restValue !== null && typeof restValue === 'object' && Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))) { + if (restValue !== null && typeof restValue === 'object' && Object.keys(restValue).some(key => key.indexOf('$') >= 0 || key.indexOf('.') >= 0)) { throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters"); } // Handle atomic values @@ -293,7 +293,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = (restKey, restValue, schema) => } // Handle normal objects by recursing - if (Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))) { + if (Object.keys(restValue).some(key => key.indexOf('$') >= 0 || key.indexOf('.') >= 0)) { throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters"); } value = _.mapValues(restValue, transformInteriorValue); diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index c364e3c6f1..994f395404 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -415,7 +415,7 @@ export class PostgresStorageAdapter { return toParseSchema(schema) }) .catch((err) => { - if (err.code === PostgresUniqueIndexViolationError && err.detail.includes(className)) { + if (err.code === PostgresUniqueIndexViolationError && err.detail.indexOf(className) >= 0) { throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`) } throw err; @@ -445,7 +445,7 @@ export class PostgresStorageAdapter { relations.push(fieldName) return; } - if (['_rperm', '_wperm'].includes(fieldName)) { + if (['_rperm', '_wperm'].indexOf(fieldName) >=0 ) { parseType.contents = { type: 'String' }; } valuesArray.push(fieldName); @@ -678,7 +678,7 @@ export class PostgresStorageAdapter { valuesArray.push(object[fieldName].objectId); break; case 'Array': - if (['_rperm', '_wperm'].includes(fieldName)) { + if (['_rperm', '_wperm'].indexOf(fieldName) >= 0) { valuesArray.push(object[fieldName]); } else { valuesArray.push(JSON.stringify(object[fieldName])); @@ -707,7 +707,7 @@ export class PostgresStorageAdapter { let initialValues = valuesArray.map((val, index) => { let termination = ''; let fieldName = columnsArray[index]; - if (['_rperm','_wperm'].includes(fieldName)) { + if (['_rperm','_wperm'].indexOf(fieldName) >= 0) { termination = '::text[]'; } else if (schema.fields[fieldName] && schema.fields[fieldName].type === 'Array') { termination = '::jsonb'; @@ -1031,9 +1031,9 @@ export class PostgresStorageAdapter { const qs = `ALTER TABLE $1:name ADD CONSTRAINT $2:name UNIQUE (${constraintPatterns.join(',')})`; return this._client.none(qs,[className, constraintName, ...fieldNames]) .catch(error => { - if (error.code === PostgresDuplicateRelationError && error.message.includes(constraintName)) { + if (error.code === PostgresDuplicateRelationError && error.message.indexOf(constraintName) >= 0) { // Index already exists. Ignore error. - } else if (error.code === PostgresUniqueIndexViolationError && error.message.includes(constraintName)) { + } else if (error.code === PostgresUniqueIndexViolationError && error.message.indexOf(constraintName) >= 0) { // Cast the error into the proper parse error throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'A duplicate value for a field with unique values was provided'); } else { diff --git a/src/ParseServer.js b/src/ParseServer.js index ca87883673..e4b325c521 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -9,10 +9,6 @@ var batch = require('./batch'), path = require('path'), authDataManager = require('./authDataManager'); -if (!global._babelPolyfill) { - require('babel-polyfill'); -} - import defaults from './defaults'; import * as logging from './logger'; import AppCache from './cache'; From 446ee353d91445952d1c5a3f79335ed0335d1f81 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 18 Sep 2016 14:52:37 -0400 Subject: [PATCH 2/5] Better support for polyfilling --- package.json | 6 +++--- spec/helper.js | 6 +++--- src/Controllers/DatabaseController.js | 14 ++++++++++++-- src/Controllers/SchemaController.js | 6 +++--- src/LiveQuery/ParseLiveQueryServer.js | 7 ++++--- src/Routers/ClassesRouter.js | 4 ++-- 6 files changed, 27 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 78843e2ed3..d0d5a5022a 100644 --- a/package.json +++ b/package.json @@ -65,10 +65,10 @@ "scripts": { "dev": "npm run build && node bin/dev", "build": "babel src/ -d lib/", - "test": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 NODE_ENV=test TESTING=1 babel-node $COVERAGE_OPTION ./node_modules/jasmine/bin/jasmine.js", - "test:win": "npm run pretest && cross-env NODE_ENV=test TESTING=1 babel-node ./node_modules/jasmine/bin/jasmine.js && npm run posttest", + "test": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 NODE_ENV=test TESTING=1 node $COVERAGE_OPTION ./node_modules/jasmine/bin/jasmine.js", + "test:win": "npm run pretest && cross-env NODE_ENV=test TESTING=1 node ./node_modules/jasmine/bin/jasmine.js && npm run posttest", "coverage": "cross-env COVERAGE_OPTION='./node_modules/.bin/istanbul cover' npm test", - "coverage:win": "npm run pretest && cross-env NODE_ENV=test TESTING=1 babel-node ./node_modules/babel-istanbul/lib/cli.js cover ./node_modules/jasmine/bin/jasmine.js && npm run posttest", + "coverage:win": "npm run pretest && cross-env NODE_ENV=test TESTING=1 node ./node_modules/babel-istanbul/lib/cli.js cover ./node_modules/jasmine/bin/jasmine.js && npm run posttest", "start": "node ./bin/parse-server", "prepublish": "npm run build" }, diff --git a/spec/helper.js b/spec/helper.js index 28fe2a2b90..27c7559036 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -214,7 +214,7 @@ afterEach(function(done) { } else { // Other system classes will break Parse.com, so make sure that we don't save anything to _SCHEMA that will // break it. - return ['_User', '_Installation', '_Role', '_Session', '_Product'].includes(className); + return ['_User', '_Installation', '_Role', '_Session', '_Product'].indexOf(className) >= 0; } }}); }); @@ -387,7 +387,7 @@ global.jfail = function(err) { } global.it_exclude_dbs = excluded => { - if (excluded.includes(process.env.PARSE_SERVER_TEST_DB)) { + if (excluded.indexOf(process.env.PARSE_SERVER_TEST_DB) >= 0) { return xit; } else { return it; @@ -395,7 +395,7 @@ global.it_exclude_dbs = excluded => { } global.fit_exclude_dbs = excluded => { - if (excluded.includes(process.env.PARSE_SERVER_TEST_DB)) { + if (excluded.indexOf(process.env.PARSE_SERVER_TEST_DB) >= 0) { return xit; } else { return fit; diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 618d304795..934fc488e3 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -44,6 +44,11 @@ const transformObjectACL = ({ ACL, ...result }) => { } const specialQuerykeys = ['$and', '$or', '_rperm', '_wperm', '_perishable_token', '_email_verify_token', '_email_verify_token_expires_at', '_account_lockout_expires_at', '_failed_login_count']; + +const isSpecialQueryKey = key => { + return specialQuerykeys.indexOf(key) >= 0; +} + const validateQuery = query => { if (query.ACL) { throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Cannot query on ACL.'); @@ -73,7 +78,7 @@ const validateQuery = query => { } } } - if (!specialQuerykeys.includes(key) && !key.match(/^[a-zA-Z][a-zA-Z0-9_\.]*$/)) { + if (!isSpecialQueryKey(key) && !key.match(/^[a-zA-Z][a-zA-Z0-9_\.]*$/)) { throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid key name: ${key}`); } }); @@ -185,6 +190,11 @@ const filterSensitiveData = (isMaster, aclGroup, className, object) => { // one of the provided strings must provide the caller with // write permissions. const specialKeysForUpdate = ['_hashed_password', '_perishable_token', '_email_verify_token', '_email_verify_token_expires_at', '_account_lockout_expires_at', '_failed_login_count']; + +const isSpecialUpdateKey = key => { + return specialKeysForUpdate.indexOf(key) >= 0; +} + DatabaseController.prototype.update = function(className, query, update, { acl, many, @@ -227,7 +237,7 @@ DatabaseController.prototype.update = function(className, query, update, { throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name for update: ${fieldName}`); } fieldName = fieldName.split('.')[0]; - if (!SchemaController.fieldNameIsValid(fieldName) && !specialKeysForUpdate.includes(fieldName)) { + if (!SchemaController.fieldNameIsValid(fieldName) && !isSpecialUpdateKey(fieldName)) { throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name for update: ${fieldName}`); } }); diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index d84f45ede2..b4d8252a46 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -218,7 +218,7 @@ const validNonRelationOrPointerTypes = [ ]; // Returns an error suitable for throwing if the type is invalid const fieldTypeIsInvalid = ({ type, targetClass }) => { - if (['Pointer', 'Relation'].includes(type)) { + if (['Pointer', 'Relation'].indexOf(type) >= 0) { if (!targetClass) { return new Parse.Error(135, `type ${type} needs a class name`); } else if (typeof targetClass !== 'string') { @@ -232,7 +232,7 @@ const fieldTypeIsInvalid = ({ type, targetClass }) => { if (typeof type !== 'string') { return invalidJsonError; } - if (!validNonRelationOrPointerTypes.includes(type)) { + if (validNonRelationOrPointerTypes.indexOf(type) < 0) { return new Parse.Error(Parse.Error.INCORRECT_TYPE, `invalid field type: ${type}`); } return undefined; @@ -541,7 +541,7 @@ export default class SchemaController { validateSchemaData(className, fields, classLevelPermissions, existingFieldNames) { for (let fieldName in fields) { - if (!existingFieldNames.includes(fieldName)) { + if (existingFieldNames.indexOf(fieldName) < 0) { if (!fieldNameIsValid(fieldName)) { return { code: Parse.Error.INVALID_KEY_NAME, diff --git a/src/LiveQuery/ParseLiveQueryServer.js b/src/LiveQuery/ParseLiveQueryServer.js index 6f737f6d58..7b752736b1 100644 --- a/src/LiveQuery/ParseLiveQueryServer.js +++ b/src/LiveQuery/ParseLiveQueryServer.js @@ -8,6 +8,7 @@ import RequestSchema from './RequestSchema'; import { matchesQuery, queryHash } from './QueryTools'; import { ParsePubSub } from './ParsePubSub'; import { SessionTokenCache } from './SessionTokenCache'; +import _ from 'lodash'; class ParseLiveQueryServer { clientId: number; @@ -116,7 +117,7 @@ class ParseLiveQueryServer { if (!isSubscriptionMatched) { continue; } - for (let [clientId, requestIds] of subscription.clientRequestIds.entries()) { + for (let [clientId, requestIds] of _.entries(subscription.clientRequestIds)) { let client = this.clients.get(clientId); if (typeof client === 'undefined') { continue; @@ -159,7 +160,7 @@ class ParseLiveQueryServer { for (let subscription of classSubscriptions.values()) { let isOriginalSubscriptionMatched = this._matchesSubscription(originalParseObject, subscription); let isCurrentSubscriptionMatched = this._matchesSubscription(currentParseObject, subscription); - for (let [clientId, requestIds] of subscription.clientRequestIds.entries()) { + for (let [clientId, requestIds] of _.entries(subscription.clientRequestIds)) { let client = this.clients.get(clientId); if (typeof client === 'undefined') { continue; @@ -269,7 +270,7 @@ class ParseLiveQueryServer { this.clients.delete(clientId); // Delete client from subscriptions - for (let [requestId, subscriptionInfo] of client.subscriptionInfos.entries()) { + for (let [requestId, subscriptionInfo] of _.entries(client.subscriptionInfos)) { let subscription = subscriptionInfo.subscription; subscription.deleteClientSubscription(clientId, requestId); diff --git a/src/Routers/ClassesRouter.js b/src/Routers/ClassesRouter.js index e1d186c1ae..ee0d23ab45 100644 --- a/src/Routers/ClassesRouter.js +++ b/src/Routers/ClassesRouter.js @@ -1,7 +1,7 @@ import PromiseRouter from '../PromiseRouter'; import rest from '../rest'; - +import _ from 'lodash'; import url from 'url'; const ALLOWED_GET_QUERY_KEYS = ['keys', 'include']; @@ -115,7 +115,7 @@ export class ClassesRouter extends PromiseRouter { static JSONFromQuery(query) { let json = {}; - for (let [key, value] of Object.entries(query)) { + for (let [key, value] of _.entries(query)) { try { json[key] = JSON.parse(value); } catch (e) { From 18ee1f70c843d9eb4b563ec7279f293bb00d4223 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 18 Sep 2016 14:59:13 -0400 Subject: [PATCH 3/5] Removes unnecessary log --- src/Controllers/SchemaController.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index b4d8252a46..316d3da362 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -520,7 +520,6 @@ export default class SchemaController { } }) .catch(error => { - console.error(error); // The schema still doesn't validate. Give up throw new Parse.Error(Parse.Error.INVALID_JSON, 'schema class name does not revalidate'); }); From 53eb24e16434991412336af002fb9f785a3c56bd Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 18 Sep 2016 14:59:41 -0400 Subject: [PATCH 4/5] Adds killswitch if tests are polyfilled --- spec/helper.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/helper.js b/spec/helper.js index 27c7559036..142bd8e670 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -14,6 +14,11 @@ global.on_db = (db, callback, elseCallback) => { } } +if (global._babelPolyfill) { + console.error('We should not use polyfilled tests'); + process.exit(1); +} + var cache = require('../src/cache').default; var express = require('express'); var facebook = require('../src/authDataManager/facebook'); From b0607fa5b94f41ad55a1f9df56d6ae2756749e20 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 18 Sep 2016 15:05:46 -0400 Subject: [PATCH 5/5] Reverts usage of includes on strings --- src/Adapters/Storage/Mongo/MongoTransform.js | 4 ++-- src/Adapters/Storage/Postgres/PostgresStorageAdapter.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index f4ecd32daf..83bb840d92 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -99,7 +99,7 @@ const transformKeyValueForUpdate = (className, restKey, restValue, parseFormatSc } const transformInteriorValue = restValue => { - if (restValue !== null && typeof restValue === 'object' && Object.keys(restValue).some(key => key.indexOf('$') >= 0 || key.indexOf('.') >= 0)) { + if (restValue !== null && typeof restValue === 'object' && Object.keys(restValue).some(key => key.includes('$')|| key.includes('.'))) { throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters"); } // Handle atomic values @@ -293,7 +293,7 @@ const parseObjectKeyValueToMongoObjectKeyValue = (restKey, restValue, schema) => } // Handle normal objects by recursing - if (Object.keys(restValue).some(key => key.indexOf('$') >= 0 || key.indexOf('.') >= 0)) { + if (Object.keys(restValue).some(key => key.includes('$') || key.includes('.'))) { throw new Parse.Error(Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters"); } value = _.mapValues(restValue, transformInteriorValue); diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index 994f395404..7e0b8edd80 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -415,7 +415,7 @@ export class PostgresStorageAdapter { return toParseSchema(schema) }) .catch((err) => { - if (err.code === PostgresUniqueIndexViolationError && err.detail.indexOf(className) >= 0) { + if (err.code === PostgresUniqueIndexViolationError && err.detail.includes(className)) { throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`) } throw err; @@ -445,7 +445,7 @@ export class PostgresStorageAdapter { relations.push(fieldName) return; } - if (['_rperm', '_wperm'].indexOf(fieldName) >=0 ) { + if (['_rperm', '_wperm'].indexOf(fieldName) >= 0) { parseType.contents = { type: 'String' }; } valuesArray.push(fieldName); @@ -1031,9 +1031,9 @@ export class PostgresStorageAdapter { const qs = `ALTER TABLE $1:name ADD CONSTRAINT $2:name UNIQUE (${constraintPatterns.join(',')})`; return this._client.none(qs,[className, constraintName, ...fieldNames]) .catch(error => { - if (error.code === PostgresDuplicateRelationError && error.message.indexOf(constraintName) >= 0) { + if (error.code === PostgresDuplicateRelationError && error.message.includes(constraintName)) { // Index already exists. Ignore error. - } else if (error.code === PostgresUniqueIndexViolationError && error.message.indexOf(constraintName) >= 0) { + } else if (error.code === PostgresUniqueIndexViolationError && error.message.includes(constraintName)) { // Cast the error into the proper parse error throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'A duplicate value for a field with unique values was provided'); } else {