Skip to content

Commit 79991f1

Browse files
refactor notification types creation
* refactor notification types * fixes --------- Co-authored-by: I524884 <[email protected]>
1 parent 8b2ca5c commit 79991f1

File tree

5 files changed

+329
-119
lines changed

5 files changed

+329
-119
lines changed

cds-plugin.js

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,26 @@
11
const cds = require("@sap/cds");
2-
const notifier = require("./lib/notifications");
3-
const {
4-
validateNotificationTypes,
5-
readFile,
6-
doesKeyExist
7-
} = require("./lib/utils");
2+
const { validateNotificationTypes, readFile } = require("./lib/utils");
3+
const { createNotificationTypesMap, processNotificationTypes} = require("./lib/notificationTypes");
84

9-
cds.once("served", () => {
10-
/**
11-
* For local testing initialise VCAP_SERVICES variables for the application
12-
* process.env.VCAP_SERVICES = Strigified VCAP_SERVICES variable
13-
*/
14-
/**
15-
* TODO: Decide the properties to be added in the alerts section for notificationtype files.
16-
*/
5+
cds.once("served", async () => {
176
const profiles = cds.env.profiles ?? [];
187
const production = profiles.includes('production');
8+
const destination = cds.env.requires.notifications?.destination ?? "SAP_Notifications";
9+
1910
if (cds.env.requires?.notifications?.types) {
2011
// read notification types
2112
const notificationTypes = readFile(cds.env.requires.notifications.types);
2213

2314
// validate notification types
2415
validateNotificationTypes(notificationTypes);
2516

17+
const notificationTypesMap = createNotificationTypesMap(notificationTypes);
18+
2619
// create notification types
2720
if (production) {
28-
notificationTypes.forEach((oNotificationType) => {
29-
notifier.postNotificationType(oNotificationType);
30-
});
21+
await processNotificationTypes(notificationTypesMap, destination);
3122
} else {
32-
const types = {};
33-
notificationTypes.forEach((oNotificationType) => {
34-
if (!doesKeyExist(types, oNotificationType.NotificationTypeKey)) {
35-
types[oNotificationType.NotificationTypeKey] = {};
36-
}
37-
38-
types[oNotificationType.NotificationTypeKey][oNotificationType.NotificationTypeVersion] = oNotificationType;
39-
});
40-
41-
cds.notifications = { local: { types }};
23+
cds.notifications = { local: { types: notificationTypesMap }};
4224
}
4325
} else if (!production) {
4426
cds.notifications = { local: { types: {} }};

lib/notificationTypes.js

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
const { executeHttpRequest, buildCsrfHeaders } = require("@sap-cloud-sdk/core");
2+
const { getNotificationDestination, doesKeyExist, getPrefix, getNotificationTypesKeyWithPrefix } = require("./utils");
3+
4+
const NOTIFICATION_TYPES_API_ENDPOINT = "v2/NotificationType.svc";
5+
6+
function createNotificationTypesMap(notificationTypesJSON) {
7+
const types = {};
8+
notificationTypesJSON.forEach((notificationType) => {
9+
const notificationTypeKeyWithPrefix = getNotificationTypesKeyWithPrefix(notificationType.NotificationTypeKey);
10+
11+
// update the notification type key with prefix
12+
notificationType.NotificationTypeKey = notificationTypeKeyWithPrefix;
13+
14+
15+
if (!doesKeyExist(types, notificationTypeKeyWithPrefix)) {
16+
types[notificationTypeKeyWithPrefix] = {};
17+
}
18+
19+
types[notificationTypeKeyWithPrefix][notificationType.NotificationTypeVersion] = notificationType;
20+
});
21+
22+
return types;
23+
}
24+
25+
async function getNotificationTypes(destination) {
26+
const notificationDestination = await getNotificationDestination(destination);
27+
const response = await executeHttpRequest(notificationDestination, {
28+
url: `${NOTIFICATION_TYPES_API_ENDPOINT}/NotificationTypes?$format=json&$expand=Templates,Actions,DeliveryChannels`,
29+
method: "get",
30+
});
31+
return response.data.d.results;
32+
}
33+
34+
async function createNotificationType(notificationType, destination) {
35+
const notificationDestination = await getNotificationDestination(destination);
36+
const csrfHeaders = await buildCsrfHeaders(notificationDestination, {
37+
url: NOTIFICATION_TYPES_API_ENDPOINT,
38+
});
39+
40+
console.log(
41+
`Notification Type of key ${notificationType.NotificationTypeKey} and version ${notificationType.NotificationTypeVersion} was not found. Creating it...`
42+
);
43+
44+
const response = await executeHttpRequest(notificationDestination, {
45+
url: `${NOTIFICATION_TYPES_API_ENDPOINT}/NotificationTypes`,
46+
method: "post",
47+
data: notificationType,
48+
headers: csrfHeaders,
49+
});
50+
return response.data.d;
51+
}
52+
53+
async function updateNotificationType(id, notificationType, destination) {
54+
const notificationDestination = await getNotificationDestination(destination);
55+
const csrfHeaders = await buildCsrfHeaders(notificationDestination, {
56+
url: NOTIFICATION_TYPES_API_ENDPOINT,
57+
});
58+
59+
console.log(
60+
`Detected change in notification type of key ${notificationType.NotificationTypeKey} and version ${notificationType.NotificationTypeVersion}. Updating it...`
61+
);
62+
63+
const response = await executeHttpRequest(notificationDestination, {
64+
url: `${NOTIFICATION_TYPES_API_ENDPOINT}/NotificationTypes(guid'${id}')`,
65+
method: "patch",
66+
data: notificationType,
67+
headers: csrfHeaders,
68+
});
69+
return response.data.d;
70+
}
71+
72+
async function deleteNotificationType(notificationType, destination) {
73+
const notificationDestination = await getNotificationDestination(destination);
74+
const csrfHeaders = await buildCsrfHeaders(notificationDestination, {
75+
url: NOTIFICATION_TYPES_API_ENDPOINT,
76+
});
77+
78+
console.log(
79+
`Notification Type of key ${notificationType.NotificationTypeKey} and version ${notificationType.NotificationTypeVersion} not present in the types file. Deleting it...`
80+
);
81+
82+
const response = await executeHttpRequest(notificationDestination, {
83+
url: `${NOTIFICATION_TYPES_API_ENDPOINT}/NotificationTypes(guid'${notificationType.NotificationTypeId}')`,
84+
method: "delete",
85+
headers: csrfHeaders,
86+
});
87+
return response.data.d;
88+
}
89+
90+
function _createChannelsMap(channels) {
91+
const channelMap = {};
92+
93+
channels.forEach((channel) => {
94+
channelMap[channel.Type] = channel;
95+
})
96+
97+
return channelMap;
98+
}
99+
100+
function areDeliveryChannelsSame(oldChannels, newChannels) {
101+
if(oldChannels.length !== newChannels.length) {
102+
return false;
103+
}
104+
105+
const oldChannelsMap = _createChannelsMap(oldChannels);
106+
const newChannelsMap = _createChannelsMap(newChannels);
107+
108+
for(type in Object.keys(oldChannelsMap)) {
109+
if(!(type in newChannelsMap)) return false;
110+
111+
const oldChannel = oldChannelsMap[type];
112+
const newChannel = newChannelsMap[type];
113+
114+
// TODO: Check if language is not there
115+
const same =
116+
oldChannel.Language == newChannel.Language.toUpperCase() &&
117+
oldChannel.ActionId == newChannel.ActionId &&
118+
oldChannel.ActionText == newChannel.ActionText &&
119+
oldChannel.GroupActionText == newChannel.GroupActionText &&
120+
oldChannel.Nature == newChannel.Nature;
121+
122+
if(!same) return false;
123+
delete newChannelsMap[type];
124+
}
125+
126+
return Object.keys(newChannelsMap).length == 0;
127+
}
128+
129+
function _createActionsMap(actions) {
130+
const actionsMap = {};
131+
132+
actions.forEach((action) => {
133+
actionsMap[ActionId] = action;
134+
})
135+
136+
return actionsMap;
137+
}
138+
139+
function areActionsSame(oldActions, newActions) {
140+
if(oldActions.length !== newActions.length) {
141+
return false;
142+
}
143+
144+
const oldActionsMap = _createActionsMap(oldActions);
145+
const newActionsMap = _createActionsMap(newActions);
146+
147+
for(actionId in Object.keys(oldActionsMap)) {
148+
if(!(actionId in newActionsMap)) return false;
149+
150+
const oldAction = oldActionsMap[actionId];
151+
const newAction = newActionsMap[actionId];
152+
153+
const same =
154+
oldAction.Language == newAction.Language.toUpperCase() &&
155+
oldAction.ActionId == newAction.ActionId &&
156+
oldAction.ActionText == newAction.ActionText &&
157+
oldAction.GroupActionText == newAction.GroupActionText &&
158+
oldAction.Nature == newAction.Nature;
159+
160+
if(!same) return false;
161+
delete newAction[actionId];
162+
}
163+
164+
return Object.keys(newActionsMap).length == 0;
165+
}
166+
167+
function _createTemplatesMap(templates) {
168+
const templatesObj = {};
169+
170+
templates.forEach((template) => {
171+
templatesObj[template.Language] = template;
172+
})
173+
174+
return templatesObj;
175+
}
176+
177+
function areTemplatesSame(oldTemplates, newTemplates) {
178+
if(oldTemplates.length !== newTemplates.length) {
179+
return false;
180+
}
181+
182+
const oldTemplatesMap = _createTemplatesMap(oldTemplates);
183+
const newTemplatesMap = _createTemplatesMap(newTemplates);
184+
185+
for(language in Object.keys(oldTemplatesMap)) {
186+
if(!(language in newTemplatesMap)) return false;
187+
188+
const oldTemplate = oldTemplatesMap[language];
189+
const newTemplate = newTemplatesMap[language];
190+
191+
const same =
192+
oldTemplate.Language == newTemplate.Language.toUpperCase() &&
193+
oldTemplate.TemplatePublic == newTemplate.TemplatePublic &&
194+
oldTemplate.TemplateSensitive == newTemplate.TemplateSensitive &&
195+
oldTemplate.TemplateGrouped == newTemplate.TemplateGrouped &&
196+
oldTemplate.Description == newTemplate.Description &&
197+
oldTemplate.TemplateLanguage == newTemplate.TemplateLanguage.toUpperCase() &&
198+
oldTemplate.Subtitle == newTemplate.Subtitle &&
199+
oldTemplate.EmailSubject == newTemplate.EmailSubject &&
200+
oldTemplate.EmailText == newTemplate.EmailText &&
201+
oldTemplate.EmailHtml == newTemplate.EmailHtml;
202+
203+
if(!same) return false;
204+
delete newTemplate[language];
205+
}
206+
207+
return Object.keys(newTemplates).length == 0;
208+
}
209+
210+
function isNotificationTypeSame(oldNotificationType, newNotificationType) {
211+
if(oldNotificationType.IsGroupable != newNotificationType.IsGroupable) {
212+
return false;
213+
}
214+
215+
if(!areTemplatesSame(oldNotificationType.Templates.results, newNotificationType.Templates)) {
216+
return false;
217+
}
218+
219+
if(!areActionsSame(oldNotificationType.Actions.results, newNotificationType.Actions)) {
220+
return false;
221+
}
222+
223+
if(!areDeliveryChannelsSame(oldNotificationType.DeliveryChannels.results, newNotificationType.DeliveryChannels)) {
224+
return false;
225+
}
226+
227+
return true;
228+
}
229+
230+
async function processNotificationTypes(notificationTypes, destination) {
231+
const prefix = getPrefix();
232+
233+
// get notficationTypes
234+
const existingTypes = await getNotificationTypes(destination);
235+
236+
// iterate through notification types
237+
existingTypes.forEach((existingType) => {
238+
if(!existingType.NotificationTypeKey.startsWith(`${prefix}/`)) {
239+
return;
240+
}
241+
242+
// if the type isn't present in the JSON file, delete it
243+
if(notificationTypes[existingType.NotificationTypeKey] === undefined || notificationTypes[existingType.NotificationTypeKey][NotificationTypeVersion] === undefined) {
244+
deleteNotificationType(existingType.NotificationTypeId, destination);
245+
}
246+
247+
const newType = JSON.parse(JSON.stringify(notificationTypes[existingType.NotificationTypeKey][NotificationTypeVersion]));
248+
249+
// if the type is there then verify if everything is same or not
250+
if(!isNotificationTypeSame(existingType, newType)) {
251+
updateNotificationType(existingType.NotificationTypeId, notificationTypes[existingType.NotificationTypeKey][NotificationTypeVersion], destination)
252+
}
253+
254+
// if notification type is same, remove it from new types map
255+
delete notificationTypes[existingType.NotificationTypeKey][NotificationTypeVersion];
256+
if(Object.keys(notificationTypes[existingType.NotificationTypeKey]).length == 0) {
257+
delete notificationTypes[existingType.NotificationTypeKey];
258+
}
259+
})
260+
261+
262+
// create notification types that aren't there
263+
for(let notificationTypeKey in notificationTypes) {
264+
for(let notificationTypeVersion in notificationTypes[notificationTypeKey]) {
265+
createNotificationType(notificationTypes[notificationTypeKey][notificationTypeVersion], destination);
266+
}
267+
}
268+
}
269+
270+
module.exports = {
271+
createNotificationTypesMap,
272+
processNotificationTypes
273+
}

0 commit comments

Comments
 (0)