Skip to content

Commit 3d98950

Browse files
committed
Get all schemas, with tests, and matches parse.com schemas API
1 parent 0547592 commit 3d98950

File tree

3 files changed

+156
-8
lines changed

3 files changed

+156
-8
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
},
2424
"devDependencies": {
2525
"codecov": "^1.0.1",
26+
"deep-diff": "^0.3.3",
2627
"istanbul": "^0.4.2",
2728
"jasmine": "^2.3.2",
2829
"mongodb-runner": "^3.1.15"

schemas.js

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,58 @@ var express = require('express'),
55

66
var router = new PromiseRouter();
77

8-
function mongoSchemaToSchemaAPIResponse(schema) {
8+
function mongoFieldTypeToApiResponseType(type) {
9+
if (type[0] === '*') {
10+
return {
11+
type: 'Pointer',
12+
targetClass: type.slice(1),
13+
};
14+
}
15+
if (type.startsWith('relation<')) {
16+
return {
17+
type: 'Relation',
18+
targetClass: type.slice('relation<'.length, type.length - 1),
19+
};
20+
}
21+
switch (type) {
22+
case 'number': return {type: 'Number'};
23+
case 'string': return {type: 'String'};
24+
case 'boolean': return {type: 'Boolean'};
25+
case 'date': return {type: 'Date'};
26+
case 'object': return {type: 'Object'};
27+
case 'array': return {type: 'Array'};
28+
case 'geopoint': return {type: 'GeoPoint'};
29+
case 'file': return {type: 'File'};
30+
}
31+
}
32+
33+
function mongoSchemaAPIResponseFields(schema) {
934
fieldNames = Object.keys(schema).filter(key => key !== '_id');
35+
response = {};
36+
fieldNames.forEach(fieldName => {
37+
response[fieldName] = mongoFieldTypeToApiResponseType(schema[fieldName]);
38+
});
39+
response.ACL = {type: 'ACL'};
40+
response.createdAt = {type: 'Date'};
41+
response.updatedAt = {type: 'Date'};
42+
response.objectId = {type: 'String'};
43+
return response;
44+
}
45+
46+
function mongoSchemaToSchemaAPIResponse(schema) {
1047
return {
1148
className: schema._id,
12-
fields: fieldNames.map(name => {
13-
result = {};
14-
result[name] = {
15-
type: schema[name],
16-
};
17-
return result;
18-
}),
49+
fields: mongoSchemaAPIResponseFields(schema),
1950
};
2051
}
2152

2253
function getAllSchemas(req) {
54+
if (!req.auth.isMaster) {
55+
return Promise.resolve({
56+
status: 401,
57+
response: {error: 'unauthorized'},
58+
});
59+
}
2360
return req.config.database.collection('_SCHEMA')
2461
.then(coll => coll.find({}).toArray())
2562
.then(schemas => ({response: {

spec/schemas.spec.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
var request = require('request');
2+
var dd = require('deep-diff');
3+
4+
describe('schemas', () => {
5+
it('requires the master key to get all schemas', (done) => {
6+
request.get({
7+
url: 'http://localhost:8378/1/schemas',
8+
json: true,
9+
headers: {
10+
'X-Parse-Application-Id': 'test',
11+
'X-Parse-REST-API-Key': 'rest',
12+
},
13+
}, (error, response, body) => {
14+
expect(response.statusCode).toEqual(401);
15+
expect(body.error).toEqual('unauthorized');
16+
done();
17+
});
18+
});
19+
20+
it('responds with empty list when there are no schemas', done => {
21+
request.get({
22+
url: 'http://localhost:8378/1/schemas',
23+
json: true,
24+
headers: {
25+
'X-Parse-Application-Id': 'test',
26+
'X-Parse-Master-Key': 'test',
27+
},
28+
}, (error, response, body) => {
29+
expect(body.results).toEqual([]);
30+
done();
31+
});
32+
});
33+
34+
it('responds with a list of schemas after creating objects', done => {
35+
var obj1 = new Parse.Object('HasAllPOD');
36+
obj1.set('aNumber', 5);
37+
obj1.set('aString', 'string');
38+
obj1.set('aBool', true);
39+
obj1.set('aDate', new Date());
40+
obj1.set('aObject', {k1: 'value', k2: true, k3: 5});
41+
obj1.set('aArray', ['contents', true, 5]);
42+
obj1.set('aGeoPoint', new Parse.GeoPoint({latitude: 0, longitude: 0}));
43+
obj1.set('aFile', new Parse.File('f.txt', { base64: 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=' }));
44+
var obj1ACL = new Parse.ACL();
45+
obj1ACL.setPublicWriteAccess(false);
46+
obj1.setACL(obj1ACL);
47+
48+
obj1.save().then(savedObj1 => {
49+
var obj2 = new Parse.Object('HasPointersAndRelations');
50+
obj2.set('aPointer', savedObj1);
51+
var relation = obj2.relation('aRelation');
52+
relation.add(obj1);
53+
return obj2.save();
54+
}).then(() => {
55+
request.get({
56+
url: 'http://localhost:8378/1/schemas',
57+
json: true,
58+
headers: {
59+
'X-Parse-Application-Id': 'test',
60+
'X-Parse-Master-Key': 'test',
61+
},
62+
}, (error, response, body) => {
63+
var expected = {
64+
results: [
65+
{
66+
className: 'HasAllPOD',
67+
fields: {
68+
//Default fields
69+
ACL: {type: 'ACL'},
70+
createdAt: {type: 'Date'},
71+
updatedAt: {type: 'Date'},
72+
objectId: {type: 'String'},
73+
//Custom fields
74+
aNumber: {type: 'Number'},
75+
aString: {type: 'String'},
76+
aBool: {type: 'Boolean'},
77+
aDate: {type: 'Date'},
78+
aObject: {type: 'Object'},
79+
aArray: {type: 'Array'},
80+
aGeoPoint: {type: 'GeoPoint'},
81+
aFile: {type: 'File'}
82+
},
83+
},
84+
{
85+
className: 'HasPointersAndRelations',
86+
fields: {
87+
//Default fields
88+
ACL: {type: 'ACL'},
89+
createdAt: {type: 'Date'},
90+
updatedAt: {type: 'Date'},
91+
objectId: {type: 'String'},
92+
//Custom fields
93+
aPointer: {
94+
type: 'Pointer',
95+
targetClass: 'HasAllPOD',
96+
},
97+
aRelation: {
98+
type: 'Relation',
99+
targetClass: 'HasAllPOD',
100+
},
101+
},
102+
}
103+
]
104+
};
105+
expect(body).toEqual(expected);
106+
done();
107+
})
108+
});
109+
});
110+
});

0 commit comments

Comments
 (0)