Skip to content

Commit 9078372

Browse files
Lucian MateescuLucian Mateescu
authored andcommitted
sync origin
2 parents 485f0de + ee5e06c commit 9078372

11 files changed

+306
-42
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ node_js:
66
- "4.1"
77
- "4.2"
88
env:
9+
- MONGODB_VERSION=2.6.11
910
- MONGODB_VERSION=3.0.8
1011
after_success: ./node_modules/.bin/codecov

ExportAdapter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ ExportAdapter.prototype.connect = function() {
3434
return this.connectionPromise;
3535
}
3636

37-
//http://regexr.com/3cn6m
37+
//http://regexr.com/3cncm
3838
if (!this.mongoURI.match(/^mongodb:\/\/((.+):(.+)@)?([^:@]+):{0,1}([^:]+)\/(.+?)$/gm)) {
3939
throw new Error("Invalid mongoURI: " + this.mongoURI)
4040
}

functions.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ function handleCloudFunction(req) {
1313
var response = createResponseObject(resolve, reject);
1414
var request = {
1515
params: req.body || {},
16-
master : req.auth ? req.auth.isMaster : false,
17-
installationId : req.auth && req.auth.installationId ? req.auth.installationId : undefined,
18-
user: req.auth && req.auth.user || {}
16+
master: req.auth && req.auth.isMaster,
17+
user: req.auth && req.auth.user,
1918
};
2019
Parse.Cloud.Functions[req.params.functionName](request, response);
2120
});

httpRequest.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
var request = require("request"),
2+
Parse = require('parse/node').Parse;
3+
4+
module.exports = function(options) {
5+
var promise = new Parse.Promise();
6+
var callbacks = {
7+
success: options.success,
8+
error: options.error
9+
};
10+
delete options.success;
11+
delete options.error;
12+
if (options.uri && !options.url) {
13+
options.uri = options.url;
14+
delete options.url;
15+
}
16+
if (typeof options.body === 'object') {
17+
options.body = JSON.stringify(options.body);
18+
}
19+
request(options, (error, response, body) => {
20+
var httpResponse = {};
21+
httpResponse.status = response.statusCode;
22+
httpResponse.headers = response.headers;
23+
httpResponse.buffer = new Buffer(response.body);
24+
httpResponse.cookies = response.headers["set-cookie"];
25+
httpResponse.text = response.body;
26+
try {
27+
httpResponse.data = JSON.parse(response.body);
28+
} catch (e) {}
29+
// Consider <200 && >= 400 as errors
30+
if (error || httpResponse.status <200 || httpResponse.status >=400) {
31+
if (callbacks.error) {
32+
return callbacks.error(httpResponse);
33+
}
34+
return promise.reject(httpResponse);
35+
} else {
36+
if (callbacks.success) {
37+
return callbacks.success(httpResponse);
38+
}
39+
return promise.resolve(httpResponse);
40+
}
41+
});
42+
return promise;
43+
};

index.js

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ var batch = require('./batch'),
1111
multer = require('multer'),
1212
Parse = require('parse/node').Parse,
1313
PromiseRouter = require('./PromiseRouter'),
14-
request = require('request');
14+
httpRequest = require('./httpRequest');
1515

