Skip to content

Commit 096c172

Browse files
committed
Add push parameter checking and query installation
1 parent 72fa1f2 commit 096c172

File tree

4 files changed

+322
-9
lines changed

4 files changed

+322
-9
lines changed

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ function ParseServer(args) {
111111
router.merge(require('./sessions'));
112112
router.merge(require('./roles'));
113113
router.merge(require('./analytics'));
114-
router.merge(require('./push'));
114+
router.merge(require('./push').router);
115115
router.merge(require('./installations'));
116116
router.merge(require('./functions'));
117117
router.merge(require('./schemas'));

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"mongodb": "~2.1.0",
2020
"multer": "^1.1.0",
2121
"parse": "^1.7.0",
22+
"moment": "^2.11.1",
2223
"request": "^2.65.0"
2324
},
2425
"devDependencies": {
@@ -30,7 +31,7 @@
3031
},
3132
"scripts": {
3233
"pretest": "MONGODB_VERSION=${MONGODB_VERSION:=3.0.8} mongodb-runner start",
33-
"test": "TESTING=1 ./node_modules/.bin/istanbul cover --include-all-sources -x **/spec/** ./node_modules/.bin/jasmine",
34+
"test": "NODE_ENV=test TESTING=1 ./node_modules/.bin/istanbul cover --include-all-sources -x **/spec/** ./node_modules/.bin/jasmine",
3435
"posttest": "mongodb-runner stop",
3536
"start": "./bin/parse-server"
3637
},

push.js

Lines changed: 113 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,123 @@
22

33
var Parse = require('parse/node').Parse,
44
PromiseRouter = require('./PromiseRouter'),
5-
rest = require('./rest');
5+
rest = require('./rest'),
6+
moment = require('moment');
67

7-
var router = new PromiseRouter();
8+
var validPushTypes = ['ios', 'android'];
9+
10+
function handlePushWithoutQueue(req) {
11+
validateMasterKey(req);
12+
var where = getQueryCondition(req);
13+
validateDeviceType(where);
14+
req.expirationTime = getExpirationTime(req);
15+
return rest.find(req.config, req.auth, '_Installation', where).then(function(response) {
16+
throw new Parse.Error(Parse.Error.COMMAND_UNAVAILABLE,
17+
'This path is not implemented yet.');
18+
});
19+
}
20+
21+
/**
22+
* Check whether the deviceType parameter in qury condition is valid or not.
23+
* @param {Object} where A query condition
24+
*/
25+
function validateDeviceType(where) {
26+
var where = where || {};
27+
var deviceTypeField = where.deviceType || {};
28+
var deviceTypes = [];
29+
if (typeof deviceTypeField === 'string') {
30+
deviceTypes.push(deviceTypeField);
31+
} else if (typeof deviceTypeField['$in'] === 'array') {
32+
deviceTypes.concat(deviceTypeField['$in']);
33+
}
34+
for (var i = 0; i < deviceTypes.length; i++) {
35+
var deviceType = deviceTypes[i];
36+
if (validPushTypes.indexOf(deviceType) < 0) {
37+
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
38+
deviceType + ' is not supported push type.');
39+
}
40+
}
41+
}
42+
43+
/**
44+
* Get expiration time from the request body.
45+
* @param {Object} request A request object
46+
* @returns {Number|undefined} The expiration time if it exists in the request
47+
*/
48+
function getExpirationTime(req) {
49+
var body = req.body || {};
50+
var hasExpirationTime = !!body['expiration_time'];
51+
if (!hasExpirationTime) {
52+
return;
53+
}
54+
var expirationTimeParam = body['expiration_time'];
55+
var expirationTime;
56+
if (typeof expirationTimeParam === 'number') {
57+
expirationTime = new Date(expirationTimeParam * 1000);
58+
} else if (typeof expirationTimeParam === 'string') {
59+
expirationTime = new Date(expirationTimeParam);
60+
} else {
61+
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
62+
body['expiration_time'] + ' is not valid time.');
63+
}
64+
// Check expirationTime is valid or not, if it is not valid, expirationTime is NaN
65+
if (!isFinite(expirationTime)) {
66+
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
67+
body['expiration_time'] + ' is not valid time.');
68+
}
69+
return expirationTime.valueOf();
70+
}
871

72+
/**
73+
* Get query condition from the request body.
74+
* @param {Object} request A request object
75+
* @returns {Object} The query condition, the where field in a query api call
76+
*/
77+
function getQueryCondition(req) {
78+
var body = req.body || {};
79+
var hasWhere = typeof body.where !== 'undefined';
80+
var hasChannels = typeof body.channels !== 'undefined';
981

82+
var where;
83+
if (hasWhere && hasChannels) {
84+
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
85+
'Channels and query can not be set at the same time.');
86+
} else if (hasWhere) {
87+
where = body.where;
88+
} else if (hasChannels) {
89+
where = {
90+
"channels": {
91+
"$in": body.channels
92+
}
93+
}
94+
} else {
95+
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
96+
'Channels and query should be set at least one.');
97+
}
98+
return where;
99+
}
10100

11-
function notImplementedYet(req) {
12-
throw new Parse.Error(Parse.Error.COMMAND_UNAVAILABLE,
13-
'This path is not implemented yet.');
101+
/**
102+
* Check whether the api call has master key or not.
103+
* @param {Object} request A request object
104+
*/
105+
function validateMasterKey(req) {
106+
if (req.info.masterKey !== req.config.masterKey) {
107+
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
108+
'Master key is invalid, you should only use master key to send push');
109+
}
14110
}
15111

