From 05475929521065b24a61ea9ca35fb0bfca2a39f8 Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Tue, 2 Feb 2016 17:38:47 -0800 Subject: [PATCH 1/3] Implement GET /parse/schemas --- index.js | 1 + schemas.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 schemas.js diff --git a/index.js b/index.js index 0bce0c729a..d7c12a2ed7 100644 --- a/index.js +++ b/index.js @@ -111,6 +111,7 @@ function ParseServer(args) { router.merge(require('./push')); router.merge(require('./installations')); router.merge(require('./functions')); + router.merge(require('./schemas')); batch.mountOnto(router); diff --git a/schemas.js b/schemas.js new file mode 100644 index 0000000000..04c84ef45e --- /dev/null +++ b/schemas.js @@ -0,0 +1,32 @@ +// schemas.js + +var express = require('express'), + PromiseRouter = require('./PromiseRouter'); + +var router = new PromiseRouter(); + +function mongoSchemaToSchemaAPIResponse(schema) { + fieldNames = Object.keys(schema).filter(key => key !== '_id'); + return { + className: schema._id, + fields: fieldNames.map(name => { + result = {}; + result[name] = { + type: schema[name], + }; + return result; + }), + }; +} + +function getAllSchemas(req) { + return req.config.database.collection('_SCHEMA') + .then(coll => coll.find({}).toArray()) + .then(schemas => ({response: { + results: schemas.map(mongoSchemaToSchemaAPIResponse) + }})); +} + +router.route('GET', '/schemas', getAllSchemas); + +module.exports = router; From 3d989501b2444753d51781216692009e5e1aa49e Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 3 Feb 2016 11:55:44 -0800 Subject: [PATCH 2/3] Get all schemas, with tests, and matches parse.com schemas API --- package.json | 1 + schemas.js | 53 +++++++++++++++++---- spec/schemas.spec.js | 110 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 8 deletions(-) create mode 100644 spec/schemas.spec.js diff --git a/package.json b/package.json index 95ef2f3a35..95003e813f 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ }, "devDependencies": { "codecov": "^1.0.1", + "deep-diff": "^0.3.3", "istanbul": "^0.4.2", "jasmine": "^2.3.2", "mongodb-runner": "^3.1.15" diff --git a/schemas.js b/schemas.js index 04c84ef45e..d44918baab 100644 --- a/schemas.js +++ b/schemas.js @@ -5,21 +5,58 @@ var express = require('express'), var router = new PromiseRouter(); -function mongoSchemaToSchemaAPIResponse(schema) { +function mongoFieldTypeToApiResponseType(type) { + if (type[0] === '*') { + return { + type: 'Pointer', + targetClass: type.slice(1), + }; + } + if (type.startsWith('relation<')) { + return { + type: 'Relation', + targetClass: type.slice('relation<'.length, type.length - 1), + }; + } + switch (type) { + case 'number': return {type: 'Number'}; + case 'string': return {type: 'String'}; + case 'boolean': return {type: 'Boolean'}; + case 'date': return {type: 'Date'}; + case 'object': return {type: 'Object'}; + case 'array': return {type: 'Array'}; + case 'geopoint': return {type: 'GeoPoint'}; + case 'file': return {type: 'File'}; + } +} + +function mongoSchemaAPIResponseFields(schema) { fieldNames = Object.keys(schema).filter(key => key !== '_id'); + response = {}; + fieldNames.forEach(fieldName => { + response[fieldName] = mongoFieldTypeToApiResponseType(schema[fieldName]); + }); + response.ACL = {type: 'ACL'}; + response.createdAt = {type: 'Date'}; + response.updatedAt = {type: 'Date'}; + response.objectId = {type: 'String'}; + return response; +} + +function mongoSchemaToSchemaAPIResponse(schema) { return { className: schema._id, - fields: fieldNames.map(name => { - result = {}; - result[name] = { - type: schema[name], - }; - return result; - }), + fields: mongoSchemaAPIResponseFields(schema), }; } function getAllSchemas(req) { + if (!req.auth.isMaster) { + return Promise.resolve({ + status: 401, + response: {error: 'unauthorized'}, + }); + } return req.config.database.collection('_SCHEMA') .then(coll => coll.find({}).toArray()) .then(schemas => ({response: { diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js new file mode 100644 index 0000000000..a4d2f6188c --- /dev/null +++ b/spec/schemas.spec.js @@ -0,0 +1,110 @@ +var request = require('request'); +var dd = require('deep-diff'); + +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', + }, + }, (error, response, body) => { + expect(response.statusCode).toEqual(401); + expect(body.error).toEqual('unauthorized'); + 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', + }, + }, (error, response, body) => { + expect(body.results).toEqual([]); + done(); + }); + }); + + it('responds with a list of schemas after creating objects', done => { + var obj1 = new Parse.Object('HasAllPOD'); + obj1.set('aNumber', 5); + obj1.set('aString', 'string'); + obj1.set('aBool', true); + obj1.set('aDate', new Date()); + obj1.set('aObject', {k1: 'value', k2: true, k3: 5}); + obj1.set('aArray', ['contents', true, 5]); + obj1.set('aGeoPoint', new Parse.GeoPoint({latitude: 0, longitude: 0})); + obj1.set('aFile', new Parse.File('f.txt', { base64: 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=' })); + var obj1ACL = new Parse.ACL(); + obj1ACL.setPublicWriteAccess(false); + obj1.setACL(obj1ACL); + + obj1.save().then(savedObj1 => { + var obj2 = new Parse.Object('HasPointersAndRelations'); + obj2.set('aPointer', savedObj1); + var relation = obj2.relation('aRelation'); + relation.add(obj1); + return obj2.save(); + }).then(() => { + request.get({ + url: 'http://localhost:8378/1/schemas', + json: true, + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', + }, + }, (error, response, body) => { + var expected = { + results: [ + { + className: 'HasAllPOD', + fields: { + //Default fields + ACL: {type: 'ACL'}, + createdAt: {type: 'Date'}, + updatedAt: {type: 'Date'}, + objectId: {type: 'String'}, + //Custom fields + aNumber: {type: 'Number'}, + aString: {type: 'String'}, + aBool: {type: 'Boolean'}, + aDate: {type: 'Date'}, + aObject: {type: 'Object'}, + aArray: {type: 'Array'}, + aGeoPoint: {type: 'GeoPoint'}, + aFile: {type: 'File'} + }, + }, + { + className: 'HasPointersAndRelations', + fields: { + //Default fields + ACL: {type: 'ACL'}, + createdAt: {type: 'Date'}, + updatedAt: {type: 'Date'}, + objectId: {type: 'String'}, + //Custom fields + aPointer: { + type: 'Pointer', + targetClass: 'HasAllPOD', + }, + aRelation: { + type: 'Relation', + targetClass: 'HasAllPOD', + }, + }, + } + ] + }; + expect(body).toEqual(expected); + done(); + }) + }); + }); +}); From 33a881764dababc71c24a53cc4e00802014effee Mon Sep 17 00:00:00 2001 From: Drew Gross Date: Wed, 3 Feb 2016 12:10:50 -0800 Subject: [PATCH 3/3] Tabs -> spaces --- schemas.js | 94 +++++++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/schemas.js b/schemas.js index d44918baab..88b0da38fe 100644 --- a/schemas.js +++ b/schemas.js @@ -6,62 +6,62 @@ var express = require('express'), var router = new PromiseRouter(); function mongoFieldTypeToApiResponseType(type) { - if (type[0] === '*') { - return { - type: 'Pointer', - targetClass: type.slice(1), - }; - } - if (type.startsWith('relation<')) { - return { - type: 'Relation', - targetClass: type.slice('relation<'.length, type.length - 1), - }; - } - switch (type) { - case 'number': return {type: 'Number'}; - case 'string': return {type: 'String'}; - case 'boolean': return {type: 'Boolean'}; - case 'date': return {type: 'Date'}; - case 'object': return {type: 'Object'}; - case 'array': return {type: 'Array'}; - case 'geopoint': return {type: 'GeoPoint'}; - case 'file': return {type: 'File'}; - } + if (type[0] === '*') { + return { + type: 'Pointer', + targetClass: type.slice(1), + }; + } + if (type.startsWith('relation<')) { + return { + type: 'Relation', + targetClass: type.slice('relation<'.length, type.length - 1), + }; + } + switch (type) { + case 'number': return {type: 'Number'}; + case 'string': return {type: 'String'}; + case 'boolean': return {type: 'Boolean'}; + case 'date': return {type: 'Date'}; + case 'object': return {type: 'Object'}; + case 'array': return {type: 'Array'}; + case 'geopoint': return {type: 'GeoPoint'}; + case 'file': return {type: 'File'}; + } } function mongoSchemaAPIResponseFields(schema) { - fieldNames = Object.keys(schema).filter(key => key !== '_id'); - response = {}; - fieldNames.forEach(fieldName => { - response[fieldName] = mongoFieldTypeToApiResponseType(schema[fieldName]); - }); - response.ACL = {type: 'ACL'}; - response.createdAt = {type: 'Date'}; - response.updatedAt = {type: 'Date'}; - response.objectId = {type: 'String'}; - return response; + fieldNames = Object.keys(schema).filter(key => key !== '_id'); + response = {}; + fieldNames.forEach(fieldName => { + response[fieldName] = mongoFieldTypeToApiResponseType(schema[fieldName]); + }); + response.ACL = {type: 'ACL'}; + response.createdAt = {type: 'Date'}; + response.updatedAt = {type: 'Date'}; + response.objectId = {type: 'String'}; + return response; } function mongoSchemaToSchemaAPIResponse(schema) { - return { - className: schema._id, - fields: mongoSchemaAPIResponseFields(schema), - }; + return { + className: schema._id, + fields: mongoSchemaAPIResponseFields(schema), + }; } function getAllSchemas(req) { - if (!req.auth.isMaster) { - return Promise.resolve({ - status: 401, - response: {error: 'unauthorized'}, - }); - } - return req.config.database.collection('_SCHEMA') - .then(coll => coll.find({}).toArray()) - .then(schemas => ({response: { - results: schemas.map(mongoSchemaToSchemaAPIResponse) - }})); + if (!req.auth.isMaster) { + return Promise.resolve({ + status: 401, + response: {error: 'unauthorized'}, + }); + } + return req.config.database.collection('_SCHEMA') + .then(coll => coll.find({}).toArray()) + .then(schemas => ({response: { + results: schemas.map(mongoSchemaToSchemaAPIResponse) + }})); } router.route('GET', '/schemas', getAllSchemas);