forked from parse-community/parse-server
-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathPushController.js
More file actions
163 lines (152 loc) · 6.11 KB
/
PushController.js
File metadata and controls
163 lines (152 loc) · 6.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import { Parse } from 'parse/node';
import PromiseRouter from '../PromiseRouter';
import rest from '../rest';
import AdaptableController from './AdaptableController';
import { PushAdapter } from '../Adapters/Push/PushAdapter';
import deepcopy from 'deepcopy';
import RestQuery from '../RestQuery';
import RestWrite from '../RestWrite';
import { master } from '../Auth';
import pushStatusHandler from '../pushStatusHandler';
const FEATURE_NAME = 'push';
const UNSUPPORTED_BADGE_KEY = "unsupported";
export class PushController extends AdaptableController {
/**
* Check whether the deviceType parameter in qury condition is valid or not.
* @param {Object} where A query condition
* @param {Array} validPushTypes An array of valid push types(string)
*/
static validatePushType(where = {}, validPushTypes = []) {
var deviceTypeField = where.deviceType || {};
var deviceTypes = [];
if (typeof deviceTypeField === 'string') {
deviceTypes.push(deviceTypeField);
} else if (typeof deviceTypeField['$in'] === 'array') {
deviceTypes.concat(deviceTypeField['$in']);
}
for (var i = 0; i < deviceTypes.length; i++) {
var deviceType = deviceTypes[i];
if (validPushTypes.indexOf(deviceType) < 0) {
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
deviceType + ' is not supported push type.');
}
}
}
get pushIsAvailable() {
return !!this.adapter;
}
sendPush(body = {}, where = {}, config, auth, onPushStatusSaved = () => {}) {
var pushAdapter = this.adapter;
if (!this.pushIsAvailable) {
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
'Push adapter is not available');
}
PushController.validatePushType(where, pushAdapter.getValidPushTypes());
// Replace the expiration_time with a valid Unix epoch milliseconds time
body['expiration_time'] = PushController.getExpirationTime(body);
// TODO: If the req can pass the checking, we return immediately instead of waiting
// pushes to be sent. We probably change this behaviour in the future.
let badgeUpdate = () => {
return Promise.resolve();
}
if (body.data && body.data.badge) {
let badge = body.data.badge;
let restUpdate = {};
if (typeof badge == 'string' && badge.toLowerCase() === 'increment') {
restUpdate = { badge: { __op: 'Increment', amount: 1 } }
} else if (Number(badge)) {
restUpdate = { badge: badge }
} else {
throw "Invalid value for badge, expected number or 'Increment'";
}
let updateWhere = deepcopy(where);
badgeUpdate = () => {
updateWhere.deviceType = 'ios';
// Build a real RestQuery so we can use it in RestWrite
let restQuery = new RestQuery(config, master(config), '_Installation', updateWhere);
return restQuery.buildRestWhere().then(() => {
let write = new RestWrite(config, master(config), '_Installation', restQuery.restWhere, restUpdate);
write.runOptions.many = true;
return write.execute();
});
}
}
let pushStatus = pushStatusHandler(config);
return Promise.resolve().then(() => {
return pushStatus.setInitial(body, where);
}).then(() => {
onPushStatusSaved(pushStatus.objectId);
return badgeUpdate();
}).then(() => {
return rest.find(config, auth, '_Installation', where);
}).then((response) => {
if (!response.results) {
return Promise.reject({error: 'PushController: no results in query'})
}
pushStatus.setRunning(response.results);
return this.sendToAdapter(body, response.results, pushStatus, config);
}).then((results) => {
return pushStatus.complete(results);
}).catch((err) => {
pushStatus.fail(err);
return Promise.reject(err);
});
}
sendToAdapter(body, installations, pushStatus, config) {
if (body.data && body.data.badge && typeof body.data.badge == 'string' && body.data.badge.toLowerCase() == "increment") {
// Collect the badges to reduce the # of calls
let badgeInstallationsMap = installations.reduce((map, installation) => {
let badge = installation.badge;
if (installation.deviceType != "ios") {
badge = UNSUPPORTED_BADGE_KEY;
}
map[badge+''] = map[badge+''] || [];
map[badge+''].push(installation);
return map;
}, {});
// Map the on the badges count and return the send result
let promises = Object.keys(badgeInstallationsMap).map((badge) => {
let payload = deepcopy(body);
if (badge == UNSUPPORTED_BADGE_KEY) {
delete payload.data.badge;
} else {
payload.data.badge = parseInt(badge);
}
return this.adapter.send(payload, badgeInstallationsMap[badge]);
});
return Promise.all(promises);
}
return this.adapter.send(body, installations);
}
/**
* Get expiration time from the request body.
* @param {Object} request A request object
* @returns {Number|undefined} The expiration time if it exists in the request
*/
static getExpirationTime(body = {}) {
var hasExpirationTime = !!body['expiration_time'];
if (!hasExpirationTime) {
return;
}
var expirationTimeParam = body['expiration_time'];
var expirationTime;
if (typeof expirationTimeParam === 'number') {
expirationTime = new Date(expirationTimeParam * 1000);
} else if (typeof expirationTimeParam === 'string') {
expirationTime = new Date(expirationTimeParam);
} else {
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
body['expiration_time'] + ' is not valid time.');
}
// Check expirationTime is valid or not, if it is not valid, expirationTime is NaN
if (!isFinite(expirationTime)) {
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
body['expiration_time'] + ' is not valid time.');
}
return expirationTime.valueOf();
}
expectedAdapterType() {
return PushAdapter;
}
}
export default PushController;