public/lib/composer-service.js (200 lines of code) (raw):
import angular from 'angular';
import './telemetry-service';
import { getListElementFormatFromLabel, contentTypeToComposerContentType, listElementArticleFormats } from './model/special-formats.ts';
angular.module('wfComposerService', ['wfTelemetryService'])
.service('wfComposerService', ['$http', '$q', 'config', '$log', 'wfHttpSessionService', 'wfTelemetryService', wfComposerService]);
function wfComposerService($http, $q, config, $log, wfHttpSessionService, wfTelemetryService) {
const request = wfHttpSessionService.request;
const composerContentFetch = config.composerContentDetails;
function composerUpdateFieldUrl(fieldName, contentId) {
function liveOrPreview(isPreview) {
return `${composerContentFetch}${contentId}/${isPreview ? "preview" : "live"}/fields/${fieldName}`
}
return {
preview: liveOrPreview(true),
live: liveOrPreview(false)
}
}
function composerUpdateSettingUrl(settingName, contentId) {
function liveOrPreview(isPreview) {
return `${composerContentFetch}${contentId}/${isPreview ? "preview" : "live"}/settings/${settingName}`
}
return {
preview: liveOrPreview(true),
live: liveOrPreview(false)
}
}
// budget composer url parser - just gets the portion after the last '/'
function parseComposerId(url) {
return url.substring(url.lastIndexOf('/') + 1);
}
function deepSearch(obj, path) {
if (path.length === 0) return obj;
const next = path[0];
if (obj[next]) return deepSearch(obj[next], path.slice(1));
else return null;
}
// Mapping of workflow content fields to transform functions on composer response
const composerParseMap = {
composerId: (d) => d.id,
contentType: (d) => d.type,
articleFormat: (d) => {
const displayHint = deepSearch(d, ['preview', 'data', 'settings', 'displayHint', 'data'])
return listElementArticleFormats.find(f => f.value === displayHint)?.label
},
headline: (d) => deepSearch(d, ['preview', 'data', 'fields', 'headline', 'data']) || undefined,
published: (d) => d.published,
timePublished: (d) => new Date(deepSearch(d, ['contentChangeDetails', 'data', 'published', 'date']) || undefined),
path: (d) => deepSearch(d, ['identifiers', 'path', 'data']),
commentable: (d) => deepSearch(d, ['preview', 'data', 'settings', 'commentable', 'data']) === 'true',
takenDown: (d) => false, // TODO: takenDown from composer feed
activeInInCopy: (d) => deepSearch(d, ['toolSettings', 'activeInInCopy', 'data']) === 'true',
composerProdOffice: (d) => deepSearch(d, ['preview', 'data', 'settings', 'productionOffice', 'data']) || undefined,
optimisedForWeb: (d) => deepSearch(d, ['toolSettings', 'seoOptimised', 'data']) === 'true',
optimisedForWebChanged: (d) => deepSearch(d, ['toolSettings', 'seoChanged', 'data']) === 'true',
revision: (d) => deepSearch(d, ['contentChangeDetails', 'data', 'revision']),
lastModified: (d) => new Date(deepSearch(d, ['contentChangeDetails', 'data', 'lastModified', 'date']) || undefined),
lastModifiedBy: (d) => deepSearch(d, ['contentChangeDetails', 'data', 'lastModified', 'user', 'firstName']) + ' ' + deepSearch(d, ['contentChangeDetails', 'data', 'lastModified', 'user', 'lastName']),
commissionedLength: (d) => deepSearch(d, ['preview', 'data', 'fields', 'commissionedLength', 'data']) || undefined,
missingCommissionedLengthReason: (d) => deepSearch(d, ['preview', 'data', 'fields', 'missingCommissionedLengthReason', 'data']) || undefined,
displayHint: (d) => deepSearch(d, ['preview', 'data', 'settings', 'displayHint', 'data']) || undefined,
};
function parseComposerData(response, target) {
target = target || {};
if (!response.data || !response.data.data || !response.data.data.id) {
$log.error("Composer response missing id field. Response: " + JSON.stringify(response) + " \n Stub metadata: " + JSON.stringify(target))
return Promise.reject({
message: "composer response did not contain id, response: " + JSON.stringify(response)})
} else {
const data = response.data.data;
Object.keys(composerParseMap).forEach((key) => {
target[key] = composerParseMap[key](data);
});
return Promise.resolve(target);
}
}
function getComposerContent(url) {
return $http({
method: 'GET',
url: composerContentFetch + parseComposerId(url),
params: {'includePreview': 'true'},
withCredentials: true
});
}
this.getComposerContent = getComposerContent;
this.parseComposerData = parseComposerData;
this.create = function createInComposer(type, commissioningDesks, commissionedLength, prodOffice, template, articleFormat, priority, missingCommissionedLengthReason) {
var selectedDisplayHint = getListElementFormatFromLabel(articleFormat)?.value;
var params = {
'type': contentTypeToComposerContentType(type),
'tracking': commissioningDesks,
'productionOffice': prodOffice,
'displayHint': selectedDisplayHint,
'originatingSystem': 'workflow'
};
if(commissionedLength) params['initialCommissionedLength'] = commissionedLength;
if(missingCommissionedLengthReason) params['missingCommissionedLengthReason'] = missingCommissionedLengthReason;
if(template) {
params['template'] = template.id;
}
const commissioningDeskExternalName = _wfConfig.commissioningDesks
.find(desk => desk.id.toString() === commissioningDesks)?.externalName;
const getPriorityName = (priority) => {
switch (priority){
case 0:
return 'Normal'
case 1:
return 'Urgent'
case 2:
return 'Very Urgent'
default:
return 'Unknown'
}
}
const tags = {
contentType: contentTypeToComposerContentType(type),
productionOffice: prodOffice,
priority: getPriorityName(priority),
}
if(selectedDisplayHint !== null && selectedDisplayHint !== undefined) tags.displayHint = selectedDisplayHint;
if(commissionedLength !== null && commissionedLength !== undefined) tags.commissionedLength = commissionedLength.toString();
if(commissioningDeskExternalName !== null && commissioningDeskExternalName !== undefined) tags.commissioningDesk = commissioningDeskExternalName;
if(missingCommissionedLengthReason !== null && missingCommissionedLengthReason !== undefined) tags.missingCommissionedLengthReason = missingCommissionedLengthReason;
wfTelemetryService.sendTelemetryEvent("WORKFLOW_CREATE_IN_COMPOSER_TRIGGERED", tags);
return request({
method: 'POST',
url: config.composerNewContent,
params: params,
withCredentials: true
});
};
this.loadTemplates = function() {
return request({
method: 'GET',
url: config.composerTemplates,
withCredentials: true
}).then(({ data }) => {
return data;
});
};
this.updateField = function (composerId, fieldName, value, live = false) {
let urls = composerUpdateFieldUrl(fieldName, composerId);
let url = live ? urls.live : urls.preview;
let req = {
method: 'PUT',
url: url,
data: `"${value}"`,
withCredentials: true
};
return request(req);
};
this.updateFieldInPreviewAndLive = function (composerId, fieldName, value) {
return Promise.all([
this.updateField(composerId, fieldName, value, false),
this.updateField(composerId, fieldName, value, true)
]);
}
this.updateSetting = function (composerId, settingName, value, live = false) {
let urls = composerUpdateSettingUrl(settingName, composerId);
let url = live ? urls.live : urls.preview;
let req = {
method: 'PUT',
url: url,
data: `"${value}"`,
withCredentials: true
};
return request(req);
};
this.updateSettingInPreviewAndLive = function (composerId, settingName, value) {
return Promise.all([
this.updateSetting(composerId, settingName, value, false),
this.updateSetting(composerId, settingName, value, true)
]);
}
this.deleteField = function (composerId, fieldName, live = false) {
let urls = composerUpdateFieldUrl(fieldName, composerId);
let url = live ? urls.live : urls.preview;
let req = {
method: 'DELETE',
url: url,
withCredentials: true
};
return request(req);
};
this.deleteFieldInPreviewAndLive = function (composerId, fieldName) {
return Promise.all([
this.deleteField(composerId, fieldName, false),
this.deleteField(composerId, fieldName, true)
]);
}
/**
* Update rights information for the given piece.
* @param {string} composerId
* @param {{
* developerCommunity: boolean,
* subscriptionDatabases: boolean,
* syndicationAggregate: boolean
* }} rightsData
* @returns Promise<Response>
*/
this.updateRights = function (
composerId,
rightsData
) {
let req = {
method: 'PUT',
url: `${composerContentFetch}${composerId}/rights`,
data: JSON.stringify(rightsData),
withCredentials: true
};
return request(req);
};
}