Merge remote-tracking branch 'ParsePlatform/master' into user-roles
This commit is contained in:
230
src/Adapters/Push/OneSignalPushAdapter.js
Normal file
230
src/Adapters/Push/OneSignalPushAdapter.js
Normal file
@@ -0,0 +1,230 @@
|
||||
"use strict";
|
||||
// ParsePushAdapter is the default implementation of
|
||||
// PushAdapter, it uses GCM for android push and APNS
|
||||
// for ios push.
|
||||
|
||||
const Parse = require('parse/node').Parse;
|
||||
var deepcopy = require('deepcopy');
|
||||
|
||||
function OneSignalPushAdapter(pushConfig) {
|
||||
this.https = require('https');
|
||||
|
||||
this.validPushTypes = ['ios', 'android'];
|
||||
this.senderMap = {};
|
||||
|
||||
pushConfig = pushConfig || {};
|
||||
this.OneSignalConfig = {};
|
||||
this.OneSignalConfig['appId'] = pushConfig['oneSignalAppId'];
|
||||
this.OneSignalConfig['apiKey'] = pushConfig['oneSignalApiKey'];
|
||||
|
||||
this.senderMap['ios'] = this.sendToAPNS.bind(this);
|
||||
this.senderMap['android'] = this.sendToGCM.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of valid push types.
|
||||
* @returns {Array} An array of valid push types
|
||||
*/
|
||||
OneSignalPushAdapter.prototype.getValidPushTypes = function() {
|
||||
return this.validPushTypes;
|
||||
}
|
||||
|
||||
OneSignalPushAdapter.prototype.send = function(data, installations) {
|
||||
console.log("Sending notification to "+installations.length+" devices.")
|
||||
let deviceMap = classifyInstallation(installations, this.validPushTypes);
|
||||
|
||||
let sendPromises = [];
|
||||
for (let pushType in deviceMap) {
|
||||
let sender = this.senderMap[pushType];
|
||||
if (!sender) {
|
||||
console.log('Can not find sender for push type %s, %j', pushType, data);
|
||||
continue;
|
||||
}
|
||||
let devices = deviceMap[pushType];
|
||||
|
||||
if(devices.length > 0) {
|
||||
sendPromises.push(sender(data, devices));
|
||||
}
|
||||
}
|
||||
return Parse.Promise.when(sendPromises);
|
||||
}
|
||||
|
||||
OneSignalPushAdapter.prototype.sendToAPNS = function(data,tokens) {
|
||||
|
||||
data= deepcopy(data['data']);
|
||||
|
||||
var post = {};
|
||||
if(data['badge']) {
|
||||
if(data['badge'] == "Increment") {
|
||||
post['ios_badgeType'] = 'Increase';
|
||||
post['ios_badgeCount'] = 1;
|
||||
} else {
|
||||
post['ios_badgeType'] = 'SetTo';
|
||||
post['ios_badgeCount'] = data['badge'];
|
||||
}
|
||||
delete data['badge'];
|
||||
}
|
||||
if(data['alert']) {
|
||||
post['contents'] = {en: data['alert']};
|
||||
delete data['alert'];
|
||||
}
|
||||
if(data['sound']) {
|
||||
post['ios_sound'] = data['sound'];
|
||||
delete data['sound'];
|
||||
}
|
||||
if(data['content-available'] == 1) {
|
||||
post['content_available'] = true;
|
||||
delete data['content-available'];
|
||||
}
|
||||
post['data'] = data;
|
||||
|
||||
let promise = new Parse.Promise();
|
||||
|
||||
var chunk = 2000 // OneSignal can process 2000 devices at a time
|
||||
var tokenlength=tokens.length;
|
||||
var offset = 0
|
||||
// handle onesignal response. Start next batch if there's not an error.
|
||||
let handleResponse = function(wasSuccessful) {
|
||||
if (!wasSuccessful) {
|
||||
return promise.reject("OneSignal Error");
|
||||
}
|
||||
|
||||
if(offset >= tokenlength) {
|
||||
promise.resolve()
|
||||
} else {
|
||||
this.sendNext();
|
||||
}
|
||||
}.bind(this)
|
||||
|
||||
this.sendNext = function() {
|
||||
post['include_ios_tokens'] = [];
|
||||
tokens.slice(offset,offset+chunk).forEach(function(i) {
|
||||
post['include_ios_tokens'].push(i['deviceToken'])
|
||||
})
|
||||
offset+=chunk;
|
||||
this.sendToOneSignal(post, handleResponse);
|
||||
}.bind(this)
|
||||
|
||||
this.sendNext()
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
OneSignalPushAdapter.prototype.sendToGCM = function(data,tokens) {
|
||||
data= deepcopy(data['data']);
|
||||
|
||||
var post = {};
|
||||
|
||||
if(data['alert']) {
|
||||
post['contents'] = {en: data['alert']};
|
||||
delete data['alert'];
|
||||
}
|
||||
if(data['title']) {
|
||||
post['title'] = {en: data['title']};
|
||||
delete data['title'];
|
||||
}
|
||||
if(data['uri']) {
|
||||
post['url'] = data['uri'];
|
||||
}
|
||||
|
||||
post['data'] = data;
|
||||
|
||||
let promise = new Parse.Promise();
|
||||
|
||||
var chunk = 2000 // OneSignal can process 2000 devices at a time
|
||||
var tokenlength=tokens.length;
|
||||
var offset = 0
|
||||
// handle onesignal response. Start next batch if there's not an error.
|
||||
let handleResponse = function(wasSuccessful) {
|
||||
if (!wasSuccessful) {
|
||||
return promise.reject("OneSIgnal Error");
|
||||
}
|
||||
|
||||
if(offset >= tokenlength) {
|
||||
promise.resolve()
|
||||
} else {
|
||||
this.sendNext();
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
this.sendNext = function() {
|
||||
post['include_android_reg_ids'] = [];
|
||||
tokens.slice(offset,offset+chunk).forEach(function(i) {
|
||||
post['include_android_reg_ids'].push(i['deviceToken'])
|
||||
})
|
||||
offset+=chunk;
|
||||
this.sendToOneSignal(post, handleResponse);
|
||||
}.bind(this)
|
||||
|
||||
|
||||
this.sendNext();
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
||||
OneSignalPushAdapter.prototype.sendToOneSignal = function(data, cb) {
|
||||
let headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Basic "+this.OneSignalConfig['apiKey']
|
||||
};
|
||||
let options = {
|
||||
host: "onesignal.com",
|
||||
port: 443,
|
||||
path: "/api/v1/notifications",
|
||||
method: "POST",
|
||||
headers: headers
|
||||
};
|
||||
data['app_id'] = this.OneSignalConfig['appId'];
|
||||
|
||||
let request = this.https.request(options, function(res) {
|
||||
if(res.statusCode < 299) {
|
||||
cb(true);
|
||||
} else {
|
||||
console.log('OneSignal Error');
|
||||
res.on('data', function(chunk) {
|
||||
console.log(chunk.toString())
|
||||
});
|
||||
cb(false)
|
||||
}
|
||||
});
|
||||
request.on('error', function(e) {
|
||||
console.log("Error connecting to OneSignal")
|
||||
console.log(e);
|
||||
cb(false);
|
||||
});
|
||||
request.write(JSON.stringify(data))
|
||||
request.end();
|
||||
}
|
||||
/**g
|
||||
* Classify the device token of installations based on its device type.
|
||||
* @param {Object} installations An array of installations
|
||||
* @param {Array} validPushTypes An array of valid push types(string)
|
||||
* @returns {Object} A map whose key is device type and value is an array of device
|
||||
*/
|
||||
function classifyInstallation(installations, validPushTypes) {
|
||||
// Init deviceTokenMap, create a empty array for each valid pushType
|
||||
let deviceMap = {};
|
||||
for (let validPushType of validPushTypes) {
|
||||
deviceMap[validPushType] = [];
|
||||
}
|
||||
for (let installation of installations) {
|
||||
// No deviceToken, ignore
|
||||
if (!installation.deviceToken) {
|
||||
continue;
|
||||
}
|
||||
let pushType = installation.deviceType;
|
||||
if (deviceMap[pushType]) {
|
||||
deviceMap[pushType].push({
|
||||
deviceToken: installation.deviceToken
|
||||
});
|
||||
} else {
|
||||
console.log('Unknown push type from installation %j', installation);
|
||||
}
|
||||
}
|
||||
return deviceMap;
|
||||
}
|
||||
|
||||
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
|
||||
OneSignalPushAdapter.classifyInstallation = classifyInstallation;
|
||||
}
|
||||
module.exports = OneSignalPushAdapter;
|
||||
@@ -330,7 +330,7 @@ RestWrite.prototype.transformUser = function() {
|
||||
if (!this.data.password) {
|
||||
return;
|
||||
}
|
||||
if (this.query) {
|
||||
if (this.query && !this.auth.isMaster ) {
|
||||
this.storage['clearSessions'] = true;
|
||||
}
|
||||
return passwordCrypto.hash(this.data.password).then((hashedPassword) => {
|
||||
@@ -509,11 +509,6 @@ RestWrite.prototype.handleInstallation = function() {
|
||||
this.data.installationId = this.data.installationId.toLowerCase();
|
||||
}
|
||||
|
||||
if (this.data.deviceToken && this.data.deviceType == 'android') {
|
||||
throw new Parse.Error(114,
|
||||
'deviceToken may not be set for deviceType android');
|
||||
}
|
||||
|
||||
var promise = Promise.resolve();
|
||||
|
||||
if (this.query && this.query.objectId) {
|
||||
|
||||
@@ -12,6 +12,8 @@ export class ClassesRouter {
|
||||
}
|
||||
if (body.limit) {
|
||||
options.limit = Number(body.limit);
|
||||
} else {
|
||||
options.limit = Number(100);
|
||||
}
|
||||
if (body.order) {
|
||||
options.order = String(body.order);
|
||||
|
||||
@@ -41,8 +41,7 @@ export class UsersRouter extends ClassesRouter {
|
||||
|
||||
handleMe(req) {
|
||||
if (!req.info || !req.info.sessionToken) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Object not found.');
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'invalid session token');
|
||||
}
|
||||
return rest.find(req.config, Auth.master(req.config), '_Session',
|
||||
{ _session_token: req.info.sessionToken },
|
||||
@@ -51,8 +50,7 @@ export class UsersRouter extends ClassesRouter {
|
||||
if (!response.results ||
|
||||
response.results.length == 0 ||
|
||||
!response.results[0].user) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Object not found.');
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'invalid session token');
|
||||
} else {
|
||||
let user = response.results[0].user;
|
||||
return { response: user };
|
||||
@@ -145,10 +143,10 @@ export class UsersRouter extends ClassesRouter {
|
||||
let router = new PromiseRouter();
|
||||
router.route('GET', '/users', req => { return this.handleFind(req); });
|
||||
router.route('POST', '/users', req => { return this.handleCreate(req); });
|
||||
router.route('GET', '/users/me', req => { return this.handleMe(req); });
|
||||
router.route('GET', '/users/:objectId', req => { return this.handleGet(req); });
|
||||
router.route('PUT', '/users/:objectId', req => { return this.handleUpdate(req); });
|
||||
router.route('DELETE', '/users/:objectId', req => { return this.handleDelete(req); });
|
||||
router.route('GET', '/users/me', req => { return this.handleMe(req); });
|
||||
router.route('GET', '/login', req => { return this.handleLogIn(req); });
|
||||
router.route('POST', '/logout', req => { return this.handleLogOut(req); });
|
||||
router.route('POST', '/requestPasswordReset', () => {
|
||||
|
||||
@@ -9,8 +9,11 @@ var router = new PromiseRouter();
|
||||
|
||||
function handleCloudFunction(req) {
|
||||
if (Parse.Cloud.Functions[req.params.functionName]) {
|
||||
|
||||
const params = Object.assign({}, req.body, req.query);
|
||||
|
||||
if (Parse.Cloud.Validators[req.params.functionName]) {
|
||||
var result = Parse.Cloud.Validators[req.params.functionName](req.body || {});
|
||||
var result = Parse.Cloud.Validators[req.params.functionName](params);
|
||||
if (!result) {
|
||||
throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Validation failed.');
|
||||
}
|
||||
@@ -19,7 +22,7 @@ function handleCloudFunction(req) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var response = createResponseObject(resolve, reject);
|
||||
var request = {
|
||||
params: req.body || {},
|
||||
params: params,
|
||||
master: req.auth && req.auth.isMaster,
|
||||
user: req.auth && req.auth.user,
|
||||
installationId: req.info.installationId
|
||||
|
||||
Reference in New Issue
Block a user