16-
router.route('POST','/push', notImplementedYet);
112+
var router = new PromiseRouter();
113+
router.route('POST','/push', handlePushWithoutQueue);
114+
115+
module.exports = {
116+
router: router
117+
}
17118

18-
module.exports = router;
119+
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
120+
module.exports.getQueryCondition = getQueryCondition;
121+
module.exports.validateMasterKey = validateMasterKey;
122+
module.exports.getExpirationTime = getExpirationTime;
123+
module.exports.validateDeviceType = validateDeviceType;
124+
}

spec/push.spec.js

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
var push = require('../push');
2+
3+
describe('push', () => {
4+
it('can check valid master key of request', (done) => {
5+
// Make mock request
6+
var request = {
7+
info: {
8+
masterKey: 'masterKey'
9+
},
10+
config: {
11+
masterKey: 'masterKey'
12+
}
13+
}
14+
15+
expect(() => {
16+
push.validateMasterKey(request);
17+
}).not.toThrow();
18+
done();
19+
});
20+
21+
it('can check invalid master key of request', (done) => {
22+
// Make mock request
23+
var request = {
24+
info: {
25+
masterKey: 'masterKey'
26+
},
27+
config: {
28+
masterKey: 'masterKeyAgain'
29+
}
30+
}
31+
32+
expect(() => {
33+
push.validateMasterKey(request);
34+
}).toThrow();
35+
done();
36+
});
37+
38+
it('can get query condition when channels is set', (done) => {
39+
// Make mock request
40+
var request = {
41+
body: {
42+
channels: ['Giants', 'Mets']
43+
}
44+
}
45+
46+
var where = push.getQueryCondition(request);
47+
expect(where).toEqual({
48+
'channels': {
49+
'$in': ['Giants', 'Mets']
50+
}
51+
});
52+
done();
53+
});
54+
55+
it('can get query condition when where is set', (done) => {
56+
// Make mock request
57+
var request = {
58+
body: {
59+
'where': {
60+
'injuryReports': true
61+
}
62+
}
63+
}
64+
65+
var where = push.getQueryCondition(request);
66+
expect(where).toEqual({
67+
'injuryReports': true
68+
});
69+
done();
70+
});
71+
72+
it('can get query condition when nothing is set', (done) => {
73+
// Make mock request
74+
var request = {
75+
body: {
76+
}
77+
}
78+
79+
expect(function() {
80+
push.getQueryCondition(request);
81+
}).toThrow();
82+
done();
83+
});
84+
85+
it('can throw on getQueryCondition when channels and where are set', (done) => {
86+
// Make mock request
87+
var request = {
88+
body: {
89+
'channels': {
90+
'$in': ['Giants', 'Mets']
91+
},
92+
'where': {
93+
'injuryReports': true
94+
}
95+
}
96+
}
97+
98+
expect(function() {
99+
push.getQueryCondition(request);
100+
}).toThrow();
101+
done();
102+
});
103+
104+
it('can validate device type when no device type is set', (done) => {
105+
// Make query condition
106+
var where = {
107+
}
108+
109+
expect(function(){
110+
push.validateDeviceType(where);
111+
}).not.toThrow();
112+
done();
113+
});
114+
115+
it('can validate device type when single valid device type is set', (done) => {
116+
// Make query condition
117+
var where = {
118+
'deviceType': 'ios'
119+
}
120+
121+
expect(function(){
122+
push.validateDeviceType(where);
123+
}).not.toThrow();
124+
done();
125+
});
126+
127+
it('can validate device type when multiple valid device types are set', (done) => {
128+
// Make query condition
129+
var where = {
130+
'deviceType': {
131+
'$in': ['android', 'ios']
132+
}
133+
}
134+
135+
expect(function(){
136+
push.validateDeviceType(where);
137+
}).not.toThrow();
138+
done();
139+
});
140+
141+
it('can throw on validateDeviceType when single invalid device type is set', (done) => {
142+
// Make query condition
143+
var where = {
144+
'deviceType': 'osx'
145+
}
146+
147+
expect(function(){
148+
push.validateDeviceType(where);
149+
}).toThrow();
150+
done();
151+
});
152+
153+
it('can throw on validateDeviceType when single invalid device type is set', (done) => {
154+
// Make query condition
155+
var where = {
156+
'deviceType': 'osx'
157+
}
158+
159+
expect(function(){
160+
push.validateDeviceType(where)
161+
}).toThrow();
162+
done();
163+
});
164+
165+
it('can get expiration time in string format', (done) => {
166+
// Make mock request
167+
var timeStr = '2015-03-19T22:05:08Z';
168+
var request = {
169+
body: {
170+
'expiration_time': timeStr
171+
}
172+
}
173+
174+
var time = push.getExpirationTime(request);
175+
expect(time).toEqual(new Date(timeStr).valueOf());
176+
done();
177+
});
178+
179+
it('can get expiration time in number format', (done) => {
180+
// Make mock request
181+
var timeNumber = 1426802708;
182+
var request = {
183+
body: {
184+
'expiration_time': timeNumber
185+
}
186+
}
187+
188+
var time = push.getExpirationTime(request);
189+
expect(time).toEqual(timeNumber * 1000);
190+
done();
191+
});
192+
193+
it('can throw on getExpirationTime in invalid format', (done) => {
194+
// Make mock request
195+
var request = {
196+
body: {
197+
'expiration_time': 'abcd'
198+
}
199+
}
200+
201+
expect(function(){
202+
push.getExpirationTime(request);
203+
}).toThrow();
204+
done();
205+
});
206+
});

0 commit comments

Comments
 (0)