1616
// Mutate the Parse object to add the Cloud Code handlers
1717
addParseCloud();
@@ -111,6 +111,7 @@ function ParseServer(args) {
111111
router.merge(require('./push'));
112112
router.merge(require('./installations'));
113113
router.merge(require('./functions'));
114+
router.merge(require('./schemas'));
114115

115116
batch.mountOnto(router);
116117

@@ -148,36 +149,7 @@ function addParseCloud() {
148149
var className = getClassName(parseClass);
149150
Parse.Cloud.Triggers.afterDelete[className] = handler;
150151
};
151-
Parse.Cloud.httpRequest = function(options) {
152-
var promise = new Parse.Promise();
153-
var callbacks = {
154-
success: options.success,
155-
error: options.error
156-
};
157-
delete options.success;
158-
delete options.error;
159-
if (options.uri && !options.url) {
160-
options.uri = options.url;
161-
delete options.url;
162-
}
163-
if (typeof options.body === 'object') {
164-
options.body = JSON.stringify(options.body);
165-
}
166-
request(options, (error, response, body) => {
167-
if (error) {
168-
if (callbacks.error) {
169-
return callbacks.error(error);
170-
}
171-
return promise.reject(error);
172-
} else {
173-
if (callbacks.success) {
174-
return callbacks.success(body);
175-
}
176-
return promise.resolve(body);
177-
}
178-
});
179-
return promise;
180-
};
152+
Parse.Cloud.httpRequest = httpRequest;
181153
global.Parse = Parse;
182154
}
183155

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,19 @@
1111
"dependencies": {
1212
"aws-sdk": "~2.2.33",
1313
"bcrypt-nodejs": "0.0.3",
14-
"body-parser": "~1.12.4",
15-
"deepcopy": "^0.5.0",
16-
"express": "~4.2.x",
14+
"body-parser": "^1.14.2",
15+
"deepcopy": "^0.6.1",
16+
"express": "^4.13.4",
1717
"hat": "~0.0.3",
1818
"mime": "^1.3.4",
19-
"mongodb": "~2.0.33",
20-
"multer": "~0.1.8",
21-
"parse": "~1.6.12",
19+
"mongodb": "~2.1.0",
20+
"multer": "^1.1.0",
21+
"parse": "^1.7.0",
2222
"request": "^2.65.0"
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: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// schemas.js
2+
3+
var express = require('express'),
4+
PromiseRouter = require('./PromiseRouter');
5+
6+
var router = new PromiseRouter();
7+
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) {
34+
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) {
47+
return {
48+
className: schema._id,
49+
fields: mongoSchemaAPIResponseFields(schema),
50+
};
51+
}
52+
53+
function getAllSchemas(req) {
54+
if (!req.auth.isMaster) {
55+
return Promise.resolve({
56+
status: 401,
57+
response: {error: 'unauthorized'},
58+
});
59+
}
60+
return req.config.database.collection('_SCHEMA')
61+
.then(coll => coll.find({}).toArray())
62+
.then(schemas => ({response: {
63+
results: schemas.map(mongoSchemaToSchemaAPIResponse)
64+
}}));
65+
}
66+
67+
router.route('GET', '/schemas', getAllSchemas);
68+
69+
module.exports = router;

spec/ParseGeoPoint.spec.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,4 +287,47 @@ describe('Parse.GeoPoint testing', () => {
287287
done();
288288
});
289289
});
290+
291+
it('supports a sub-object with a geo point', done => {
292+
var point = new Parse.GeoPoint(44.0, -11.0);
293+
var obj = new TestObject();
294+
obj.set('subobject', { location: point });
295+
obj.save(null, {
296+
success: function() {
297+
var query = new Parse.Query(TestObject);
298+
query.find({
299+
success: function(results) {
300+
equal(results.length, 1);
301+
var pointAgain = results[0].get('subobject')['location'];
302+
ok(pointAgain);
303+
equal(pointAgain.latitude, 44.0);
304+
equal(pointAgain.longitude, -11.0);
305+
done();
306+
}
307+
});
308+
}
309+
});
310+
});
311+
312+
it('supports array of geo points', done => {
313+
var point1 = new Parse.GeoPoint(44.0, -11.0);
314+
var point2 = new Parse.GeoPoint(22.0, -55.0);
315+
var obj = new TestObject();
316+
obj.set('locations', [ point1, point2 ]);
317+
obj.save(null, {
318+
success: function() {
319+
var query = new Parse.Query(TestObject);
320+
query.find({
321+
success: function(results) {
322+
equal(results.length, 1);
323+
var locations = results[0].get('locations');
324+
expect(locations.length).toEqual(2);
325+
expect(locations[0]).toEqual(point1);
326+
expect(locations[1]).toEqual(point2);
327+
done();
328+
}
329+
});
330+
}
331+
});
332+
});
290333
});

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)