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(); + }); + }); });