Name
sn_cd.cd_ContentProcessor
Description
API to process content such as emails and push notification
Script
var cd_ContentProcessor = Class.create();
cd_ContentProcessor.prototype = {
initialize: function() {},
/** Process content for a content type
* @param contentType string content type value e.g. 'email'
*/
processContent: function(contentType, waitDuration, retryCount) {
if (gs.nil(contentType)) {
gs.error("processContent : Empty content type. Exiting ");
return;
}
var grContentVisibility = new GlideRecord('sn_cd_content_visibility');
grContentVisibility.addActiveQuery();
grContentVisibility.addNotNullQuery('when_to_process');
grContentVisibility.addNullQuery('approvers').addOrCondition('state', 'published');
grContentVisibility.addQuery("notification_status", "pending");
if (cd_CommonConstants.NOTIFICATIONS_TYPE_EMAIL == contentType)
grContentVisibility.addQuery('content.content_type.value', cd_CommonConstants.NOTIFICATIONS_TYPE_EMAIL);
else if (cd_CommonConstants.NOTIFICATIONS_TYPE_PUSH == contentType)
grContentVisibility.addQuery('content.content_type.value', cd_CommonConstants.NOTIFICATIONS_TYPE_PUSH);
else if (cd_CommonConstants.NOTIFICATIONS_TYPE_TEAMS == contentType) {
grContentVisibility.addNotNullQuery('when_to_process').addCondition('when_to_process', '<=', new GlideDateTime());
grContentVisibility.addQuery('content.content_type.value', cd_CommonConstants.NOTIFICATIONS_TYPE_TEAMS);
} else {
gs.error("processContent : Invalid or unsupported content type : " + contentType + ". Exiting");
return;
}
grContentVisibility.orderBy('when_to_process');
grContentVisibility.query();
if (!grContentVisibility.hasNext()) {
gs.error("processContent : No Content to schedule. Exiting ");
return;
}
if (cd_CommonConstants.NOTIFICATIONS_TYPE_EMAIL == contentType)
this.processEmail(grContentVisibility);
else if (cd_CommonConstants.NOTIFICATIONS_TYPE_PUSH == contentType)
this.processPush(grContentVisibility);
else if (cd_CommonConstants.NOTIFICATIONS_TYPE_TEAMS == contentType)
new sn_cd.cd_ContentTeamsProcessor().processTeams(grContentVisibility, waitDuration, retryCount);
},
/** Process email for content visibility audience
* @param grContentVisibility GlideRecord [sn_cd_content_visibility] Content visibility record to retrieve audience from
*/
processEmail: function(grContentVisibility) {
var EMAIL_EVENT_NAME = 'sn_cd.notification.content_email';
this._processContentTypeAndRaiseEvent(grContentVisibility, cd_CommonConstants.NOTIFICATIONS_TYPE_EMAIL, EMAIL_EVENT_NAME);
},
/** Process push for content visibility audience
* @param grContentVisibility GlideRecord [sn_cd_content_visibility] Content visibility record to retrieve audience from
*/
processPush: function(grContentVisibility) {
var PUSH_EVENT_NAME = 'sn_cd.notification.content_push';
this._processContentTypeAndRaiseEvent(grContentVisibility, cd_CommonConstants.NOTIFICATIONS_TYPE_PUSH, PUSH_EVENT_NAME);
},
_processContentTypeAndRaiseEvent: function(grContentVisibility, contentType, eventName) {
var MAX_RUN_TIME = this._getNumberProperty("sn_cd.notification.max_run_time", 600);
var MAX_RECIPIENTS = this._getNumberProperty("sn_cd.notification.max_recipients", 50000);
var MAX_USERS_PER_EVENT = this._getNumberProperty("sn_cd.notification.max_users_per_event", 1000);
var MAX_EVENTS = this._getNumberProperty("sn_cd.notification.max_events", 50);
var EVENT_STAGGER = this._getNumberProperty("sn_cd.notification.event_stagger", 60);
var cd_Audience = new sn_cd.cd_Audience();
var cd_Translations = new sn_cd.cd_ContentTranslations();
var gdtStart = new GlideDateTime();
var gdtEnd = new GlideDateTime();
gdtEnd.addSeconds(MAX_RUN_TIME);
var eventCount = 0;
while (eventCount < MAX_EVENTS &&
gdtEnd.after(new GlideDateTime()) &&
grContentVisibility.next()) {
if ((grContentVisibility.getValue('use_adhoc_users') == true && gs.nil(grContentVisibility.getValue('users'))) || (grContentVisibility.getValue('use_adhoc_users') != true && gs.nil(grContentVisibility.getValue('audience'))))
continue;
var grContentNotification = grContentVisibility.content.getRefRecord();
if (!grContentNotification.isValidRecord() || !grContentNotification.active) {
grContentVisibility.setValue("notification_status", "error");
grContentVisibility.setValue("active", false);
grContentVisibility.update();
gs.warn("Script - " + this.type + ": Failed to find active content for schedule " + grContentVisibility.getUniqueValue());
continue;
}
var recipients;
if (grContentVisibility.getValue('use_adhoc_users') == true) {
var systemLanguage = gs.getProperty('glide.sys.language');
var grUsers = new GlideRecord('sys_user');
grUsers.addEncodedQuery('sys_idIN' + grContentVisibility.getValue('users'));
grUsers.query();
recipients = {};
while (grUsers.next()) {
var userSysId = grUsers.getUniqueValue();
var userLang = grUsers.getValue('preferred_language') || systemLanguage;
if (!recipients.hasOwnProperty(userLang))
recipients[userLang] = [];
recipients[userLang].push(userSysId);
}
} else
recipients = cd_Audience.getAudienceByLanguage(grContentVisibility.getValue('audience'));
var recipientStats = this._getRecipientCounts(recipients, MAX_USERS_PER_EVENT);
// Filter out content visibilities that can never process due to exceeding maximums
if (recipientStats[1] > MAX_RECIPIENTS || recipientStats[0] > MAX_EVENTS) {
grContentVisibility.setValue("notification_status", "max_exceeded");
grContentVisibility.setValue("active", false);
grContentVisibility.update();
continue;
}
// Skip if content visibility will exceed leftover event allotment. NOTE: It's possible that every next content visibility exceeds the leftover
if (eventCount + recipientStats[0] > MAX_EVENTS)
continue;
// Mark completed_on before queueing events to prevent duplicate emails if transaction times out
grContentVisibility.setValue("notification_status", "sent");
grContentVisibility.setValue("active", false);
grContentVisibility.completed_on = new GlideDateTime();
grContentVisibility.update();
// Chunk and stagger events
var gdtWhenToProcess = new GlideDateTime(grContentVisibility.when_to_process);
// Adjust this time to now if in the past
if (gdtWhenToProcess.before(gdtStart))
gdtWhenToProcess = new GlideDateTime();
for (var languagePreference in recipients) {
var eventGlideRecord = contentType == cd_CommonConstants.NOTIFICATIONS_TYPE_PUSH ? grContentVisibility : grContentNotification;
var eventParameter = contentType == cd_CommonConstants.NOTIFICATIONS_TYPE_PUSH ? "" : JSON.stringify(cd_Translations.getTranslatedEmailContent(grContentNotification, languagePreference));
for (var i = 0; i < recipients[languagePreference].length && MAX_USERS_PER_EVENT > 0;) {
var recipientsChunk = recipients[languagePreference].slice(i, i + MAX_USERS_PER_EVENT);
gs.eventQueueScheduled(eventName, eventGlideRecord, recipientsChunk, eventParameter, gdtWhenToProcess);
eventCount++;
gdtWhenToProcess.addSeconds(EVENT_STAGGER);
i += recipientsChunk.length;
}
}
}
},
/** Return a number value for a given property
* @param propName String Name of a property
* @param defaultValue Number The default value to use for a property
* @return Number The number value for a given property
*/
_getNumberProperty: function(propName, defaultValue) {
var propValue = Number(gs.getProperty(propName, defaultValue));
if (isNaN(propValue)) {
gs.warn("Script - " + this.type + ": Overriding invalid property value for " + propName);
propValue = defaultValue;
}
return propValue;
},
/** Return several counts including events to create, number of users, number of languages
* @param notificationRecipients object Map of {languageCode : [list of users]}
* @return Array an array of integers such that [eventCount, userCount, languageCount]
*/
_getRecipientCounts: function(notificationRecipients, max_users) {
var eventsToQueue = 0;
var userCount = 0;
var languageCount = 0;
for (var language in notificationRecipients) {
eventsToQueue += Math.ceil(notificationRecipients[language].length / max_users);
userCount += notificationRecipients[language].length;
languageCount++;
}
return [eventsToQueue, userCount, languageCount];
},
type: 'cd_ContentProcessor'
};
Sys ID
25b0adfb0b6303008cd6e7ae37673a65