diff --git a/plugins/push/api/jobs/util/connector.js b/plugins/push/api/jobs/util/connector.js index 66947665725..72b8a49cc95 100644 --- a/plugins/push/api/jobs/util/connector.js +++ b/plugins/push/api/jobs/util/connector.js @@ -134,7 +134,12 @@ class Connector extends SynFlushTransform { }, callback); } else if (!message.is(State.Streaming)) { - message.updateAtomically({_id: message._id, state: message.state}, {$set: {status: Status.Sending}, $bit: {state: {or: State.Streaming}}}) + let date = new Date(), + update = {$set: {status: Status.Sending, 'info.startedLast': date}, $bit: {state: {or: State.Streaming}}}; + if (!message.info.started) { + update.$set['info.started'] = date; + } + message.updateAtomically({_id: message._id, state: message.state}, update) .then(ok => { if (ok) { this.do_transform(push, encoding, callback); diff --git a/plugins/push/api/jobs/util/resultor.js b/plugins/push/api/jobs/util/resultor.js index 800bb9acd4a..1baff25ac65 100644 --- a/plugins/push/api/jobs/util/resultor.js +++ b/plugins/push/api/jobs/util/resultor.js @@ -265,6 +265,9 @@ class Resultor extends SynFlushTransform { this.log.d('saving message', m.id, m.result.json, 'state', state, 'status', status, 'error', error); m.state = state; m.status = status; + if (status === Status.Sent || status === Status.Failed) { + m.info.finished = new Date(); + } if (error) { m.result.error = error; } diff --git a/plugins/push/api/send/audience.js b/plugins/push/api/send/audience.js index aa4060aeed9..fefac88b5ad 100644 --- a/plugins/push/api/send/audience.js +++ b/plugins/push/api/send/audience.js @@ -347,7 +347,7 @@ class Mapper { this.f = f; this.pf = p + f; this.topUserFields = []; - message.userFields.forEach(k => this.topUserFields.push(k.indexOf('.') === -1 ? k : k.substr(0, k.indexOf('.')))); // make sure we have 'custom', not 'custom.x' + Message.userFieldsFor(message.contents, true).forEach(k => this.topUserFields.push(k.indexOf('.') === -1 ? k : k.substr(0, k.indexOf('.')))); // make sure we have 'custom', not 'custom.x' } /** @@ -514,7 +514,7 @@ class PusherPopper { await this.audience.addFilter(steps, this.audience.message.filter); } - let userFields = Message.userFieldsFor(this.audience.message.contents.concat(this.contents || [])); + let userFields = Message.userFieldsFor(this.audience.message.contents.concat(this.contents || []), true); // Decrease amount of data we process here await this.audience.addProjection(steps, userFields); diff --git a/plugins/push/api/send/data/const.js b/plugins/push/api/send/data/const.js index e9742a459f2..630cab7af62 100644 --- a/plugins/push/api/send/data/const.js +++ b/plugins/push/api/send/data/const.js @@ -157,6 +157,7 @@ const TriggerKind = { * Separator used in property keys */ const S = '|'; +const S_REGEXP = '\\|'; /** * Type of personalization object @@ -191,6 +192,7 @@ function toDate(date) { module.exports = { S, + S_REGEXP, State, Status, diff --git a/plugins/push/api/send/data/content.js b/plugins/push/api/send/data/content.js index c21abee01ca..6aa3fe2728f 100644 --- a/plugins/push/api/send/data/content.js +++ b/plugins/push/api/send/data/content.js @@ -219,6 +219,18 @@ class Content extends Validatable { } } + /** + * Getter for titlePers with leading "up." removed from field names + * + * @returns {object|undefined} title personalization + */ + get titlePersDeup() { + if (!this._titlePersDeup && this._data.titlePers) { + this._titlePersDeup = Content.deupPers(this._data.titlePers); + } + return this._titlePersDeup; + } + /** * Getter for message * @@ -265,6 +277,38 @@ class Content extends Validatable { } } + /** + * Getter for titlePers with leading "up." removed from field names + * + * @returns {object|undefined} title personalization + */ + get messagePersDeup() { + if (!this._messagePersDeup && this._data.messagePers) { + this._messagePersDeup = Content.deupPers(this._data.messagePers); + } + return this._messagePersDeup; + } + + /** + * Deup (remove leading "up.") from personalisation object and return new one + * + * @param {object} obj object to deup + * @returns {object} object with keys deupped + */ + static deupPers(obj) { + let ret = {}; + Object.keys(obj).forEach(idx => { + let {f, c, k, t} = obj[idx]; + ret[idx] = { + f, + c, + t, + k: k.indexOf('up.') === 0 ? k.substring(3) : k, + }; + }); + return ret; + } + /** * Getter for sound * @@ -360,6 +404,28 @@ class Content extends Validatable { } } + /** + * Getter for extras with leading "up." removed + * + * @returns {string[]} array of user prop keys to send + */ + get extrasDeup() { + if (!this._extrasDeup && this._data.extras) { + this._extrasDeup = Content.deupExtras(this._data.extras); + } + return this._extrasDeup; + } + + /** + * Deup (remove leading "up.") property key array + * + * @param {string[]} arr array of property keys + * @returns {string[]} array with keys deupped + */ + static deupExtras(arr) { + return arr.map(x => x.indexOf('up.') === 0 ? x.substring(3) : x); + } + /** * Getter for expiration * diff --git a/plugins/push/api/send/data/message.js b/plugins/push/api/send/data/message.js index 736d9e6c4bb..526398bbb5e 100644 --- a/plugins/push/api/send/data/message.js +++ b/plugins/push/api/send/data/message.js @@ -1,5 +1,5 @@ 'use strict'; -const { State, Status, STATUSES, Mongoable, DEFAULTS, S } = require('./const'), +const { State, Status, STATUSES, Mongoable, DEFAULTS, S, S_REGEXP } = require('./const'), { Filter } = require('./filter'), { Content } = require('./content'), { Trigger, PlainTrigger, TriggerKind } = require('./trigger'), @@ -394,10 +394,25 @@ class Message extends Mongoable { * Get user fields used in a Content * * @param {Content[]} contents array of Content instances + * @param {boolean} deup remove leading 'up.' * @returns {string[]} array of app user field names */ - static userFieldsFor(contents) { - let keys = contents.map(content => Object.values(content.messagePers || {}).concat(Object.values(content.titlePers || {})).map(obj => obj.k).concat(content.extras || []).map(Message.decodeFieldKey)).flat(); + static userFieldsFor(contents, deup) { + let keys = contents.map(content => Object.values(content.messagePers || {}).concat(Object.values(content.titlePers || {})) + .map(obj => obj.k) + .concat(content.extras || []) + .map(Message.decodeFieldKey)) + .flat(); + if (deup) { + keys = keys.map(f => { + if (f.indexOf('up.') === 0) { + return f.substring(3); + } + else { + return f; + } + }); + } // if (contents.length > 1) { // commenting out for now because we always need locale now - for result subs if (keys.indexOf('la') === -1) { keys.push('la'); @@ -424,7 +439,7 @@ class Message extends Mongoable { * @returns {string} original field name */ static decodeFieldKey(key) { - return key.replace(new RegExp(S, 'g'), '.'); + return key.replace(new RegExp(S_REGEXP, 'g'), '.'); } /** diff --git a/plugins/push/api/send/data/template.js b/plugins/push/api/send/data/template.js index 8fbe019edd9..5fc890d55c9 100644 --- a/plugins/push/api/send/data/template.js +++ b/plugins/push/api/send/data/template.js @@ -1,5 +1,6 @@ const { LRU } = require('./lru'), - personalize = require('./pers'); + personalize = require('./pers'), + { Content } = require('./content'); /** * Simple class for abstracting template/compilation-related logic @@ -117,7 +118,8 @@ class Template { message = push.ov && push.ov.message ? null : undefined, buttons = push.ov && push.ov.buttons ? null : undefined, data = push.ov && push.ov.data ? null : undefined, - extras = push.ov && push.ov.extras ? null : undefined; + extras = push.ov && push.ov.extras ? null : undefined, + specific = push.ov && push.ov.specific || {}; // now go backwards through all contents picking the latest title/message/etc // this ensures that any overrides don't mess with less important values (i.e. we cannot apply data without picking, message/messagePers etc are inter dependent as well) @@ -130,7 +132,7 @@ class Template { if (title === undefined) { if (c.titlePers) { - title = this.title(c.title, c.titlePers, push.pr); + title = this.title(c.title, c.titlePersDeup, push.pr); } else { title = c.title; @@ -138,7 +140,7 @@ class Template { } if (message === undefined) { if (c.messagePers) { - message = this.message(c.message, c.messagePers, push.pr); + message = this.message(c.message, c.messagePersDeup, push.pr); } else { message = c.message; @@ -151,14 +153,21 @@ class Template { data = c.data; } if (extras === undefined) { - extras = c.extras; + extras = c.extrasDeup; + } + if (c.specific) { + for (let k in c.specific) { + if (specific[k] === undefined) { + specific[k] = c.specific[k]; + } + } } } // now apply all picked content items unless there's an override in push.ov if (push.ov && push.ov.title) { if (push.ov.titlePers) { - this.platform.map.title(this, personalize(push.ov.title, push.ov.titlePers)(push.pr)); + this.platform.map.title(this, personalize(push.ov.title, Content.deupPers(push.ov.titlePers))(push.pr)); } else { this.platform.map.title(this, push.ov.title, push.pr); @@ -170,7 +179,7 @@ class Template { if (push.ov && push.ov.message) { if (push.ov.messagePers) { - this.platform.map.message(this, personalize(push.ov.message, push.ov.messagePers)(push.pr)); + this.platform.map.message(this, personalize(push.ov.message, Content.deupPers(push.ov.messagePers))(push.pr)); } else { this.platform.map.message(this, push.ov.message, push.pr); @@ -196,12 +205,23 @@ class Template { } if (push.ov && push.ov.extras) { - this.platform.map.extras(this, push.ov.extras, push.pr); + this.platform.map.extras(this, Content.deupExtras(push.ov.extras), push.pr); } else if (extras) { this.platform.map.extras(this, extras, push.pr); } + if (push.ov && push.ov.extras) { + this.platform.map.extras(this, Content.deupExtras(push.ov.extras), push.pr); + } + else if (extras) { + this.platform.map.extras(this, extras, push.pr); + } + + if (specific) { + this.platform.map.specific(this, specific, push.pr); + } + // apply other overrides if any if (push.ov) { this.appl(this.platform.fields, push.ov, push.pr); diff --git a/plugins/push/api/send/platforms/a.js b/plugins/push/api/send/platforms/a.js index d97b7e08715..12bd6a0e3cd 100644 --- a/plugins/push/api/send/platforms/a.js +++ b/plugins/push/api/send/platforms/a.js @@ -370,6 +370,20 @@ const map = { } } }, + + /** + * Sends platform specific fields + * + * @param {Template} template template + * @param {object} specific platform specific props to be sent + */ + specific: function(template, specific) { + if (specific) { + if (specific.large_icon) { + template.result.data['c.li'] = specific.large_icon; + } + } + }, }; /** diff --git a/plugins/push/api/send/platforms/i.js b/plugins/push/api/send/platforms/i.js index 14e2325bc9b..ba2b3900ca2 100644 --- a/plugins/push/api/send/platforms/i.js +++ b/plugins/push/api/send/platforms/i.js @@ -395,7 +395,7 @@ const map = { */ buttons: function(t, buttons) { if (buttons) { - t.result.c.b = buttons.map(b => ({t: b.title, l: b.link})); + t.result.c.b = buttons.map(b => ({t: b.title, l: b.url})); } }, @@ -490,6 +490,21 @@ const map = { template.result.c.e = e; } }, + + /** + * Sends platform specific fields + * + * @param {Template} template template + * @param {object} specific platform specific props to be sent + */ + specific: function(template, specific) { + if (specific) { + if (specific.subtitle) { + template.result.aps.alert = template.result.aps.alert || {}; + template.result.aps.alert.subtitle = specific.subtitle; + } + } + }, }; /**