Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 133 additions & 1 deletion spec/PushWorker.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ describe('PushWorker', () => {
},
response: { error: 'invalid error...' }
}
], true);
], undefined, true);
expect(spy).toHaveBeenCalled();
expect(spy.calls.count()).toBe(1);
const lastCall = spy.calls.mostRecent();
Expand All @@ -241,5 +241,137 @@ describe('PushWorker', () => {
});
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();
});
});
});
});
30 changes: 16 additions & 14 deletions src/Controllers/SchemaController.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,22 @@ const defaultColumns = Object.freeze({
"subtitle": {type:'String'},
},
_PushStatus: {
"pushTime": {type:'String'},
"source": {type:'String'}, // rest or webui
"query": {type:'String'}, // the stringified JSON query
"payload": {type:'String'}, // the stringified JSON payload,
"title": {type:'String'},
"expiry": {type:'Number'},
"status": {type:'String'},
"numSent": {type:'Number'},
"numFailed": {type:'Number'},
"pushHash": {type:'String'},
"errorMessage": {type:'Object'},
"sentPerType": {type:'Object'},
"failedPerType":{type:'Object'},
"count": {type:'Number'}
"pushTime": {type:'String'},
"source": {type:'String'}, // rest or webui
"query": {type:'String'}, // the stringified JSON query
"payload": {type:'String'}, // the stringified JSON payload,
"title": {type:'String'},
"expiry": {type:'Number'},
"status": {type:'String'},
"numSent": {type:'Number'},
"numFailed": {type:'Number'},
"pushHash": {type:'String'},
"errorMessage": {type:'Object'},
"sentPerType": {type:'Object'},
"failedPerType": {type:'Object'},
"sentPerUTCOffset": {type:'Object'},
"failedPerUTCOffset": {type:'Object'},
"count": {type:'Number'}
},
_JobStatus: {
"jobName": {type: 'String'},
Expand Down
12 changes: 6 additions & 6 deletions src/Push/PushWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 auth = master(config);
const where = utils.applyDeviceTokenExists(query.where);
Expand All @@ -56,13 +56,13 @@ export class PushWorker {
if (results.length == 0) {
return;
}
return this.sendToAdapter(body, results, pushStatus, config);
return this.sendToAdapter(body, results, pushStatus, config, UTCOffset);
}, 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);
// Check if we have locales in the push body
const locales = utils.getLocalesFromPush(body);
Expand All @@ -75,15 +75,15 @@ export class PushWorker {
const promises = Object.keys(grouppedInstallations).map((locale) => {
const installations = grouppedInstallations[locale];
const body = bodiesPerLocales[locale];
return this.sendToAdapter(body, installations, pushStatus, config);
return this.sendToAdapter(body, installations, pushStatus, config, UTCOffset);
});
return Promise.all(promises);
}

if (!utils.isPushIncrementing(body)) {
logger.verbose(`Sending push to ${installations.length}`);
return this.adapter.send(body, installations, pushStatus.objectId).then((results) => {
return pushStatus.trackSent(results);
return pushStatus.trackSent(results, UTCOffset).then(() => results);
});
}

Expand All @@ -95,7 +95,7 @@ export class PushWorker {
const payload = deepcopy(body);
payload.data.badge = parseInt(badge);
const installations = badgeInstallationsMap[badge];
return this.sendToAdapter(payload, installations, pushStatus, config);
return this.sendToAdapter(payload, installations, pushStatus, config, UTCOffset);
});
return Promise.all(promises);
}
Expand Down
6 changes: 5 additions & 1 deletion src/StatusHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export function pushStatusHandler(config, objectId = newObjectId(config.objectId
{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 = {
updatedAt: new Date(),
numSent: 0,
Expand All @@ -179,6 +179,10 @@ export function pushStatusHandler(config, objectId = newObjectId(config.objectId
const deviceType = result.device.deviceType;
const key = result.transmitted ? `sentPerType.${deviceType}` : `failedPerType.${deviceType}`;
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) {
memo.numSent++;
} else {
Expand Down