Skip to content

Commit

Permalink
Merge pull request #221 from drew-gross/master
Browse files Browse the repository at this point in the history
Implment GET /parse/schemas
  • Loading branch information
drew-gross committed Feb 3, 2016
2 parents c866afd + 33a8817 commit 39fb0d0
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 0 deletions.
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
69 changes: 69 additions & 0 deletions schemas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// schemas.js

var express = require('express'),
PromiseRouter = require('./PromiseRouter');

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'};
}
}

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: 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)
}}));
}

router.route('GET', '/schemas', getAllSchemas);

module.exports = router;
110 changes: 110 additions & 0 deletions spec/schemas.spec.js
Original file line number Diff line number Diff line change
@@ -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();
})
});
});
});

0 comments on commit 39fb0d0

Please sign in to comment.