@sap/cds-compiler 4.0.2 → 4.2.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 +200 -5
- package/bin/cdsc.js +18 -15
- package/doc/CHANGELOG_BETA.md +16 -0
- package/doc/CHANGELOG_DEPRECATED.md +15 -0
- package/lib/api/main.js +33 -13
- package/lib/api/options.js +2 -2
- package/lib/api/validate.js +25 -25
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +123 -42
- package/lib/base/messages.js +18 -10
- package/lib/base/model.js +43 -10
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/elements.js +11 -10
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/manyNavigations.js +33 -0
- package/lib/checks/onConditions.js +22 -14
- package/lib/checks/queryNoDbArtifacts.js +132 -73
- package/lib/checks/selectItems.js +4 -55
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +4 -3
- package/lib/checks/validator.js +3 -1
- package/lib/compiler/.eslintrc.json +2 -1
- package/lib/compiler/assert-consistency.js +71 -40
- package/lib/compiler/base.js +7 -2
- package/lib/compiler/builtins.js +40 -41
- package/lib/compiler/checks.js +415 -367
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +9 -9
- package/lib/compiler/define.js +124 -90
- package/lib/compiler/extend.js +115 -88
- package/lib/compiler/finalize-parse-cdl.js +26 -25
- package/lib/compiler/generate.js +57 -49
- package/lib/compiler/index.js +56 -56
- package/lib/compiler/kick-start.js +10 -7
- package/lib/compiler/moduleLayers.js +1 -1
- package/lib/compiler/populate.js +180 -144
- package/lib/compiler/propagator.js +10 -9
- package/lib/compiler/resolve.js +321 -246
- package/lib/compiler/shared.js +812 -433
- package/lib/compiler/tweak-assocs.js +114 -50
- package/lib/compiler/utils.js +241 -46
- package/lib/edm/.eslintrc.json +40 -1
- package/lib/edm/annotations/genericTranslation.js +721 -707
- package/lib/edm/annotations/preprocessAnnotations.js +88 -77
- package/lib/edm/csn2edm.js +389 -378
- package/lib/edm/edm.js +679 -770
- package/lib/edm/edmAnnoPreprocessor.js +132 -146
- package/lib/edm/edmInboundChecks.js +29 -27
- package/lib/edm/edmPreprocessor.js +689 -648
- package/lib/edm/edmUtils.js +279 -300
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2857 -2856
- package/lib/json/from-csn.js +77 -51
- package/lib/json/to-csn.js +15 -15
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +61 -64
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +65 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +51 -18
- package/lib/model/revealInternalProperties.js +30 -22
- package/lib/modelCompare/compare.js +149 -41
- package/lib/modelCompare/utils/filter.js +55 -25
- package/lib/optionProcessor.js +21 -9
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +63 -23
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +82 -35
- package/lib/render/utils/common.js +11 -9
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +62 -21
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +9 -9
- package/lib/transform/db/constraints.js +47 -17
- package/lib/transform/db/expansion.js +138 -68
- package/lib/transform/db/flattening.js +98 -30
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +10 -7
- package/lib/transform/forRelationalDB.js +148 -136
- package/lib/transform/localized.js +92 -54
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/lib/utils/file.js +7 -7
- package/lib/utils/moduleResolve.js +210 -121
- package/lib/utils/objectUtils.js +1 -1
- package/package.json +5 -5
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
|
@@ -23,43 +23,34 @@ const { forEachGeneric } = require('../model/csnUtils');
|
|
|
23
23
|
|
|
24
24
|
/* eslint max-statements-per-line:off */
|
|
25
25
|
|
|
26
|
-
function mapAnnotationAssignment(artifact, parent, mappingDictionary)
|
|
27
|
-
|
|
28
|
-
let props = edmUtils.intersect(Object.keys(mappingDictionary), Object.keys(artifact));
|
|
26
|
+
function mapAnnotationAssignment( artifact, parent, mappingDictionary ) {
|
|
27
|
+
const props = edmUtils.intersect(Object.keys(mappingDictionary), Object.keys(artifact));
|
|
29
28
|
// now start the substitution
|
|
30
|
-
props.forEach(prop => {
|
|
31
|
-
|
|
32
|
-
if(mapping instanceof Function)
|
|
33
|
-
{
|
|
29
|
+
props.forEach((prop) => {
|
|
30
|
+
const [ mapping, value, removeOriginal ] = mappingDictionary[prop];
|
|
31
|
+
if (mapping instanceof Function)
|
|
34
32
|
mapping(artifact, parent, prop);
|
|
35
|
-
|
|
33
|
+
|
|
36
34
|
else
|
|
37
|
-
{
|
|
38
35
|
edmUtils.assignAnnotation(artifact, mapping, value || artifact[prop]['='] || artifact[prop]);
|
|
39
|
-
}
|
|
40
36
|
|
|
41
|
-
|
|
37
|
+
|
|
38
|
+
if (removeOriginal)
|
|
42
39
|
delete artifact[prop];
|
|
43
40
|
});
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
function addToSetAttr(carrier, propName, propValue, removeFromType=true) {
|
|
43
|
+
function addToSetAttr( carrier, propName, propValue, removeFromType = true ) {
|
|
47
44
|
edmUtils.assignProp(carrier, '_SetAttributes', Object.create(null));
|
|
48
45
|
edmUtils.assignAnnotation(carrier._SetAttributes, propName, propValue);
|
|
49
|
-
if(removeFromType)
|
|
46
|
+
if (removeFromType)
|
|
50
47
|
delete carrier[propName];
|
|
51
|
-
}
|
|
52
48
|
}
|
|
53
49
|
|
|
54
|
-
function applyAppSpecificLateCsnTransformationOnElement(options, element, struct, error)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if(struct['@Aggregation.ApplySupported.PropertyRestrictions'])
|
|
59
|
-
{
|
|
60
|
-
mapAnnotationAssignment(element, struct, AnalyticalAnnotations());
|
|
61
|
-
}
|
|
62
|
-
}
|
|
50
|
+
function applyAppSpecificLateCsnTransformationOnElement( options, element, struct, error ) {
|
|
51
|
+
if (options.isV2() && struct['@Aggregation.ApplySupported.PropertyRestrictions'])
|
|
52
|
+
mapAnnotationAssignment(element, struct, AnalyticalAnnotations());
|
|
53
|
+
|
|
63
54
|
|
|
64
55
|
// etag requires Core.OptimisticConcurrency to be set in V4 (cap/issues#2641)
|
|
65
56
|
// Oliver Heinrich mentions in the issue that the Okra runtime must be set to a
|
|
@@ -67,51 +58,45 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
|
|
|
67
58
|
// it doesn't appear in the forOData processed CSN, meaning that the
|
|
68
59
|
// runtime cannot set that okra flag (alternatively the runtime has to search
|
|
69
60
|
// for @[odata|cds].etag annotations...
|
|
70
|
-
if(options.isV4())
|
|
71
|
-
{
|
|
61
|
+
if (options.isV4() && (element['@odata.etag'] || element['@cds.etag'])) {
|
|
72
62
|
// eslint-disable-next-line sonarjs/no-redundant-boolean
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
(struct['@Core.OptimisticConcurrency'] || [])/*.push(element.name)*/);
|
|
79
|
-
}
|
|
63
|
+
// don't put element name into collection as per advice from Ralf Handl, as
|
|
64
|
+
// no runtime is interested in the property itself, it is sufficient to mark
|
|
65
|
+
// the entity set.
|
|
66
|
+
edmUtils.assignAnnotation(struct, '@Core.OptimisticConcurrency',
|
|
67
|
+
(struct['@Core.OptimisticConcurrency'] || [])/* .push(element.name) */);
|
|
80
68
|
}
|
|
81
69
|
|
|
82
|
-
function AnalyticalAnnotations()
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
{ anno: '@Common.Attributes', code: JSON.stringify(CommonAttributes) },
|
|
90
|
-
'Expecting array value for $(ANNO): $(CODE)');
|
|
70
|
+
function AnalyticalAnnotations() {
|
|
71
|
+
function mapCommonAttributes( elt, structure, prop ) {
|
|
72
|
+
const CommonAttributes = elt[prop];
|
|
73
|
+
if (!Array.isArray(CommonAttributes)) {
|
|
74
|
+
error(null, [ 'definitions', structure.name, 'elements', elt.name ],
|
|
75
|
+
{ anno: '@Common.Attributes', code: JSON.stringify(CommonAttributes) },
|
|
76
|
+
'Expecting array value for $(ANNO): $(CODE)');
|
|
91
77
|
return;
|
|
92
78
|
}
|
|
93
79
|
|
|
94
|
-
|
|
95
|
-
targets.forEach(tgt => {
|
|
96
|
-
edmUtils.assignAnnotation(
|
|
80
|
+
const targets = edmUtils.intersect(CommonAttributes, Object.keys(structure.elements));
|
|
81
|
+
targets.forEach((tgt) => {
|
|
82
|
+
edmUtils.assignAnnotation(structure.elements[tgt], '@sap.attribute-for', elt.name);
|
|
97
83
|
});
|
|
98
84
|
}
|
|
99
85
|
|
|
100
|
-
function mapContextDefiningProperties(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
'Expecting array value for $(ANNO): $(CODE)');
|
|
86
|
+
function mapContextDefiningProperties( elt, structure, prop ) {
|
|
87
|
+
const ContextDefiningProperties = elt[prop];
|
|
88
|
+
if (!Array.isArray(ContextDefiningProperties)) {
|
|
89
|
+
error(null, [ 'definitions', structure.name, 'elements', elt.name ],
|
|
90
|
+
{ anno: '@Aggregation.ContextDefiningProperties', code: JSON.stringify(ContextDefiningProperties) },
|
|
91
|
+
'Expecting array value for $(ANNO): $(CODE)');
|
|
107
92
|
return;
|
|
108
93
|
}
|
|
109
|
-
if(ContextDefiningProperties.length > 0)
|
|
110
|
-
edmUtils.assignAnnotation(
|
|
94
|
+
if (ContextDefiningProperties.length > 0)
|
|
95
|
+
edmUtils.assignAnnotation(elt, '@sap.super-ordinate', ContextDefiningProperties[ContextDefiningProperties.length - 1]);
|
|
111
96
|
}
|
|
112
97
|
|
|
113
|
-
|
|
114
|
-
//analytics term definition unknown, lower case
|
|
98
|
+
const dict = Object.create(null);
|
|
99
|
+
// analytics term definition unknown, lower case
|
|
115
100
|
dict['@Analytics.Measure'] = [ '@sap.aggregation-role', 'measure' ];
|
|
116
101
|
dict['@Analytics.Dimension'] = [ '@sap.aggregation-role', 'dimension' ];
|
|
117
102
|
dict['@Semantics.currencyCode'] = [ '@sap.semantics', 'currency-code', true ];
|
|
@@ -126,92 +111,90 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
|
|
|
126
111
|
dict['@Common.Attributes'] = [ mapCommonAttributes ];
|
|
127
112
|
|
|
128
113
|
// respect flattened annotation $value
|
|
129
|
-
Object.entries(dict).forEach(([k, v]) =>
|
|
114
|
+
Object.entries(dict).forEach(([ k, v ]) => {
|
|
115
|
+
dict[`${k}.$value`] = v;
|
|
116
|
+
});
|
|
130
117
|
return dict;
|
|
131
118
|
}
|
|
132
119
|
}
|
|
133
120
|
|
|
134
|
-
function applyAppSpecificLateCsnTransformationOnStructure(options, struct, error)
|
|
135
|
-
{
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if(struct['@Aggregation.ApplySupported.PropertyRestrictions'])
|
|
139
|
-
{
|
|
140
|
-
transformAnalyticalModel(struct);
|
|
141
|
-
mapAnnotationAssignment(struct, undefined, AnalyticalAnnotations());
|
|
142
|
-
}
|
|
121
|
+
function applyAppSpecificLateCsnTransformationOnStructure( options, struct, error ) {
|
|
122
|
+
if (options.isV2() && struct['@Aggregation.ApplySupported.PropertyRestrictions']) {
|
|
123
|
+
transformAnalyticalModel(struct);
|
|
124
|
+
mapAnnotationAssignment(struct, undefined, AnalyticalAnnotations());
|
|
143
125
|
}
|
|
144
126
|
|
|
145
127
|
// nested functions begin
|
|
146
|
-
function transformAnalyticalModel(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if(struct == undefined || struct.elements == undefined || struct.elements[keyName] != undefined)
|
|
128
|
+
function transformAnalyticalModel( structure ) {
|
|
129
|
+
const keyName = 'ID__';
|
|
130
|
+
if (!structure?.elements || structure.elements[keyName])
|
|
150
131
|
return;
|
|
151
132
|
|
|
152
133
|
// remove key prop from elements, add new key to elements
|
|
153
|
-
|
|
154
|
-
|
|
134
|
+
const elements = Object.create(null);
|
|
135
|
+
const key = {
|
|
136
|
+
name: keyName, key: true, type: 'cds.String', '@sap.sortable': false, '@sap.filterable': false, '@UI.Hidden': true,
|
|
137
|
+
};
|
|
155
138
|
elements[keyName] = key;
|
|
156
|
-
setProp(
|
|
157
|
-
forEachGeneric(
|
|
158
|
-
|
|
159
|
-
|
|
139
|
+
setProp(structure, '$keys', { [keyName]: key } );
|
|
140
|
+
forEachGeneric(structure.items || structure, 'elements', (e, n) => {
|
|
141
|
+
if (e.key)
|
|
142
|
+
delete e.key;
|
|
160
143
|
elements[n] = e;
|
|
161
144
|
});
|
|
162
|
-
|
|
145
|
+
structure.elements = elements;
|
|
163
146
|
}
|
|
164
147
|
|
|
165
|
-
function AnalyticalAnnotations()
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
stringDict
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
148
|
+
function AnalyticalAnnotations() {
|
|
149
|
+
function mapFilterRestrictions( structure, parent, prop ) {
|
|
150
|
+
const stringDict = Object.create(null);
|
|
151
|
+
stringDict.SingleValue = 'single-value';
|
|
152
|
+
stringDict.MultiValue = 'multi-value';
|
|
153
|
+
stringDict.SingleRange = 'interval';
|
|
154
|
+
|
|
155
|
+
const filterRestrictions = structure[prop];
|
|
156
|
+
if (!Array.isArray(filterRestrictions)) {
|
|
157
|
+
error(null, [ 'definitions', structure.name ],
|
|
158
|
+
{
|
|
159
|
+
anno: '@Capabilities.FilterRestrictions.FilterExpressionRestrictions',
|
|
160
|
+
code: JSON.stringify(filterRestrictions),
|
|
161
|
+
},
|
|
162
|
+
'Expected array value for $(ANNO): $(CODE)');
|
|
180
163
|
return;
|
|
181
164
|
}
|
|
182
|
-
filterRestrictions.forEach(v => {
|
|
183
|
-
|
|
184
|
-
if(e)
|
|
165
|
+
filterRestrictions.forEach((v) => {
|
|
166
|
+
const e = structure.elements[v.Property];
|
|
167
|
+
if (e)
|
|
185
168
|
edmUtils.assignAnnotation(e, '@sap.filter-restriction', stringDict[v.AllowedExpressions]);
|
|
186
169
|
});
|
|
187
170
|
}
|
|
188
171
|
|
|
189
|
-
function mapRequiredProperties(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
172
|
+
function mapRequiredProperties( structure, parent, prop ) {
|
|
173
|
+
const requiredProperties = structure[prop];
|
|
174
|
+
if (!Array.isArray(requiredProperties)) {
|
|
175
|
+
error(null, [ 'definitions', structure.name ],
|
|
176
|
+
{
|
|
177
|
+
anno: '@Capabilities.FilterRestrictions.RequiredProperties',
|
|
178
|
+
code: JSON.stringify(requiredProperties),
|
|
179
|
+
},
|
|
180
|
+
'Expecting array value for $(ANNO): $(CODE)');
|
|
197
181
|
return;
|
|
198
182
|
}
|
|
199
183
|
|
|
200
|
-
|
|
201
|
-
props.forEach(p => {
|
|
202
|
-
edmUtils.assignAnnotation(
|
|
184
|
+
const props = edmUtils.intersect(Object.keys(structure.elements), requiredProperties);
|
|
185
|
+
props.forEach((p) => {
|
|
186
|
+
edmUtils.assignAnnotation(structure.elements[p], '@sap.required-in-filter', true);
|
|
203
187
|
});
|
|
204
188
|
}
|
|
205
189
|
|
|
206
|
-
function mapRequiresFilter(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
edmUtils.assignAnnotation(struct._SetAttributes, '@sap.requires-filter', requiresFilter);
|
|
190
|
+
function mapRequiresFilter( structure, parent, prop ) {
|
|
191
|
+
const requiresFilter = structure[prop];
|
|
192
|
+
if (requiresFilter)
|
|
193
|
+
edmUtils.assignAnnotation(structure._SetAttributes, '@sap.requires-filter', requiresFilter);
|
|
211
194
|
}
|
|
212
195
|
|
|
213
|
-
|
|
214
|
-
|
|
196
|
+
// Entity Props
|
|
197
|
+
const dict = Object.create(null);
|
|
215
198
|
dict['@Aggregation.ApplySupported.PropertyRestrictions'] = [ '@sap.semantics', 'aggregate' ];
|
|
216
199
|
dict['@Common.Label'] = [ '@sap.label' ];
|
|
217
200
|
dict['@Capabilities.FilterRestrictions.RequiresFilter'] = [ mapRequiresFilter ];
|
|
@@ -219,43 +202,47 @@ function applyAppSpecificLateCsnTransformationOnStructure(options, struct, error
|
|
|
219
202
|
dict['@Capabilities.FilterRestrictions.FilterExpressionRestrictions'] = [ mapFilterRestrictions ];
|
|
220
203
|
|
|
221
204
|
// respect flattened annotation $value
|
|
222
|
-
Object.keys(dict).forEach(k =>
|
|
205
|
+
Object.keys(dict).forEach((k) => {
|
|
206
|
+
dict[`${k}.$value`] = dict[k];
|
|
207
|
+
});
|
|
223
208
|
|
|
224
209
|
return dict;
|
|
225
210
|
}
|
|
226
211
|
}
|
|
227
212
|
|
|
228
|
-
function setSAPSpecificV2AnnotationsToEntityContainer(options, carrier) {
|
|
229
|
-
if(!options.isV2())
|
|
213
|
+
function setSAPSpecificV2AnnotationsToEntityContainer( options, carrier ) {
|
|
214
|
+
if (!options.isV2())
|
|
230
215
|
return;
|
|
231
216
|
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0#SAPAnnotationsforODataVersion2.0-Elementedm:EntityContainer
|
|
232
217
|
const SetAttributes = {
|
|
233
218
|
// EntityContainer only
|
|
234
|
-
'@sap.supported.formats'
|
|
219
|
+
'@sap.supported.formats': addToSetAttr,
|
|
235
220
|
'@sap.use.batch': addToSetAttr,
|
|
236
221
|
'@sap.message.scope.supported': addToSetAttr,
|
|
237
222
|
};
|
|
238
223
|
|
|
239
|
-
Object.entries(carrier).forEach(([p, v]) => {
|
|
240
|
-
(SetAttributes[p] || function() { /* no-op */ })(carrier, p, v);
|
|
224
|
+
Object.entries(carrier).forEach(([ p, v ]) => {
|
|
225
|
+
(SetAttributes[p] || function () { /* no-op */ })(carrier, p, v); // eslint-disable-line func-names
|
|
241
226
|
});
|
|
242
227
|
}
|
|
243
228
|
|
|
244
|
-
function setSAPSpecificV2AnnotationsToEntitySet(options, carrier) {
|
|
245
|
-
if(!options.isV2())
|
|
229
|
+
function setSAPSpecificV2AnnotationsToEntitySet( options, carrier ) {
|
|
230
|
+
if (!options.isV2())
|
|
246
231
|
return;
|
|
247
232
|
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0#SAPAnnotationsforODataVersion2.0-Elementedm:EntitySet
|
|
248
233
|
const SetAttributes = {
|
|
249
234
|
// EntitySet, EntityType
|
|
250
|
-
'@sap.label'
|
|
235
|
+
'@sap.label': (s, pn, pv) => {
|
|
236
|
+
addToSetAttr(s, pn, pv, false);
|
|
237
|
+
},
|
|
251
238
|
'@sap.semantics': checkSemantics,
|
|
252
239
|
// EntitySet only
|
|
253
|
-
'@sap.creatable'
|
|
254
|
-
'@sap.updatable'
|
|
240
|
+
'@sap.creatable': addToSetAttr,
|
|
241
|
+
'@sap.updatable': addToSetAttr,
|
|
255
242
|
'@sap.deletable': addToSetAttr,
|
|
256
243
|
'@sap.updatable.path': addToSetAttr,
|
|
257
244
|
'@sap.deletable.path': addToSetAttr,
|
|
258
|
-
'@sap.searchable'
|
|
245
|
+
'@sap.searchable': addToSetAttr,
|
|
259
246
|
'@sap.pagable': addToSetAttr,
|
|
260
247
|
'@sap.topable': addToSetAttr,
|
|
261
248
|
'@sap.countable': addToSetAttr,
|
|
@@ -266,25 +253,27 @@ function setSAPSpecificV2AnnotationsToEntitySet(options, carrier) {
|
|
|
266
253
|
'@sap.delta.link.validity': addToSetAttr,
|
|
267
254
|
};
|
|
268
255
|
|
|
269
|
-
Object.entries(carrier).forEach(([p, v]) => {
|
|
270
|
-
(SetAttributes[p] || function() { /* no-op */ })(carrier, p, v);
|
|
256
|
+
Object.entries(carrier).forEach(([ p, v ]) => {
|
|
257
|
+
(SetAttributes[p] || function () { /* no-op */ })(carrier, p, v); // eslint-disable-line func-names
|
|
271
258
|
});
|
|
272
259
|
|
|
273
|
-
function checkSemantics(struct, propName, propValue) {
|
|
274
|
-
if(propValue === 'timeseries' || propValue === 'aggregate') {
|
|
260
|
+
function checkSemantics( struct, propName, propValue ) {
|
|
261
|
+
if (propValue === 'timeseries' || propValue === 'aggregate') {
|
|
275
262
|
// aggregate is forwarded to Set and must remain on Type
|
|
276
263
|
addToSetAttr(struct, propName, propValue, propValue !== 'aggregate');
|
|
277
264
|
}
|
|
278
265
|
}
|
|
279
266
|
}
|
|
280
267
|
|
|
281
|
-
function setSAPSpecificV2AnnotationsToAssociation(carrier) {
|
|
268
|
+
function setSAPSpecificV2AnnotationsToAssociation( carrier ) {
|
|
282
269
|
// documented in https://wiki.scn.sap.com/wiki/display/EmTech/SAP+Annotations+for+OData+Version+2.0
|
|
283
270
|
const SetAttributes = {
|
|
284
271
|
// Applicable to NavProp and foreign keys, add to AssociationSet
|
|
285
|
-
'@sap.creatable'
|
|
272
|
+
'@sap.creatable': (c, pn, pv) => {
|
|
273
|
+
addToAssociationSet(c, pn, pv, false);
|
|
274
|
+
},
|
|
286
275
|
// Not applicable to NavProp, applicable to foreign keys, add to AssociationSet
|
|
287
|
-
'@sap.updatable'
|
|
276
|
+
'@sap.updatable': addToAssociationSet,
|
|
288
277
|
// Not applicable to NavProp, not applicable to foreign key, add to AssociationSet
|
|
289
278
|
'@sap.deletable': (c, pn, pv) => {
|
|
290
279
|
addToAssociationSet(c, pn, pv);
|
|
@@ -295,33 +284,30 @@ function setSAPSpecificV2AnnotationsToAssociation(carrier) {
|
|
|
295
284
|
'@sap.filterable': removeFromForeignKey,
|
|
296
285
|
};
|
|
297
286
|
|
|
298
|
-
Object.entries(carrier).forEach(([p, v]) => {
|
|
299
|
-
(SetAttributes[p] || function() {/* no-op */})(carrier, p, v);
|
|
287
|
+
Object.entries(carrier).forEach(([ p, v ]) => {
|
|
288
|
+
(SetAttributes[p] || function () { /* no-op */ })(carrier, p, v); // eslint-disable-line func-names
|
|
300
289
|
});
|
|
301
290
|
|
|
302
|
-
function addToAssociationSet(
|
|
303
|
-
if(
|
|
304
|
-
edmUtils.assignProp(
|
|
305
|
-
edmUtils.assignAnnotation(
|
|
306
|
-
if(removeFromType)
|
|
307
|
-
delete
|
|
308
|
-
}
|
|
291
|
+
function addToAssociationSet( target, propName, propValue, removeFromType = true ) {
|
|
292
|
+
if (target.target) {
|
|
293
|
+
edmUtils.assignProp(target, '_SetAttributes', Object.create(null));
|
|
294
|
+
edmUtils.assignAnnotation(target._SetAttributes, propName, propValue);
|
|
295
|
+
if (removeFromType)
|
|
296
|
+
delete target[propName];
|
|
309
297
|
}
|
|
310
298
|
}
|
|
311
299
|
|
|
312
|
-
function removeFromForeignKey(
|
|
313
|
-
if(
|
|
314
|
-
delete
|
|
315
|
-
}
|
|
300
|
+
function removeFromForeignKey( target, propName ) {
|
|
301
|
+
if (target['@odata.foreignKey4'] && target[propName] !== undefined)
|
|
302
|
+
delete target[propName];
|
|
316
303
|
}
|
|
317
304
|
}
|
|
318
305
|
|
|
319
306
|
|
|
320
|
-
|
|
321
307
|
module.exports = {
|
|
322
308
|
applyAppSpecificLateCsnTransformationOnElement,
|
|
323
309
|
applyAppSpecificLateCsnTransformationOnStructure,
|
|
324
310
|
setSAPSpecificV2AnnotationsToEntityContainer,
|
|
325
311
|
setSAPSpecificV2AnnotationsToEntitySet,
|
|
326
|
-
setSAPSpecificV2AnnotationsToAssociation
|
|
312
|
+
setSAPSpecificV2AnnotationsToAssociation,
|
|
327
313
|
};
|
|
@@ -6,13 +6,13 @@ const {
|
|
|
6
6
|
} = require('../model/csnUtils');
|
|
7
7
|
|
|
8
8
|
// eslint-disable-next-line no-unused-vars
|
|
9
|
-
function resolveForeignKeyRefs(csn, csnUtils) {
|
|
9
|
+
function resolveForeignKeyRefs( csn, csnUtils ) {
|
|
10
10
|
forEachDefinition(csn, (def, defName) => {
|
|
11
|
-
|
|
11
|
+
const currPath = [ 'definitions', defName ];
|
|
12
12
|
forEachMemberRecursively(def, (construct, _constructName, _prop, path) => {
|
|
13
|
-
if(construct.target && construct.keys) {
|
|
13
|
+
if (construct.target && construct.keys) {
|
|
14
14
|
construct.keys.forEach((fk, i) => {
|
|
15
|
-
setProp(fk, '_artifact', csnUtils.inspectRef([...path, 'keys', i]).art);
|
|
15
|
+
setProp(fk, '_artifact', csnUtils.inspectRef([ ...path, 'keys', i ]).art);
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
}, currPath, true, { elementsOnly: true });
|
|
@@ -20,34 +20,34 @@ function resolveForeignKeyRefs(csn, csnUtils) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
function inboundQualificationChecks(csn, options, messageFunctions,
|
|
24
|
-
|
|
23
|
+
function inboundQualificationChecks( csn, options, messageFunctions,
|
|
24
|
+
serviceRootNames, requestedServiceNames, isMyServiceRequested, whatsMyServiceRootName, csnUtils ) {
|
|
25
25
|
const { message, throwWithError } = messageFunctions;
|
|
26
26
|
|
|
27
27
|
forEachDefinition(csn, [ attach$path, checkProperArrayUsage ]);
|
|
28
28
|
checkNestedContextsAndServices();
|
|
29
29
|
throwWithError();
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
function attach$path(def, defName) {
|
|
31
|
+
// attach $path to all
|
|
32
|
+
function attach$path( def, defName ) {
|
|
33
33
|
setProp(def, '$path', [ 'definitions', defName ]);
|
|
34
34
|
forEachMemberRecursively(def,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
(member, _memberName, _prop, path) => {
|
|
36
|
+
setProp(member, '$path', path);
|
|
37
|
+
}, [ 'definitions', defName ]);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
function checkProperArrayUsage(def, defName) {
|
|
40
|
+
function checkProperArrayUsage( def, defName ) {
|
|
41
41
|
if (!isMyServiceRequested(defName))
|
|
42
42
|
return;
|
|
43
|
-
|
|
43
|
+
const currPath = [ 'definitions', defName ];
|
|
44
44
|
checkIfItemsOfItems(def, undefined, undefined, currPath);
|
|
45
45
|
forEachMemberRecursively(def, checkIfItemsOfItems, currPath);
|
|
46
46
|
|
|
47
|
-
function checkIfItemsOfItems(construct, _constructName, _prop, path) {
|
|
47
|
+
function checkIfItemsOfItems( construct, _constructName, _prop, path ) {
|
|
48
48
|
const constructType = csnUtils.effectiveType(construct);
|
|
49
49
|
if (constructType.items) {
|
|
50
|
-
if(constructType.items.target) {
|
|
50
|
+
if (constructType.items.target) {
|
|
51
51
|
const isComp = constructType.items.type === 'cds.Composition';
|
|
52
52
|
message('type-invalid-items', path, { '#': isComp ? 'comp' : 'assoc', prop: 'items' });
|
|
53
53
|
return;
|
|
@@ -65,24 +65,26 @@ function inboundQualificationChecks(csn, options, messageFunctions,
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function checkNestedContextsAndServices() {
|
|
68
|
-
!isBetaEnabled(options, 'nestedServices')
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
if (!isBetaEnabled(options, 'nestedServices')) {
|
|
69
|
+
serviceRootNames.forEach((sn) => {
|
|
70
|
+
const parent = whatsMyServiceRootName(sn, false);
|
|
71
|
+
if (parent && requestedServiceNames.includes(parent) && parent !== sn) {
|
|
72
|
+
message( 'service-nested-service', [ 'definitions', sn ], { art: parent },
|
|
73
|
+
'A service can\'t be nested within a service $(ART)' );
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
75
77
|
|
|
76
|
-
Object.entries(csn.definitions).forEach(([fqName, art]) => {
|
|
77
|
-
if(art.kind === 'context') {
|
|
78
|
+
Object.entries(csn.definitions).forEach(([ fqName, art ]) => {
|
|
79
|
+
if (art.kind === 'context') {
|
|
78
80
|
const parent = whatsMyServiceRootName(fqName);
|
|
79
|
-
if(requestedServiceNames.includes(parent)) {
|
|
81
|
+
if (requestedServiceNames.includes(parent)) {
|
|
80
82
|
message( 'service-nested-context', [ 'definitions', fqName ], { art: parent },
|
|
81
|
-
|
|
83
|
+
'A context can\'t be nested within a service $(ART)' );
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
});
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
|
|
88
|
-
module.exports = { inboundQualificationChecks }
|
|
90
|
+
module.exports = { inboundQualificationChecks };
|