@cap-js/change-tracking 2.0.0-beta.2 → 2.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -1
- package/README.md +87 -244
- package/cds-plugin.js +1 -1
- package/index.cds +326 -41
- package/lib/csn-enhancements/annotations.js +77 -0
- package/lib/{model-enhancer.js → csn-enhancements/index.js} +7 -70
- package/lib/csn-enhancements/timezoneProperties.js +81 -0
- package/lib/h2/java-codegen.js +1 -1
- package/lib/hana/register.js +36 -7
- package/lib/hana/sql-expressions.js +1 -1
- package/lib/postgres/register.js +12 -0
- package/lib/postgres/sql-expressions.js +1 -1
- package/lib/sqlite/sql-expressions.js +1 -1
- package/lib/utils/change-tracking.js +6 -2
- package/lib/utils/composition-helpers.js +4 -6
- package/lib/utils/entity-collector.js +67 -42
- package/package.json +1 -2
package/index.cds
CHANGED
|
@@ -33,27 +33,18 @@ entity aspect @(UI.Facets: [{
|
|
|
33
33
|
view ChangeView as
|
|
34
34
|
select from Changes as change
|
|
35
35
|
left outer join i18nKeys as attributeI18n
|
|
36
|
-
on
|
|
37
|
-
and
|
|
38
|
-
attributeI18n.locale = $user.locale
|
|
39
|
-
or attributeI18n.locale = 'en'
|
|
40
|
-
)
|
|
36
|
+
on attributeI18n.ID = change.attribute
|
|
37
|
+
and attributeI18n.locale = $user.locale
|
|
41
38
|
left outer join i18nKeys as entityI18n
|
|
42
|
-
on
|
|
43
|
-
and
|
|
44
|
-
entityI18n.locale = $user.locale
|
|
45
|
-
or entityI18n.locale = 'en'
|
|
46
|
-
)
|
|
39
|
+
on entityI18n.ID = change.entity
|
|
40
|
+
and entityI18n.locale = $user.locale
|
|
47
41
|
left outer join i18nKeys as modificationI18n
|
|
48
|
-
on
|
|
49
|
-
and
|
|
50
|
-
modificationI18n.locale = $user.locale
|
|
51
|
-
or modificationI18n.locale = 'en'
|
|
52
|
-
)
|
|
42
|
+
on modificationI18n.ID = change.modification
|
|
43
|
+
and modificationI18n.locale = $user.locale
|
|
53
44
|
{
|
|
54
|
-
key change.ID
|
|
55
|
-
change.parent: redirected to ChangeView,
|
|
56
|
-
change.children: redirected to ChangeView,
|
|
45
|
+
key change.ID @UI.Hidden,
|
|
46
|
+
change.parent : redirected to ChangeView,
|
|
47
|
+
change.children : redirected to ChangeView,
|
|
57
48
|
change.attribute,
|
|
58
49
|
change.valueChangedFrom,
|
|
59
50
|
change.valueChangedTo,
|
|
@@ -66,25 +57,174 @@ view ChangeView as
|
|
|
66
57
|
change.createdBy,
|
|
67
58
|
change.transactionID,
|
|
68
59
|
COALESCE(
|
|
69
|
-
attributeI18n.text,
|
|
70
|
-
|
|
60
|
+
attributeI18n.text, (
|
|
61
|
+
select text from i18nKeys
|
|
62
|
+
where
|
|
63
|
+
ID = change.attribute
|
|
64
|
+
and locale = 'en'
|
|
65
|
+
), change.attribute
|
|
66
|
+
) as attributeLabel : String(15) @title: '{i18n>Changes.attribute}',
|
|
71
67
|
COALESCE(
|
|
72
|
-
entityI18n.text,
|
|
73
|
-
|
|
68
|
+
entityI18n.text, (
|
|
69
|
+
select text from i18nKeys
|
|
70
|
+
where
|
|
71
|
+
ID = change.entity
|
|
72
|
+
and locale = 'en'
|
|
73
|
+
), change.entity
|
|
74
|
+
) as entityLabel : String(24) @title: '{i18n>Changes.entity}',
|
|
74
75
|
COALESCE(
|
|
75
|
-
modificationI18n.text,
|
|
76
|
-
|
|
76
|
+
modificationI18n.text, (
|
|
77
|
+
select text from i18nKeys
|
|
78
|
+
where
|
|
79
|
+
ID = change.modification
|
|
80
|
+
and locale = 'en'
|
|
81
|
+
), change.modification
|
|
82
|
+
) as modificationLabel : String(16) @title: '{i18n>Changes.modification}',
|
|
77
83
|
COALESCE(
|
|
78
84
|
change.valueChangedFromLabel, change.valueChangedFrom
|
|
79
|
-
) as valueChangedFromLabel
|
|
85
|
+
) as valueChangedFromLabel : String(5000) @(
|
|
86
|
+
title: '{i18n>Changes.valueChangedFrom}',
|
|
87
|
+
UI.MultiLineText
|
|
88
|
+
),
|
|
89
|
+
(
|
|
90
|
+
case
|
|
91
|
+
when valueDataType = 'cds.DateTime'
|
|
92
|
+
then COALESCE(
|
|
93
|
+
change.valueChangedFromLabel, change.valueChangedFrom
|
|
94
|
+
)
|
|
95
|
+
else null
|
|
96
|
+
end
|
|
97
|
+
) as valueChangedFromLabelDateTime : DateTime @(title: '{i18n>Changes.valueChangedFrom}',
|
|
98
|
+
),
|
|
99
|
+
(
|
|
100
|
+
case
|
|
101
|
+
when valueDataType = 'cds.DateTime' or valueDataType = 'cds.Timestamp'
|
|
102
|
+
then COALESCE(
|
|
103
|
+
change.valueChangedFromLabel, change.valueChangedFrom
|
|
104
|
+
)
|
|
105
|
+
else null
|
|
106
|
+
end
|
|
107
|
+
) as valueChangedFromLabelDateTimeWTZ : DateTime @(
|
|
108
|
+
title : '{i18n>Changes.valueChangedFrom}',
|
|
109
|
+
Common.Timezone: valueTimeZone
|
|
110
|
+
),
|
|
111
|
+
(
|
|
112
|
+
case
|
|
113
|
+
when valueDataType = 'cds.Time'
|
|
114
|
+
then COALESCE(
|
|
115
|
+
change.valueChangedFromLabel, change.valueChangedFrom
|
|
116
|
+
)
|
|
117
|
+
else null
|
|
118
|
+
end
|
|
119
|
+
) as valueChangedFromLabelTime : Time @(title: '{i18n>Changes.valueChangedFrom}',
|
|
120
|
+
),
|
|
121
|
+
(
|
|
122
|
+
case
|
|
123
|
+
when valueDataType = 'cds.Date'
|
|
124
|
+
then COALESCE(
|
|
125
|
+
change.valueChangedFromLabel, change.valueChangedFrom
|
|
126
|
+
)
|
|
127
|
+
else null
|
|
128
|
+
end
|
|
129
|
+
) as valueChangedFromLabelDate : Date @(title: '{i18n>Changes.valueChangedFrom}',
|
|
130
|
+
),
|
|
131
|
+
(
|
|
132
|
+
case
|
|
133
|
+
when valueDataType = 'cds.Timestamp'
|
|
134
|
+
then COALESCE(
|
|
135
|
+
change.valueChangedFromLabel, change.valueChangedFrom
|
|
136
|
+
)
|
|
137
|
+
else null
|
|
138
|
+
end
|
|
139
|
+
) as valueChangedFromLabelTimestamp : Timestamp @(title: '{i18n>Changes.valueChangedFrom}',
|
|
140
|
+
),
|
|
141
|
+
(
|
|
142
|
+
case
|
|
143
|
+
when valueDataType = 'cds.Decimal'
|
|
144
|
+
then COALESCE(
|
|
145
|
+
change.valueChangedFromLabel, change.valueChangedFrom
|
|
146
|
+
)
|
|
147
|
+
else null
|
|
148
|
+
end
|
|
149
|
+
) as valueChangedFromLabelDecimal : Decimal @(title: '{i18n>Changes.valueChangedFrom}',
|
|
150
|
+
),
|
|
80
151
|
COALESCE(
|
|
81
152
|
change.valueChangedToLabel, change.valueChangedTo
|
|
82
|
-
) as valueChangedToLabel
|
|
153
|
+
) as valueChangedToLabel : String(5000) @(
|
|
154
|
+
title: '{i18n>Changes.valueChangedTo}',
|
|
155
|
+
UI.MultiLineText
|
|
156
|
+
),
|
|
157
|
+
(
|
|
158
|
+
case
|
|
159
|
+
when valueDataType = 'cds.DateTime'
|
|
160
|
+
then COALESCE(
|
|
161
|
+
change.valueChangedToLabel, change.valueChangedTo
|
|
162
|
+
)
|
|
163
|
+
else null
|
|
164
|
+
end
|
|
165
|
+
) as valueChangedToLabelDateTime : DateTime @(title: '{i18n>Changes.valueChangedTo}',
|
|
166
|
+
),
|
|
167
|
+
(
|
|
168
|
+
case
|
|
169
|
+
when valueDataType = 'cds.DateTime' or valueDataType = 'cds.Timestamp'
|
|
170
|
+
then COALESCE(
|
|
171
|
+
change.valueChangedFromLabel, change.valueChangedTo
|
|
172
|
+
)
|
|
173
|
+
else null
|
|
174
|
+
end
|
|
175
|
+
) as valueChangedToLabelDateTimeWTZ : DateTime @(
|
|
176
|
+
title : '{i18n>Changes.valueChangedTo}',
|
|
177
|
+
Common.Timezone: valueTimeZone
|
|
178
|
+
),
|
|
179
|
+
(
|
|
180
|
+
case
|
|
181
|
+
when valueDataType = 'cds.Time'
|
|
182
|
+
then COALESCE(
|
|
183
|
+
change.valueChangedToLabel, change.valueChangedTo
|
|
184
|
+
)
|
|
185
|
+
else null
|
|
186
|
+
end
|
|
187
|
+
) as valueChangedToLabelTime : Time @(title: '{i18n>Changes.valueChangedTo}',
|
|
188
|
+
),
|
|
189
|
+
(
|
|
190
|
+
case
|
|
191
|
+
when valueDataType = 'cds.Date'
|
|
192
|
+
then COALESCE(
|
|
193
|
+
change.valueChangedToLabel, change.valueChangedTo
|
|
194
|
+
)
|
|
195
|
+
else null
|
|
196
|
+
end
|
|
197
|
+
) as valueChangedToLabelDate : Date @(title: '{i18n>Changes.valueChangedTo}',
|
|
198
|
+
),
|
|
199
|
+
(
|
|
200
|
+
case
|
|
201
|
+
when valueDataType = 'cds.Timestamp'
|
|
202
|
+
then COALESCE(
|
|
203
|
+
change.valueChangedToLabel, change.valueChangedTo
|
|
204
|
+
)
|
|
205
|
+
else null
|
|
206
|
+
end
|
|
207
|
+
) as valueChangedToLabelTimestamp : Timestamp @(title: '{i18n>Changes.valueChangedTo}',
|
|
208
|
+
),
|
|
209
|
+
(
|
|
210
|
+
case
|
|
211
|
+
when valueDataType = 'cds.Decimal'
|
|
212
|
+
then COALESCE(
|
|
213
|
+
change.valueChangedToLabel, change.valueChangedTo
|
|
214
|
+
)
|
|
215
|
+
else null
|
|
216
|
+
end
|
|
217
|
+
) as valueChangedToLabelDecimal : Decimal @(title: '{i18n>Changes.valueChangedTo}',
|
|
218
|
+
),
|
|
219
|
+
null as valueTimeZone : String @(
|
|
220
|
+
UI.Hidden,
|
|
221
|
+
Common.IsTimezone
|
|
222
|
+
),
|
|
83
223
|
// For the hierarchy
|
|
84
|
-
null as LimitedDescendantCount
|
|
85
|
-
null as DistanceFromRoot
|
|
86
|
-
null as DrillState
|
|
87
|
-
null as LimitedRank
|
|
224
|
+
null as LimitedDescendantCount : Int16 @UI.Hidden,
|
|
225
|
+
null as DistanceFromRoot : Int16 @UI.Hidden,
|
|
226
|
+
null as DrillState : String @UI.Hidden,
|
|
227
|
+
null as LimitedRank : Int16 @UI.Hidden,
|
|
88
228
|
};
|
|
89
229
|
|
|
90
230
|
entity i18nKeys {
|
|
@@ -93,7 +233,7 @@ entity i18nKeys {
|
|
|
93
233
|
text : String(5000);
|
|
94
234
|
}
|
|
95
235
|
|
|
96
|
-
// Dummy table necessary for HANA triggers
|
|
236
|
+
// Dummy table necessary for HANA triggers because DUMMY cannot be used in HDI
|
|
97
237
|
@cds.persistence.skip
|
|
98
238
|
entity CHANGE_TRACKING_DUMMY {
|
|
99
239
|
key X : String(5);
|
|
@@ -162,11 +302,15 @@ annotate ChangeView with @(UI: {
|
|
|
162
302
|
@UI.Importance: #Medium
|
|
163
303
|
},
|
|
164
304
|
{
|
|
165
|
-
|
|
305
|
+
$Type : 'UI.DataFieldForAnnotation',
|
|
306
|
+
Target : '@UI.FieldGroup#valueChangedTo',
|
|
307
|
+
Label : '{i18n>Changes.valueChangedTo}',
|
|
166
308
|
@UI.Importance: #High
|
|
167
309
|
},
|
|
168
310
|
{
|
|
169
|
-
|
|
311
|
+
$Type : 'UI.DataFieldForAnnotation',
|
|
312
|
+
Target : '@UI.FieldGroup#valueChangedFrom',
|
|
313
|
+
Label : '{i18n>Changes.valueChangedFrom}',
|
|
170
314
|
@UI.Importance: #High
|
|
171
315
|
},
|
|
172
316
|
{
|
|
@@ -179,13 +323,103 @@ annotate ChangeView with @(UI: {
|
|
|
179
323
|
},
|
|
180
324
|
],
|
|
181
325
|
DeleteHidden : true,
|
|
326
|
+
FieldGroup #valueChangedFrom : {
|
|
327
|
+
Label: '{i18n>Changes.valueChangedFrom}',
|
|
328
|
+
Data : [
|
|
329
|
+
{
|
|
330
|
+
Value : valueChangedFromLabel,
|
|
331
|
+
@UI.Hidden: ($self.valueDataType = 'cds.Decimal'
|
|
332
|
+
or $self.valueDataType = 'cds.DateTime'
|
|
333
|
+
or $self.valueDataType = 'cds.Date'
|
|
334
|
+
or $self.valueDataType = 'cds.Time'
|
|
335
|
+
or $self.valueDataType = 'cds.Timestamp')
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
Value : valueChangedFromLabelDateTime,
|
|
339
|
+
@UI.Hidden: ($self.valueDataType != 'cds.DateTime'
|
|
340
|
+
or $self.valueTimeZone != null)
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
Value : valueChangedFromLabelDateTimeWTZ,
|
|
344
|
+
@UI.Hidden: ($self.valueDataType != 'cds.DateTime'
|
|
345
|
+
or $self.valueTimeZone = null)
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
Value : valueChangedFromLabelDate,
|
|
349
|
+
@UI.Hidden: ($self.valueDataType != 'cds.Date')
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
Value : valueChangedFromLabelTime,
|
|
353
|
+
@UI.Hidden: ($self.valueDataType != 'cds.Time')
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
Value : valueChangedFromLabelTimestamp,
|
|
357
|
+
@UI.Hidden: ($self.valueDataType != 'cds.Timestamp')
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
Value : valueChangedFromLabelDecimal,
|
|
361
|
+
@UI.Hidden: ($self.valueDataType != 'cds.Decimal')
|
|
362
|
+
}
|
|
363
|
+
]
|
|
364
|
+
},
|
|
365
|
+
FieldGroup #valueChangedTo : {
|
|
366
|
+
Label: '{i18n>Changes.valueChangedTo}',
|
|
367
|
+
Data : [
|
|
368
|
+
{
|
|
369
|
+
Value : valueChangedToLabel,
|
|
370
|
+
@UI.Hidden: ($self.valueDataType = 'cds.Decimal'
|
|
371
|
+
or $self.valueDataType = 'cds.DateTime'
|
|
372
|
+
or $self.valueDataType = 'cds.Date'
|
|
373
|
+
or $self.valueDataType = 'cds.Time'
|
|
374
|
+
or $self.valueDataType = 'cds.Timestamp')
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
Value : valueChangedToLabelDateTime,
|
|
378
|
+
@UI.Hidden: ($self.valueDataType != 'cds.DateTime'
|
|
379
|
+
or $self.valueTimeZone != null)
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
Value : valueChangedToLabelDateTimeWTZ,
|
|
383
|
+
@UI.Hidden: ($self.valueDataType != 'cds.DateTime'
|
|
384
|
+
or $self.valueTimeZone = null)
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
Value : valueChangedToLabelDate,
|
|
388
|
+
@UI.Hidden: ($self.valueDataType != 'cds.Date')
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
Value : valueChangedToLabelTime,
|
|
392
|
+
@UI.Hidden: ($self.valueDataType != 'cds.Time')
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
Value : valueChangedToLabelTimestamp,
|
|
396
|
+
@UI.Hidden: ($self.valueDataType != 'cds.Timestamp')
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
Value : valueChangedToLabelDecimal,
|
|
400
|
+
@UI.Hidden: ($self.valueDataType != 'cds.Decimal')
|
|
401
|
+
}
|
|
402
|
+
]
|
|
403
|
+
}
|
|
182
404
|
}) {
|
|
183
|
-
valueChangedFrom
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
405
|
+
valueChangedFrom @UI.Hidden;
|
|
406
|
+
valueChangedFromLabelDate @UI.AdaptationHidden @UI.Hidden: ($self.valueDataType != 'cds.Date');
|
|
407
|
+
valueChangedFromLabelDateTime @UI.AdaptationHidden @UI.Hidden: ($self.valueDataType != 'cds.DateTime');
|
|
408
|
+
valueChangedFromLabelDateTimeWTZ @UI.AdaptationHidden @UI.Hidden: ($self.valueDataType != 'cds.DateTime');
|
|
409
|
+
valueChangedFromLabelTime @UI.AdaptationHidden @UI.Hidden: ($self.valueDataType != 'cds.Time');
|
|
410
|
+
valueChangedFromLabelTimestamp @UI.AdaptationHidden @UI.Hidden: ($self.valueDataType != 'cds.Timestamp');
|
|
411
|
+
valueChangedFromLabelDecimal @UI.AdaptationHidden @UI.Hidden: ($self.valueDataType != 'cds.Decimal');
|
|
412
|
+
valueChangedTo @UI.Hidden;
|
|
413
|
+
valueChangedToLabelDate @UI.AdaptationHidden @UI.Hidden: ($self.valueDataType != 'cds.Date');
|
|
414
|
+
valueChangedToLabelDateTime @UI.AdaptationHidden @UI.Hidden: ($self.valueDataType != 'cds.DateTime');
|
|
415
|
+
valueChangedToLabelDateTimeWTZ @UI.AdaptationHidden @UI.Hidden: ($self.valueDataType != 'cds.DateTime');
|
|
416
|
+
valueChangedToLabelTime @UI.AdaptationHidden @UI.Hidden: ($self.valueDataType != 'cds.Time');
|
|
417
|
+
valueChangedToLabelTimestamp @UI.AdaptationHidden @UI.Hidden: ($self.valueDataType != 'cds.Timestamp');
|
|
418
|
+
valueChangedToLabelDecimal @UI.AdaptationHidden @UI.Hidden: ($self.valueDataType != 'cds.Decimal');
|
|
419
|
+
parent @UI.Hidden;
|
|
420
|
+
entityKey @UI.Hidden;
|
|
421
|
+
entity @UI.Hidden;
|
|
422
|
+
attribute @UI.Hidden;
|
|
189
423
|
};
|
|
190
424
|
|
|
191
425
|
annotate ChangeView with @(
|
|
@@ -206,13 +440,64 @@ annotate ChangeView with @(
|
|
|
206
440
|
'LimitedDescendantCount',
|
|
207
441
|
'DistanceFromRoot',
|
|
208
442
|
'DrillState',
|
|
209
|
-
'LimitedRank'
|
|
443
|
+
'LimitedRank',
|
|
444
|
+
valueChangedFromLabelDate,
|
|
445
|
+
valueChangedFromLabelDateTime,
|
|
446
|
+
valueChangedFromLabelDateTimeWTZ,
|
|
447
|
+
valueChangedFromLabelTime,
|
|
448
|
+
valueChangedFromLabelTimestamp,
|
|
449
|
+
valueChangedFromLabelDecimal,
|
|
450
|
+
valueChangedToLabelDate,
|
|
451
|
+
valueChangedToLabelDateTime,
|
|
452
|
+
valueChangedToLabelDateTimeWTZ,
|
|
453
|
+
valueChangedToLabelTime,
|
|
454
|
+
valueChangedToLabelTimestamp,
|
|
455
|
+
valueChangedToLabelDecimal,
|
|
456
|
+
valueTimeZone
|
|
210
457
|
],
|
|
211
458
|
// Disallow sorting on these properties from Fiori UIs
|
|
212
459
|
Capabilities.SortRestrictions.NonSortableProperties : [
|
|
213
460
|
'LimitedDescendantCount',
|
|
214
461
|
'DistanceFromRoot',
|
|
215
462
|
'DrillState',
|
|
216
|
-
'LimitedRank'
|
|
463
|
+
'LimitedRank',
|
|
464
|
+
valueChangedFromLabelDate,
|
|
465
|
+
valueChangedFromLabelDateTime,
|
|
466
|
+
valueChangedFromLabelDateTimeWTZ,
|
|
467
|
+
valueChangedFromLabelTime,
|
|
468
|
+
valueChangedFromLabelTimestamp,
|
|
469
|
+
valueChangedFromLabelDecimal,
|
|
470
|
+
valueChangedToLabelDate,
|
|
471
|
+
valueChangedToLabelDateTime,
|
|
472
|
+
valueChangedToLabelDateTimeWTZ,
|
|
473
|
+
valueChangedToLabelTime,
|
|
474
|
+
valueChangedToLabelTimestamp,
|
|
475
|
+
valueChangedToLabelDecimal,
|
|
476
|
+
valueTimeZone
|
|
217
477
|
],
|
|
218
478
|
);
|
|
479
|
+
|
|
480
|
+
// Annotations for searching
|
|
481
|
+
annotate ChangeView with @(cds.search: {
|
|
482
|
+
valueChangedFrom: false,
|
|
483
|
+
valueChangedTo : false,
|
|
484
|
+
entity : false,
|
|
485
|
+
attribute : false,
|
|
486
|
+
modification : false,
|
|
487
|
+
valueDataType : false,
|
|
488
|
+
modificationLabel,
|
|
489
|
+
entityLabel,
|
|
490
|
+
entityKey,
|
|
491
|
+
objectID,
|
|
492
|
+
attributeLabel,
|
|
493
|
+
valueChangedFromLabel,
|
|
494
|
+
valueChangedToLabel,
|
|
495
|
+
createdBy,
|
|
496
|
+
}) {
|
|
497
|
+
entityLabel @Search.ranking: HIGH;
|
|
498
|
+
attributeLabel @Search.ranking: HIGH;
|
|
499
|
+
objectID @Search.ranking: HIGH;
|
|
500
|
+
|
|
501
|
+
entityKey @Search.ranking: LOW;
|
|
502
|
+
modificationLabel @Search.ranking: LOW;
|
|
503
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const cds = require('@sap/cds');
|
|
2
|
+
const LOG = cds.log('change-tracking');
|
|
3
|
+
const DEBUG = cds.debug('change-tracking');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Add side effects annotations for actions to refresh the changes association.
|
|
7
|
+
*/
|
|
8
|
+
function addSideEffects(actions, entityName, hierarchyMap, model) {
|
|
9
|
+
const isRootEntity = !hierarchyMap.has(entityName);
|
|
10
|
+
|
|
11
|
+
// If not a root entity, find the parent association name
|
|
12
|
+
let parentAssociationName = null;
|
|
13
|
+
if (!isRootEntity) {
|
|
14
|
+
const parentEntityName = hierarchyMap.get(entityName);
|
|
15
|
+
const parentEntity = model.definitions[parentEntityName];
|
|
16
|
+
if (parentEntity?.elements) {
|
|
17
|
+
// Find the composition element in the parent that points to this entity
|
|
18
|
+
for (const [elemName, elem] of Object.entries(parentEntity.elements)) {
|
|
19
|
+
if (elem.type === 'cds.Composition' && elem.target === entityName) {
|
|
20
|
+
parentAssociationName = elemName;
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (const se of Object.values(actions)) {
|
|
28
|
+
const target = isRootEntity ? 'TargetProperties' : 'TargetEntities';
|
|
29
|
+
const sideEffectAttr = se[`@Common.SideEffects.${target}`];
|
|
30
|
+
const property = isRootEntity ? 'changes' : { '=': `${parentAssociationName}.changes` };
|
|
31
|
+
if (sideEffectAttr?.length >= 0) {
|
|
32
|
+
sideEffectAttr.findIndex((item) => (item['='] ? item['='] : item) === (property['='] ? property['='] : property)) === -1 && sideEffectAttr.push(property);
|
|
33
|
+
} else {
|
|
34
|
+
se[`@Common.SideEffects.${target}`] = [property];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function addUIFacet(entity, m) {
|
|
40
|
+
const { 'sap.changelog.aspect': aspect } = m.definitions;
|
|
41
|
+
const {
|
|
42
|
+
'@UI.Facets': [facet],
|
|
43
|
+
elements: { changes }
|
|
44
|
+
} = aspect;
|
|
45
|
+
if (entity['@changelog.disable_facet'] !== undefined) {
|
|
46
|
+
LOG.warn(
|
|
47
|
+
`@changelog.disable_facet is deprecated! You can just define your own Facet for the changes association or annotate the changes association on ${entity.name} with not readable via @Capabilities.NavigationRestrictions.RestrictedProperties`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let facets = entity['@UI.Facets'];
|
|
52
|
+
|
|
53
|
+
if (!facets) {
|
|
54
|
+
DEBUG?.(`${entity.name} does not have a @UI.Facets annotation and thus the change tracking section is not added.`);
|
|
55
|
+
}
|
|
56
|
+
// Add UI.Facet for Change History List
|
|
57
|
+
if (
|
|
58
|
+
facets &&
|
|
59
|
+
!entity['@changelog.disable_facet'] &&
|
|
60
|
+
!hasFacetForComp(changes, entity['@UI.Facets']) &&
|
|
61
|
+
!entity['@Capabilities.NavigationRestrictions.RestrictedProperties']?.some((restriction) => restriction.NavigationProperty?.['='] === 'changes' && restriction.ReadRestrictions?.Readable === false)
|
|
62
|
+
) {
|
|
63
|
+
facets.push(facet);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Check if a facet already exists for the changes composition.
|
|
69
|
+
*/
|
|
70
|
+
function hasFacetForComp(comp, facets) {
|
|
71
|
+
return facets.some((f) => f.Target === `${comp.name}/@UI.LineItem` || (f.Facets && hasFacetForComp(comp, f.Facets)));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
addSideEffects,
|
|
76
|
+
addUIFacet
|
|
77
|
+
};
|
|
@@ -1,42 +1,9 @@
|
|
|
1
1
|
const cds = require('@sap/cds');
|
|
2
|
-
const LOG = cds.log('change-tracking');
|
|
3
2
|
const DEBUG = cds.debug('change-tracking');
|
|
4
3
|
|
|
5
|
-
const { isChangeTracked, getBaseEntity, analyzeCompositions, getService } = require('
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* Add side effects annotations for actions to refresh the changes association.
|
|
9
|
-
*/
|
|
10
|
-
function addSideEffects(actions, entityName, hierarchyMap, model) {
|
|
11
|
-
const isRootEntity = !hierarchyMap.has(entityName);
|
|
12
|
-
|
|
13
|
-
// If not a root entity, find the parent association name
|
|
14
|
-
let parentAssociationName = null;
|
|
15
|
-
if (!isRootEntity) {
|
|
16
|
-
const parentEntityName = hierarchyMap.get(entityName);
|
|
17
|
-
const parentEntity = model.definitions[parentEntityName];
|
|
18
|
-
if (parentEntity?.elements) {
|
|
19
|
-
// Find the composition element in the parent that points to this entity
|
|
20
|
-
for (const [elemName, elem] of Object.entries(parentEntity.elements)) {
|
|
21
|
-
if (elem.type === 'cds.Composition' && elem.target === entityName) {
|
|
22
|
-
parentAssociationName = elemName;
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
for (const se of Object.values(actions)) {
|
|
30
|
-
const target = isRootEntity ? 'TargetProperties' : 'TargetEntities';
|
|
31
|
-
const sideEffectAttr = se[`@Common.SideEffects.${target}`];
|
|
32
|
-
const property = isRootEntity ? 'changes' : { '=': `${parentAssociationName}.changes` };
|
|
33
|
-
if (sideEffectAttr?.length >= 0) {
|
|
34
|
-
sideEffectAttr.findIndex((item) => (item['='] ? item['='] : item) === (property['='] ? property['='] : property)) === -1 && sideEffectAttr.push(property);
|
|
35
|
-
} else {
|
|
36
|
-
se[`@Common.SideEffects.${target}`] = [property];
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
4
|
+
const { isChangeTracked, getBaseEntity, analyzeCompositions, getService } = require('../utils/entity-collector.js');
|
|
5
|
+
const { addSideEffects, addUIFacet } = require('./annotations.js');
|
|
6
|
+
const { enhanceChangeViewWithTimeZones } = require('./timezoneProperties.js');
|
|
40
7
|
|
|
41
8
|
/**
|
|
42
9
|
* Returns a CQN expression for the composite key of an entity.
|
|
@@ -82,13 +49,6 @@ function _replaceTablePlaceholders(on, tableName) {
|
|
|
82
49
|
});
|
|
83
50
|
}
|
|
84
51
|
|
|
85
|
-
/**
|
|
86
|
-
* Check if a facet already exists for the changes composition.
|
|
87
|
-
*/
|
|
88
|
-
function hasFacetForComp(comp, facets) {
|
|
89
|
-
return facets.some((f) => f.Target === `${comp.name}/@UI.LineItem` || (f.Facets && hasFacetForComp(comp, f.Facets)));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
52
|
/**
|
|
93
53
|
* Enhance the CDS model with change tracking associations, facets, and side effects.
|
|
94
54
|
* Returns the updated hierarchyMap and collectedEntities for use by trigger generation.
|
|
@@ -107,7 +67,6 @@ function enhanceModel(m) {
|
|
|
107
67
|
const { 'sap.changelog.aspect': aspect } = m.definitions;
|
|
108
68
|
if (!aspect) return; // some other model
|
|
109
69
|
const {
|
|
110
|
-
'@UI.Facets': [facet],
|
|
111
70
|
elements: { changes }
|
|
112
71
|
} = aspect;
|
|
113
72
|
|
|
@@ -143,6 +102,7 @@ function enhanceModel(m) {
|
|
|
143
102
|
m.definitions['sap.changelog.ChangeView'].elements[parents.join('_') + '_' + 'entityKey'] = structuredClone(m.definitions['sap.changelog.ChangeView'].elements.entityKey);
|
|
144
103
|
m.definitions['sap.changelog.ChangeView'].elements[parents.join('_') + '_' + 'entity'] = structuredClone(m.definitions['sap.changelog.ChangeView'].elements.entity);
|
|
145
104
|
}
|
|
105
|
+
enhanceChangeViewWithTimeZones(m.definitions['sap.changelog.ChangeView'], m);
|
|
146
106
|
}
|
|
147
107
|
for (let name in m.definitions) {
|
|
148
108
|
const entity = m.definitions[name];
|
|
@@ -228,35 +188,12 @@ function enhanceModel(m) {
|
|
|
228
188
|
(query.columns ??= ['*']).push({ as: 'changes', cast: assoc });
|
|
229
189
|
entity.elements.changes = assoc;
|
|
230
190
|
}
|
|
231
|
-
|
|
232
|
-
if (entity['@changelog.disable_facet'] !== undefined) {
|
|
233
|
-
LOG.warn(
|
|
234
|
-
`@changelog.disable_facet is deprecated! You can just define your own Facet for the changes association or annotate the changes association on ${entity.name} with not readable via @Capabilities.NavigationRestrictions.RestrictedProperties`
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
let facets = entity['@UI.Facets'];
|
|
239
|
-
|
|
240
|
-
if (!facets) {
|
|
241
|
-
DEBUG?.(`${entity.name} does not have a @UI.Facets annotation and thus the change tracking section is not added.`);
|
|
242
|
-
}
|
|
243
|
-
// Add UI.Facet for Change History List
|
|
244
|
-
if (
|
|
245
|
-
facets &&
|
|
246
|
-
!entity['@changelog.disable_facet'] &&
|
|
247
|
-
!hasFacetForComp(changes, entity['@UI.Facets']) &&
|
|
248
|
-
!entity['@Capabilities.NavigationRestrictions.RestrictedProperties']?.some((restriction) => restriction.NavigationProperty?.['='] === 'changes' && restriction.ReadRestrictions?.Readable === false)
|
|
249
|
-
) {
|
|
250
|
-
facets.push(facet);
|
|
251
|
-
}
|
|
191
|
+
addUIFacet(entity, m);
|
|
252
192
|
}
|
|
253
193
|
|
|
254
194
|
if (entity.actions) {
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
const { baseRef: dbEntityName } = baseInfo;
|
|
258
|
-
addSideEffects(entity.actions, dbEntityName, hierarchyMap, m);
|
|
259
|
-
}
|
|
195
|
+
const { baseRef: dbEntityName } = baseInfo;
|
|
196
|
+
addSideEffects(entity.actions, dbEntityName, hierarchyMap, m);
|
|
260
197
|
}
|
|
261
198
|
}
|
|
262
199
|
}
|