Adds support for PushScheduling (#3722)

* Add support for push scheduling

Add a configuration flag on the server to handle the availability of
push scheduling.

* Update push controller to skip sending only if scheduling is configured

Only skip push sending if scheduling is configured

* Update bad conventions

* Add CLI definitions for push scheduling

* Adds tests for pushTime

* Adds test for scheduling

* nits

* Test for not scheduled
This commit is contained in:
Florent Vilmart
2017-04-15 17:20:55 -04:00
committed by GitHub
parent 302a0dda73
commit 907b160fc7
7 changed files with 186 additions and 8 deletions

View File

@@ -61,6 +61,7 @@ export class Config {
this.pushControllerQueue = cacheInfo.pushControllerQueue;
this.pushWorker = cacheInfo.pushWorker;
this.hasPushSupport = cacheInfo.hasPushSupport;
this.hasPushScheduledSupport = cacheInfo.hasPushScheduledSupport;
this.loggerController = cacheInfo.loggerController;
this.userController = cacheInfo.userController;
this.authDataManager = cacheInfo.authDataManager;

View File

@@ -12,8 +12,9 @@ export class PushController {
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
'Missing push configuration');
}
// Replace the expiration_time with a valid Unix epoch milliseconds time
body['expiration_time'] = PushController.getExpirationTime(body);
// Replace the expiration_time and push_time with a valid Unix epoch milliseconds time
body.expiration_time = PushController.getExpirationTime(body);
body.push_time = PushController.getPushTime(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 = () => {
@@ -49,6 +50,9 @@ export class PushController {
onPushStatusSaved(pushStatus.objectId);
return badgeUpdate();
}).then(() => {
if (body.push_time && config.hasPushScheduledSupport) {
return Promise.resolve();
}
return config.pushControllerQueue.enqueue(body, where, config, auth, pushStatus);
}).catch((err) => {
return pushStatus.fail(err).then(() => {
@@ -63,7 +67,7 @@ export class PushController {
* @returns {Number|undefined} The expiration time if it exists in the request
*/
static getExpirationTime(body = {}) {
var hasExpirationTime = !!body['expiration_time'];
var hasExpirationTime = body.hasOwnProperty('expiration_time');
if (!hasExpirationTime) {
return;
}
@@ -84,6 +88,34 @@ export class PushController {
}
return expirationTime.valueOf();
}
/**
* Get push time from the request body.
* @param {Object} request A request object
* @returns {Number|undefined} The push time if it exists in the request
*/
static getPushTime(body = {}) {
var hasPushTime = body.hasOwnProperty('push_time');
if (!hasPushTime) {
return;
}
var pushTimeParam = body['push_time'];
var pushTime;
if (typeof pushTimeParam === 'number') {
pushTime = new Date(pushTimeParam * 1000);
} else if (typeof pushTimeParam === 'string') {
pushTime = new Date(pushTimeParam);
} else {
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
body['push_time'] + ' is not valid time.');
}
// Check pushTime is valid or not, if it is not valid, pushTime is NaN
if (!isFinite(pushTime)) {
throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED,
body['push_time'] + ' is not valid time.');
}
return pushTime;
}
}
export default PushController;

View File

@@ -95,6 +95,7 @@ class ParseServer {
analyticsAdapter,
filesAdapter,
push,
scheduledPush = false,
loggerAdapter,
jsonLogs = defaults.jsonLogs,
logsFolder = defaults.logsFolder,
@@ -182,6 +183,7 @@ class ParseServer {
const pushController = new PushController();
const hasPushSupport = pushAdapter && push;
const hasPushScheduledSupport = pushAdapter && push && scheduledPush;
const {
disablePushWorker
@@ -259,7 +261,8 @@ class ParseServer {
userSensitiveFields,
pushWorker,
pushControllerQueue,
hasPushSupport
hasPushSupport,
hasPushScheduledSupport
});
Config.validate(AppCache.get(appId));

View File

@@ -30,7 +30,7 @@ export class FeaturesRouter extends PromiseRouter {
},
push: {
immediatePush: req.config.hasPushSupport,
scheduledPush: false,
scheduledPush: req.config.hasPushScheduledSupport,
storedPushData: req.config.hasPushSupport,
pushAudiences: false,
},

View File

@@ -110,6 +110,18 @@ export function pushStatusHandler(config, objectId = newObjectId()) {
const handler = statusHandler(PUSH_STATUS_COLLECTION, database);
const setInitial = function(body = {}, where, options = {source: 'rest'}) {
const now = new Date();
let pushTime = new Date();
let status = 'pending';
if (body.hasOwnProperty('push_time')) {
if (config.hasPushScheduledSupport) {
pushTime = body.push_time;
status = 'scheduled';
} else {
logger.warn('Trying to schedule a push while server is not configured.');
logger.warn('Push will be sent immediately');
}
}
const data = body.data || {};
const payloadString = JSON.stringify(data);
let pushHash;
@@ -123,13 +135,13 @@ export function pushStatusHandler(config, objectId = newObjectId()) {
const object = {
objectId,
createdAt: now,
pushTime: now.toISOString(),
pushTime: pushTime.toISOString(),
query: JSON.stringify(where),
payload: payloadString,
source: options.source,
title: options.title,
expiry: body.expiration_time,
status: "pending",
status: status,
numSent: 0,
pushHash,
// lockdown!

View File

@@ -81,6 +81,11 @@ export default {
help: "Configuration for push, as stringified JSON. See https://github.com/ParsePlatform/parse-server/wiki/Push",
action: objectParser
},
"scheduledPush": {
env: "PARSE_SERVER_SCHEDULED_PUSH",
help: "Configuration for push scheduling. Defaults to false.",
action: booleanParser
},
"oauth": {
env: "PARSE_SERVER_OAUTH_PROVIDERS",
help: "[DEPRECATED (use auth option)] Configuration for your oAuth providers, as stringified JSON. See https://github.com/ParsePlatform/parse-server/wiki/Parse-Server-Guide#oauth",