@sap/cds-compiler 2.12.0 → 2.15.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 +221 -15
- package/bin/cdsc.js +125 -50
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +47 -84
- package/lib/api/options.js +5 -6
- package/lib/api/validate.js +6 -11
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +114 -18
- package/lib/base/messages.js +101 -90
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +177 -123
- package/lib/checks/annotationsOData.js +12 -33
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +6 -11
- package/lib/compiler/assert-consistency.js +6 -3
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +19 -6
- package/lib/compiler/checks.js +23 -60
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1151 -0
- package/lib/compiler/extend.js +1000 -0
- package/lib/compiler/finalize-parse-cdl.js +237 -0
- package/lib/compiler/index.js +107 -39
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1227 -0
- package/lib/compiler/propagator.js +114 -46
- package/lib/compiler/resolve.js +1521 -0
- package/lib/compiler/shared.js +126 -65
- package/lib/compiler/tweak-assocs.js +535 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -24
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +219 -100
- package/lib/edm/edm.js +302 -230
- package/lib/edm/edmPreprocessor.js +554 -419
- package/lib/edm/edmUtils.js +138 -44
- package/lib/gen/Dictionary.json +100 -19
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5765 -4480
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +15 -3
- package/lib/json/to-csn.js +126 -68
- package/lib/language/docCommentParser.js +4 -4
- package/lib/language/genericAntlrParser.js +123 -5
- package/lib/language/language.g4 +355 -156
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +486 -59
- package/lib/main.js +41 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +252 -156
- package/lib/model/csnUtils.js +384 -297
- package/lib/model/enrichCsn.js +71 -29
- package/lib/model/revealInternalProperties.js +29 -8
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +23 -18
- package/lib/optionProcessor.js +63 -26
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +897 -947
- package/lib/render/toHdbcds.js +205 -257
- package/lib/render/toSql.js +264 -225
- package/lib/render/utils/common.js +136 -25
- package/lib/render/utils/sql.js +4 -3
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/db/.eslintrc.json +3 -1
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +104 -306
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +58 -53
- package/lib/transform/db/expansion.js +60 -33
- package/lib/transform/db/flattening.js +582 -104
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +66 -13
- package/lib/transform/db/views.js +11 -7
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +109 -208
- package/lib/transform/forOdataNew.js +59 -212
- package/lib/transform/localized.js +46 -26
- package/lib/transform/odata/toFinalBaseType.js +85 -11
- package/lib/transform/odata/typesExposure.js +147 -199
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +44 -33
- package/lib/transform/translateAssocsToJoins.js +3 -20
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +172 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -290
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
- package/lib/transform/universalCsnEnricher.js +0 -237
package/lib/edm/csn2edm.js
CHANGED
|
@@ -7,13 +7,14 @@ const NAVPROP_TRENNER = '_';
|
|
|
7
7
|
const VALUELIST_NAVPROP_PREFIX = '';
|
|
8
8
|
|
|
9
9
|
const edmUtils = require('./edmUtils.js')
|
|
10
|
-
const { initializeModel } = require('./edmPreprocessor.js');
|
|
10
|
+
const { initializeModel, assignAnnotation } = require('./edmPreprocessor.js');
|
|
11
11
|
const translate = require('./annotations/genericTranslation.js');
|
|
12
12
|
const { setProp } = require('../base/model');
|
|
13
|
-
const {
|
|
13
|
+
const { cloneCsnNonDict, isEdmPropertyRendered, isBuiltinType } = require('../model/csnUtils');
|
|
14
14
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
15
15
|
const { makeMessageFunction } = require('../base/messages');
|
|
16
|
-
|
|
16
|
+
const { EdmTypeFacetMap, EdmTypeFacetNames, EdmPrimitiveTypeMap, getEdm } = require('./edm.js');
|
|
17
|
+
|
|
17
18
|
/*
|
|
18
19
|
OData V2 spec 06/01/2017 PDF version is available from here:
|
|
19
20
|
https://msdn.microsoft.com/en-us/library/dd541474.aspx
|
|
@@ -25,11 +26,11 @@ function csn2edm(_csn, serviceName, _options) {
|
|
|
25
26
|
|
|
26
27
|
function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
27
28
|
// get us a fresh model copy that we can work with
|
|
28
|
-
const csn =
|
|
29
|
+
const csn = cloneCsnNonDict(_csn, _options);
|
|
29
30
|
|
|
30
31
|
// use original options for messages; cloned CSN for semantic location
|
|
31
32
|
const messageFunctions = makeMessageFunction(csn, _options, 'to.edmx');
|
|
32
|
-
const { info, warning, error, message,
|
|
33
|
+
const { info, warning, error, message, throwWithAnyError } = messageFunctions;
|
|
33
34
|
checkCSNVersion(csn, _options);
|
|
34
35
|
|
|
35
36
|
let rc = Object.create(null);
|
|
@@ -42,12 +43,14 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
42
43
|
setProp(csn.meta, 'options', _csn.meta.options);
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
const [ allServices,
|
|
46
47
|
allSchemas,
|
|
48
|
+
reqDefs,
|
|
47
49
|
whatsMyServiceRootName,
|
|
48
|
-
|
|
50
|
+
fallBackSchemaName,
|
|
51
|
+
options ] = initializeModel(csn, _options, messageFunctions, serviceNames);
|
|
49
52
|
|
|
50
|
-
const Edm =
|
|
53
|
+
const Edm = getEdm(options, messageFunctions);
|
|
51
54
|
|
|
52
55
|
const v = options.v;
|
|
53
56
|
if(Object.keys(allServices).length === 0) {
|
|
@@ -73,7 +76,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
73
76
|
services[serviceCsn.name] = createEdm(serviceCsn);
|
|
74
77
|
return services; }, rc);
|
|
75
78
|
}
|
|
76
|
-
|
|
79
|
+
throwWithAnyError();
|
|
77
80
|
return rc;
|
|
78
81
|
|
|
79
82
|
//--------------------------------------------------------------------------------
|
|
@@ -188,9 +191,9 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
188
191
|
fqSchemaXRef.sort((a,b) => b.length-a.length);
|
|
189
192
|
|
|
190
193
|
// Bring the schemas in alphabetical order, service first, root last
|
|
191
|
-
const sortedSchemaNames = Object.keys(subSchemaDictionary).filter(n => n !==
|
|
192
|
-
if(subSchemaDictionary
|
|
193
|
-
sortedSchemaNames.push(
|
|
194
|
+
const sortedSchemaNames = Object.keys(subSchemaDictionary).filter(n => n !== fallBackSchemaName && n !== serviceCsn.name).sort();
|
|
195
|
+
if(subSchemaDictionary[fallBackSchemaName])
|
|
196
|
+
sortedSchemaNames.push(fallBackSchemaName);
|
|
194
197
|
|
|
195
198
|
// Finally create the schemas and register them in the service.
|
|
196
199
|
LeadSchema = createSchema(subSchemaDictionary[serviceCsn.name]);
|
|
@@ -220,7 +223,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
220
223
|
service._children.forEach(c => {
|
|
221
224
|
c._ec && Object.entries(c._ec._registry).forEach((([setName, arr]) => {
|
|
222
225
|
if(arr.length > 1) {
|
|
223
|
-
error(null, null, { name: c.Namespace, id: setName },
|
|
226
|
+
error(null, null, { name: c._edmAttributes.Namespace, id: setName },
|
|
224
227
|
`Namespace $(NAME): Duplicate entries in EntityContainer with Name=$(ID) for ${arr.map(a =>a.getDuplicateMessage()).join(', ')} `);
|
|
225
228
|
}
|
|
226
229
|
}));
|
|
@@ -231,10 +234,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
231
234
|
|
|
232
235
|
// Sort definitions into their schema container
|
|
233
236
|
function populateSchemas(schemas) {
|
|
234
|
-
Object.entries(
|
|
237
|
+
Object.entries(reqDefs.definitions).forEach(([fqName, art]) => {
|
|
235
238
|
// Identify service members by their definition name only, this allows
|
|
236
239
|
// to let the internal object.name have the sub-schema name.
|
|
237
|
-
// With nested services we must do a longest path match and check
|
|
240
|
+
// With nested services we must do a longest path match and check whether
|
|
238
241
|
// the current definition belongs to the current toplevel service definition.
|
|
239
242
|
|
|
240
243
|
// Definition is to be considered if
|
|
@@ -246,7 +249,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
246
249
|
// not marked to be ignored as schema member
|
|
247
250
|
if(mySchemaName &&
|
|
248
251
|
serviceCsn.name === whatsMyServiceRootName(fqName, false) &&
|
|
249
|
-
|
|
252
|
+
art.kind !== 'context' && art.kind !== 'service') {
|
|
250
253
|
|
|
251
254
|
// Strip the toplevel serviceName from object.name
|
|
252
255
|
// except if the schema name is the service name itself.
|
|
@@ -300,10 +303,18 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
300
303
|
|
|
301
304
|
// Same check for alias (if supported by us)
|
|
302
305
|
const reservedNames = ['Edm', 'odata', 'System', 'Transient'];
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
+
const loc = ['definitions', schema.name];
|
|
307
|
+
if(reservedNames.includes(schema.name))
|
|
308
|
+
warning('odata-spec-violation-namespace', loc, { names: reservedNames });
|
|
309
|
+
if (schema.name.length > 511)
|
|
310
|
+
warning('odata-spec-violation-namespace', loc, { '#': 'length' });
|
|
311
|
+
else {
|
|
312
|
+
schema.name.split('.').forEach(id => {
|
|
313
|
+
if (!edmUtils.isODataSimpleIdentifier(id))
|
|
314
|
+
message('odata-spec-violation-id', loc, { id });
|
|
315
|
+
});
|
|
306
316
|
}
|
|
317
|
+
|
|
307
318
|
/** @type {any} */
|
|
308
319
|
const Schema = new Edm.Schema(v, schema.name, undefined /* unset alias */, schema._csn, /* annotations */ [], schema.container);
|
|
309
320
|
const EntityContainer = Schema._ec || (LeadSchema && LeadSchema._ec);
|
|
@@ -342,10 +353,11 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
342
353
|
|
|
343
354
|
// fetch all exising children names in a map
|
|
344
355
|
const NamesInSchemaXRef = Schema._children.reduce((acc, cur) => {
|
|
345
|
-
|
|
346
|
-
|
|
356
|
+
const name = cur._edmAttributes.Name;
|
|
357
|
+
if(acc[name] === undefined) {
|
|
358
|
+
acc[name] = [ cur ];
|
|
347
359
|
} else {
|
|
348
|
-
acc[
|
|
360
|
+
acc[name].push(cur);
|
|
349
361
|
}
|
|
350
362
|
return acc;
|
|
351
363
|
}, Object.create(null) );
|
|
@@ -367,12 +379,12 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
367
379
|
}
|
|
368
380
|
if(Schema._children.length === 0) {
|
|
369
381
|
// FIXME: Location for sub schemas?
|
|
370
|
-
warning(null, ['definitions', Schema.Namespace], { name: Schema.Namespace }, 'Schema $(NAME) is empty');
|
|
382
|
+
warning(null, ['definitions', Schema._edmAttributes.Namespace], { name: Schema._edmAttributes.Namespace }, 'Schema $(NAME) is empty');
|
|
371
383
|
}
|
|
372
384
|
|
|
373
385
|
Object.entries(NamesInSchemaXRef).forEach(([name, refs]) => {
|
|
374
386
|
if(refs.length > 1) {
|
|
375
|
-
error(null, ['definitions', `${Schema.Namespace}.${name}`], { name: Schema.Namespace },
|
|
387
|
+
error(null, ['definitions', `${Schema._edmAttributes.Namespace}.${name}`], { name: Schema._edmAttributes.Namespace },
|
|
376
388
|
'Duplicate name in Schema $(NAME)');
|
|
377
389
|
}
|
|
378
390
|
});
|
|
@@ -388,21 +400,29 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
388
400
|
|
|
389
401
|
const loc = ['definitions', entityCsn.name];
|
|
390
402
|
const type = `${schema.name}.${EntityTypeName}`;
|
|
391
|
-
if(properties.length === 0)
|
|
403
|
+
if(properties.length === 0)
|
|
392
404
|
warning(null, loc, { type }, 'EDM EntityType $(TYPE) has no properties');
|
|
393
|
-
|
|
405
|
+
else if(entityCsn.$edmKeyPaths.length === 0)
|
|
394
406
|
message('odata-spec-violation-no-key', loc);
|
|
395
|
-
|
|
407
|
+
|
|
408
|
+
if(!edmUtils.isODataSimpleIdentifier(EntityTypeName))
|
|
409
|
+
message('odata-spec-violation-id', loc, { id: EntityTypeName });
|
|
410
|
+
|
|
396
411
|
properties.forEach(p => {
|
|
397
|
-
const pLoc = [...loc, 'elements', p.Name];
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
if(p.Name === EntityTypeName) {
|
|
412
|
+
const pLoc = [...loc, 'elements', p._edmAttributes.Name];
|
|
413
|
+
edmTypeCompatibilityCheck(p, pLoc);
|
|
414
|
+
if(p._edmAttributes.Name === EntityTypeName)
|
|
402
415
|
warning('odata-spec-violation-property-name', pLoc, { kind: entityCsn.kind });
|
|
403
|
-
|
|
404
|
-
if(options.isV2() && p._isCollection && !edmUtils.isAssociationOrComposition(p._csn))
|
|
405
|
-
warning('odata-spec-violation-array', pLoc, {
|
|
416
|
+
|
|
417
|
+
if(options.isV2() && p._isCollection && !edmUtils.isAssociationOrComposition(p._csn))
|
|
418
|
+
warning('odata-spec-violation-array', pLoc, { version: '2.0' });
|
|
419
|
+
|
|
420
|
+
if(!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name))
|
|
421
|
+
message('odata-spec-violation-id', pLoc, { id: p._edmAttributes.Name });
|
|
422
|
+
else if (options.isV2() && /^(_|[0-9])/.test(p._edmAttributes.Name)) {
|
|
423
|
+
// FIXME: Rewrite signalIllegalIdentifier function to be more flexible
|
|
424
|
+
error('odata-spec-violation-id', pLoc,
|
|
425
|
+
{ prop: p._edmAttributes.Name[0], id: p._edmAttributes.Name, version: '2.0', '#': 'v2firstchar' });
|
|
406
426
|
}
|
|
407
427
|
});
|
|
408
428
|
|
|
@@ -410,8 +430,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
410
430
|
const attributes = { Name : EntityTypeName };
|
|
411
431
|
|
|
412
432
|
// CDXCORE-CDXCORE-173
|
|
413
|
-
if(options.isV2() && hasStream)
|
|
414
|
-
attributes['m:HasStream'] =
|
|
433
|
+
if(options.isV2() && hasStream) {
|
|
434
|
+
attributes['m:HasStream'] = true;
|
|
435
|
+
assignAnnotation(entityCsn, '@Core.MediaType', hasStream);
|
|
436
|
+
}
|
|
415
437
|
|
|
416
438
|
Schema.append(new Edm.EntityType(v, attributes, properties, entityCsn));
|
|
417
439
|
|
|
@@ -423,7 +445,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
423
445
|
if(edmUtils.isSingleton(entityCsn) && options.isV4()) {
|
|
424
446
|
containerEntry = new Edm.Singleton(v, { Name: EntitySetName, Type: fullQualified(EntityTypeName) }, entityCsn);
|
|
425
447
|
if(entityCsn['@odata.singleton.nullable'])
|
|
426
|
-
containerEntry.Nullable= true;
|
|
448
|
+
containerEntry._edmAttributes.Nullable= true;
|
|
427
449
|
}
|
|
428
450
|
else {
|
|
429
451
|
containerEntry = new Edm.EntitySet(v, { Name: EntitySetName, EntityType: fullQualified(EntityTypeName) }, entityCsn);
|
|
@@ -446,14 +468,19 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
446
468
|
}
|
|
447
469
|
|
|
448
470
|
// add bound/unbound actions/functions for V4
|
|
449
|
-
function createActionV4(actionCsn,
|
|
471
|
+
function createActionV4(actionCsn, _name, entityCsn=undefined)
|
|
450
472
|
{
|
|
451
473
|
const iAmAnAction = actionCsn.kind === 'action';
|
|
452
|
-
|
|
453
474
|
const actionName = edmUtils.getBaseName(actionCsn.name);
|
|
454
|
-
|
|
455
475
|
const attributes = { Name: actionName, IsBound : false };
|
|
456
476
|
|
|
477
|
+
const loc = entityCsn
|
|
478
|
+
? [ 'definitions', entityCsn.name, 'actions', actionCsn.name ]
|
|
479
|
+
: [ 'definitions', actionCsn.name ];
|
|
480
|
+
|
|
481
|
+
if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
|
|
482
|
+
message('odata-spec-violation-id', loc, { id: attributes.Name });
|
|
483
|
+
|
|
457
484
|
if(!iAmAnAction)
|
|
458
485
|
attributes.IsComposable = false;
|
|
459
486
|
|
|
@@ -467,7 +494,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
467
494
|
|
|
468
495
|
if(entityCsn != undefined)
|
|
469
496
|
{
|
|
470
|
-
actionNode.IsBound
|
|
497
|
+
actionNode.setEdmAttribute('IsBound', true);
|
|
471
498
|
const bpType = fullQualified(entityCsn.name);
|
|
472
499
|
// Binding Parameter: 'in' at first position in sequence, this is decisive!
|
|
473
500
|
if(actionCsn['@cds.odata.bindingparameter.collection'])
|
|
@@ -488,7 +515,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
488
515
|
const definition = schemaCsn.definitions[rt];
|
|
489
516
|
if(definition && definition.kind === 'entity' && !definition.abstract)
|
|
490
517
|
{
|
|
491
|
-
actionImport.EntitySet
|
|
518
|
+
actionImport.setEdmAttribute('EntitySet', edmUtils.getBaseName(rt));
|
|
492
519
|
}
|
|
493
520
|
}
|
|
494
521
|
EntityContainer.register(actionImport);
|
|
@@ -496,15 +523,22 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
496
523
|
|
|
497
524
|
// Parameter Nodes
|
|
498
525
|
edmUtils.forAll(actionCsn.params, (parameterCsn, parameterName) => {
|
|
499
|
-
|
|
526
|
+
const p = new Edm.Parameter(v, { Name: parameterName }, parameterCsn );
|
|
527
|
+
const pLoc = [ ...loc, 'params', p._edmAttributes.Name ];
|
|
528
|
+
if(!edmUtils.isODataSimpleIdentifier(parameterName))
|
|
529
|
+
message('odata-spec-violation-id', pLoc, { id: parameterName });
|
|
530
|
+
|
|
531
|
+
edmTypeCompatibilityCheck(p, pLoc);
|
|
532
|
+
actionNode.append(p);
|
|
500
533
|
});
|
|
501
534
|
|
|
502
535
|
// return type if any
|
|
503
536
|
if(actionCsn.returns) {
|
|
504
537
|
actionNode._returnType = new Edm.ReturnType(v, actionCsn.returns);
|
|
538
|
+
edmTypeCompatibilityCheck(actionNode._returnType, [ ...loc, 'returns' ]);
|
|
505
539
|
// if binding type matches return type add attribute EntitySetPath
|
|
506
540
|
if(entityCsn != undefined && fullQualified(entityCsn.name) === actionNode._returnType._type) {
|
|
507
|
-
actionNode.EntitySetPath
|
|
541
|
+
actionNode.setEdmAttribute('EntitySetPath', bpName);
|
|
508
542
|
}
|
|
509
543
|
}
|
|
510
544
|
Schema.addAction(actionNode);
|
|
@@ -514,7 +548,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
514
548
|
function createActionV2(actionCsn, name, entityCsn=undefined)
|
|
515
549
|
{
|
|
516
550
|
/** @type {object} */
|
|
517
|
-
const
|
|
551
|
+
const attributes = { Name: name.replace(schemaNamePrefix, '') };
|
|
552
|
+
const functionImport = new Edm.FunctionImport(v, attributes );
|
|
518
553
|
|
|
519
554
|
// inserted now to maintain attribute order with old odata generator...
|
|
520
555
|
/*
|
|
@@ -527,19 +562,25 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
527
562
|
in the spec and advised mention it as in V4
|
|
528
563
|
*/
|
|
529
564
|
|
|
530
|
-
const
|
|
565
|
+
const loc = entityCsn
|
|
566
|
+
? [ 'definitions', entityCsn.name, 'actions', actionCsn.name ]
|
|
567
|
+
: [ 'definitions', actionCsn.name ];
|
|
568
|
+
|
|
569
|
+
if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
|
|
570
|
+
message('odata-spec-violation-id', loc, { id: attributes.Name });
|
|
571
|
+
|
|
531
572
|
const rt = actionCsn.returns && ((actionCsn.returns.items && actionCsn.returns.items.type) || actionCsn.returns.type);
|
|
532
573
|
if(rt) // add EntitySet attribute only if return type is an entity
|
|
533
574
|
{
|
|
534
575
|
const defintion = schemaCsn.definitions[rt];
|
|
535
576
|
if(defintion && edmUtils.isEntity(defintion))
|
|
536
577
|
{
|
|
537
|
-
functionImport.EntitySet
|
|
578
|
+
functionImport.setEdmAttribute('EntitySet', rt.replace(schemaNamePrefix, ''));
|
|
538
579
|
}
|
|
539
580
|
}
|
|
540
581
|
|
|
541
582
|
if(actionCsn.returns)
|
|
542
|
-
functionImport.ReturnType
|
|
583
|
+
functionImport.setEdmAttribute('ReturnType', getReturnType(actionCsn));
|
|
543
584
|
|
|
544
585
|
if(actionCsn.kind === 'function')
|
|
545
586
|
functionImport.setXml( {'m:HttpMethod': 'GET' });
|
|
@@ -550,7 +591,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
550
591
|
{
|
|
551
592
|
// Make bound function names always unique as per Ralf's recommendation
|
|
552
593
|
functionImport.setXml( {'sap:action-for': fullQualified(entityCsn.name) } );
|
|
553
|
-
|
|
594
|
+
const name = entityCsn.name.replace(schemaNamePrefix, '') + '_' + functionImport._edmAttributes.Name;
|
|
595
|
+
functionImport.setEdmAttribute('Name', name);
|
|
554
596
|
|
|
555
597
|
// Binding Parameter: Primary Keys at first position in sequence, this is decisive!
|
|
556
598
|
// V2 XML: Nullable=false is set because we reuse the primary key property for the parameter
|
|
@@ -572,14 +614,22 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
572
614
|
// V2 XML spec does only mention default Nullable=true for Properties not for Parameters so omitting Nullable=true let
|
|
573
615
|
// the client assume that Nullable is false.... Correct Nullable Handling is done inside Parameter constructor
|
|
574
616
|
edmUtils.forAll(actionCsn.params, (parameterCsn, parameterName) => {
|
|
575
|
-
const
|
|
617
|
+
const pLoc = [...loc, 'params', parameterName];
|
|
576
618
|
const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
619
|
+
edmTypeCompatibilityCheck(param, pLoc);
|
|
620
|
+
if(!edmUtils.isODataSimpleIdentifier(parameterName))
|
|
621
|
+
message('odata-spec-violation-id', pLoc, { id: parameterName });
|
|
622
|
+
|
|
623
|
+
// only scalar or structured type in V2 (not entity)
|
|
624
|
+
if(param._type &&
|
|
625
|
+
!param._type.startsWith('Edm.') &&
|
|
626
|
+
csn.definitions[param._type] &&
|
|
627
|
+
!edmUtils.isStructuredType(csn.definitions[param._type]))
|
|
628
|
+
warning('odata-spec-violation-param', pLoc, { version: '2.0' });
|
|
629
|
+
|
|
630
|
+
if(param._isCollection)
|
|
631
|
+
warning('odata-spec-violation-array', pLoc, { version: '2.0' });
|
|
632
|
+
|
|
583
633
|
functionImport.append(param);
|
|
584
634
|
});
|
|
585
635
|
|
|
@@ -589,19 +639,33 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
589
639
|
function getReturnType(action)
|
|
590
640
|
{
|
|
591
641
|
// it is safe to assume that either type or items.type are set
|
|
642
|
+
const returnsLoc = [ ...loc, 'returns'];
|
|
592
643
|
const returns = action.returns.items || action.returns;
|
|
593
644
|
let type = returns.type;
|
|
594
|
-
if(type){
|
|
595
|
-
if(!isBuiltinType(type) &&
|
|
596
|
-
|
|
597
|
-
warning('odata-spec-violation-returns', returnsLoc, { kind: action.kind, api: 'OData V2' });
|
|
645
|
+
if (type) {
|
|
646
|
+
if (!isBuiltinType(type) && csn.definitions[type].kind !== 'entity' && csn.definitions[type].kind !== 'type') {
|
|
647
|
+
warning('odata-spec-violation-returns', returnsLoc, { kind: action.kind, version: '2.0' });
|
|
598
648
|
}
|
|
599
|
-
|
|
649
|
+
else if(isBuiltinType(type)) {
|
|
650
|
+
type = edmUtils.mapCdsToEdmType(returns, messageFunctions, true);
|
|
651
|
+
if(type) {
|
|
652
|
+
const td = EdmPrimitiveTypeMap[type];
|
|
653
|
+
if(td && !td.v2) {
|
|
654
|
+
message('odata-spec-violation-type', returnsLoc,
|
|
655
|
+
{ type, version: '2.0', '#': 'incompatible' });
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
message('odata-spec-violation-type-unknown', returnsLoc, { type });
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
if(action.returns._isCollection)
|
|
663
|
+
type = `Collection(${type})`
|
|
664
|
+
}
|
|
665
|
+
else {
|
|
666
|
+
// type is missing
|
|
667
|
+
message('odata-spec-violation-type', returnsLoc);
|
|
600
668
|
}
|
|
601
|
-
|
|
602
|
-
if(action.returns._isCollection)
|
|
603
|
-
type = `Collection(${type})`
|
|
604
|
-
|
|
605
669
|
return type;
|
|
606
670
|
}
|
|
607
671
|
}
|
|
@@ -609,14 +673,16 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
609
673
|
/**
|
|
610
674
|
* @param {object} elementsCsn
|
|
611
675
|
* @param {object} edmParentCsn
|
|
612
|
-
* @returns {[object[],
|
|
676
|
+
* @returns {[object[], any]} Returns a [ [ Edm Properties ], boolean hasStream ]:
|
|
613
677
|
* array of Edm Properties
|
|
614
|
-
*
|
|
678
|
+
* hasStream : value of @Core.MediaType assignment
|
|
615
679
|
*/
|
|
616
680
|
function createProperties(elementsCsn, edmParentCsn=elementsCsn)
|
|
617
681
|
{
|
|
618
682
|
const props = [];
|
|
619
683
|
let hasStream = false;
|
|
684
|
+
const streamProps = [];
|
|
685
|
+
|
|
620
686
|
edmUtils.forAll(elementsCsn.elements, (elementCsn, elementName) =>
|
|
621
687
|
{
|
|
622
688
|
if(elementCsn._edmParentCsn == undefined)
|
|
@@ -652,12 +718,12 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
652
718
|
// CDXCORE-CDXCORE-173
|
|
653
719
|
// V2: filter @Core.MediaType
|
|
654
720
|
if ( options.isV2() && elementCsn['@Core.MediaType']) {
|
|
721
|
+
hasStream = elementCsn['@Core.MediaType'];
|
|
722
|
+
delete elementCsn['@Core.MediaType'];
|
|
655
723
|
// CDXCORE-CDXCORE-177:
|
|
656
724
|
// V2: don't render element but add attribute 'm:HasStream="true' to EntityType
|
|
657
725
|
// V4: render property type 'Edm.Stream'
|
|
658
|
-
|
|
659
|
-
info(null, ['definitions', elementsCsn.name], { name: elementsCsn.name, id: elementName, anno: '@Core.MediaType' },
|
|
660
|
-
'$(NAME): Property $(ID) annotated with $(ANNO) is removed from EDM in OData V2');
|
|
726
|
+
streamProps.push(elementName);
|
|
661
727
|
|
|
662
728
|
} else {
|
|
663
729
|
props.push(new Edm.Property(v, { Name: elementName }, elementCsn));
|
|
@@ -666,6 +732,16 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
666
732
|
}
|
|
667
733
|
|
|
668
734
|
});
|
|
735
|
+
if(options.isV2()) {
|
|
736
|
+
if(streamProps.length > 1) {
|
|
737
|
+
error(null, ['definitions', elementsCsn.name], { names: streamProps, version: '2.0', anno: '@Core.MediaType' },
|
|
738
|
+
`Expected only one element to be annotated with $(ANNO) for OData $(VERSION) but found $(NAMES)`);
|
|
739
|
+
}
|
|
740
|
+
else if(streamProps.length === 1) {
|
|
741
|
+
info(null, ['definitions', elementsCsn.name], { id: streamProps[0], version: '2.0', anno: '@Core.MediaType' },
|
|
742
|
+
'Property $(ID) annotated with $(ANNO) is removed from EDM for OData $(VERSION)');
|
|
743
|
+
}
|
|
744
|
+
}
|
|
669
745
|
return [ props, hasStream ];
|
|
670
746
|
}
|
|
671
747
|
|
|
@@ -680,24 +756,27 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
680
756
|
const loc = ['definitions', structuredTypeCsn.name];
|
|
681
757
|
|
|
682
758
|
if(properties.length === 0) {
|
|
683
|
-
warning(null,
|
|
759
|
+
warning(null, loc, { name: structuredTypeCsn.name },
|
|
684
760
|
'EDM ComplexType $(NAME) has no properties');
|
|
685
761
|
}
|
|
762
|
+
if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
|
|
763
|
+
message('odata-spec-violation-id', loc, { id: attributes.Name });
|
|
764
|
+
|
|
686
765
|
properties.forEach(p => {
|
|
687
|
-
const pLoc = [ ...loc, ...(structuredTypeCsn.items ? ['items', 'elements'] : [ 'elements' ]), p.Name ];
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
}
|
|
691
|
-
if(p.Name === complexType.Name) {
|
|
766
|
+
const pLoc = [ ...loc, ...(structuredTypeCsn.items ? ['items', 'elements'] : [ 'elements' ]), p._edmAttributes.Name ];
|
|
767
|
+
edmTypeCompatibilityCheck(p, pLoc);
|
|
768
|
+
if(p._edmAttributes.Name === complexType._edmAttributes.Name)
|
|
692
769
|
warning('odata-spec-violation-property-name', pLoc, { kind: structuredTypeCsn.kind });
|
|
693
|
-
|
|
770
|
+
|
|
771
|
+
if(!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name))
|
|
772
|
+
message('odata-spec-violation-id', pLoc, { id: p._edmAttributes.Name });
|
|
773
|
+
|
|
694
774
|
if(options.isV2()) {
|
|
695
|
-
if(p._isCollection && !edmUtils.isAssociationOrComposition(p._csn))
|
|
696
|
-
warning('odata-spec-violation-array', pLoc, {
|
|
697
|
-
|
|
698
|
-
if(edmUtils.isAssociationOrComposition(p._csn))
|
|
699
|
-
warning('odata-spec-violation-assoc', pLoc, {
|
|
700
|
-
}
|
|
775
|
+
if(p._isCollection && !edmUtils.isAssociationOrComposition(p._csn))
|
|
776
|
+
warning('odata-spec-violation-array', pLoc, { version: '2.0' });
|
|
777
|
+
|
|
778
|
+
if(edmUtils.isAssociationOrComposition(p._csn))
|
|
779
|
+
warning('odata-spec-violation-assoc', pLoc, { version: '2.0' });
|
|
701
780
|
}
|
|
702
781
|
});
|
|
703
782
|
|
|
@@ -711,8 +790,12 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
711
790
|
function createTypeDefinition(typeCsn)
|
|
712
791
|
{
|
|
713
792
|
// derived types are already resolved to base types
|
|
714
|
-
const
|
|
715
|
-
|
|
793
|
+
const attributes = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
|
|
794
|
+
if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
|
|
795
|
+
message('odata-spec-violation-id', ['definitions', typeCsn.name], { id: attributes.Name });
|
|
796
|
+
|
|
797
|
+
const typeDef = new Edm.TypeDefinition(v, attributes, typeCsn );
|
|
798
|
+
edmTypeCompatibilityCheck(typeDef, [ 'definitions', typeCsn.name ]);
|
|
716
799
|
Schema.append(typeDef);
|
|
717
800
|
}
|
|
718
801
|
|
|
@@ -734,7 +817,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
734
817
|
{
|
|
735
818
|
let constraints = navigationProperty._csn._constraints;
|
|
736
819
|
let parentName = navigationProperty._csn._edmParentCsn.name.replace(schemaNamePrefix, '');
|
|
737
|
-
let plainAssocName = parentName + NAVPROP_TRENNER + navigationProperty.Name.replace(VALUELIST_NAVPROP_PREFIX, '');
|
|
820
|
+
let plainAssocName = parentName + NAVPROP_TRENNER + navigationProperty._edmAttributes.Name.replace(VALUELIST_NAVPROP_PREFIX, '');
|
|
738
821
|
let assocName = plainAssocName;
|
|
739
822
|
let i = 1;
|
|
740
823
|
while(NamesInSchemaXRef[assocName] !== undefined) {
|
|
@@ -742,7 +825,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
742
825
|
}
|
|
743
826
|
|
|
744
827
|
let fromRole = parentName;
|
|
745
|
-
let toRole = navigationProperty.Type.replace(schemaAliasPrefix, ''); // <= navprops type should be prefixed with alias
|
|
828
|
+
let toRole = navigationProperty._edmAttributes.Type.replace(schemaAliasPrefix, ''); // <= navprops type should be prefixed with alias
|
|
746
829
|
|
|
747
830
|
let fromEntityType = fromRole;
|
|
748
831
|
let toEntityType = toRole;
|
|
@@ -762,17 +845,14 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
762
845
|
}
|
|
763
846
|
|
|
764
847
|
// add V2 attributes to navigationProperty
|
|
765
|
-
navigationProperty.Relationship
|
|
766
|
-
navigationProperty.FromRole
|
|
767
|
-
navigationProperty.ToRole
|
|
848
|
+
navigationProperty.setEdmAttribute('Relationship', fullQualified(assocName));
|
|
849
|
+
navigationProperty.setEdmAttribute('FromRole', fromRole);
|
|
850
|
+
navigationProperty.setEdmAttribute('ToRole', toRole);
|
|
768
851
|
|
|
769
852
|
// remove V4 attributes
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
delete navigationProperty.Partner;
|
|
774
|
-
if(navigationProperty.ContainsTarget != undefined)
|
|
775
|
-
delete navigationProperty.ContainsTarget;
|
|
853
|
+
navigationProperty.removeEdmAttribute('Type');
|
|
854
|
+
navigationProperty.removeEdmAttribute('Partner');
|
|
855
|
+
navigationProperty.removeEdmAttribute('ContainsTarget');
|
|
776
856
|
|
|
777
857
|
/*
|
|
778
858
|
If NavigationProperty is a backlink association (constraints._originAssocCsn is set), then there are two options:
|
|
@@ -798,7 +878,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
798
878
|
assocName = plainAssocName + '_' + i++;
|
|
799
879
|
}
|
|
800
880
|
|
|
801
|
-
navigationProperty.Relationship
|
|
881
|
+
navigationProperty.setEdmAttribute('Relationship', fullQualified(assocName));
|
|
802
882
|
|
|
803
883
|
reuseAssoc = !!forwardAssocCsn._NavigationProperty;
|
|
804
884
|
constraints = forwardAssocCsn._constraints;
|
|
@@ -857,13 +937,14 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
857
937
|
// distribute edm:Annotations into the schemas
|
|
858
938
|
// Distribute each anno into Schema
|
|
859
939
|
annos.forEach(anno => {
|
|
860
|
-
let targetSchema = whatsMySchemaName(anno.Target);
|
|
940
|
+
let targetSchema = whatsMySchemaName(anno._edmAttributes.Target);
|
|
861
941
|
// if no target schema has been found, it's a service annotation that applies to the service schema
|
|
862
942
|
if(targetSchema === undefined)
|
|
863
943
|
targetSchema = serviceCsn.name;
|
|
864
944
|
if(targetSchema) {
|
|
865
945
|
if(targetSchema !== serviceCsn.name) {
|
|
866
|
-
|
|
946
|
+
const newTarget = anno._edmAttributes.Target.replace(serviceCsn.name + '.', '');
|
|
947
|
+
anno.setEdmAttribute('Target', newTarget);
|
|
867
948
|
}
|
|
868
949
|
edm._service._schemas[targetSchema]._annotations.push(anno);
|
|
869
950
|
}
|
|
@@ -876,6 +957,44 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
|
|
|
876
957
|
edm._defaultRefs.push(r);
|
|
877
958
|
})
|
|
878
959
|
}
|
|
960
|
+
|
|
961
|
+
function edmTypeCompatibilityCheck(p, pLoc) {
|
|
962
|
+
const edmType = p._type;
|
|
963
|
+
if(!edmType) {
|
|
964
|
+
message('odata-spec-violation-type', pLoc);
|
|
965
|
+
}
|
|
966
|
+
else if(p._scalarType) {
|
|
967
|
+
const td = EdmPrimitiveTypeMap[edmType];
|
|
968
|
+
if(td) {
|
|
969
|
+
// The renderer/type mapper doesn't/shouldn't produce incompatible types and facets.
|
|
970
|
+
// Only the unknown type warning may be triggered by an unknown @odata.Type override.
|
|
971
|
+
if(td.v2 !== p.v2 && td.v4 !== p.v4)
|
|
972
|
+
message('odata-spec-violation-type', pLoc,
|
|
973
|
+
{ type:edmType, version: (p.v4 ? '4.0' : '2.0'), '#': 'incompatible' });
|
|
974
|
+
EdmTypeFacetNames.forEach(name => {
|
|
975
|
+
const facet = EdmTypeFacetMap[name];
|
|
976
|
+
const optional =
|
|
977
|
+
(facet.optional !== undefined)
|
|
978
|
+
? (Array.isArray(facet.optional)
|
|
979
|
+
? facet.optional.includes(edmType)
|
|
980
|
+
: facet.optional)
|
|
981
|
+
: false;
|
|
982
|
+
|
|
983
|
+
// facet is not in attributes
|
|
984
|
+
// facet is member of type definition and mandatory
|
|
985
|
+
// node and facet version match
|
|
986
|
+
if(!p._edmAttributes[name] && td[name] && !optional && (p.v2 === facet.v2 || p.v4 === facet.v4)) {
|
|
987
|
+
message('odata-spec-violation-type', pLoc,
|
|
988
|
+
{ type:edmType, name, version: (p.v4 ? '4.0' : '2.0'), '#': 'facet' });
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
message('odata-spec-violation-type-unknown', pLoc,
|
|
994
|
+
{ type:edmType });
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
879
998
|
}
|
|
880
999
|
}
|
|
881
1000
|
module.exports = { csn2edm, csn2edmAll };
|