@sap/cds-compiler 3.0.0 → 3.1.2
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 +104 -9
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +28 -16
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +24 -2
- package/doc/CHANGELOG_DEPRECATED.md +21 -1
- package/lib/api/main.js +92 -40
- package/lib/api/options.js +2 -3
- package/lib/base/keywords.js +64 -1
- package/lib/base/message-registry.js +33 -5
- package/lib/base/messages.js +54 -65
- package/lib/base/model.js +2 -0
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +8 -7
- package/lib/checks/selectItems.js +96 -14
- package/lib/checks/types.js +5 -8
- package/lib/checks/validator.js +1 -2
- package/lib/compiler/assert-consistency.js +65 -13
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +93 -4
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +28 -23
- package/lib/compiler/extend.js +20 -11
- package/lib/compiler/finalize-parse-cdl.js +5 -9
- package/lib/compiler/index.js +2 -0
- package/lib/compiler/populate.js +37 -32
- package/lib/compiler/propagator.js +11 -6
- package/lib/compiler/resolve.js +15 -19
- package/lib/compiler/shared.js +54 -18
- package/lib/compiler/tweak-assocs.js +5 -11
- package/lib/compiler/utils.js +15 -6
- package/lib/edm/annotations/genericTranslation.js +12 -2
- package/lib/edm/annotations/preprocessAnnotations.js +18 -15
- package/lib/edm/csn2edm.js +18 -17
- package/lib/edm/edm.js +22 -13
- package/lib/edm/edmAnnoPreprocessor.js +349 -0
- package/lib/edm/edmInboundChecks.js +85 -0
- package/lib/edm/edmPreprocessor.js +336 -665
- package/lib/edm/edmUtils.js +86 -45
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -2
- package/lib/gen/languageLexer.js +3 -0
- package/lib/gen/languageParser.js +4332 -4496
- package/lib/inspect/.eslintrc.json +4 -0
- package/lib/inspect/index.js +14 -0
- package/lib/inspect/inspectModelStatistics.js +81 -0
- package/lib/inspect/inspectPropagation.js +189 -0
- package/lib/inspect/inspectUtils.js +44 -0
- package/lib/json/from-csn.js +19 -20
- package/lib/json/to-csn.js +11 -8
- package/lib/language/genericAntlrParser.js +150 -92
- package/lib/language/language.g4 +47 -74
- package/lib/main.d.ts +1 -0
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +56 -29
- package/lib/model/csnUtils.js +29 -14
- package/lib/model/revealInternalProperties.js +6 -4
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +81 -38
- package/lib/render/toCdl.js +57 -32
- package/lib/render/toHdbcds.js +1 -1
- package/lib/render/toSql.js +31 -11
- package/lib/render/utils/common.js +3 -4
- package/lib/transform/db/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +0 -1
- package/lib/transform/db/flattening.js +3 -4
- package/lib/transform/db/transformExists.js +7 -5
- package/lib/transform/draft/db.js +1 -1
- package/lib/transform/forHanaNew.js +11 -2
- package/lib/transform/forOdataNew.js +4 -4
- package/lib/transform/localized.js +15 -11
- package/lib/transform/odata/typesExposure.js +14 -5
- package/lib/utils/file.js +28 -18
- package/lib/utils/moduleResolve.js +0 -1
- package/package.json +3 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- package/lib/checks/unknownMagic.js +0 -41
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const edmUtils = require('./edmUtils.js');
|
|
4
|
+
const { setProp } = require('../base/model');
|
|
5
|
+
const { forEachGeneric } = require('../model/csnUtils');
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* Late application specific transformations
|
|
9
|
+
* At present there are two transformation targets: Structure and Element
|
|
10
|
+
* These transformations are available today:
|
|
11
|
+
*
|
|
12
|
+
* Analytical Scenario:
|
|
13
|
+
* If a structure is annotated with @Aggregation.ApplySupported.PropertyRestrictions
|
|
14
|
+
* then a number of annotation rewrites are done to this structure and to the
|
|
15
|
+
* elements of this structure
|
|
16
|
+
* Also the key properties of all structure elements are removed and a new
|
|
17
|
+
* artificial key element 'key _ID : String' is inserted at first position of
|
|
18
|
+
* the elements dictionary
|
|
19
|
+
*
|
|
20
|
+
* PDM (Personal Data Management)
|
|
21
|
+
* Planned but not yet implemented annotation rewriting (pending to finalization)
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/* eslint max-statements-per-line:off */
|
|
25
|
+
|
|
26
|
+
function mapAnnotationAssignment(artifact, parent, mappingDictionary)
|
|
27
|
+
{
|
|
28
|
+
let props = edmUtils.intersect(Object.keys(mappingDictionary), Object.keys(artifact));
|
|
29
|
+
// now start the substitution
|
|
30
|
+
props.forEach(prop => {
|
|
31
|
+
let [ mapping, value, remove_original ] = mappingDictionary[prop];
|
|
32
|
+
if(mapping instanceof Function)
|
|
33
|
+
{
|
|
34
|
+
mapping(artifact, parent, prop);
|
|
35
|
+
}
|
|
36
|
+
else
|
|
37
|
+
{
|
|
38
|
+
edmUtils.assignAnnotation(artifact, mapping, value || artifact[prop]['='] || artifact[prop]);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if(remove_original)
|
|
42
|
+
delete artifact[prop];
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function addToSetAttr(carrier, propName, propValue, removeFromType=true) {
|
|
47
|
+
edmUtils.assignProp(carrier, '_SetAttributes', Object.create(null));
|
|
48
|
+
edmUtils.assignAnnotation(carrier._SetAttributes, propName, propValue);
|
|
49
|
+
if(removeFromType) {
|
|
50
|
+
delete carrier[propName];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function applyAppSpecificLateCsnTransformationOnElement(options, element, struct, error)
|
|
55
|
+
{
|
|
56
|
+
if(options.isV2())
|
|
57
|
+
{
|
|
58
|
+
if(struct['@Aggregation.ApplySupported.PropertyRestrictions'])
|
|
59
|
+
{
|
|
60
|
+
mapAnnotationAssignment(element, struct, AnalyticalAnnotations());
|
|
61
|
+
}
|
|
62
|
+
mapAnnotationAssignment(element, struct, PDMSemantics());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// etag requires Core.OptimisticConcurrency to be set in V4 (cap/issues#2641)
|
|
66
|
+
// Oliver Heinrich mentions in the issue that the Okra runtime must be set to a
|
|
67
|
+
// concurrent runtime mode by the caller, if the annotation is added this late,
|
|
68
|
+
// it doesn't appear in the forOData processed CSN, meaning that the
|
|
69
|
+
// runtime cannot set that okra flag (alternatively the runtime has to search
|
|
70
|
+
// for @[odata|cds].etag annotations...
|
|
71
|
+
if(options.isV4())
|
|
72
|
+
{
|
|
73
|
+
if(element['@odata.etag'] == true || element['@cds.etag'] == true) {
|
|
74
|
+
// don't put element name into collection as per advice from Ralf Handl, as
|
|
75
|
+
// no runtime is interested in the property itself, it is sufficient to mark
|
|
76
|
+
// the entity set.
|
|
77
|
+
edmUtils.assignAnnotation(struct, '@Core.OptimisticConcurrency',
|
|
78
|
+
(struct['@Core.OptimisticConcurrency'] || [])/*.push(element.name)*/);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// nested functions begin
|
|
83
|
+
function PDMSemantics()
|
|
84
|
+
{
|
|
85
|
+
/*
|
|
86
|
+
let dict = Object.create(null);
|
|
87
|
+
|
|
88
|
+
dict['@PDM.xxx1'] = [ '@sap.pdm-semantics' ];
|
|
89
|
+
dict['@PDM.xxx2'] = [ '@sap.pdm-propery' ];
|
|
90
|
+
dict['@PDM.xxx3'] = [ '@sap.pdm-display-sq-no' ];
|
|
91
|
+
dict['@PDM.xxx4'] = [ '@sap.pdm-record-identifier' ];
|
|
92
|
+
dict['@PDM.xxx5'] = [ '@sap.pdm-field-group' ];
|
|
93
|
+
dict['@PDM.xxx6'] = [ '@sap.pdm-mask-find-pattern' ];
|
|
94
|
+
dict['@PDM.xxx7'] = [ '@sap.pdm-mask-replacement-pattern' ];
|
|
95
|
+
dict['@PDM.xxx8'] = [ '@sap.deletable' ];
|
|
96
|
+
dict['@PDM.xxx8'] = [ '@sap.updatable' ];
|
|
97
|
+
|
|
98
|
+
// respect flattened annotation $value
|
|
99
|
+
Object.keys(dict).forEach(k => dict[k+'.$value'] = dict[k]);
|
|
100
|
+
*/
|
|
101
|
+
return Object.create(null);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function AnalyticalAnnotations()
|
|
105
|
+
{
|
|
106
|
+
function mapCommonAttributes(element, struct, prop)
|
|
107
|
+
{
|
|
108
|
+
let CommonAttributes = element[prop];
|
|
109
|
+
if(!Array.isArray(CommonAttributes)) {
|
|
110
|
+
error(null, ['definitions', struct.name, 'elements', element.name],
|
|
111
|
+
{ anno: '@Common.attribute', code: JSON.stringify(CommonAttributes) },
|
|
112
|
+
`Expect array value for $(ANNOTATION): $(CODE)`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let targets = edmUtils.intersect(CommonAttributes, Object.keys(struct.elements));
|
|
117
|
+
targets.forEach(tgt => {
|
|
118
|
+
edmUtils.assignAnnotation(struct.elements[tgt], '@sap.attribute-for', element.name);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function mapContextDefiningProperties(element, struct, prop)
|
|
123
|
+
{
|
|
124
|
+
let ContextDefiningProperties = element[prop];
|
|
125
|
+
if(!Array.isArray(ContextDefiningProperties)) {
|
|
126
|
+
error(null, ['definitions', struct.name, 'elements', element.name],
|
|
127
|
+
{ anno: '@Aggregation.ContextDefiningProperties', code: JSON.stringify(ContextDefiningProperties) },
|
|
128
|
+
`Expect array value for $(ANNOTATION): $(CODE)`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if(ContextDefiningProperties.length > 0)
|
|
132
|
+
edmUtils.assignAnnotation(element, '@sap.super-ordinate', ContextDefiningProperties[ContextDefiningProperties.length-1]);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let dict = Object.create(null);
|
|
136
|
+
//analytics term definition unknown, lower case
|
|
137
|
+
dict['@Analytics.Measure'] = [ '@sap.aggregation-role', 'measure' ];
|
|
138
|
+
dict['@Analytics.Dimension'] = [ '@sap.aggregation-role', 'dimension' ];
|
|
139
|
+
dict['@Semantics.currencyCode'] = [ '@sap.semantics', 'currency-code', true ];
|
|
140
|
+
dict['@Semantics.unitOfMeasure'] = [ '@sap.semantics', 'unit-of-measure', true ];
|
|
141
|
+
|
|
142
|
+
dict['@Measures.ISOCurrency'] = [ '@sap.unit' ];
|
|
143
|
+
dict['@Measures.Unit'] = [ '@sap.unit' ];
|
|
144
|
+
|
|
145
|
+
dict['@Common.Label'] = [ '@sap.label' ];
|
|
146
|
+
dict['@Common.Text'] = [ '@sap.text' ];
|
|
147
|
+
dict['@Aggregation.ContextDefiningProperties'] = [ mapContextDefiningProperties ];
|
|
148
|
+
dict['@Common.Attributes'] = [ mapCommonAttributes ];
|
|
149
|
+
|
|
150
|
+
// respect flattened annotation $value
|
|
151
|
+
Object.entries(dict).forEach(([k, v]) => dict[k+'.$value'] = v);
|
|
152
|
+
return dict;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function applyAppSpecificLateCsnTransformationOnStructure(options, struct, error)
|
|
157
|
+
{
|
|
158
|
+
if(options.isV2())
|
|
159
|
+
{
|
|
160
|
+
if(struct['@Aggregation.ApplySupported.PropertyRestrictions'])
|
|
161
|
+
{
|
|
162
|
+
transformAnalyticalModel(struct);
|
|
163
|
+
mapAnnotationAssignment(struct, undefined, AnalyticalAnnotations());
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// nested functions begin
|
|
168
|
+
function transformAnalyticalModel(struct)
|
|
169
|
+
{
|
|
170
|
+
let keyName = 'ID__';
|
|
171
|
+
if(struct == undefined || struct.elements == undefined || struct.elements[keyName] != undefined)
|
|
172
|
+
return;
|
|
173
|
+
|
|
174
|
+
// remove key prop from elements, add new key to elements
|
|
175
|
+
let elements = Object.create(null);
|
|
176
|
+
let key = { name: keyName, key : true, type : 'cds.String', '@sap.sortable':false, '@sap.filterable':false, '@UI.Hidden': true };
|
|
177
|
+
elements[keyName] = key;
|
|
178
|
+
setProp(struct, '$keys',{ [keyName] : key } );
|
|
179
|
+
forEachGeneric(struct.items || struct, 'elements', (e,n) =>
|
|
180
|
+
{
|
|
181
|
+
if(e.key) delete e.key;
|
|
182
|
+
elements[n] = e;
|
|
183
|
+
});
|
|
184
|
+
struct.elements = elements;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function AnalyticalAnnotations()
|
|
188
|
+
{
|
|
189
|
+
function mapFilterRestrictions(struct, parent, prop)
|
|
190
|
+
{
|
|
191
|
+
let stringDict = Object.create(null);
|
|
192
|
+
stringDict['SingleValue'] = 'single-value';
|
|
193
|
+
stringDict['MultiValue'] = 'multi-value';
|
|
194
|
+
stringDict['SingleRange'] = 'interval';
|
|
195
|
+
|
|
196
|
+
let filterRestrictions = struct[prop];
|
|
197
|
+
if(!Array.isArray(filterRestrictions)) {
|
|
198
|
+
error(null, ['definitions', struct.name ],
|
|
199
|
+
{ anno: '@Capabilities.FilterRestrictions.FilterExpressionRestrictions',
|
|
200
|
+
code: JSON.stringify(filterRestrictions) },
|
|
201
|
+
`Expect array value for $(ANNOTATION): $(CODE)`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
filterRestrictions.forEach(v => {
|
|
205
|
+
let e = struct.elements[v.Property];
|
|
206
|
+
if(e)
|
|
207
|
+
edmUtils.assignAnnotation(e, '@sap.filter-restriction', stringDict[v.AllowedExpressions]);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function mapRequiredProperties(struct, parent, prop)
|
|
212
|
+
{
|
|
213
|
+
let requiredProperties = struct[prop];
|
|
214
|
+
if(!Array.isArray(requiredProperties)) {
|
|
215
|
+
error(null, ['definitions', struct.name],
|
|
216
|
+
{ anno: '@Capabilities.FilterRestrictions.RequiredProperties',
|
|
217
|
+
code: JSON.stringify(requiredProperties) },
|
|
218
|
+
`Expect array value for $(ANNOTATION): $(CODE)`);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let props = edmUtils.intersect(Object.keys(struct.elements), requiredProperties)
|
|
223
|
+
props.forEach(p => {
|
|
224
|
+
edmUtils.assignAnnotation(struct.elements[p], '@sap.required-in-filter', true);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function mapRequiresFilter(struct, parent, prop)
|
|
229
|
+
{
|
|
230
|
+
let requiresFilter = struct[prop];
|
|
231
|
+
if(requiresFilter)
|
|
232
|
+
edmUtils.assignAnnotation(struct._SetAttributes, '@sap.requires-filter', requiresFilter);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Entity Props
|
|
236
|
+
let dict = Object.create(null);
|
|
237
|
+
dict['@Aggregation.ApplySupported.PropertyRestrictions'] = [ '@sap.semantics', 'aggregate' ];
|
|
238
|
+
dict['@Common.Label'] = [ '@sap.label' ];
|
|
239
|
+
dict['@Capabilities.FilterRestrictions.RequiresFilter'] = [ mapRequiresFilter ];
|
|
240
|
+
dict['@Capabilities.FilterRestrictions.RequiredProperties'] = [ mapRequiredProperties ];
|
|
241
|
+
dict['@Capabilities.FilterRestrictions.FilterExpressionRestrictions'] = [ mapFilterRestrictions ];
|
|
242
|
+
|
|
243
|
+
// respect flattened annotation $value
|
|
244
|
+
Object.keys(dict).forEach(k => dict[k+'.$value'] = dict[k]);
|
|
245
|
+
|
|
246
|
+
return dict;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function setSAPSpecificV2AnnotationsToEntityContainer(options, carrier) {
|
|
251
|
+
if(!options.isV2())
|
|
252
|
+
return;
|
|
253
|
+
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0#SAPAnnotationsforODataVersion2.0-Elementedm:EntityContainer
|
|
254
|
+
const SetAttributes = {
|
|
255
|
+
// EntityContainer only
|
|
256
|
+
'@sap.supported.formats' : addToSetAttr,
|
|
257
|
+
'@sap.use.batch': addToSetAttr,
|
|
258
|
+
'@sap.message.scope.supported': addToSetAttr,
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
Object.entries(carrier).forEach(([p, v]) => {
|
|
262
|
+
(SetAttributes[p] || function() { /* no-op */ })(carrier, p, v);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function setSAPSpecificV2AnnotationsToEntitySet(options, carrier) {
|
|
267
|
+
if(!options.isV2())
|
|
268
|
+
return;
|
|
269
|
+
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0#SAPAnnotationsforODataVersion2.0-Elementedm:EntitySet
|
|
270
|
+
const SetAttributes = {
|
|
271
|
+
// EntitySet, EntityType
|
|
272
|
+
'@sap.label' : (s,pn, pv) => { addToSetAttr(s, pn, pv, false); },
|
|
273
|
+
'@sap.semantics': checkSemantics,
|
|
274
|
+
// EntitySet only
|
|
275
|
+
'@sap.creatable' : addToSetAttr,
|
|
276
|
+
'@sap.updatable' : addToSetAttr,
|
|
277
|
+
'@sap.deletable': addToSetAttr,
|
|
278
|
+
'@sap.updatable.path': addToSetAttr,
|
|
279
|
+
'@sap.deletable.path': addToSetAttr,
|
|
280
|
+
'@sap.searchable' : addToSetAttr,
|
|
281
|
+
'@sap.pagable': addToSetAttr,
|
|
282
|
+
'@sap.topable': addToSetAttr,
|
|
283
|
+
'@sap.countable': addToSetAttr,
|
|
284
|
+
'@sap.addressable': addToSetAttr,
|
|
285
|
+
'@sap.requires.filter': addToSetAttr,
|
|
286
|
+
'@sap.change.tracking': addToSetAttr,
|
|
287
|
+
'@sap.maxpagesize': addToSetAttr,
|
|
288
|
+
'@sap.delta.link.validity': addToSetAttr,
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
Object.entries(carrier).forEach(([p, v]) => {
|
|
292
|
+
(SetAttributes[p] || function() { /* no-op */ })(carrier, p, v);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
function checkSemantics(struct, propName, propValue) {
|
|
296
|
+
if(propValue === 'timeseries' || propValue === 'aggregate') {
|
|
297
|
+
// aggregate is forwarded to Set and must remain on Type
|
|
298
|
+
addToSetAttr(struct, propName, propValue, propValue !== 'aggregate');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function setSAPSpecificV2AnnotationsToAssociation(carrier) {
|
|
304
|
+
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0
|
|
305
|
+
const SetAttributes = {
|
|
306
|
+
// Applicable to NavProp and foreign keys, add to AssociationSet
|
|
307
|
+
'@sap.creatable' : (c, pn, pv) => { addToAssociationSet(c, pn, pv, false); },
|
|
308
|
+
// Not applicable to NavProp, applicable to foreign keys, add to AssociationSet
|
|
309
|
+
'@sap.updatable' : addToAssociationSet,
|
|
310
|
+
// Not applicable to NavProp, not applicable to foreign key, add to AssociationSet
|
|
311
|
+
'@sap.deletable': (c, pn, pv) => {
|
|
312
|
+
addToAssociationSet(c, pn, pv);
|
|
313
|
+
removeFromForeignKey(c, pn);
|
|
314
|
+
},
|
|
315
|
+
// applicable to NavProp, not applicable to foreign keys, not applicable to AssociationSet
|
|
316
|
+
'@sap.creatable.path': removeFromForeignKey,
|
|
317
|
+
'@sap.filterable': removeFromForeignKey,
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
Object.entries(carrier).forEach(([p, v]) => {
|
|
321
|
+
(SetAttributes[p] || function() {/* no-op */})(carrier, p, v);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
function addToAssociationSet(carrier, propName, propValue, removeFromType=true) {
|
|
325
|
+
if(carrier.target) {
|
|
326
|
+
edmUtils.assignProp(carrier, '_SetAttributes', Object.create(null));
|
|
327
|
+
edmUtils.assignAnnotation(carrier._SetAttributes, propName, propValue);
|
|
328
|
+
if(removeFromType) {
|
|
329
|
+
delete carrier[propName];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function removeFromForeignKey(carrier, propName) {
|
|
335
|
+
if(carrier['@odata.foreignKey4'] && carrier[propName] !== undefined) {
|
|
336
|
+
delete carrier[propName];
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
module.exports = {
|
|
344
|
+
applyAppSpecificLateCsnTransformationOnElement,
|
|
345
|
+
applyAppSpecificLateCsnTransformationOnStructure,
|
|
346
|
+
setSAPSpecificV2AnnotationsToEntityContainer,
|
|
347
|
+
setSAPSpecificV2AnnotationsToEntitySet,
|
|
348
|
+
setSAPSpecificV2AnnotationsToAssociation
|
|
349
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { setProp, isBetaEnabled } = require('../base/model');
|
|
4
|
+
const {
|
|
5
|
+
forEachDefinition, forEachMemberRecursively, getUtils,
|
|
6
|
+
} = require('../model/csnUtils');
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line no-unused-vars
|
|
9
|
+
function resolveForeignKeyRefs(csn) {
|
|
10
|
+
const csnUtils = getUtils(csn);
|
|
11
|
+
forEachDefinition(csn, (def, defName) => {
|
|
12
|
+
let currPath = ['definitions', defName ];
|
|
13
|
+
forEachMemberRecursively(def, (construct, _constructName, _prop, path) => {
|
|
14
|
+
if(construct.target && construct.keys) {
|
|
15
|
+
construct.keys.forEach((fk, i) => {
|
|
16
|
+
setProp(fk, '_artifact', csnUtils.inspectRef([...path, 'keys', i]).art);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}, currPath, true, { elementsOnly: true });
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
function inboundQualificationChecks(csn, options, messageFunctions,
|
|
25
|
+
serviceRootNames, requestedServiceNames, isMyServiceRequested, whatsMyServiceRootName) {
|
|
26
|
+
const csnUtils = getUtils(csn);
|
|
27
|
+
const { message, throwWithAnyError } = messageFunctions;
|
|
28
|
+
|
|
29
|
+
forEachDefinition(csn, [ attach$path, checkChainedArray ]);
|
|
30
|
+
checkNestedContextsAndServices();
|
|
31
|
+
throwWithAnyError();
|
|
32
|
+
|
|
33
|
+
// attach $path to all
|
|
34
|
+
function attach$path(def, defName) {
|
|
35
|
+
setProp(def, '$path', [ 'definitions', defName ]);
|
|
36
|
+
forEachMemberRecursively(def,
|
|
37
|
+
(member, _memberName, _prop, path) => {
|
|
38
|
+
setProp(member, '$path', path);
|
|
39
|
+
}, [ 'definitions', defName ]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function checkChainedArray(def, defName) {
|
|
43
|
+
if (!isMyServiceRequested(defName))
|
|
44
|
+
return;
|
|
45
|
+
let currPath = ['definitions', defName];
|
|
46
|
+
checkIfItemsOfItems(def, undefined, undefined, currPath);
|
|
47
|
+
forEachMemberRecursively(def, checkIfItemsOfItems, currPath);
|
|
48
|
+
|
|
49
|
+
function checkIfItemsOfItems(construct, _constructName, _prop, path) {
|
|
50
|
+
const constructType = csnUtils.effectiveType(construct);
|
|
51
|
+
if (constructType.items) {
|
|
52
|
+
if (constructType.items.items) {
|
|
53
|
+
message('chained-array-of', path);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const itemsType = csnUtils.effectiveType(constructType.items);
|
|
58
|
+
if (itemsType.items)
|
|
59
|
+
message('chained-array-of', path);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function checkNestedContextsAndServices() {
|
|
65
|
+
!isBetaEnabled(options, 'nestedServices') && serviceRootNames.forEach(sn => {
|
|
66
|
+
const parent = whatsMyServiceRootName(sn, false);
|
|
67
|
+
if(parent && requestedServiceNames.includes(parent) && parent !== sn) {
|
|
68
|
+
message( 'service-nested-service', [ 'definitions', sn ], { art: parent },
|
|
69
|
+
'A service can\'t be nested within a service $(ART)' );
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
Object.entries(csn.definitions).forEach(([fqName, art]) => {
|
|
74
|
+
if(art.kind === 'context') {
|
|
75
|
+
const parent = whatsMyServiceRootName(fqName);
|
|
76
|
+
if(requestedServiceNames.includes(parent)) {
|
|
77
|
+
message( 'service-nested-context', [ 'definitions', fqName ], { art: parent },
|
|
78
|
+
'A context can\'t be nested within a service $(ART)' );
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = { inboundQualificationChecks }
|