1
+ const { executeHttpRequest } = require ( "@sap-cloud-sdk/http-client" ) ;
2
+ const { buildHeadersForDestination } = require ( "@sap-cloud-sdk/connectivity" ) ;
3
+ const { getNotificationDestination, doesKeyExist, getPrefix, getNotificationTypesKeyWithPrefix, executeRequest } = require ( "./utils" ) ;
4
+ const _ = require ( "lodash" ) ;
5
+
6
+ const NOTIFICATION_TYPES_API_ENDPOINT = "v2/NotificationType.svc" ;
7
+
8
+ const defaultTemplate = {
9
+ "NotificationTypeKey" : "Default" ,
10
+ "NotificationTypeVersion" : "1" ,
11
+ "Templates" : [
12
+ {
13
+ "Language" : "en" ,
14
+ "Description" : "Other Notifications" ,
15
+ "TemplatePublic" : "{{title}}" ,
16
+ "TemplateSensitive" : "{{title}}" ,
17
+ "TemplateGrouped" : "Other Notifications" ,
18
+ "TemplateLanguage" : "mustache" ,
19
+ "Subtitle" : "{{description}}"
20
+ }
21
+ ]
22
+ }
23
+
24
+ function fromOdataArrayFormat ( objectInArray ) {
25
+ if ( objectInArray === undefined || objectInArray === null || Array . isArray ( objectInArray ) ) {
26
+ return objectInArray ;
27
+ } else {
28
+ return objectInArray . results ;
29
+ }
30
+ }
31
+
32
+ function createNotificationTypesMap ( notificationTypesJSON , isLocal = false ) {
33
+ const types = { } ;
34
+
35
+ if ( isLocal ) {
36
+ types [ "Default" ] = { "1" : defaultTemplate } ;
37
+ }
38
+
39
+ // add user provided templates
40
+ notificationTypesJSON . forEach ( ( notificationType ) => {
41
+ // set default NotificationTypeVersion if required
42
+ if ( notificationType . NotificationTypeVersion === undefined ) {
43
+ notificationType . NotificationTypeVersion = "1" ;
44
+ }
45
+
46
+ const notificationTypeKeyWithPrefix = getNotificationTypesKeyWithPrefix ( notificationType . NotificationTypeKey ) ;
47
+
48
+ // update the notification type key with prefix
49
+ notificationType . NotificationTypeKey = notificationTypeKeyWithPrefix ;
50
+
51
+ if ( ! doesKeyExist ( types , notificationTypeKeyWithPrefix ) ) {
52
+ types [ notificationTypeKeyWithPrefix ] = { } ;
53
+ }
54
+
55
+ types [ notificationTypeKeyWithPrefix ] [ notificationType . NotificationTypeVersion ] = notificationType ;
56
+ } ) ;
57
+
58
+ return types ;
59
+ }
60
+
61
+ async function getNotificationTypes ( ) {
62
+ const notificationDestination = await getNotificationDestination ( ) ;
63
+ const response = await executeHttpRequest ( notificationDestination , {
64
+ url : `${ NOTIFICATION_TYPES_API_ENDPOINT } /NotificationTypes?$format=json&$expand=Templates,Actions,DeliveryChannels` ,
65
+ method : "get" ,
66
+ } ) ;
67
+ return response . data . d . results ;
68
+ }
69
+
70
+ async function createNotificationType ( notificationType ) {
71
+ const notificationDestination = await getNotificationDestination ( ) ;
72
+ const csrfHeaders = await buildHeadersForDestination ( notificationDestination , {
73
+ url : NOTIFICATION_TYPES_API_ENDPOINT ,
74
+ } ) ;
75
+
76
+ console . log (
77
+ `Notification Type of key ${ notificationType . NotificationTypeKey } and version ${ notificationType . NotificationTypeVersion } was not found. Creating it...`
78
+ ) ;
79
+
80
+ const response = await executeHttpRequest ( notificationDestination , {
81
+ url : `${ NOTIFICATION_TYPES_API_ENDPOINT } /NotificationTypes` ,
82
+ method : "post" ,
83
+ data : notificationType ,
84
+ headers : csrfHeaders ,
85
+ } ) ;
86
+ return response . data . d ;
87
+ }
88
+
89
+ async function updateNotificationType ( id , notificationType ) {
90
+ const notificationDestination = await getNotificationDestination ( ) ;
91
+ const csrfHeaders = await buildHeadersForDestination ( notificationDestination , {
92
+ url : NOTIFICATION_TYPES_API_ENDPOINT ,
93
+ } ) ;
94
+
95
+ console . log (
96
+ `Detected change in notification type of key ${ notificationType . NotificationTypeKey } and version ${ notificationType . NotificationTypeVersion } . Updating it...`
97
+ ) ;
98
+
99
+ const response = await executeHttpRequest ( notificationDestination , {
100
+ url : `${ NOTIFICATION_TYPES_API_ENDPOINT } /NotificationTypes(guid'${ id } ')` ,
101
+ method : "patch" ,
102
+ data : notificationType ,
103
+ headers : csrfHeaders ,
104
+ } ) ;
105
+ return response . status ;
106
+ }
107
+
108
+ async function deleteNotificationType ( notificationType ) {
109
+ const notificationDestination = await getNotificationDestination ( ) ;
110
+ const csrfHeaders = await buildHeadersForDestination ( notificationDestination , {
111
+ url : NOTIFICATION_TYPES_API_ENDPOINT ,
112
+ } ) ;
113
+
114
+ console . log (
115
+ `Notification Type of key ${ notificationType . NotificationTypeKey } and version ${ notificationType . NotificationTypeVersion } not present in the types file. Deleting it...`
116
+ ) ;
117
+
118
+ const response = await executeHttpRequest ( notificationDestination , {
119
+ url : `${ NOTIFICATION_TYPES_API_ENDPOINT } /NotificationTypes(guid'${ notificationType . NotificationTypeId } ')` ,
120
+ method : "delete" ,
121
+ headers : csrfHeaders ,
122
+ } ) ;
123
+ return response . status ;
124
+ }
125
+
126
+ function _createChannelsMap ( channels ) {
127
+ if ( channels === null || channels === undefined ) {
128
+ return { } ;
129
+ }
130
+
131
+ const channelMap = { } ;
132
+
133
+ channels . forEach ( ( channel ) => {
134
+ channelMap [ channel . Type ] = channel ;
135
+ } )
136
+
137
+ return channelMap ;
138
+ }
139
+
140
+ function areDeliveryChannelsEqual ( oldChannels , newChannels ) {
141
+ if ( _ . size ( oldChannels ) !== _ . size ( newChannels ) ) {
142
+ return false ;
143
+ }
144
+
145
+ const oldChannelsMap = _createChannelsMap ( oldChannels ) ;
146
+ const newChannelsMap = _createChannelsMap ( newChannels ) ;
147
+
148
+ for ( type of Object . keys ( oldChannelsMap ) ) {
149
+ if ( ! ( type in newChannelsMap ) ) return false ;
150
+
151
+ const oldChannel = oldChannelsMap [ type ] ;
152
+ const newChannel = newChannelsMap [ type ] ;
153
+
154
+ // TODO: Check if language is not there
155
+ const equal =
156
+ oldChannel . Type == newChannel . Type . toUpperCase ( ) &&
157
+ oldChannel . Enabled == newChannel . Enabled &&
158
+ oldChannel . DefaultPreference == newChannel . DefaultPreference &&
159
+ oldChannel . EditablePreference == newChannel . EditablePreference ;
160
+
161
+ if ( ! equal ) return false ;
162
+ delete newChannelsMap [ type ] ;
163
+ }
164
+
165
+ return Object . keys ( newChannelsMap ) . length == 0 ;
166
+ }
167
+
168
+ function isActionEqual ( oldAction , newAction ) {
169
+ return (
170
+ oldAction . Language == newAction . Language . toUpperCase ( ) &&
171
+ oldAction . ActionId == newAction . ActionId &&
172
+ oldAction . ActionText == newAction . ActionText &&
173
+ oldAction . GroupActionText == newAction . GroupActionText &&
174
+ oldAction . Nature == newAction . Nature
175
+ )
176
+ }
177
+
178
+ function areActionsEqual ( oldActions , newActions ) {
179
+ if ( _ . size ( oldActions ) !== _ . size ( newActions ) ) {
180
+ return false ;
181
+ }
182
+
183
+ let matchFound = false ;
184
+ for ( const oldAction of oldActions ) {
185
+ for ( const newAction of newActions ) {
186
+ if ( isActionEqual ( oldAction , newAction ) ) {
187
+ matchFound = true ;
188
+ break ;
189
+ }
190
+ }
191
+ if ( ! matchFound ) {
192
+ return false ;
193
+ }
194
+ }
195
+
196
+ return true ;
197
+ }
198
+
199
+ function isTemplateEqual ( oldTemplate , newTemplate ) {
200
+ return (
201
+ oldTemplate . Language == newTemplate . Language . toUpperCase ( ) &&
202
+ oldTemplate . TemplatePublic == newTemplate . TemplatePublic &&
203
+ oldTemplate . TemplateSensitive == newTemplate . TemplateSensitive &&
204
+ oldTemplate . TemplateGrouped == newTemplate . TemplateGrouped &&
205
+ oldTemplate . Description == newTemplate . Description &&
206
+ oldTemplate . TemplateLanguage == newTemplate . TemplateLanguage . toUpperCase ( ) &&
207
+ oldTemplate . Subtitle == newTemplate . Subtitle &&
208
+ oldTemplate . EmailSubject == newTemplate . EmailSubject &&
209
+ oldTemplate . EmailText == newTemplate . EmailText &&
210
+ oldTemplate . EmailHtml == newTemplate . EmailHtml
211
+ )
212
+ }
213
+
214
+ function areTemplatesEqual ( oldTemplates , newTemplates ) {
215
+ if ( _ . size ( oldTemplates ) !== _ . size ( newTemplates ) ) {
216
+ return false ;
217
+ }
218
+
219
+ let matchFound = false ;
220
+ for ( const oldTemplate of oldTemplates ) {
221
+ for ( const newTemplate of newTemplates ) {
222
+ if ( isTemplateEqual ( oldTemplate , newTemplate ) ) {
223
+ matchFound = true ;
224
+ break ;
225
+ }
226
+ }
227
+ if ( ! matchFound ) {
228
+ return false ;
229
+ }
230
+ }
231
+
232
+ return true ;
233
+ }
234
+
235
+ function isNotificationTypeEqual ( oldNotificationType , newNotificationType ) {
236
+ if ( newNotificationType . IsGroupable === undefined ) {
237
+ newNotificationType . IsGroupable = true ;
238
+ }
239
+
240
+ return (
241
+ oldNotificationType . IsGroupable == newNotificationType . IsGroupable &&
242
+ areTemplatesEqual ( oldNotificationType . Templates . results , fromOdataArrayFormat ( newNotificationType . Templates ) ) &&
243
+ areActionsEqual ( oldNotificationType . Actions . results , fromOdataArrayFormat ( newNotificationType . Actions ) ) &&
244
+ areDeliveryChannelsEqual ( oldNotificationType . DeliveryChannels . results , fromOdataArrayFormat ( newNotificationType . DeliveryChannels ) )
245
+ )
246
+ }
247
+
248
+ async function processNotificationTypes ( notificationTypesJSON ) {
249
+ const notificationTypes = createNotificationTypesMap ( notificationTypesJSON ) ;
250
+ const prefix = getPrefix ( ) ;
251
+ let defaultTemplateExists = false ;
252
+
253
+ // get notficationTypes
254
+ const existingTypes = await getNotificationTypes ( ) ;
255
+
256
+ // iterate through notification types
257
+ for ( const existingType of existingTypes ) {
258
+ if ( existingType . NotificationTypeKey == "Default" ) {
259
+ defaultTemplateExists = true ;
260
+ continue ;
261
+ }
262
+
263
+ if ( ! existingType . NotificationTypeKey . startsWith ( `${ prefix } /` ) ) {
264
+ console . log (
265
+ `Skipping Notification Type of other application: ${ existingType . NotificationTypeKey } .`
266
+ ) ;
267
+ continue ;
268
+ }
269
+
270
+ // if the type isn't present in the JSON file, delete it
271
+ if ( notificationTypes [ existingType . NotificationTypeKey ] === undefined || notificationTypes [ existingType . NotificationTypeKey ] [ existingType . NotificationTypeVersion ] === undefined ) {
272
+ await deleteNotificationType ( existingType ) ;
273
+ continue ;
274
+ }
275
+
276
+ const newType = JSON . parse ( JSON . stringify ( notificationTypes [ existingType . NotificationTypeKey ] [ existingType . NotificationTypeVersion ] ) ) ;
277
+
278
+ // if the type is there then verify if everything is same or not
279
+ if ( ! isNotificationTypeEqual ( existingType , newType ) ) {
280
+ await updateNotificationType ( existingType . NotificationTypeId , notificationTypes [ existingType . NotificationTypeKey ] [ existingType . NotificationTypeVersion ] )
281
+ } else {
282
+ console . log (
283
+ `Notification Type of key ${ existingType . NotificationTypeKey } and version ${ existingType . NotificationTypeVersion } unchanged.`
284
+ ) ;
285
+ }
286
+
287
+ delete notificationTypes [ existingType . NotificationTypeKey ] [ existingType . NotificationTypeVersion ] ;
288
+ if ( Object . keys ( notificationTypes [ existingType . NotificationTypeKey ] ) . length == 0 ) {
289
+ delete notificationTypes [ existingType . NotificationTypeKey ] ;
290
+ }
291
+ }
292
+
293
+ // create default template if required
294
+ if ( ! defaultTemplateExists ) {
295
+ await createNotificationType ( defaultTemplate ) ;
296
+ }
297
+
298
+ // create notification types that aren't there
299
+ for ( const notificationTypeKey in notificationTypes ) {
300
+ for ( const notificationTypeVersion in notificationTypes [ notificationTypeKey ] ) {
301
+ await createNotificationType ( notificationTypes [ notificationTypeKey ] [ notificationTypeVersion ] ) ;
302
+ }
303
+ }
304
+ }
305
+
306
+ module . exports = {
307
+ createNotificationTypesMap,
308
+ processNotificationTypes
309
+ }
0 commit comments