Merge pull request #277 from drew-gross/schemas-post

Implement POST /schemas
This commit is contained in:
Drew
2016-02-06 17:54:12 -08:00
2 changed files with 226 additions and 31 deletions

View File

@@ -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,46 @@ 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.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);
router.route('GET', '/schemas/:className', getOneSchema);
router.route('POST', '/schemas', createSchema);
router.route('POST', '/schemas/:className', createSchema);
module.exports = router;

View File

@@ -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);
@@ -16,7 +18,7 @@ var hasAllPODobject = () => {
return obj;
}
var expectedResponseForHasAllPOD = {
var plainOldDataSchema = {
className: 'HasAllPOD',
fields: {
//Default fields
@@ -36,7 +38,7 @@ var expectedResponseForHasAllPOD = {
},
};
var expectedResponseforHasPointersAndRelations = {
var pointersAndRelationsSchema = {
className: 'HasPointersAndRelations',
fields: {
//Default fields
@@ -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();
});
@@ -76,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');
@@ -87,14 +99,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,13 +134,10 @@ 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]
results: [plainOldDataSchema,pointersAndRelationsSchema]
};
expect(body).toEqual(expected);
done();
@@ -133,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();
});
});
@@ -150,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({
@@ -164,4 +176,146 @@ 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",
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",
}
}, (error, response, body) => {
expect(body).toEqual({
className: 'NewClass',
fields: {
ACL: {type: 'ACL'},
createdAt: {type: 'Date'},
updatedAt: {type: 'Date'},
objectId: {type: 'String'},
}
});
done();
});
});
});