@sap/cds-compiler 3.4.0 → 3.4.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/bin/cdsc.js +3 -4
- package/bin/cdshi.js +19 -6
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/lib/api/main.js +6 -3
- package/lib/base/message-registry.js +61 -38
- package/lib/base/messages.js +7 -3
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/compiler/.eslintrc.json +4 -1
- package/lib/compiler/assert-consistency.js +8 -6
- package/lib/compiler/builtins.js +13 -13
- package/lib/compiler/checks.js +50 -33
- package/lib/compiler/define.js +9 -6
- package/lib/compiler/extend.js +83 -55
- package/lib/compiler/finalize-parse-cdl.js +3 -3
- package/lib/compiler/populate.js +16 -5
- package/lib/compiler/propagator.js +22 -29
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +4 -4
- package/lib/compiler/utils.js +14 -0
- package/lib/edm/annotations/genericTranslation.js +68 -56
- package/lib/edm/csn2edm.js +214 -174
- package/lib/edm/edmAnnoPreprocessor.js +5 -5
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +1 -1
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/Dictionary.json +176 -8
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4776 -4513
- package/lib/json/from-csn.js +21 -16
- package/lib/json/to-csn.js +37 -41
- package/lib/language/.eslintrc.json +4 -1
- package/lib/language/antlrParser.js +5 -2
- package/lib/language/docCommentParser.js +6 -6
- package/lib/language/errorStrategy.js +43 -23
- package/lib/language/genericAntlrParser.js +54 -95
- package/lib/language/language.g4 +92 -66
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +2 -2
- package/lib/model/csnRefs.js +5 -0
- package/lib/modelCompare/compare.js +2 -2
- package/lib/modelCompare/utils/.eslintrc.json +22 -0
- package/lib/modelCompare/utils/filter.js +99 -0
- package/lib/render/.eslintrc.json +1 -0
- package/lib/render/toCdl.js +96 -127
- package/lib/render/toHdbcds.js +38 -35
- package/lib/render/toSql.js +75 -161
- package/lib/render/utils/common.js +133 -83
- package/lib/render/utils/delta.js +227 -0
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/draft/.eslintrc.json +1 -35
- package/lib/transform/forOdataNew.js +33 -22
- package/lib/transform/forRelationalDB.js +1 -1
- package/lib/transform/localized.js +9 -8
- package/lib/transform/odata/typesExposure.js +26 -4
- package/lib/transform/transformUtilsNew.js +15 -8
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/package.json +2 -3
- package/lib/modelCompare/filter.js +0 -83
package/lib/edm/csn2edm.js
CHANGED
|
@@ -30,7 +30,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
30
30
|
|
|
31
31
|
// use original options for messages; cloned CSN for semantic location
|
|
32
32
|
const messageFunctions = makeMessageFunction(csn, _options, 'to.edmx');
|
|
33
|
-
const { info, warning, error, message,
|
|
33
|
+
const { info, warning, error, message, throwWithError } = messageFunctions;
|
|
34
34
|
checkCSNVersion(csn, _options);
|
|
35
35
|
|
|
36
36
|
let rc = Object.create(null);
|
|
@@ -54,7 +54,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
54
54
|
|
|
55
55
|
const v = options.v;
|
|
56
56
|
if(Object.keys(allServices).length === 0) {
|
|
57
|
-
info(null, null,
|
|
57
|
+
info(null, null, 'No Services in model');
|
|
58
58
|
return rc;
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -76,7 +76,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
76
76
|
services[serviceCsn.name] = createEdm(serviceCsn);
|
|
77
77
|
return services; }, rc);
|
|
78
78
|
}
|
|
79
|
-
|
|
79
|
+
throwWithError();
|
|
80
80
|
return rc;
|
|
81
81
|
|
|
82
82
|
//--------------------------------------------------------------------------------
|
|
@@ -91,6 +91,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
91
91
|
// let alias = serviceCsn.alias || baseName(baseName(serviceCsn.name, '::'), '.');
|
|
92
92
|
// FIXME: UI5 cannot deal with spec conforming simpleid alias names
|
|
93
93
|
|
|
94
|
+
function markRendered(def) { setProp(def, '$isRendered', true); }
|
|
95
|
+
|
|
94
96
|
const service = new Edm.DataServices(v);
|
|
95
97
|
/** @type {object} */
|
|
96
98
|
const edm = new Edm.Edm(v, service);
|
|
@@ -131,8 +133,19 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
131
133
|
-----------------------------------------------*/
|
|
132
134
|
let LeadSchema;
|
|
133
135
|
const fqSchemaXRef = [serviceCsn.name];
|
|
134
|
-
const whatsMySchemaName = function(n) {
|
|
135
|
-
return
|
|
136
|
+
const whatsMySchemaName = function(n, arr=fqSchemaXRef) {
|
|
137
|
+
return arr.reduce((rc, sn) => !rc && n && n.startsWith(sn + '.') ? sn : rc, undefined);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
let xServiceRefs = [];
|
|
141
|
+
const UsedTypes = {};
|
|
142
|
+
function collectUsedType(csn, typeName = (csn.items?.type || csn.type)) {
|
|
143
|
+
if(typeName) {
|
|
144
|
+
if(UsedTypes[typeName])
|
|
145
|
+
UsedTypes[typeName].push(csn)
|
|
146
|
+
else
|
|
147
|
+
UsedTypes[typeName] = [ csn ];
|
|
148
|
+
}
|
|
136
149
|
}
|
|
137
150
|
|
|
138
151
|
// create schema containers
|
|
@@ -175,20 +188,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
175
188
|
|
|
176
189
|
// Fill the schemas and references, fqSchemaXRef must be complete
|
|
177
190
|
populateSchemas(subSchemaDictionary);
|
|
178
|
-
|
|
179
|
-
/* TODO:
|
|
180
|
-
const references = Object.entries(allSchemas).reduce((references, [fqName, art]) => {
|
|
181
|
-
// add references
|
|
182
|
-
if(fqName.startsWith(serviceCsn.name + '.') && art.kind === 'reference') {
|
|
183
|
-
fqSchemaXRef.push(fqName);
|
|
184
|
-
references.push(art);
|
|
185
|
-
}
|
|
186
|
-
return references;
|
|
187
|
-
}, []);
|
|
188
|
-
*/
|
|
189
|
-
// Add xrefs to full schema cross ref list for further usage
|
|
190
|
-
fqSchemaXRef.push(...xServiceRefs);
|
|
191
|
-
fqSchemaXRef.sort((a,b) => b.length-a.length);
|
|
191
|
+
xServiceRefs = populateXserviceRefs();
|
|
192
192
|
|
|
193
193
|
// Bring the schemas in alphabetical order, service first, root last
|
|
194
194
|
const sortedSchemaNames = Object.keys(subSchemaDictionary).filter(n => n !== fallBackSchemaName && n !== serviceCsn.name).sort();
|
|
@@ -223,13 +223,52 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
223
223
|
service._children.forEach(c => {
|
|
224
224
|
c._ec && Object.entries(c._ec._registry).forEach((([setName, arr]) => {
|
|
225
225
|
if(arr.length > 1) {
|
|
226
|
-
error(null, null, {
|
|
227
|
-
|
|
226
|
+
error(null, null, {
|
|
227
|
+
name: c._edmAttributes.Namespace,
|
|
228
|
+
id: setName,
|
|
229
|
+
names: arr.map(a => a.getDuplicateMessage())
|
|
230
|
+
}, 'Namespace $(NAME): Duplicate entries in EntityContainer with Name=$(ID) for $(NAMES)');
|
|
228
231
|
}
|
|
229
232
|
}));
|
|
230
233
|
});
|
|
231
234
|
// Create annotations and distribute into Schemas
|
|
232
235
|
addAnnotations();
|
|
236
|
+
|
|
237
|
+
// type cross check
|
|
238
|
+
const xServiceRefNames = xServiceRefs.map(r => r.name).sort((a,b)=>b.length-a.length);
|
|
239
|
+
for(let typeName in UsedTypes) {
|
|
240
|
+
if(!isBuiltinType(typeName)) {
|
|
241
|
+
let iTypeName = typeName;
|
|
242
|
+
/*
|
|
243
|
+
Report type ref, if the type is
|
|
244
|
+
- not a builtin,
|
|
245
|
+
- not included in required definitions (for this service)
|
|
246
|
+
- not a type clash (reported in type exposure),
|
|
247
|
+
- a @cds.external service member but can't be rendered
|
|
248
|
+
- and not a cross referenced type
|
|
249
|
+
*/
|
|
250
|
+
if(!typeName.startsWith(serviceCsn.name + '.'))
|
|
251
|
+
iTypeName = serviceCsn.name + '.' + typeName;
|
|
252
|
+
const def = reqDefs.definitions[iTypeName];
|
|
253
|
+
|
|
254
|
+
const usages = UsedTypes[typeName].filter(u => !u.$NameClashReported);
|
|
255
|
+
if(usages.length > 0) {
|
|
256
|
+
if(def && !def.$isRendered && def['@cds.external'])
|
|
257
|
+
message('odata-spec-violation-type', usages[0].$location,
|
|
258
|
+
{
|
|
259
|
+
type: typeName,
|
|
260
|
+
anno: '@cds.external',
|
|
261
|
+
name: serviceCsn.name,
|
|
262
|
+
code: def.elements ? 'Edm.ComplexType' : 'Edm.TypeDefinition',
|
|
263
|
+
version: options.isV4() ? '4.0' : '2.0',
|
|
264
|
+
'#': 'external' } );
|
|
265
|
+
else if(!def && !whatsMySchemaName(typeName, xServiceRefNames))
|
|
266
|
+
message('odata-spec-violation-type', usages[0].$location,
|
|
267
|
+
{ type: typeName, name: serviceCsn.name, '#': 'missing' } );
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
233
272
|
return edm
|
|
234
273
|
|
|
235
274
|
// Sort definitions into their schema container
|
|
@@ -290,7 +329,6 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
290
329
|
return Object.entries(allSchemas).reduce((references, [fqName, art]) => {
|
|
291
330
|
// add references
|
|
292
331
|
if(art.kind === 'reference' && whatsMySchemaName(fqName) && serviceCsn.name === whatsMyServiceRootName(fqName, false)) {
|
|
293
|
-
fqSchemaXRef.push(fqName);
|
|
294
332
|
references.push(art);
|
|
295
333
|
}
|
|
296
334
|
return references;
|
|
@@ -331,17 +369,17 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
331
369
|
*/
|
|
332
370
|
edmUtils.foreach(schemaCsn.definitions,
|
|
333
371
|
a => a.kind === 'entity' && !a.abstract && a.name.startsWith(schemaNamePrefix),
|
|
334
|
-
createEntityTypeAndSet
|
|
372
|
+
[createEntityTypeAndSet, markRendered]
|
|
335
373
|
);
|
|
336
374
|
// create unbound actions/functions
|
|
337
|
-
edmUtils.foreach(schemaCsn.definitions,
|
|
375
|
+
edmUtils.foreach(schemaCsn.definitions,
|
|
338
376
|
a => (a.kind === 'action' || a.kind === 'function') && a.name.startsWith(schemaNamePrefix),
|
|
339
|
-
(options.isV4()) ? createActionV4 : createActionV2);
|
|
377
|
+
[(options.isV4()) ? createActionV4 : createActionV2, markRendered]);
|
|
340
378
|
|
|
341
379
|
// create the complex types
|
|
342
380
|
edmUtils.foreach(schemaCsn.definitions,
|
|
343
381
|
a => edmUtils.isStructuredType(a) && a.name.startsWith(schemaNamePrefix),
|
|
344
|
-
createComplexType);
|
|
382
|
+
[createComplexType, markRendered]);
|
|
345
383
|
|
|
346
384
|
if(options.isV4())
|
|
347
385
|
{
|
|
@@ -349,10 +387,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
349
387
|
artifact => edmUtils.isDerivedType(artifact) &&
|
|
350
388
|
!artifact.target &&
|
|
351
389
|
artifact.name.startsWith(schemaNamePrefix),
|
|
352
|
-
|
|
390
|
+
[createTypeDefinitionV4, markRendered]);
|
|
353
391
|
}
|
|
354
392
|
|
|
355
|
-
// fetch all
|
|
393
|
+
// fetch all existing child names in a map
|
|
356
394
|
const NamesInSchemaXRef = Schema._children.reduce((acc, cur) => {
|
|
357
395
|
const name = cur._edmAttributes.Name;
|
|
358
396
|
if(acc[name] === undefined) {
|
|
@@ -370,15 +408,12 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
370
408
|
np.addReferentialConstraintNodes();
|
|
371
409
|
}
|
|
372
410
|
else {
|
|
373
|
-
|
|
411
|
+
addAssociationV2(np);
|
|
374
412
|
}
|
|
375
413
|
});
|
|
376
414
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
Schema._children.splice(Schema._children.indexOf(Schema._ec), 1);
|
|
380
|
-
}
|
|
381
|
-
if(Schema._children.length === 0) {
|
|
415
|
+
if(Schema._children.length === 0 ||
|
|
416
|
+
(Schema._children.length === 1 && Schema._children[0].kind === 'EntityContainer')) {
|
|
382
417
|
// FIXME: Location for sub schemas?
|
|
383
418
|
warning(null, ['definitions', Schema._edmAttributes.Namespace], { name: Schema._edmAttributes.Namespace }, 'Schema $(NAME) is empty');
|
|
384
419
|
}
|
|
@@ -468,6 +503,134 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
468
503
|
});
|
|
469
504
|
}
|
|
470
505
|
|
|
506
|
+
function createComplexType(structuredTypeCsn)
|
|
507
|
+
{
|
|
508
|
+
// V4 attributes: Name, BaseType, Abstract, OpenType
|
|
509
|
+
const attributes = { Name: structuredTypeCsn.name.replace(schemaNamePrefix, '') };
|
|
510
|
+
|
|
511
|
+
const complexType = new Edm.ComplexType(v, attributes, structuredTypeCsn);
|
|
512
|
+
const elementsCsn = structuredTypeCsn.items || structuredTypeCsn;
|
|
513
|
+
const properties = createProperties(elementsCsn, structuredTypeCsn)[0];
|
|
514
|
+
const loc = ['definitions', structuredTypeCsn.name];
|
|
515
|
+
|
|
516
|
+
if(properties.length === 0) {
|
|
517
|
+
warning(null, loc, { name: structuredTypeCsn.name },
|
|
518
|
+
'EDM ComplexType $(NAME) has no properties');
|
|
519
|
+
}
|
|
520
|
+
if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
|
|
521
|
+
message('odata-spec-violation-id', loc, { id: attributes.Name });
|
|
522
|
+
|
|
523
|
+
properties.forEach(p => {
|
|
524
|
+
const pLoc = [ ...loc, ...(structuredTypeCsn.items ? ['items', 'elements'] : [ 'elements' ]), p._edmAttributes.Name ];
|
|
525
|
+
edmTypeCompatibilityCheck(p, pLoc);
|
|
526
|
+
if(p._edmAttributes.Name === complexType._edmAttributes.Name)
|
|
527
|
+
message('odata-spec-violation-property-name', pLoc, { kind: structuredTypeCsn.kind });
|
|
528
|
+
|
|
529
|
+
if(!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name))
|
|
530
|
+
message('odata-spec-violation-id', pLoc, { id: p._edmAttributes.Name });
|
|
531
|
+
|
|
532
|
+
if(options.isV2()) {
|
|
533
|
+
if(p._isCollection && !p._csn.target)
|
|
534
|
+
message('odata-spec-violation-array', pLoc, { version: '2.0' });
|
|
535
|
+
|
|
536
|
+
if(p._csn.target)
|
|
537
|
+
message('odata-spec-violation-assoc', pLoc, { version: '2.0' });
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
complexType.append(...(properties));
|
|
543
|
+
|
|
544
|
+
Schema.append(complexType);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* @param {object} elementsCsn
|
|
549
|
+
* @param {object} edmParentCsn
|
|
550
|
+
* @returns {[object[], any]} Returns a [ [ Edm Properties ], boolean hasStream ]:
|
|
551
|
+
* array of Edm Properties
|
|
552
|
+
* hasStream : value of @Core.MediaType assignment
|
|
553
|
+
*/
|
|
554
|
+
function createProperties(elementsCsn, edmParentCsn=elementsCsn)
|
|
555
|
+
{
|
|
556
|
+
const props = [];
|
|
557
|
+
let hasStream = false;
|
|
558
|
+
const streamProps = [];
|
|
559
|
+
|
|
560
|
+
elementsCsn.elements && Object.entries(elementsCsn.elements).forEach(([elementName, elementCsn]) =>
|
|
561
|
+
{
|
|
562
|
+
if(elementCsn._edmParentCsn == undefined)
|
|
563
|
+
setProp(elementCsn, '_edmParentCsn', edmParentCsn);
|
|
564
|
+
|
|
565
|
+
if(elementCsn.target) {
|
|
566
|
+
// Foreign keys are part of the generic elementCsn.elements property creation
|
|
567
|
+
|
|
568
|
+
// This is the V4 edmx:NavigationProperty
|
|
569
|
+
// gets rewritten for V2 in addAssociations()
|
|
570
|
+
|
|
571
|
+
// suppress navprop creation only if @odata.navigable:false is not annotated.
|
|
572
|
+
// (undefined !== false) still evaluates to true
|
|
573
|
+
if (!elementCsn._target.abstract && elementCsn['@odata.navigable'] !== false)
|
|
574
|
+
{
|
|
575
|
+
const navProp = new Edm.NavigationProperty(v, {
|
|
576
|
+
Name: elementName,
|
|
577
|
+
Type: elementCsn._target.name
|
|
578
|
+
}, elementCsn);
|
|
579
|
+
collectUsedType(elementCsn, elementCsn._target.name);
|
|
580
|
+
props.push(navProp);
|
|
581
|
+
// save the navProp in the global array for late constraint building
|
|
582
|
+
navigationProperties.push(navProp);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
// render ordinary property if element is NOT ...
|
|
586
|
+
// 1) ... annotated @cds.api.ignore
|
|
587
|
+
// 2) ... annotated @odata.foreignKey4 and odataFormat: structured
|
|
588
|
+
|
|
589
|
+
else if(isEdmPropertyRendered(elementCsn, options))
|
|
590
|
+
{
|
|
591
|
+
// CDXCORE-CDXCORE-173
|
|
592
|
+
// V2: filter @Core.MediaType
|
|
593
|
+
if ( options.isV2() && elementCsn['@Core.MediaType']) {
|
|
594
|
+
hasStream = elementCsn['@Core.MediaType'];
|
|
595
|
+
delete elementCsn['@Core.MediaType'];
|
|
596
|
+
// CDXCORE-CDXCORE-177:
|
|
597
|
+
// V2: don't render element but add attribute 'm:HasStream="true' to EntityType
|
|
598
|
+
// V4: render property type 'Edm.Stream'
|
|
599
|
+
streamProps.push(elementName);
|
|
600
|
+
|
|
601
|
+
} else {
|
|
602
|
+
collectUsedType(elementCsn);
|
|
603
|
+
props.push(new Edm.Property(v, { Name: elementName }, elementCsn));
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
});
|
|
608
|
+
if(options.isV2()) {
|
|
609
|
+
if(streamProps.length > 1) { // TODO: why not mention 2.0 in text?
|
|
610
|
+
error(null, ['definitions', elementsCsn.name], { names: streamProps, version: '2.0', anno: '@Core.MediaType' },
|
|
611
|
+
'Expected only one element to be annotated with $(ANNO) for OData $(VERSION) but found $(NAMES)');
|
|
612
|
+
}
|
|
613
|
+
else if(streamProps.length === 1) {
|
|
614
|
+
info(null, ['definitions', elementsCsn.name], { id: streamProps[0], version: '2.0', anno: '@Core.MediaType' },
|
|
615
|
+
'Property $(ID) annotated with $(ANNO) is removed from EDM for OData $(VERSION)');
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return [ props, hasStream ];
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// V4 <TypeDefintion>
|
|
622
|
+
function createTypeDefinitionV4(typeCsn)
|
|
623
|
+
{
|
|
624
|
+
// derived types are already resolved to base types
|
|
625
|
+
const attributes = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
|
|
626
|
+
if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
|
|
627
|
+
message('odata-spec-violation-id', ['definitions', typeCsn.name], { id: attributes.Name });
|
|
628
|
+
|
|
629
|
+
const typeDef = new Edm.TypeDefinition(v, attributes, typeCsn );
|
|
630
|
+
edmTypeCompatibilityCheck(typeDef, [ 'definitions', typeCsn.name ]);
|
|
631
|
+
Schema.append(typeDef);
|
|
632
|
+
}
|
|
633
|
+
|
|
471
634
|
// add bound/unbound actions/functions for V4
|
|
472
635
|
function createActionV4(actionCsn, _name, entityCsn=undefined)
|
|
473
636
|
{
|
|
@@ -528,7 +691,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
528
691
|
const pLoc = [ ...loc, 'params', p._edmAttributes.Name ];
|
|
529
692
|
if(!edmUtils.isODataSimpleIdentifier(parameterName))
|
|
530
693
|
message('odata-spec-violation-id', pLoc, { id: parameterName });
|
|
531
|
-
|
|
694
|
+
collectUsedType(parameterCsn);
|
|
532
695
|
edmTypeCompatibilityCheck(p, pLoc);
|
|
533
696
|
actionNode.append(p);
|
|
534
697
|
});
|
|
@@ -536,6 +699,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
536
699
|
// return type if any
|
|
537
700
|
if(actionCsn.returns) {
|
|
538
701
|
actionNode._returnType = new Edm.ReturnType(v, actionCsn.returns);
|
|
702
|
+
collectUsedType(actionCsn.returns);
|
|
539
703
|
edmTypeCompatibilityCheck(actionNode._returnType, [ ...loc, 'returns' ]);
|
|
540
704
|
// if binding type matches return type add attribute EntitySetPath
|
|
541
705
|
if(entityCsn != undefined && fullQualified(entityCsn.name) === actionNode._returnType._type) {
|
|
@@ -617,6 +781,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
617
781
|
actionCsn.params && Object.entries(actionCsn.params).forEach(([parameterName, parameterCsn]) => {
|
|
618
782
|
const pLoc = [...loc, 'params', parameterName];
|
|
619
783
|
const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
|
|
784
|
+
collectUsedType(parameterCsn);
|
|
620
785
|
edmTypeCompatibilityCheck(param, pLoc);
|
|
621
786
|
if(!edmUtils.isODataSimpleIdentifier(parameterName))
|
|
622
787
|
message('odata-spec-violation-id', pLoc, { id: parameterName });
|
|
@@ -644,6 +809,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
644
809
|
const returns = action.returns.items || action.returns;
|
|
645
810
|
let type = returns.type;
|
|
646
811
|
if (type) {
|
|
812
|
+
collectUsedType(action.returns);
|
|
647
813
|
if (!isBuiltinType(type) && csn.definitions[type].kind !== 'entity' && csn.definitions[type].kind !== 'type') {
|
|
648
814
|
message('odata-spec-violation-returns', returnsLoc, { kind: action.kind, version: '2.0' });
|
|
649
815
|
}
|
|
@@ -671,147 +837,21 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
671
837
|
}
|
|
672
838
|
}
|
|
673
839
|
|
|
674
|
-
/**
|
|
675
|
-
* @param {object} elementsCsn
|
|
676
|
-
* @param {object} edmParentCsn
|
|
677
|
-
* @returns {[object[], any]} Returns a [ [ Edm Properties ], boolean hasStream ]:
|
|
678
|
-
* array of Edm Properties
|
|
679
|
-
* hasStream : value of @Core.MediaType assignment
|
|
680
|
-
*/
|
|
681
|
-
function createProperties(elementsCsn, edmParentCsn=elementsCsn)
|
|
682
|
-
{
|
|
683
|
-
const props = [];
|
|
684
|
-
let hasStream = false;
|
|
685
|
-
const streamProps = [];
|
|
686
|
-
|
|
687
|
-
elementsCsn.elements && Object.entries(elementsCsn.elements).forEach(([elementName, elementCsn]) =>
|
|
688
|
-
{
|
|
689
|
-
if(elementCsn._edmParentCsn == undefined)
|
|
690
|
-
setProp(elementCsn, '_edmParentCsn', edmParentCsn);
|
|
691
|
-
|
|
692
|
-
if(elementCsn.target) {
|
|
693
|
-
// Foreign keys are part of the generic elementCsn.elements property creation
|
|
694
|
-
|
|
695
|
-
// This is the V4 edmx:NavigationProperty
|
|
696
|
-
// gets rewritten for V2 in addAssociations()
|
|
697
|
-
|
|
698
|
-
// suppress navprop creation only if @odata.navigable:false is not annotated.
|
|
699
|
-
// (undefined !== false) still evaluates to true
|
|
700
|
-
if (!elementCsn._target.abstract && elementCsn['@odata.navigable'] !== false)
|
|
701
|
-
{
|
|
702
|
-
const navProp = new Edm.NavigationProperty(v, {
|
|
703
|
-
Name: elementName,
|
|
704
|
-
Type: elementCsn._target.name
|
|
705
|
-
}, elementCsn);
|
|
706
|
-
props.push(navProp);
|
|
707
|
-
// save the navProp in the global array for late constraint building
|
|
708
|
-
navigationProperties.push(navProp);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
// render ordinary property if element is NOT ...
|
|
712
|
-
// 1) ... annotated @cds.api.ignore
|
|
713
|
-
// 2) ... annotated @odata.foreignKey4 and odataFormat: structured
|
|
714
|
-
|
|
715
|
-
else if(isEdmPropertyRendered(elementCsn, options))
|
|
716
|
-
{
|
|
717
|
-
// CDXCORE-CDXCORE-173
|
|
718
|
-
// V2: filter @Core.MediaType
|
|
719
|
-
if ( options.isV2() && elementCsn['@Core.MediaType']) {
|
|
720
|
-
hasStream = elementCsn['@Core.MediaType'];
|
|
721
|
-
delete elementCsn['@Core.MediaType'];
|
|
722
|
-
// CDXCORE-CDXCORE-177:
|
|
723
|
-
// V2: don't render element but add attribute 'm:HasStream="true' to EntityType
|
|
724
|
-
// V4: render property type 'Edm.Stream'
|
|
725
|
-
streamProps.push(elementName);
|
|
726
|
-
|
|
727
|
-
} else {
|
|
728
|
-
props.push(new Edm.Property(v, { Name: elementName }, elementCsn));
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
});
|
|
733
|
-
if(options.isV2()) {
|
|
734
|
-
if(streamProps.length > 1) { // TODO: why not mention 2.0 in text?
|
|
735
|
-
error(null, ['definitions', elementsCsn.name], { names: streamProps, version: '2.0', anno: '@Core.MediaType' },
|
|
736
|
-
`Expected only one element to be annotated with $(ANNO) for OData $(VERSION) but found $(NAMES)`);
|
|
737
|
-
}
|
|
738
|
-
else if(streamProps.length === 1) {
|
|
739
|
-
info(null, ['definitions', elementsCsn.name], { id: streamProps[0], version: '2.0', anno: '@Core.MediaType' },
|
|
740
|
-
'Property $(ID) annotated with $(ANNO) is removed from EDM for OData $(VERSION)');
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
return [ props, hasStream ];
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
function createComplexType(structuredTypeCsn)
|
|
747
|
-
{
|
|
748
|
-
// V4 attributes: Name, BaseType, Abstract, OpenType
|
|
749
|
-
const attributes = { Name: structuredTypeCsn.name.replace(schemaNamePrefix, '') };
|
|
750
|
-
|
|
751
|
-
const complexType = new Edm.ComplexType(v, attributes, structuredTypeCsn);
|
|
752
|
-
const elementsCsn = structuredTypeCsn.items || structuredTypeCsn;
|
|
753
|
-
const properties = createProperties(elementsCsn, structuredTypeCsn)[0];
|
|
754
|
-
const loc = ['definitions', structuredTypeCsn.name];
|
|
755
|
-
|
|
756
|
-
if(properties.length === 0) {
|
|
757
|
-
warning(null, loc, { name: structuredTypeCsn.name },
|
|
758
|
-
'EDM ComplexType $(NAME) has no properties');
|
|
759
|
-
}
|
|
760
|
-
if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
|
|
761
|
-
message('odata-spec-violation-id', loc, { id: attributes.Name });
|
|
762
|
-
|
|
763
|
-
properties.forEach(p => {
|
|
764
|
-
const pLoc = [ ...loc, ...(structuredTypeCsn.items ? ['items', 'elements'] : [ 'elements' ]), p._edmAttributes.Name ];
|
|
765
|
-
edmTypeCompatibilityCheck(p, pLoc);
|
|
766
|
-
if(p._edmAttributes.Name === complexType._edmAttributes.Name)
|
|
767
|
-
message('odata-spec-violation-property-name', pLoc, { kind: structuredTypeCsn.kind });
|
|
768
|
-
|
|
769
|
-
if(!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name))
|
|
770
|
-
message('odata-spec-violation-id', pLoc, { id: p._edmAttributes.Name });
|
|
771
|
-
|
|
772
|
-
if(options.isV2()) {
|
|
773
|
-
if(p._isCollection && !p._csn.target)
|
|
774
|
-
message('odata-spec-violation-array', pLoc, { version: '2.0' });
|
|
775
|
-
|
|
776
|
-
if(p._csn.target)
|
|
777
|
-
message('odata-spec-violation-assoc', pLoc, { version: '2.0' });
|
|
778
|
-
}
|
|
779
|
-
});
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
complexType.append(...(properties));
|
|
783
|
-
|
|
784
|
-
Schema.append(complexType);
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
// V4 <TypeDefintion>
|
|
788
|
-
function createTypeDefinition(typeCsn)
|
|
789
|
-
{
|
|
790
|
-
// derived types are already resolved to base types
|
|
791
|
-
const attributes = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
|
|
792
|
-
if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
|
|
793
|
-
message('odata-spec-violation-id', ['definitions', typeCsn.name], { id: attributes.Name });
|
|
794
|
-
|
|
795
|
-
const typeDef = new Edm.TypeDefinition(v, attributes, typeCsn );
|
|
796
|
-
edmTypeCompatibilityCheck(typeDef, [ 'definitions', typeCsn.name ]);
|
|
797
|
-
Schema.append(typeDef);
|
|
798
|
-
}
|
|
799
|
-
|
|
800
840
|
/*
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
841
|
+
addAssociation() constructs a V2 association.
|
|
842
|
+
In V4 all this has been simplified very much, the only thing actually left over is
|
|
843
|
+
<ReferentialConstriant> that is then a sub element to <NavigationProperty>.
|
|
844
|
+
However, referential constraints are substantially different to its V2 counterpart,
|
|
845
|
+
so it is better to reimplement proper V4 construction of<NavigationProperty> in a separate
|
|
846
|
+
function.
|
|
847
|
+
|
|
848
|
+
This method does:
|
|
849
|
+
rewrite <NavigationProperty> attributes to be V2 compliant
|
|
850
|
+
add <Association> elements to the schema
|
|
851
|
+
add <End>, <ReferentialConstraint>, <Dependent> and <Principal> sub elements to <Association>
|
|
852
|
+
add <AssociationSet> to the EntityContainer for each <Association>
|
|
813
853
|
*/
|
|
814
|
-
function
|
|
854
|
+
function addAssociationV2(navigationProperty)
|
|
815
855
|
{
|
|
816
856
|
let constraints = navigationProperty._csn._constraints;
|
|
817
857
|
let parentName = navigationProperty._csn._edmParentCsn.name.replace(schemaNamePrefix, '');
|
|
@@ -109,7 +109,7 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
|
|
|
109
109
|
if(!Array.isArray(CommonAttributes)) {
|
|
110
110
|
error(null, ['definitions', struct.name, 'elements', element.name],
|
|
111
111
|
{ anno: '@Common.attribute', code: JSON.stringify(CommonAttributes) },
|
|
112
|
-
|
|
112
|
+
'Expect array value for $(ANNOTATION): $(CODE)');
|
|
113
113
|
return;
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -125,7 +125,7 @@ function applyAppSpecificLateCsnTransformationOnElement(options, element, struct
|
|
|
125
125
|
if(!Array.isArray(ContextDefiningProperties)) {
|
|
126
126
|
error(null, ['definitions', struct.name, 'elements', element.name],
|
|
127
127
|
{ anno: '@Aggregation.ContextDefiningProperties', code: JSON.stringify(ContextDefiningProperties) },
|
|
128
|
-
|
|
128
|
+
'Expect array value for $(ANNOTATION): $(CODE)');
|
|
129
129
|
return;
|
|
130
130
|
}
|
|
131
131
|
if(ContextDefiningProperties.length > 0)
|
|
@@ -198,7 +198,7 @@ function applyAppSpecificLateCsnTransformationOnStructure(options, struct, error
|
|
|
198
198
|
error(null, ['definitions', struct.name ],
|
|
199
199
|
{ anno: '@Capabilities.FilterRestrictions.FilterExpressionRestrictions',
|
|
200
200
|
code: JSON.stringify(filterRestrictions) },
|
|
201
|
-
|
|
201
|
+
'Expect array value for $(ANNOTATION): $(CODE)');
|
|
202
202
|
return;
|
|
203
203
|
}
|
|
204
204
|
filterRestrictions.forEach(v => {
|
|
@@ -215,7 +215,7 @@ function applyAppSpecificLateCsnTransformationOnStructure(options, struct, error
|
|
|
215
215
|
error(null, ['definitions', struct.name],
|
|
216
216
|
{ anno: '@Capabilities.FilterRestrictions.RequiredProperties',
|
|
217
217
|
code: JSON.stringify(requiredProperties) },
|
|
218
|
-
|
|
218
|
+
'Expect array value for $(ANNOTATION): $(CODE)');
|
|
219
219
|
return;
|
|
220
220
|
}
|
|
221
221
|
|
|
@@ -346,4 +346,4 @@ module.exports = {
|
|
|
346
346
|
setSAPSpecificV2AnnotationsToEntityContainer,
|
|
347
347
|
setSAPSpecificV2AnnotationsToEntitySet,
|
|
348
348
|
setSAPSpecificV2AnnotationsToAssociation
|
|
349
|
-
};
|
|
349
|
+
};
|
|
@@ -24,11 +24,11 @@ function resolveForeignKeyRefs(csn) {
|
|
|
24
24
|
function inboundQualificationChecks(csn, options, messageFunctions,
|
|
25
25
|
serviceRootNames, requestedServiceNames, isMyServiceRequested, whatsMyServiceRootName) {
|
|
26
26
|
const csnUtils = getUtils(csn);
|
|
27
|
-
const { message,
|
|
27
|
+
const { message, throwWithError } = messageFunctions;
|
|
28
28
|
|
|
29
29
|
forEachDefinition(csn, [ attach$path, checkChainedArray ]);
|
|
30
30
|
checkNestedContextsAndServices();
|
|
31
|
-
|
|
31
|
+
throwWithError();
|
|
32
32
|
|
|
33
33
|
// attach $path to all
|
|
34
34
|
function attach$path(def, defName) {
|
|
@@ -264,7 +264,7 @@ function initializeModel(csn, _options, messageFunctions, requestedServiceNames=
|
|
|
264
264
|
const art = csn.definitions[newDefName];
|
|
265
265
|
if(art !== undefined) {
|
|
266
266
|
error(null, [ 'definitions', defName ], { name: newDefName },
|
|
267
|
-
|
|
267
|
+
'Artifact name containing dots can\'t be mapped to an OData compliant name because it conflicts with existing definition $(NAME)');
|
|
268
268
|
}
|
|
269
269
|
else {
|
|
270
270
|
csn.definitions[newDefName] = def;
|
package/lib/edm/edmUtils.js
CHANGED
|
@@ -205,7 +205,7 @@ function resolveOnConditionAndPrepareConstraints(csn, assocCsn, messageFunctions
|
|
|
205
205
|
function fillConstraints(arg, pos)
|
|
206
206
|
{
|
|
207
207
|
if(arg.xpr)
|
|
208
|
-
arg.xpr
|
|
208
|
+
getExpressionArguments(arg.xpr);
|
|
209
209
|
else if(pos > 0 && pos < expr.length)
|
|
210
210
|
{
|
|
211
211
|
let lhs = expr[pos-1];
|
|
@@ -485,7 +485,7 @@ function mapCdsToEdmType(csn, messageFunctions, isV2=false, isMediaType=false, l
|
|
|
485
485
|
const { error } = messageFunctions || { error: ()=>true };
|
|
486
486
|
let cdsType = csn.type;
|
|
487
487
|
if(cdsType === undefined) {
|
|
488
|
-
error(null, location,
|
|
488
|
+
error(null, location, 'no type found');
|
|
489
489
|
return '<NOTYPE>';
|
|
490
490
|
}
|
|
491
491
|
if(!isBuiltinType(cdsType))
|
|
@@ -544,7 +544,7 @@ function mapCdsToEdmType(csn, messageFunctions, isV2=false, isMediaType=false, l
|
|
|
544
544
|
*/
|
|
545
545
|
}[cdsType];
|
|
546
546
|
if (edmType == undefined) {
|
|
547
|
-
error(null, location, { type: cdsType },
|
|
547
|
+
error(null, location, { type: cdsType }, 'No EDM type available for $(TYPE)');
|
|
548
548
|
}
|
|
549
549
|
if(isV2)
|
|
550
550
|
{
|