Adds ability to track sent/failed PerUTCOffset in the PushWorker (#4158)
* Adds ability to track sent/failed PerUTCOffset in the PushWorker - for scheduled push notifications at a certain time, it helps keep track of the state * Makes sure we track it all correctly * Adds to Postgres
This commit is contained in:
@@ -228,7 +228,7 @@ describe('PushWorker', () => {
|
|||||||
},
|
},
|
||||||
response: { error: 'invalid error...' }
|
response: { error: 'invalid error...' }
|
||||||
}
|
}
|
||||||
], true);
|
], undefined, true);
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(spy).toHaveBeenCalled();
|
||||||
expect(spy.calls.count()).toBe(1);
|
expect(spy.calls.count()).toBe(1);
|
||||||
const lastCall = spy.calls.mostRecent();
|
const lastCall = spy.calls.mostRecent();
|
||||||
@@ -241,5 +241,137 @@ describe('PushWorker', () => {
|
|||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('tracks push status per UTC offsets', (done) => {
|
||||||
|
const config = new Config('test');
|
||||||
|
const handler = pushStatusHandler(config, 'ABCDEF1234');
|
||||||
|
const spy = spyOn(config.database, "update").and.callThrough();
|
||||||
|
const UTCOffset = 1;
|
||||||
|
handler.setInitial().then(() => {
|
||||||
|
return handler.trackSent([
|
||||||
|
{
|
||||||
|
transmitted: false,
|
||||||
|
device: {
|
||||||
|
deviceToken: 1,
|
||||||
|
deviceType: 'ios',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transmitted: true,
|
||||||
|
device: {
|
||||||
|
deviceToken: 1,
|
||||||
|
deviceType: 'ios',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
], UTCOffset)
|
||||||
|
}).then(() => {
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
expect(spy.calls.count()).toBe(1);
|
||||||
|
const lastCall = spy.calls.mostRecent();
|
||||||
|
expect(lastCall.args[0]).toBe('_PushStatus');
|
||||||
|
const updatePayload = lastCall.args[2];
|
||||||
|
expect(updatePayload.updatedAt instanceof Date).toBeTruthy();
|
||||||
|
// remove the updatedAt as not testable
|
||||||
|
delete updatePayload.updatedAt;
|
||||||
|
|
||||||
|
expect(lastCall.args[2]).toEqual({
|
||||||
|
numSent: { __op: 'Increment', amount: 1 },
|
||||||
|
numFailed: { __op: 'Increment', amount: 1 },
|
||||||
|
'sentPerType.ios': { __op: 'Increment', amount: 1 },
|
||||||
|
'failedPerType.ios': { __op: 'Increment', amount: 1 },
|
||||||
|
[`sentPerUTCOffset.${UTCOffset}`]: { __op: 'Increment', amount: 1 },
|
||||||
|
[`failedPerUTCOffset.${UTCOffset}`]: { __op: 'Increment', amount: 1 },
|
||||||
|
count: { __op: 'Increment', amount: -2 },
|
||||||
|
});
|
||||||
|
const query = new Parse.Query('_PushStatus');
|
||||||
|
return query.get('ABCDEF1234', { useMasterKey: true });
|
||||||
|
}).then((pushStatus) => {
|
||||||
|
const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset');
|
||||||
|
expect(sentPerUTCOffset['1']).toBe(1);
|
||||||
|
const failedPerUTCOffset = pushStatus.get('failedPerUTCOffset');
|
||||||
|
expect(failedPerUTCOffset['1']).toBe(1);
|
||||||
|
return handler.trackSent([
|
||||||
|
{
|
||||||
|
transmitted: false,
|
||||||
|
device: {
|
||||||
|
deviceToken: 1,
|
||||||
|
deviceType: 'ios',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transmitted: true,
|
||||||
|
device: {
|
||||||
|
deviceToken: 1,
|
||||||
|
deviceType: 'ios',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transmitted: true,
|
||||||
|
device: {
|
||||||
|
deviceToken: 1,
|
||||||
|
deviceType: 'ios',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
], UTCOffset)
|
||||||
|
}).then(() => {
|
||||||
|
const query = new Parse.Query('_PushStatus');
|
||||||
|
return query.get('ABCDEF1234', { useMasterKey: true });
|
||||||
|
}).then((pushStatus) => {
|
||||||
|
const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset');
|
||||||
|
expect(sentPerUTCOffset['1']).toBe(3);
|
||||||
|
const failedPerUTCOffset = pushStatus.get('failedPerUTCOffset');
|
||||||
|
expect(failedPerUTCOffset['1']).toBe(2);
|
||||||
|
}).then(done).catch(done.fail);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('tracks push status per UTC offsets with negative offsets', (done) => {
|
||||||
|
const config = new Config('test');
|
||||||
|
const handler = pushStatusHandler(config);
|
||||||
|
spyOn(config.database, "create").and.callFake(() => {
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
|
const spy = spyOn(config.database, "update").and.callFake(() => {
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
|
const UTCOffset = -6;
|
||||||
|
handler.trackSent([
|
||||||
|
{
|
||||||
|
transmitted: false,
|
||||||
|
device: {
|
||||||
|
deviceToken: 1,
|
||||||
|
deviceType: 'ios',
|
||||||
|
},
|
||||||
|
response: { error: 'Unregistered' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transmitted: true,
|
||||||
|
device: {
|
||||||
|
deviceToken: 1,
|
||||||
|
deviceType: 'ios',
|
||||||
|
},
|
||||||
|
response: { error: 'Unregistered' }
|
||||||
|
},
|
||||||
|
], UTCOffset).then(() => {
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
expect(spy.calls.count()).toBe(1);
|
||||||
|
const lastCall = spy.calls.mostRecent();
|
||||||
|
expect(lastCall.args[0]).toBe('_PushStatus');
|
||||||
|
const updatePayload = lastCall.args[2];
|
||||||
|
expect(updatePayload.updatedAt instanceof Date).toBeTruthy();
|
||||||
|
// remove the updatedAt as not testable
|
||||||
|
delete updatePayload.updatedAt;
|
||||||
|
|
||||||
|
expect(lastCall.args[2]).toEqual({
|
||||||
|
numSent: { __op: 'Increment', amount: 1 },
|
||||||
|
numFailed: { __op: 'Increment', amount: 1 },
|
||||||
|
'sentPerType.ios': { __op: 'Increment', amount: 1 },
|
||||||
|
'failedPerType.ios': { __op: 'Increment', amount: 1 },
|
||||||
|
[`sentPerUTCOffset.${UTCOffset}`]: { __op: 'Increment', amount: 1 },
|
||||||
|
[`failedPerUTCOffset.${UTCOffset}`]: { __op: 'Increment', amount: 1 },
|
||||||
|
count: { __op: 'Increment', amount: -2 },
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -72,20 +72,22 @@ const defaultColumns = Object.freeze({
|
|||||||
"subtitle": {type:'String'},
|
"subtitle": {type:'String'},
|
||||||
},
|
},
|
||||||
_PushStatus: {
|
_PushStatus: {
|
||||||
"pushTime": {type:'String'},
|
"pushTime": {type:'String'},
|
||||||
"source": {type:'String'}, // rest or webui
|
"source": {type:'String'}, // rest or webui
|
||||||
"query": {type:'String'}, // the stringified JSON query
|
"query": {type:'String'}, // the stringified JSON query
|
||||||
"payload": {type:'String'}, // the stringified JSON payload,
|
"payload": {type:'String'}, // the stringified JSON payload,
|
||||||
"title": {type:'String'},
|
"title": {type:'String'},
|
||||||
"expiry": {type:'Number'},
|
"expiry": {type:'Number'},
|
||||||
"status": {type:'String'},
|
"status": {type:'String'},
|
||||||
"numSent": {type:'Number'},
|
"numSent": {type:'Number'},
|
||||||
"numFailed": {type:'Number'},
|
"numFailed": {type:'Number'},
|
||||||
"pushHash": {type:'String'},
|
"pushHash": {type:'String'},
|
||||||
"errorMessage": {type:'Object'},
|
"errorMessage": {type:'Object'},
|
||||||
"sentPerType": {type:'Object'},
|
"sentPerType": {type:'Object'},
|
||||||
"failedPerType":{type:'Object'},
|
"failedPerType": {type:'Object'},
|
||||||
"count": {type:'Number'}
|
"sentPerUTCOffset": {type:'Object'},
|
||||||
|
"failedPerUTCOffset": {type:'Object'},
|
||||||
|
"count": {type:'Number'}
|
||||||
},
|
},
|
||||||
_JobStatus: {
|
_JobStatus: {
|
||||||
"jobName": {type: 'String'},
|
"jobName": {type: 'String'},
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export class PushWorker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run({ body, query, pushStatus, applicationId }: any): Promise<*> {
|
run({ body, query, pushStatus, applicationId, UTCOffset }: any): Promise<*> {
|
||||||
const config = new Config(applicationId);
|
const config = new Config(applicationId);
|
||||||
const auth = master(config);
|
const auth = master(config);
|
||||||
const where = utils.applyDeviceTokenExists(query.where);
|
const where = utils.applyDeviceTokenExists(query.where);
|
||||||
@@ -56,13 +56,13 @@ export class PushWorker {
|
|||||||
if (results.length == 0) {
|
if (results.length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return this.sendToAdapter(body, results, pushStatus, config);
|
return this.sendToAdapter(body, results, pushStatus, config, UTCOffset);
|
||||||
}, err => {
|
}, err => {
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendToAdapter(body: any, installations: any[], pushStatus: any, config: Config): Promise<*> {
|
sendToAdapter(body: any, installations: any[], pushStatus: any, config: Config, UTCOffset: ?any): Promise<*> {
|
||||||
pushStatus = pushStatusHandler(config, pushStatus.objectId);
|
pushStatus = pushStatusHandler(config, pushStatus.objectId);
|
||||||
// Check if we have locales in the push body
|
// Check if we have locales in the push body
|
||||||
const locales = utils.getLocalesFromPush(body);
|
const locales = utils.getLocalesFromPush(body);
|
||||||
@@ -75,7 +75,7 @@ export class PushWorker {
|
|||||||
const promises = Object.keys(grouppedInstallations).map((locale) => {
|
const promises = Object.keys(grouppedInstallations).map((locale) => {
|
||||||
const installations = grouppedInstallations[locale];
|
const installations = grouppedInstallations[locale];
|
||||||
const body = bodiesPerLocales[locale];
|
const body = bodiesPerLocales[locale];
|
||||||
return this.sendToAdapter(body, installations, pushStatus, config);
|
return this.sendToAdapter(body, installations, pushStatus, config, UTCOffset);
|
||||||
});
|
});
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ export class PushWorker {
|
|||||||
if (!utils.isPushIncrementing(body)) {
|
if (!utils.isPushIncrementing(body)) {
|
||||||
logger.verbose(`Sending push to ${installations.length}`);
|
logger.verbose(`Sending push to ${installations.length}`);
|
||||||
return this.adapter.send(body, installations, pushStatus.objectId).then((results) => {
|
return this.adapter.send(body, installations, pushStatus.objectId).then((results) => {
|
||||||
return pushStatus.trackSent(results);
|
return pushStatus.trackSent(results, UTCOffset).then(() => results);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ export class PushWorker {
|
|||||||
const payload = deepcopy(body);
|
const payload = deepcopy(body);
|
||||||
payload.data.badge = parseInt(badge);
|
payload.data.badge = parseInt(badge);
|
||||||
const installations = badgeInstallationsMap[badge];
|
const installations = badgeInstallationsMap[badge];
|
||||||
return this.sendToAdapter(payload, installations, pushStatus, config);
|
return this.sendToAdapter(payload, installations, pushStatus, config, UTCOffset);
|
||||||
});
|
});
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ export function pushStatusHandler(config, objectId = newObjectId(config.objectId
|
|||||||
{status: "running", updatedAt: new Date(), count });
|
{status: "running", updatedAt: new Date(), count });
|
||||||
}
|
}
|
||||||
|
|
||||||
const trackSent = function(results, cleanupInstallations = process.env.PARSE_SERVER_CLEANUP_INVALID_INSTALLATIONS) {
|
const trackSent = function(results, UTCOffset, cleanupInstallations = process.env.PARSE_SERVER_CLEANUP_INVALID_INSTALLATIONS) {
|
||||||
const update = {
|
const update = {
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
numSent: 0,
|
numSent: 0,
|
||||||
@@ -179,6 +179,10 @@ export function pushStatusHandler(config, objectId = newObjectId(config.objectId
|
|||||||
const deviceType = result.device.deviceType;
|
const deviceType = result.device.deviceType;
|
||||||
const key = result.transmitted ? `sentPerType.${deviceType}` : `failedPerType.${deviceType}`;
|
const key = result.transmitted ? `sentPerType.${deviceType}` : `failedPerType.${deviceType}`;
|
||||||
memo[key] = incrementOp(memo, key);
|
memo[key] = incrementOp(memo, key);
|
||||||
|
if (typeof UTCOffset !== 'undefined') {
|
||||||
|
const offsetKey = result.transmitted ? `sentPerUTCOffset.${UTCOffset}` : `failedPerUTCOffset.${UTCOffset}`;
|
||||||
|
memo[offsetKey] = incrementOp(memo, offsetKey);
|
||||||
|
}
|
||||||
if (result.transmitted) {
|
if (result.transmitted) {
|
||||||
memo.numSent++;
|
memo.numSent++;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user