From fdc85cc5c2ff20e5fd249f12866cd21c69490b7d Mon Sep 17 00:00:00 2001 From: Eric Watson Date: Thu, 4 Feb 2016 11:39:11 -0600 Subject: [PATCH 01/16] Ignore '_metadata', convert 'map' to Object Legacy Parse platform databases have additional fields that database-to-api-response conversion. This commit accounts for - the '_metadata' field, which doesn't appear in the api-response version of Schema, and whose value (an object), crashes the conversion function (which expects only string values) - the 'map' type, which appears in legacy database representations to describe Objects --- schemas.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/schemas.js b/schemas.js index 88b0da38..9b74f270 100644 --- a/schemas.js +++ b/schemas.js @@ -23,6 +23,7 @@ function mongoFieldTypeToApiResponseType(type) { case 'string': return {type: 'String'}; case 'boolean': return {type: 'Boolean'}; case 'date': return {type: 'Date'}; + case 'map': case 'object': return {type: 'Object'}; case 'array': return {type: 'Array'}; case 'geopoint': return {type: 'GeoPoint'}; @@ -31,7 +32,7 @@ function mongoFieldTypeToApiResponseType(type) { } function mongoSchemaAPIResponseFields(schema) { - fieldNames = Object.keys(schema).filter(key => key !== '_id'); + fieldNames = Object.keys(schema).filter(key => key !== '_id' && key !== '_metadata'); response = {}; fieldNames.forEach(fieldName => { response[fieldName] = mongoFieldTypeToApiResponseType(schema[fieldName]); From 53dfca2d6f899086bc988202afc00a8e494580fb Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Fri, 5 Feb 2016 14:56:11 -0800 Subject: [PATCH 02/16] First part of schemas POST --- schemas.js | 50 +++++++++++++- spec/schemas.spec.js | 161 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 196 insertions(+), 15 deletions(-) diff --git a/schemas.js b/schemas.js index 875967cd..e374a127 100644 --- a/schemas.js +++ b/schemas.js @@ -1,7 +1,9 @@ // schemas.js var express = require('express'), - PromiseRouter = require('./PromiseRouter'); + Parse = require('parse/node').Parse, + PromiseRouter = require('./PromiseRouter'), + Schema = require('./Schema'); var router = new PromiseRouter(); @@ -54,7 +56,7 @@ function getAllSchemas(req) { if (!req.auth.isMaster) { return Promise.resolve({ status: 401, - response: {error: 'unauthorized'}, + response: {error: 'master key not specified'}, }); } return req.config.database.collection('_SCHEMA') @@ -83,7 +85,51 @@ function getOneSchema(req) { })); } +function createSchema(req) { + if (!req.auth.isMaster) { + return Promise.resolve({ + status: 401, + response: {error: 'master key not specified'}, + }); + } + if (req.params.className && req.body.className) { + if (req.params.className != req.body.className) { + return Promise.resolve({ + status: 400, + response: { + code: Parse.Error.INVALID_CLASS_NAME, + error: 'class name mismatch between ' + req.body.className + ' and ' + req.params.className, + }, + }); + } + } + var className = req.params.className || req.body.className; + if (!className) { + return Promise.resolve({ + status: 400, + response: { + code: 135, + error: 'POST ' + req.path + ' needs class name', + }, + }); + } + return req.config.database.collection('_SCHEMA') + .then(coll => Schema.load(coll)) + .then(schema => schema.validateClassName(req.body.className)) + .catch(error => { + console.log(arguments); + return {response: error}; + }) + .then(newSchema => { + for (key in newSchema.data) { + } + return {response: {}}; + }); +} + router.route('GET', '/schemas', getAllSchemas); router.route('GET', '/schemas/:className', getOneSchema); +router.route('POST', '/schemas', createSchema); +router.route('POST', '/schemas/:className', createSchema); module.exports = router; diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index 8c7434da..92f9716e 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -1,5 +1,7 @@ +var Parse = require('parse/node').Parse; var request = require('request'); var dd = require('deep-diff'); + var hasAllPODobject = () => { var obj = new Parse.Object('HasAllPOD'); obj.set('aNumber', 5); @@ -56,17 +58,30 @@ var expectedResponseforHasPointersAndRelations = { }, } +var noAuthHeaders = { + 'X-Parse-Application-Id': 'test', +}; + +var restKeyHeaders = { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', +}; + +var masterKeyHeaders = { + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', +}; + describe('schemas', () => { it('requires the master key to get all schemas', (done) => { request.get({ url: 'http://localhost:8378/1/schemas', json: true, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - }, + headers: noAuthHeaders, }, (error, response, body) => { - expect(response.statusCode).toEqual(401); + //api.parse.com uses status code 401, but due to the lack of keys + //being necessary in parse-server, 403 makes more sense + expect(response.statusCode).toEqual(403); expect(body.error).toEqual('unauthorized'); done(); }); @@ -87,14 +102,23 @@ describe('schemas', () => { }); }); + it('asks for the master key if you use the rest key', (done) => { + request.get({ + url: 'http://localhost:8378/1/schemas', + json: true, + headers: restKeyHeaders, + }, (error, response, body) => { + expect(response.statusCode).toEqual(401); + expect(body.error).toEqual('master key not specified'); + done(); + }); + }); + it('responds with empty list when there are no schemas', done => { request.get({ url: 'http://localhost:8378/1/schemas', json: true, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', - }, + headers: masterKeyHeaders, }, (error, response, body) => { expect(body.results).toEqual([]); done(); @@ -113,10 +137,7 @@ describe('schemas', () => { request.get({ url: 'http://localhost:8378/1/schemas', json: true, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', - }, + headers: masterKeyHeaders, }, (error, response, body) => { var expected = { results: [expectedResponseForHasAllPOD,expectedResponseforHasPointersAndRelations] @@ -164,4 +185,118 @@ describe('schemas', () => { }); }); }); + + it('requires the master key to create a schema', done => { + request.post({ + url: 'http://localhost:8378/1/schemas', + json: true, + headers: noAuthHeaders, + body: { + className: 'MyClass', + } + }, (error, response, body) => { + expect(response.statusCode).toEqual(403); + expect(body.error).toEqual('unauthorized'); + done(); + }); + }); + + it('asks for the master key if you use the rest key', done => { + request.post({ + url: 'http://localhost:8378/1/schemas', + json: true, + headers: restKeyHeaders, + body: { + className: 'MyClass', + }, + }, (error, response, body) => { + expect(response.statusCode).toEqual(401); + expect(body.error).toEqual('master key not specified'); + done(); + }); + }); + + it('sends an error if you use mismatching class names', done => { + request.post({ + url: 'http://localhost:8378/1/schemas/A', + headers: masterKeyHeaders, + json: true, + body: { + className: 'B', + } + }, (error, response, body) => { + expect(response.statusCode).toEqual(400); + expect(body).toEqual({ + code: Parse.Error.INVALID_CLASS_NAME, + error: 'class name mismatch between B and A', + }); + done(); + }); + }); + + it('sends an error if you use no class name', done => { + request.post({ + url: 'http://localhost:8378/1/schemas', + headers: masterKeyHeaders, + json: true, + body: {}, + }, (error, response, body) => { + expect(response.statusCode).toEqual(400); + expect(body).toEqual({ + code: 135, + error: 'POST /schemas needs class name', + }); + done(); + }) + }); + + it('sends an error if you try to create the same class twice', done => { + request.post({ + url: 'http://localhost:8378/1/schemas', + headers: masterKeyHeaders, + json: true, + body: { + className: 'A', + }, + }, (error, response, body) => { + expect(error).toEqual(null); + request.post({ + url: 'http://localhost:8378/1/schemas', + headers: masterKeyHeaders, + json: true, + body: { + className: 'A', + } + }, (error, response, body) => { + expect(response.statusCode).toEqual(400); + expect(body).toEqual({ + code: Parse.Error.INVALID_CLASS_NAME, + error: 'class A already exists', + }); + done(); + }); + }); + }); + + it('responds with all fields when you create a class', done => { + request.post({ + url: 'http://localhost:8378/1/schemas', + headers: masterKeyHeaders, + json: true, + body: { + className: "NewClass", + } + }, (error, response, body) => { + expect(body).toEqual({ + className: 'NewClass', + fields: { + ACL: {type: 'ACL'}, + createdAt: {type: 'Date'}, + updatedAt: {type: 'Date'}, + objectId: {type: 'String'}, + } + }); + done(); + }); + }); }); From 0b5cfb2a6ab7797812e3387f8e1b506a1dd58e84 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Fri, 5 Feb 2016 18:36:23 -0800 Subject: [PATCH 03/16] Schemas POST fix tests --- schemas.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/schemas.js b/schemas.js index e374a127..6849a6a4 100644 --- a/schemas.js +++ b/schemas.js @@ -113,18 +113,13 @@ function createSchema(req) { }, }); } - return req.config.database.collection('_SCHEMA') - .then(coll => Schema.load(coll)) - .then(schema => schema.validateClassName(req.body.className)) - .catch(error => { - console.log(arguments); - return {response: error}; - }) - .then(newSchema => { - for (key in newSchema.data) { - } - return {response: {}}; - }); + return req.config.database.loadSchema() + .then(schema => schema.addClassIfNotExists(className, req.body.fields)) + .then(result => ({ response: mongoSchemaToSchemaAPIResponse(result) })) + .catch(error => ({ + status: 400, + response: error, + })); } router.route('GET', '/schemas', getAllSchemas); From b9bc904aad58d3eeae469b817e1ed1ea5ccf2dab Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Fri, 5 Feb 2016 20:38:58 -0800 Subject: [PATCH 04/16] Add tests to get to 100% branch coverage --- spec/schemas.spec.js | 51 ++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index 92f9716e..2378caf5 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -18,7 +18,7 @@ var hasAllPODobject = () => { return obj; } -var expectedResponseForHasAllPOD = { +var plainOldDataSchema = { className: 'HasAllPOD', fields: { //Default fields @@ -38,7 +38,7 @@ var expectedResponseForHasAllPOD = { }, }; -var expectedResponseforHasPointersAndRelations = { +var pointersAndRelationsSchema = { className: 'HasPointersAndRelations', fields: { //Default fields @@ -91,10 +91,7 @@ describe('schemas', () => { request.get({ url: 'http://localhost:8378/1/schemas/SomeSchema', json: true, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - }, + headers: restKeyHeaders, }, (error, response, body) => { expect(response.statusCode).toEqual(401); expect(body.error).toEqual('unauthorized'); @@ -140,7 +137,7 @@ describe('schemas', () => { headers: masterKeyHeaders, }, (error, response, body) => { var expected = { - results: [expectedResponseForHasAllPOD,expectedResponseforHasPointersAndRelations] + results: [plainOldDataSchema,pointersAndRelationsSchema] }; expect(body).toEqual(expected); done(); @@ -154,12 +151,9 @@ describe('schemas', () => { request.get({ url: 'http://localhost:8378/1/schemas/HasAllPOD', json: true, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', - }, + headers: masterKeyHeaders, }, (error, response, body) => { - expect(body).toEqual(expectedResponseForHasAllPOD); + expect(body).toEqual(plainOldDataSchema); done(); }); }); @@ -171,10 +165,7 @@ describe('schemas', () => { request.get({ url: 'http://localhost:8378/1/schemas/HASALLPOD', json: true, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', - }, + headers: masterKeyHeaders, }, (error, response, body) => { expect(response.statusCode).toEqual(400); expect(body).toEqual({ @@ -283,6 +274,34 @@ describe('schemas', () => { url: 'http://localhost:8378/1/schemas', headers: masterKeyHeaders, json: true, + body: { + className: "NewClass", + fields: { + foo: {type: 'Number'}, + ptr: {type: 'Pointer', targetClass: 'SomeClass'} + } + } + }, (error, response, body) => { + expect(body).toEqual({ + className: 'NewClass', + fields: { + ACL: {type: 'ACL'}, + createdAt: {type: 'Date'}, + updatedAt: {type: 'Date'}, + objectId: {type: 'String'}, + foo: {type: 'Number'}, + ptr: {type: 'Pointer', targetClass: 'SomeClass'}, + } + }); + done(); + }); + }); + + it('lets you specify class name in both places', done => { + request.post({ + url: 'http://localhost:8378/1/schemas/NewClass', + headers: masterKeyHeaders, + json: true, body: { className: "NewClass", } From 4f05cfc5629503c6b89c514303c29bafd84f9826 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Sat, 6 Feb 2016 18:49:16 -0800 Subject: [PATCH 05/16] Fixed querying objects with equal constraint on array columns. --- spec/ParseQuery.spec.js | 18 +++++++++++++++++- transform.js | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index 88c1f53a..f5b6dc1a 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -2056,7 +2056,7 @@ describe('Parse.Query testing', () => { }); }); - it('query match on array value', (done) => { + it('query match on array with single object', (done) => { var target = {__type: 'Pointer', className: 'TestObject', objectId: 'abc123'}; var obj = new Parse.Object('TestObject'); obj.set('someObjs', [target]); @@ -2072,4 +2072,20 @@ describe('Parse.Query testing', () => { }); }); + it('query match on array with multiple objects', (done) => { + var target1 = {__type: 'Pointer', className: 'TestObject', objectId: 'abc'}; + var target2 = {__type: 'Pointer', className: 'TestObject', objectId: '123'}; + var obj= new Parse.Object('TestObject'); + obj.set('someObjs', [target1, target2]); + obj.save().then(() => { + var query = new Parse.Query('TestObject'); + query.equalTo('someObjs', target1); + return query.find(); + }).then((results) => { + expect(results.length).toEqual(1); + done(); + }, (error) => { + console.log(error); + }); + }); }); diff --git a/transform.js b/transform.js index 051bc75a..48b02c75 100644 --- a/transform.js +++ b/transform.js @@ -126,7 +126,7 @@ function transformKeyValue(schema, className, restKey, restValue, options) { if (inArray && options.query && !(restValue instanceof Array)) { return { - key: key, value: [restValue] + key: key, value: { '$all' : [restValue] } }; } From 86d6bc268f80e7666270e8eeea222fcbf052370f Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 7 Feb 2016 09:01:49 -0500 Subject: [PATCH 06/16] Adds cache for mongodb directories --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index e34b4a44..dc081ced 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,4 +8,7 @@ node_js: env: - MONGODB_VERSION=2.6.11 - MONGODB_VERSION=3.0.8 +cache: + directories: + - $HOME/.mongodb/versions/downloads after_success: ./node_modules/.bin/codecov From f504cbb7865349b2f2cbe3a791ea2a83d395a58a Mon Sep 17 00:00:00 2001 From: Taylor Stine Date: Mon, 8 Feb 2016 08:05:58 -0500 Subject: [PATCH 07/16] reverted uri encoding --- ExportAdapter.js | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/ExportAdapter.js b/ExportAdapter.js index df417ac8..c21042fb 100644 --- a/ExportAdapter.js +++ b/ExportAdapter.js @@ -34,21 +34,8 @@ ExportAdapter.prototype.connect = function() { return this.connectionPromise; } - //http://regexr.com/3cncm - if (!this.mongoURI.match(/^mongodb:\/\/((.+):(.+)@)?([^:@]+):{0,1}([^:]+)\/(.+?)$/gm)) { - throw new Error("Invalid mongoURI: " + this.mongoURI) - } - var usernameStart = this.mongoURI.indexOf('://') + 3; - var lastAtIndex = this.mongoURI.lastIndexOf('@'); - var encodedMongoURI = this.mongoURI; - var split = null; - if (lastAtIndex > 0) { - split = this.mongoURI.slice(usernameStart, lastAtIndex).split(':'); - encodedMongoURI = this.mongoURI.slice(0, usernameStart) + encodeURIComponent(split[0]) + ':' + encodeURIComponent(split[1]) + this.mongoURI.slice(lastAtIndex); - } - this.connectionPromise = Promise.resolve().then(() => { - return MongoClient.connect(encodedMongoURI, {uri_decode_auth:true}); + return MongoClient.connect(this.mongoURI); }).then((db) => { this.db = db; }); From b989bbcaae264c45bbebb2a73977678a629ed9b2 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Mon, 8 Feb 2016 19:41:07 -0800 Subject: [PATCH 08/16] Move all source files into 'src' folder. --- APNS.js => src/APNS.js | 0 Auth.js => src/Auth.js | 0 Config.js => src/Config.js | 0 DatabaseAdapter.js => src/DatabaseAdapter.js | 0 ExportAdapter.js => src/ExportAdapter.js | 0 FilesAdapter.js => src/FilesAdapter.js | 0 GCM.js => src/GCM.js | 0 GridStoreAdapter.js => src/GridStoreAdapter.js | 0 PromiseRouter.js => src/PromiseRouter.js | 0 README.md => src/README.md | 0 RestQuery.js => src/RestQuery.js | 0 RestWrite.js => src/RestWrite.js | 0 S3Adapter.js => src/S3Adapter.js | 0 Schema.js => src/Schema.js | 2 +- analytics.js => src/analytics.js | 0 batch.js => src/batch.js | 0 cache.js => src/cache.js | 0 classes.js => src/classes.js | 0 {cloud => src/cloud}/main.js | 0 facebook.js => src/facebook.js | 0 files.js => src/files.js | 0 functions.js => src/functions.js | 0 httpRequest.js => src/httpRequest.js | 0 index.js => src/index.js | 0 installations.js => src/installations.js | 0 middlewares.js => src/middlewares.js | 0 password.js => src/password.js | 0 push.js => src/push.js | 0 rest.js => src/rest.js | 0 roles.js => src/roles.js | 0 schemas.js => src/schemas.js | 0 sessions.js => src/sessions.js | 0 testing-routes.js => src/testing-routes.js | 0 transform.js => src/transform.js | 2 +- triggers.js => src/triggers.js | 0 users.js => src/users.js | 0 36 files changed, 2 insertions(+), 2 deletions(-) rename APNS.js => src/APNS.js (100%) rename Auth.js => src/Auth.js (100%) rename Config.js => src/Config.js (100%) rename DatabaseAdapter.js => src/DatabaseAdapter.js (100%) rename ExportAdapter.js => src/ExportAdapter.js (100%) rename FilesAdapter.js => src/FilesAdapter.js (100%) rename GCM.js => src/GCM.js (100%) rename GridStoreAdapter.js => src/GridStoreAdapter.js (100%) rename PromiseRouter.js => src/PromiseRouter.js (100%) rename README.md => src/README.md (100%) rename RestQuery.js => src/RestQuery.js (100%) rename RestWrite.js => src/RestWrite.js (100%) rename S3Adapter.js => src/S3Adapter.js (100%) rename Schema.js => src/Schema.js (99%) rename analytics.js => src/analytics.js (100%) rename batch.js => src/batch.js (100%) rename cache.js => src/cache.js (100%) rename classes.js => src/classes.js (100%) rename {cloud => src/cloud}/main.js (100%) rename facebook.js => src/facebook.js (100%) rename files.js => src/files.js (100%) rename functions.js => src/functions.js (100%) rename httpRequest.js => src/httpRequest.js (100%) rename index.js => src/index.js (100%) rename installations.js => src/installations.js (100%) rename middlewares.js => src/middlewares.js (100%) rename password.js => src/password.js (100%) rename push.js => src/push.js (100%) rename rest.js => src/rest.js (100%) rename roles.js => src/roles.js (100%) rename schemas.js => src/schemas.js (100%) rename sessions.js => src/sessions.js (100%) rename testing-routes.js => src/testing-routes.js (100%) rename transform.js => src/transform.js (99%) rename triggers.js => src/triggers.js (100%) rename users.js => src/users.js (100%) diff --git a/APNS.js b/src/APNS.js similarity index 100% rename from APNS.js rename to src/APNS.js diff --git a/Auth.js b/src/Auth.js similarity index 100% rename from Auth.js rename to src/Auth.js diff --git a/Config.js b/src/Config.js similarity index 100% rename from Config.js rename to src/Config.js diff --git a/DatabaseAdapter.js b/src/DatabaseAdapter.js similarity index 100% rename from DatabaseAdapter.js rename to src/DatabaseAdapter.js diff --git a/ExportAdapter.js b/src/ExportAdapter.js similarity index 100% rename from ExportAdapter.js rename to src/ExportAdapter.js diff --git a/FilesAdapter.js b/src/FilesAdapter.js similarity index 100% rename from FilesAdapter.js rename to src/FilesAdapter.js diff --git a/GCM.js b/src/GCM.js similarity index 100% rename from GCM.js rename to src/GCM.js diff --git a/GridStoreAdapter.js b/src/GridStoreAdapter.js similarity index 100% rename from GridStoreAdapter.js rename to src/GridStoreAdapter.js diff --git a/PromiseRouter.js b/src/PromiseRouter.js similarity index 100% rename from PromiseRouter.js rename to src/PromiseRouter.js diff --git a/README.md b/src/README.md similarity index 100% rename from README.md rename to src/README.md diff --git a/RestQuery.js b/src/RestQuery.js similarity index 100% rename from RestQuery.js rename to src/RestQuery.js diff --git a/RestWrite.js b/src/RestWrite.js similarity index 100% rename from RestWrite.js rename to src/RestWrite.js diff --git a/S3Adapter.js b/src/S3Adapter.js similarity index 100% rename from S3Adapter.js rename to src/S3Adapter.js diff --git a/Schema.js b/src/Schema.js similarity index 99% rename from Schema.js rename to src/Schema.js index 25e301cd..69c923dd 100644 --- a/Schema.js +++ b/src/Schema.js @@ -17,7 +17,7 @@ var Parse = require('parse/node').Parse; var transform = require('./transform'); -defaultColumns = { +var defaultColumns = { // Contain the default columns for every parse object type (except _Join collection) _Default: { "objectId": {type:'String'}, diff --git a/analytics.js b/src/analytics.js similarity index 100% rename from analytics.js rename to src/analytics.js diff --git a/batch.js b/src/batch.js similarity index 100% rename from batch.js rename to src/batch.js diff --git a/cache.js b/src/cache.js similarity index 100% rename from cache.js rename to src/cache.js diff --git a/classes.js b/src/classes.js similarity index 100% rename from classes.js rename to src/classes.js diff --git a/cloud/main.js b/src/cloud/main.js similarity index 100% rename from cloud/main.js rename to src/cloud/main.js diff --git a/facebook.js b/src/facebook.js similarity index 100% rename from facebook.js rename to src/facebook.js diff --git a/files.js b/src/files.js similarity index 100% rename from files.js rename to src/files.js diff --git a/functions.js b/src/functions.js similarity index 100% rename from functions.js rename to src/functions.js diff --git a/httpRequest.js b/src/httpRequest.js similarity index 100% rename from httpRequest.js rename to src/httpRequest.js diff --git a/index.js b/src/index.js similarity index 100% rename from index.js rename to src/index.js diff --git a/installations.js b/src/installations.js similarity index 100% rename from installations.js rename to src/installations.js diff --git a/middlewares.js b/src/middlewares.js similarity index 100% rename from middlewares.js rename to src/middlewares.js diff --git a/password.js b/src/password.js similarity index 100% rename from password.js rename to src/password.js diff --git a/push.js b/src/push.js similarity index 100% rename from push.js rename to src/push.js diff --git a/rest.js b/src/rest.js similarity index 100% rename from rest.js rename to src/rest.js diff --git a/roles.js b/src/roles.js similarity index 100% rename from roles.js rename to src/roles.js diff --git a/schemas.js b/src/schemas.js similarity index 100% rename from schemas.js rename to src/schemas.js diff --git a/sessions.js b/src/sessions.js similarity index 100% rename from sessions.js rename to src/sessions.js diff --git a/testing-routes.js b/src/testing-routes.js similarity index 100% rename from testing-routes.js rename to src/testing-routes.js diff --git a/transform.js b/src/transform.js similarity index 99% rename from transform.js rename to src/transform.js index 48b02c75..802bf075 100644 --- a/transform.js +++ b/src/transform.js @@ -21,7 +21,7 @@ var Parse = require('parse/node').Parse; // validate: true indicates that key names are to be validated. // // Returns an object with {key: key, value: value}. -function transformKeyValue(schema, className, restKey, restValue, options) { +export function transformKeyValue(schema, className, restKey, restValue, options) { options = options || {}; // Check if the schema is known since it's a built-in field. diff --git a/triggers.js b/src/triggers.js similarity index 100% rename from triggers.js rename to src/triggers.js diff --git a/users.js b/src/users.js similarity index 100% rename from users.js rename to src/users.js From cc481911119862fdda0de7a03ac4e8e1ec8b0453 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Mon, 8 Feb 2016 19:42:00 -0800 Subject: [PATCH 09/16] Add .babelrc --- .babelrc | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .babelrc diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..3c078e9f --- /dev/null +++ b/.babelrc @@ -0,0 +1,5 @@ +{ + "presets": [ + "es2015" + ] +} From 7b270c2c83e60e95397ad70175697d5fa5f72e56 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Mon, 8 Feb 2016 19:42:20 -0800 Subject: [PATCH 10/16] Add lib folder to ignore file. --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2d9748d6..318fed20 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,7 @@ node_modules *~ # WebStorm/IntelliJ -.idea \ No newline at end of file +.idea + +# Babel.js +lib/ From 07aa00f382263425f445ef18f8976fd3f74ca5a9 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Mon, 8 Feb 2016 20:19:49 -0800 Subject: [PATCH 11/16] Fix invalid JavaScript. --- src/APNS.js | 2 +- src/Schema.js | 25 ++++++++++++------------- src/classes.js | 2 +- src/schemas.js | 4 ++-- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/APNS.js b/src/APNS.js index 5fc73ab0..85c97401 100644 --- a/src/APNS.js +++ b/src/APNS.js @@ -60,7 +60,7 @@ APNS.prototype.send = function(data, deviceTokens) { var generateNotification = function(coreData, expirationTime) { var notification = new apn.notification(); var payload = {}; - for (key in coreData) { + for (var key in coreData) { switch (key) { case 'alert': notification.setAlertText(coreData.alert); diff --git a/src/Schema.js b/src/Schema.js index 69c923dd..3656507a 100644 --- a/src/Schema.js +++ b/src/Schema.js @@ -43,13 +43,13 @@ var defaultColumns = { "GCMSenderId": {type:'String'}, "timeZone": {type:'String'}, "localeIdentifier": {type:'String'}, - "badge": {type:'Number'}, + "badge": {type:'Number'} }, // The additional default columns for the _User collection (in addition to DefaultCols) _Role: { "name": {type:'String'}, "users": {type:'Relation',className:'_User'}, - "roles": {type:'Relation',className:'_Role'}, + "roles": {type:'Relation',className:'_Role'} }, // The additional default columns for the _User collection (in addition to DefaultCols) _Session: { @@ -58,9 +58,9 @@ var defaultColumns = { "installationId": {type:'String'}, "sessionToken": {type:'String'}, "expiresAt": {type:'Date'}, - "createdWith": {type:'Object'}, - }, -} + "createdWith": {type:'Object'} + } +}; // Valid classes must: // Be one of _User, _Installation, _Role, _Session OR @@ -221,7 +221,7 @@ Schema.prototype.addClassIfNotExists = function(className, fields) { error: invalidClassNameMessage(className), }); } - for (fieldName in fields) { + for (var fieldName in fields) { if (!fieldNameIsValid(fieldName)) { return Promise.reject({ code: Parse.Error.INVALID_KEY_NAME, @@ -240,18 +240,18 @@ Schema.prototype.addClassIfNotExists = function(className, fields) { _id: className, objectId: 'string', updatedAt: 'string', - createdAt: 'string', + createdAt: 'string' }; - for (fieldName in defaultColumns[className]) { - validatedField = schemaAPITypeToMongoFieldType(defaultColumns[className][fieldName]); + for (var fieldName in defaultColumns[className]) { + var validatedField = schemaAPITypeToMongoFieldType(defaultColumns[className][fieldName]); if (validatedField.code) { return Promise.reject(validatedField); } mongoObject[fieldName] = validatedField.result; } - for (fieldName in fields) { - validatedField = schemaAPITypeToMongoFieldType(fields[fieldName]); + for (var fieldName in fields) { + var validatedField = schemaAPITypeToMongoFieldType(fields[fieldName]); if (validatedField.code) { return Promise.reject(validatedField); } @@ -259,7 +259,6 @@ Schema.prototype.addClassIfNotExists = function(className, fields) { } var geoPoints = Object.keys(mongoObject).filter(key => mongoObject[key] === 'geopoint'); - if (geoPoints.length > 1) { return Promise.reject({ code: Parse.Error.INCORRECT_TYPE, @@ -278,7 +277,7 @@ Schema.prototype.addClassIfNotExists = function(className, fields) { } return Promise.reject(error); }); -} +}; // Returns a promise that resolves successfully to the new schema // object or fails with a reason. diff --git a/src/classes.js b/src/classes.js index 98e94871..f1400914 100644 --- a/src/classes.js +++ b/src/classes.js @@ -42,7 +42,7 @@ function handleFind(req) { req.params.className, body.where, options) .then((response) => { if (response && response.results) { - for (result of response.results) { + for (var result of response.results) { if (result.sessionToken) { result.sessionToken = req.info.sessionToken || result.sessionToken; } diff --git a/src/schemas.js b/src/schemas.js index 6145b7e6..837224ab 100644 --- a/src/schemas.js +++ b/src/schemas.js @@ -34,8 +34,8 @@ function mongoFieldTypeToSchemaAPIType(type) { } function mongoSchemaAPIResponseFields(schema) { - fieldNames = Object.keys(schema).filter(key => key !== '_id' && key !== '_metadata'); - response = fieldNames.reduce((obj, fieldName) => { + var fieldNames = Object.keys(schema).filter(key => key !== '_id' && key !== '_metadata'); + var response = fieldNames.reduce((obj, fieldName) => { obj[fieldName] = mongoFieldTypeToSchemaAPIType(schema[fieldName]) return obj; }, {}); From 3271b45102c0c1952f0ca5b98b8b5fb26673e5a7 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Mon, 8 Feb 2016 20:20:08 -0800 Subject: [PATCH 12/16] Update imports in all specs. --- spec/APNS.spec.js | 2 +- spec/ExportAdapter.spec.js | 2 +- spec/GCM.spec.js | 2 +- spec/ParseACL.spec.js | 3 +++ spec/ParseAPI.spec.js | 2 +- spec/ParseInstallation.spec.js | 10 +++++----- spec/ParseUser.spec.js | 2 +- spec/RestCreate.spec.js | 10 +++++----- spec/RestQuery.spec.js | 8 ++++---- spec/Schema.spec.js | 8 ++++---- spec/helper.js | 8 ++++---- spec/push.spec.js | 2 +- spec/schemas.spec.js | 4 ++-- spec/support/jasmine.json | 2 +- spec/transform.spec.js | 2 +- 15 files changed, 35 insertions(+), 32 deletions(-) diff --git a/spec/APNS.spec.js b/spec/APNS.spec.js index c50bb5c9..72490e97 100644 --- a/spec/APNS.spec.js +++ b/spec/APNS.spec.js @@ -1,4 +1,4 @@ -var APNS = require('../APNS'); +var APNS = require('../src/APNS'); describe('APNS', () => { it('can generate APNS notification', (done) => { diff --git a/spec/ExportAdapter.spec.js b/spec/ExportAdapter.spec.js index 95fbdd21..a4f3f9b6 100644 --- a/spec/ExportAdapter.spec.js +++ b/spec/ExportAdapter.spec.js @@ -1,4 +1,4 @@ -var ExportAdapter = require('../ExportAdapter'); +var ExportAdapter = require('../src/ExportAdapter'); describe('ExportAdapter', () => { it('can be constructed', (done) => { diff --git a/spec/GCM.spec.js b/spec/GCM.spec.js index d7484b0e..4bad883e 100644 --- a/spec/GCM.spec.js +++ b/spec/GCM.spec.js @@ -1,4 +1,4 @@ -var GCM = require('../GCM'); +var GCM = require('../src/GCM'); describe('GCM', () => { it('can generate GCM Payload without expiration time', (done) => { diff --git a/spec/ParseACL.spec.js b/spec/ParseACL.spec.js index fead537e..370550c0 100644 --- a/spec/ParseACL.spec.js +++ b/spec/ParseACL.spec.js @@ -251,6 +251,9 @@ describe('Parse.ACL', () => { equal(results.length, 1); var result = results[0]; ok(result); + if (!result) { + return fail(); + } equal(result.id, object.id); equal(result.getACL().getReadAccess(user), true); equal(result.getACL().getWriteAccess(user), true); diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index 24edf38f..8670bdd2 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -1,7 +1,7 @@ // A bunch of different tests are in here - it isn't very thematic. // It would probably be better to refactor them into different files. -var DatabaseAdapter = require('../DatabaseAdapter'); +var DatabaseAdapter = require('../src/DatabaseAdapter'); var request = require('request'); describe('miscellaneous', function() { diff --git a/spec/ParseInstallation.spec.js b/spec/ParseInstallation.spec.js index 6d8e6162..91bb9a23 100644 --- a/spec/ParseInstallation.spec.js +++ b/spec/ParseInstallation.spec.js @@ -1,12 +1,12 @@ // These tests check the Installations functionality of the REST API. // Ported from installation_collection_test.go -var auth = require('../Auth'); -var cache = require('../cache'); -var Config = require('../Config'); -var DatabaseAdapter = require('../DatabaseAdapter'); +var auth = require('../src/Auth'); +var cache = require('../src/cache'); +var Config = require('../src/Config'); +var DatabaseAdapter = require('../src/DatabaseAdapter'); var Parse = require('parse/node').Parse; -var rest = require('../rest'); +var rest = require('../src/rest'); var config = new Config('test'); var database = DatabaseAdapter.getDatabaseConnection('test'); diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index b364adf1..c9f25bd8 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -6,7 +6,7 @@ // Tests that involve sending password reset emails. var request = require('request'); -var passwordCrypto = require('../password'); +var passwordCrypto = require('../src/password'); describe('Parse.User testing', () => { it("user sign up class method", (done) => { diff --git a/spec/RestCreate.spec.js b/spec/RestCreate.spec.js index 59de11ea..24455507 100644 --- a/spec/RestCreate.spec.js +++ b/spec/RestCreate.spec.js @@ -1,10 +1,10 @@ // These tests check the "create" functionality of the REST API. -var auth = require('../Auth'); -var cache = require('../cache'); -var Config = require('../Config'); -var DatabaseAdapter = require('../DatabaseAdapter'); +var auth = require('../src/Auth'); +var cache = require('../src/cache'); +var Config = require('../src/Config'); +var DatabaseAdapter = require('../src/DatabaseAdapter'); var Parse = require('parse/node').Parse; -var rest = require('../rest'); +var rest = require('../src/rest'); var request = require('request'); var config = new Config('test'); diff --git a/spec/RestQuery.spec.js b/spec/RestQuery.spec.js index 08d01766..b93a07d5 100644 --- a/spec/RestQuery.spec.js +++ b/spec/RestQuery.spec.js @@ -1,8 +1,8 @@ // These tests check the "find" functionality of the REST API. -var auth = require('../Auth'); -var cache = require('../cache'); -var Config = require('../Config'); -var rest = require('../rest'); +var auth = require('../src/Auth'); +var cache = require('../src/cache'); +var Config = require('../src/Config'); +var rest = require('../src/rest'); var config = new Config('test'); var nobody = auth.nobody(config); diff --git a/spec/Schema.spec.js b/spec/Schema.spec.js index ccc83525..636311a6 100644 --- a/spec/Schema.spec.js +++ b/spec/Schema.spec.js @@ -1,6 +1,6 @@ // These tests check that the Schema operates correctly. -var Config = require('../Config'); -var Schema = require('../Schema'); +var Config = require('../src/Config'); +var Schema = require('../src/Schema'); var dd = require('deep-diff'); var config = new Config('test'); @@ -252,7 +252,7 @@ describe('Schema', () => { it('refuses to add fields with invalid pointer types', done => { config.database.loadSchema() .then(schema => schema.addClassIfNotExists('NewClass', { - foo: {type: 'Pointer'}, + foo: {type: 'Pointer'} })) .catch(error => { expect(error.code).toEqual(135); @@ -398,7 +398,7 @@ describe('Schema', () => { config.database.loadSchema() .then(schema => schema.addClassIfNotExists('NewClass', { geo1: {type: 'GeoPoint'}, - geo2: {type: 'GeoPoint'}, + geo2: {type: 'GeoPoint'} })) .catch(error => { expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE); diff --git a/spec/helper.js b/spec/helper.js index cca4d1a5..3e6c6d98 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -2,11 +2,11 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000; -var cache = require('../cache'); -var DatabaseAdapter = require('../DatabaseAdapter'); +var cache = require('../src/cache'); +var DatabaseAdapter = require('../src/DatabaseAdapter'); var express = require('express'); -var facebook = require('../facebook'); -var ParseServer = require('../index').ParseServer; +var facebook = require('../src/facebook'); +var ParseServer = require('../src/index').ParseServer; var databaseURI = process.env.DATABASE_URI; var cloudMain = process.env.CLOUD_CODE_MAIN || './cloud/main.js'; diff --git a/spec/push.spec.js b/spec/push.spec.js index ba5b533b..a2ea41b5 100644 --- a/spec/push.spec.js +++ b/spec/push.spec.js @@ -1,4 +1,4 @@ -var push = require('../push'); +var push = require('../src/push'); describe('push', () => { it('can check valid master key of request', (done) => { diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index 2378caf5..68ac31c9 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -16,7 +16,7 @@ var hasAllPODobject = () => { objACL.setPublicWriteAccess(false); obj.setACL(objACL); return obj; -} +}; var plainOldDataSchema = { className: 'HasAllPOD', @@ -35,7 +35,7 @@ var plainOldDataSchema = { aArray: {type: 'Array'}, aGeoPoint: {type: 'GeoPoint'}, aFile: {type: 'File'} - }, + } }; var pointersAndRelationsSchema = { diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index b1aae666..e0347ebf 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -4,7 +4,7 @@ "*spec.js" ], "helpers": [ + "../node_modules/babel-core/register.js", "helper.js" ] } - diff --git a/spec/transform.spec.js b/spec/transform.spec.js index 528c46bf..c7780ffb 100644 --- a/spec/transform.spec.js +++ b/spec/transform.spec.js @@ -1,6 +1,6 @@ // These tests are unit tests designed to only test transform.js. -var transform = require('../transform'); +var transform = require('../src/transform'); var dummySchema = { data: {}, From a0d695709608e58c49dbc1fe943f06e34d0dc4d2 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Mon, 8 Feb 2016 20:20:21 -0800 Subject: [PATCH 13/16] Add babel.js compilation steps/dependencies to package.json. --- package.json | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3d145ee4..689110c0 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "parse-server", "version": "2.0.7", "description": "An express module providing a Parse-compatible API server", - "main": "index.js", + "main": "lib/index.js", "repository": { "type": "git", "url": "https://github.com/ParsePlatform/parse-server" @@ -11,6 +11,7 @@ "dependencies": { "apn": "^1.7.5", "aws-sdk": "~2.2.33", + "babel-runtime": "^6.5.0", "bcrypt-nodejs": "0.0.3", "body-parser": "^1.14.2", "deepcopy": "^0.6.1", @@ -19,23 +20,29 @@ "mime": "^1.3.4", "mongodb": "~2.1.0", "multer": "^1.1.0", + "node-gcm": "^0.14.0", "parse": "^1.7.0", "randomstring": "^1.1.3", - "node-gcm": "^0.14.0", "request": "^2.65.0" }, "devDependencies": { + "babel-cli": "^6.5.1", + "babel-core": "^6.5.1", + "babel-istanbul": "^0.6.0", + "babel-preset-es2015": "^6.5.0", + "babel-register": "^6.5.1", "codecov": "^1.0.1", "deep-diff": "^0.3.3", - "istanbul": "^0.4.2", "jasmine": "^2.3.2", "mongodb-runner": "^3.1.15" }, "scripts": { + "build": "./node_modules/.bin/babel src/ -d lib/", "pretest": "MONGODB_VERSION=${MONGODB_VERSION:=3.0.8} mongodb-runner start", - "test": "NODE_ENV=test TESTING=1 ./node_modules/.bin/istanbul cover --include-all-sources -x **/spec/** ./node_modules/.bin/jasmine", + "test": "NODE_ENV=test TESTING=1 ./node_modules/.bin/babel-node ./node_modules/.bin/babel-istanbul cover -x **/spec/** ./node_modules/.bin/jasmine", "posttest": "mongodb-runner stop", - "start": "./bin/parse-server" + "start": "./bin/parse-server", + "prepublish": "npm run build" }, "engines": { "node": ">=4.1" From 9ce90b06f501754b033ec367824a7182b8f77c1a Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Mon, 8 Feb 2016 20:20:33 -0800 Subject: [PATCH 14/16] Update imports in parse-server binary. --- bin/parse-server | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/parse-server b/bin/parse-server index c2606f4b..902e43b2 100755 --- a/bin/parse-server +++ b/bin/parse-server @@ -1,6 +1,6 @@ #!/usr/bin/env node var express = require('express'); -var ParseServer = require("../index").ParseServer; +var ParseServer = require("../lib/index").ParseServer; var app = express(); From 2750a4c25fbde111d0941c22ca94316991222494 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Mon, 8 Feb 2016 22:01:42 -0800 Subject: [PATCH 15/16] Clarify run tests step in contributing guidelines. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 39762126..6a1923cf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ We really want Parse to be yours, to see it grow and thrive in the open source c ##### Please Do's * Take testing seriously! Aim to increase the test coverage with every pull request. -* Run the tests for the file you are working on with `TESTING=1 (repo-root)/node_modules/jasmine/bin/jasmine.js spec/MyFile.spec.js` +* Run the tests for the file you are working on with `npm test spec/MyFile.spec.js` * Run the tests for the whole project and look at the coverage report to make sure your tests are exhaustive by running `npm test` and looking at (project-root)/lcov-report/parse-server/FileUnderTest.js.html ##### Code of Conduct From 244febf4a9140f7961f0420902dd38f456e22d23 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Mon, 8 Feb 2016 22:52:57 -0800 Subject: [PATCH 16/16] Unmove readme. --- src/README.md => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/README.md => README.md (100%) diff --git a/src/README.md b/README.md similarity index 100% rename from src/README.md rename to README.md