@sap/cds-compiler 3.4.2 → 3.5.0

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.
Files changed (143) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +1 -0
  3. package/bin/cds_update_identifiers.js +5 -5
  4. package/bin/cdsc.js +15 -16
  5. package/bin/cdshi.js +19 -6
  6. package/doc/CHANGELOG_ARCHIVE.md +2 -2
  7. package/doc/CHANGELOG_BETA.md +9 -1
  8. package/doc/CHANGELOG_DEPRECATED.md +2 -0
  9. package/lib/api/main.js +61 -59
  10. package/lib/api/options.js +4 -2
  11. package/lib/api/validate.js +2 -2
  12. package/lib/base/cleanSymbols.js +2 -3
  13. package/lib/base/dictionaries.js +6 -6
  14. package/lib/base/error.js +2 -2
  15. package/lib/base/keywords.js +6 -6
  16. package/lib/base/location.js +11 -12
  17. package/lib/base/message-registry.js +177 -58
  18. package/lib/base/messages.js +252 -180
  19. package/lib/base/model.js +14 -11
  20. package/lib/base/node-helpers.js +9 -10
  21. package/lib/base/optionProcessorHelper.js +138 -129
  22. package/lib/checks/.eslintrc.json +2 -0
  23. package/lib/checks/actionsFunctions.js +5 -5
  24. package/lib/checks/annotationsOData.js +4 -4
  25. package/lib/checks/arrayOfs.js +1 -1
  26. package/lib/checks/cdsPersistence.js +1 -1
  27. package/lib/checks/checkForTypes.js +3 -3
  28. package/lib/checks/defaultValues.js +3 -3
  29. package/lib/checks/elements.js +7 -7
  30. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  31. package/lib/checks/foreignKeys.js +1 -1
  32. package/lib/checks/invalidTarget.js +4 -4
  33. package/lib/checks/managedInType.js +1 -1
  34. package/lib/checks/managedWithoutKeys.js +1 -1
  35. package/lib/checks/nonexpandableStructured.js +5 -3
  36. package/lib/checks/nullableKeys.js +1 -1
  37. package/lib/checks/onConditions.js +5 -6
  38. package/lib/checks/parameters.js +1 -1
  39. package/lib/checks/queryNoDbArtifacts.js +2 -2
  40. package/lib/checks/selectItems.js +4 -4
  41. package/lib/checks/sql-snippets.js +4 -4
  42. package/lib/checks/types.js +7 -7
  43. package/lib/checks/utils.js +4 -4
  44. package/lib/checks/validator.js +16 -13
  45. package/lib/compiler/.eslintrc.json +4 -1
  46. package/lib/compiler/assert-consistency.js +8 -7
  47. package/lib/compiler/builtins.js +14 -14
  48. package/lib/compiler/checks.js +123 -48
  49. package/lib/compiler/define.js +12 -13
  50. package/lib/compiler/extend.js +266 -60
  51. package/lib/compiler/finalize-parse-cdl.js +10 -5
  52. package/lib/compiler/index.js +17 -14
  53. package/lib/compiler/populate.js +14 -6
  54. package/lib/compiler/propagator.js +2 -0
  55. package/lib/compiler/resolve.js +2 -15
  56. package/lib/compiler/shared.js +27 -16
  57. package/lib/compiler/tweak-assocs.js +5 -6
  58. package/lib/compiler/utils.js +20 -0
  59. package/lib/edm/annotations/genericTranslation.js +604 -358
  60. package/lib/edm/annotations/preprocessAnnotations.js +39 -35
  61. package/lib/edm/csn2edm.js +275 -222
  62. package/lib/edm/edm.js +17 -3
  63. package/lib/edm/edmAnnoPreprocessor.js +6 -6
  64. package/lib/edm/edmInboundChecks.js +2 -2
  65. package/lib/edm/edmPreprocessor.js +107 -77
  66. package/lib/edm/edmUtils.js +44 -5
  67. package/lib/gen/Dictionary.json +210 -8
  68. package/lib/gen/language.checksum +1 -1
  69. package/lib/gen/language.interp +67 -63
  70. package/lib/gen/language.tokens +81 -81
  71. package/lib/gen/languageLexer.interp +4 -10
  72. package/lib/gen/languageLexer.js +854 -869
  73. package/lib/gen/languageLexer.tokens +79 -81
  74. package/lib/gen/languageParser.js +14309 -13832
  75. package/lib/inspect/inspectModelStatistics.js +2 -2
  76. package/lib/inspect/inspectPropagation.js +6 -6
  77. package/lib/inspect/inspectUtils.js +2 -2
  78. package/lib/json/from-csn.js +102 -55
  79. package/lib/json/to-csn.js +119 -198
  80. package/lib/language/antlrParser.js +5 -2
  81. package/lib/language/docCommentParser.js +6 -6
  82. package/lib/language/errorStrategy.js +43 -23
  83. package/lib/language/genericAntlrParser.js +113 -133
  84. package/lib/language/language.g4 +1550 -1506
  85. package/lib/language/multiLineStringParser.js +3 -3
  86. package/lib/language/textUtils.js +2 -2
  87. package/lib/main.js +3 -3
  88. package/lib/model/csnRefs.js +5 -0
  89. package/lib/model/csnUtils.js +130 -122
  90. package/lib/model/revealInternalProperties.js +1 -1
  91. package/lib/model/sortViews.js +4 -6
  92. package/lib/modelCompare/compare.js +2 -2
  93. package/lib/modelCompare/utils/.eslintrc.json +22 -0
  94. package/lib/modelCompare/utils/filter.js +100 -0
  95. package/lib/optionProcessor.js +5 -0
  96. package/lib/render/.eslintrc.json +1 -0
  97. package/lib/render/DuplicateChecker.js +1 -1
  98. package/lib/render/manageConstraints.js +12 -12
  99. package/lib/render/toCdl.js +311 -276
  100. package/lib/render/toHdbcds.js +97 -94
  101. package/lib/render/toRename.js +5 -5
  102. package/lib/render/toSql.js +127 -223
  103. package/lib/render/utils/common.js +141 -108
  104. package/lib/render/utils/delta.js +227 -0
  105. package/lib/render/utils/sql.js +22 -6
  106. package/lib/render/utils/stringEscapes.js +3 -3
  107. package/lib/transform/db/.eslintrc.json +2 -0
  108. package/lib/transform/db/applyTransformations.js +3 -3
  109. package/lib/transform/db/assertUnique.js +13 -12
  110. package/lib/transform/db/associations.js +5 -5
  111. package/lib/transform/db/cdsPersistence.js +10 -8
  112. package/lib/transform/db/constraints.js +14 -14
  113. package/lib/transform/db/expansion.js +20 -22
  114. package/lib/transform/db/flattening.js +24 -42
  115. package/lib/transform/db/groupByOrderBy.js +3 -3
  116. package/lib/transform/db/temporal.js +6 -6
  117. package/lib/transform/db/transformExists.js +23 -23
  118. package/lib/transform/db/views.js +16 -16
  119. package/lib/transform/draft/.eslintrc.json +1 -35
  120. package/lib/transform/draft/db.js +10 -10
  121. package/lib/transform/draft/odata.js +2 -2
  122. package/lib/transform/forOdataNew.js +8 -29
  123. package/lib/transform/forRelationalDB.js +16 -6
  124. package/lib/transform/localized.js +11 -10
  125. package/lib/transform/odata/toFinalBaseType.js +41 -27
  126. package/lib/transform/odata/typesExposure.js +113 -47
  127. package/lib/transform/parseExpr.js +209 -106
  128. package/lib/transform/transformUtilsNew.js +17 -10
  129. package/lib/transform/translateAssocsToJoins.js +24 -19
  130. package/lib/transform/universalCsn/coreComputed.js +10 -10
  131. package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
  132. package/lib/transform/universalCsn/utils.js +3 -3
  133. package/lib/utils/file.js +5 -5
  134. package/lib/utils/moduleResolve.js +13 -13
  135. package/lib/utils/objectUtils.js +6 -6
  136. package/lib/utils/term.js +5 -2
  137. package/lib/utils/timetrace.js +51 -24
  138. package/package.json +5 -8
  139. package/share/messages/check-proper-type-of.md +1 -1
  140. package/share/messages/message-explanations.json +1 -1
  141. package/share/messages/redirected-to-complex.md +4 -4
  142. package/share/messages/{syntax-expecting-integer.md → syntax-expecting-unsigned-int.md} +7 -4
  143. package/lib/modelCompare/filter.js +0 -83
@@ -9,7 +9,7 @@ const VALUELIST_NAVPROP_PREFIX = '';
9
9
  const edmUtils = require('./edmUtils.js')
10
10
  const { initializeModel } = require('./edmPreprocessor.js');
11
11
  const translate = require('./annotations/genericTranslation.js');
12
- const { setProp } = require('../base/model');
12
+ const { setProp, isBetaEnabled } = require('../base/model');
13
13
  const { cloneCsnNonDict, isEdmPropertyRendered, isBuiltinType } = require('../model/csnUtils');
14
14
  const { checkCSNVersion } = require('../json/csnVersion');
15
15
  const { makeMessageFunction } = require('../base/messages');
@@ -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, throwWithAnyError } = messageFunctions;
33
+ const { info, warning, error, message, throwWithError } = messageFunctions;
34
34
  checkCSNVersion(csn, _options);
35
35
 
36
36
  let rc = Object.create(null);
@@ -43,7 +43,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
43
43
  setProp(csn.meta, 'options', _csn.meta.options);
44
44
  }
45
45
 
46
- const [ allServices,
46
+ const [
47
+ allServices,
47
48
  allSchemas,
48
49
  reqDefs,
49
50
  whatsMyServiceRootName,
@@ -54,7 +55,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
54
55
 
55
56
  const v = options.v;
56
57
  if(Object.keys(allServices).length === 0) {
57
- info(null, null, `No Services in model`);
58
+ info(null, null, 'No Services in model');
58
59
  return rc;
59
60
  }
60
61
 
@@ -76,7 +77,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
76
77
  services[serviceCsn.name] = createEdm(serviceCsn);
77
78
  return services; }, rc);
78
79
  }
79
- throwWithAnyError();
80
+ throwWithError();
80
81
  return rc;
81
82
 
82
83
  //--------------------------------------------------------------------------------
@@ -91,6 +92,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
91
92
  // let alias = serviceCsn.alias || baseName(baseName(serviceCsn.name, '::'), '.');
92
93
  // FIXME: UI5 cannot deal with spec conforming simpleid alias names
93
94
 
95
+ function markRendered(def) { setProp(def, '$isRendered', true); }
96
+
94
97
  const service = new Edm.DataServices(v);
95
98
  /** @type {object} */
96
99
  const edm = new Edm.Edm(v, service);
@@ -135,6 +138,24 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
135
138
  return fqSchemaXRef.reduce((rc, sn) => !rc && n && n.startsWith(sn + '.') ? sn : rc, undefined);
136
139
  }
137
140
 
141
+ // tunnel schema xref and servicename in options to edm.Typebase to rectify
142
+ // type references that are eventually also prefixed with the service schema name.
143
+ options.serviceName = serviceCsn.name;
144
+ // List of all schema names in this service, including the service itself
145
+ options.whatsMySchemaName = whatsMySchemaName;
146
+ options.whatsMyServiceRootName = whatsMyServiceRootName;
147
+
148
+ let xServiceRefs = {};
149
+ const UsedTypes = {};
150
+ function collectUsedType(csn, typeName = (csn.items?.type || csn.type)) {
151
+ if(typeName) {
152
+ if(UsedTypes[typeName])
153
+ UsedTypes[typeName].push(csn)
154
+ else
155
+ UsedTypes[typeName] = [ csn ];
156
+ }
157
+ }
158
+
138
159
  // create schema containers
139
160
  const subSchemaDictionary = {
140
161
  [serviceCsn.name]: {
@@ -147,26 +168,24 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
147
168
  };
148
169
 
149
170
  if(options.isV4()) {
150
- // tunnel schema xref and servicename in options to edm.Typebase to rectify
151
- // type references that are eventually also prefixed with the service schema name.
152
- options.serviceName = serviceCsn.name;
153
- // List of all schema names in this service, including the service itself
154
- options.whatsMySchemaName = whatsMySchemaName;
155
-
156
171
  // Add additional schema containers as sub contexts to the service
157
172
  Object.entries(allSchemas).forEach(([fqName, art]) => {
158
173
  if(serviceCsn.name === whatsMyServiceRootName(fqName) &&
159
- fqName.startsWith(serviceCsn.name + '.') && art.kind === 'schema') {
160
- fqSchemaXRef.push(fqName);
161
- // Strip the toplevel service schema name (see comment above)
162
- const name = fqName.replace(serviceCsn.name + '.', '');
163
- subSchemaDictionary[name] = {
164
- name,
165
- fqName,
166
- _csn: art,
167
- container: false,
168
- definitions: Object.create(null)
169
- };
174
+ fqName.startsWith(serviceCsn.name + '.')) {
175
+ if(art.kind === 'reference')
176
+ fqSchemaXRef.push(fqName);
177
+ if(art.kind === 'schema') {
178
+ fqSchemaXRef.push(fqName);
179
+ // Strip the toplevel service schema name (see comment above)
180
+ const name = fqName.replace(serviceCsn.name + '.', '');
181
+ subSchemaDictionary[name] = {
182
+ name,
183
+ fqName,
184
+ _csn: art,
185
+ container: false,
186
+ definitions: Object.create(null)
187
+ };
188
+ }
170
189
  }
171
190
  }, subSchemaDictionary);
172
191
 
@@ -175,20 +194,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
175
194
 
176
195
  // Fill the schemas and references, fqSchemaXRef must be complete
177
196
  populateSchemas(subSchemaDictionary);
178
- const xServiceRefs = populateXserviceRefs();
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);
197
+ xServiceRefs = populateXserviceRefs();
192
198
 
193
199
  // Bring the schemas in alphabetical order, service first, root last
194
200
  const sortedSchemaNames = Object.keys(subSchemaDictionary).filter(n => n !== fallBackSchemaName && n !== serviceCsn.name).sort();
@@ -203,13 +209,6 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
203
209
  const schema = subSchemaDictionary[name];
204
210
  service.registerSchema(schema.fqName, createSchema(schema));
205
211
  });
206
-
207
- // Add cross service references to the EDM
208
- xServiceRefs.forEach(ref => {
209
- let r = new Edm.Reference(v, ref.ref);
210
- r.append(new Edm.Include(v, ref.inc))
211
- edm._defaultRefs.push(r);
212
- });
213
212
  }
214
213
  else {
215
214
  populateSchemas(subSchemaDictionary);
@@ -223,13 +222,55 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
223
222
  service._children.forEach(c => {
224
223
  c._ec && Object.entries(c._ec._registry).forEach((([setName, arr]) => {
225
224
  if(arr.length > 1) {
226
- error(null, null, { name: c._edmAttributes.Namespace, id: setName },
227
- `Namespace $(NAME): Duplicate entries in EntityContainer with Name=$(ID) for ${arr.map(a =>a.getDuplicateMessage()).join(', ')} `);
225
+ error(null, null, {
226
+ name: c._edmAttributes.Namespace,
227
+ id: setName,
228
+ names: arr.map(a => a.getDuplicateMessage())
229
+ }, 'Namespace $(NAME): Duplicate entries in EntityContainer with Name=$(ID) for $(NAMES)');
228
230
  }
229
231
  }));
230
232
  });
231
- // Create annotations and distribute into Schemas
232
- addAnnotations();
233
+ // Create annotations and distribute into Schemas, merge vocabulary cross refs into xServiceRefs
234
+ addAnnotations(xServiceRefs);
235
+
236
+ // Finally add cross service references into the EDM and extract the targetSchemaNames
237
+ // for the type cross check
238
+ Object.values(xServiceRefs).forEach(ref => {
239
+ let r = new Edm.Reference(v, ref.ref);
240
+ r.append(new Edm.Include(v, ref.inc))
241
+ edm._defaultRefs.push(r);
242
+ });
243
+
244
+ for(let typeName in UsedTypes) {
245
+ if(!isBuiltinType(typeName)) {
246
+ let iTypeName = typeName;
247
+ /*
248
+ Report type ref, if the type is
249
+ - not a builtin,
250
+ - not included in required definitions
251
+ - not a type clash (reported in type exposure),
252
+ - a @cds.external service member but can't be rendered
253
+ */
254
+ if(!typeName.startsWith(serviceCsn.name + '.'))
255
+ iTypeName = serviceCsn.name + '.' + typeName;
256
+ const def = reqDefs.definitions[iTypeName];
257
+
258
+ const usages = UsedTypes[typeName].filter(u => !u.$NameClashReported);
259
+ if(usages.length > 0 && def && !def.$isRendered && def['@cds.external']) {
260
+ message('odata-spec-violation-type', usages[0].$location,
261
+ {
262
+ type: typeName,
263
+ anno: '@cds.external',
264
+ name: serviceCsn.name,
265
+ code: def.elements ? 'Edm.ComplexType' : 'Edm.TypeDefinition',
266
+ version: options.isV4() ? '4.0' : '2.0',
267
+ '#': 'external'
268
+ }
269
+ );
270
+ }
271
+ }
272
+ }
273
+
233
274
  return edm
234
275
 
235
276
  // Sort definitions into their schema container
@@ -289,12 +330,13 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
289
330
 
290
331
  return Object.entries(allSchemas).reduce((references, [fqName, art]) => {
291
332
  // add references
292
- if(art.kind === 'reference' && whatsMySchemaName(fqName) && serviceCsn.name === whatsMyServiceRootName(fqName, false)) {
293
- fqSchemaXRef.push(fqName);
294
- references.push(art);
333
+ if(art.kind === 'reference' &&
334
+ whatsMySchemaName(fqName) &&
335
+ serviceCsn.name === whatsMyServiceRootName(fqName, false)) {
336
+ references[art.inc.Namespace] = art;
295
337
  }
296
338
  return references;
297
- }, []);
339
+ }, {});
298
340
  }
299
341
 
300
342
  // Main schema creator function
@@ -331,25 +373,31 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
331
373
  */
332
374
  edmUtils.foreach(schemaCsn.definitions,
333
375
  a => a.kind === 'entity' && !a.abstract && a.name.startsWith(schemaNamePrefix),
334
- createEntityTypeAndSet
376
+ [createEntityTypeAndSet, markRendered]
335
377
  );
336
378
  // create unbound actions/functions
337
- edmUtils.foreach(schemaCsn.definitions,
379
+ edmUtils.foreach(schemaCsn.definitions,
338
380
  a => (a.kind === 'action' || a.kind === 'function') && a.name.startsWith(schemaNamePrefix),
339
- (options.isV4()) ? createActionV4 : createActionV2);
381
+ [(options.isV4()) ? createActionV4 : createActionV2, markRendered]);
340
382
 
341
383
  // create the complex types
342
384
  edmUtils.foreach(schemaCsn.definitions,
343
385
  a => edmUtils.isStructuredType(a) && a.name.startsWith(schemaNamePrefix),
344
- createComplexType);
386
+ [createComplexType, markRendered]);
345
387
 
346
388
  if(options.isV4())
347
389
  {
348
390
  edmUtils.foreach(schemaCsn.definitions,
349
391
  artifact => edmUtils.isDerivedType(artifact) &&
350
- !artifact.target &&
392
+ !artifact.target&&
351
393
  artifact.name.startsWith(schemaNamePrefix),
352
- createTypeDefinition);
394
+ [createTypeDefinitionV4, markRendered]);
395
+ }
396
+
397
+ if(isBetaEnabled(options, 'odataTerms')) {
398
+ edmUtils.foreach(schemaCsn.definitions,
399
+ a => a.kind === 'annotation' && a.name.startsWith(schemaNamePrefix),
400
+ createTerm);
353
401
  }
354
402
 
355
403
  // fetch all exising children names in a map
@@ -370,19 +418,10 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
370
418
  np.addReferentialConstraintNodes();
371
419
  }
372
420
  else {
373
- addAssociation(np);
421
+ addAssociationV2(np);
374
422
  }
375
423
  });
376
424
 
377
- // remove EntityContainer if empty
378
- if(Schema._ec && Schema._ec._children.length === 0) {
379
- Schema._children.splice(Schema._children.indexOf(Schema._ec), 1);
380
- }
381
- if(Schema._children.length === 0) {
382
- // FIXME: Location for sub schemas?
383
- warning(null, ['definitions', Schema._edmAttributes.Namespace], { name: Schema._edmAttributes.Namespace }, 'Schema $(NAME) is empty');
384
- }
385
-
386
425
  Object.entries(NamesInSchemaXRef).forEach(([name, refs]) => {
387
426
  if(refs.length > 1) {
388
427
  error(null, ['definitions', `${Schema._edmAttributes.Namespace}.${name}`], { name: Schema._edmAttributes.Namespace },
@@ -413,7 +452,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
413
452
  const pLoc = [...loc, 'elements', p._edmAttributes.Name];
414
453
  edmTypeCompatibilityCheck(p, pLoc);
415
454
  if(p._edmAttributes.Name === EntityTypeName)
416
- message('odata-spec-violation-property-name', pLoc, { kind: entityCsn.kind });
455
+ message('odata-spec-violation-property-name', pLoc, { meta: entityCsn.kind });
417
456
 
418
457
  if(options.isV2() && p._isCollection && !p._csn.target)
419
458
  message('odata-spec-violation-array', pLoc, { version: '2.0' });
@@ -468,6 +507,140 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
468
507
  });
469
508
  }
470
509
 
510
+ function createComplexType(structuredTypeCsn)
511
+ {
512
+ // V4 attributes: Name, BaseType, Abstract, OpenType
513
+ const attributes = { Name: structuredTypeCsn.name.replace(schemaNamePrefix, '') };
514
+
515
+ const complexType = new Edm.ComplexType(v, attributes, structuredTypeCsn);
516
+ const elementsCsn = structuredTypeCsn.items || structuredTypeCsn;
517
+ const properties = createProperties(elementsCsn, structuredTypeCsn)[0];
518
+ const loc = ['definitions', structuredTypeCsn.name];
519
+
520
+ if(properties.length === 0) {
521
+ warning(null, loc, { name: structuredTypeCsn.name },
522
+ 'EDM ComplexType $(NAME) has no properties');
523
+ }
524
+ if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
525
+ message('odata-spec-violation-id', loc, { id: attributes.Name });
526
+
527
+ properties.forEach(p => {
528
+ const pLoc = [ ...loc, ...(structuredTypeCsn.items ? ['items', 'elements'] : [ 'elements' ]), p._edmAttributes.Name ];
529
+ edmTypeCompatibilityCheck(p, pLoc);
530
+ if(p._edmAttributes.Name === complexType._edmAttributes.Name)
531
+ message('odata-spec-violation-property-name', pLoc, { meta: structuredTypeCsn.kind });
532
+
533
+ if(!edmUtils.isODataSimpleIdentifier(p._edmAttributes.Name))
534
+ message('odata-spec-violation-id', pLoc, { id: p._edmAttributes.Name });
535
+
536
+ if(options.isV2()) {
537
+ if(p._isCollection && !p._csn.target)
538
+ message('odata-spec-violation-array', pLoc, { version: '2.0' });
539
+
540
+ if(p._csn.target)
541
+ message('odata-spec-violation-assoc', pLoc, { version: '2.0' });
542
+ }
543
+ });
544
+
545
+
546
+ complexType.append(...(properties));
547
+
548
+ Schema.append(complexType);
549
+ }
550
+
551
+ /**
552
+ * @param {object} elementsCsn
553
+ * @param {object} edmParentCsn
554
+ * @returns {[object[], any]} Returns a [ [ Edm Properties ], boolean hasStream ]:
555
+ * array of Edm Properties
556
+ * hasStream : value of @Core.MediaType assignment
557
+ */
558
+ function createProperties(elementsCsn, edmParentCsn=elementsCsn)
559
+ {
560
+ const props = [];
561
+ let hasStream = false;
562
+ const streamProps = [];
563
+
564
+ elementsCsn.elements && Object.entries(elementsCsn.elements).forEach(([elementName, elementCsn]) =>
565
+ {
566
+ if(elementCsn._edmParentCsn == undefined)
567
+ setProp(elementCsn, '_edmParentCsn', edmParentCsn);
568
+
569
+ if(elementCsn.target) {
570
+ // Foreign keys are part of the generic elementCsn.elements property creation
571
+
572
+ // This is the V4 edmx:NavigationProperty
573
+ // gets rewritten for V2 in addAssociations()
574
+
575
+ // suppress navprop creation only if @odata.navigable:false is not annotated.
576
+ // (undefined !== false) still evaluates to true
577
+ if (!elementCsn._target.abstract && elementCsn['@odata.navigable'] !== false)
578
+ {
579
+ const navProp = new Edm.NavigationProperty(v, {
580
+ Name: elementName,
581
+ Type: elementCsn._target.name
582
+ }, elementCsn);
583
+ collectUsedType(elementCsn, elementCsn._target.name);
584
+ props.push(navProp);
585
+ // save the navProp in the global array for late constraint building
586
+ navigationProperties.push(navProp);
587
+ }
588
+ }
589
+ // render ordinary property if element is NOT ...
590
+ // 1) ... annotated @cds.api.ignore
591
+ // 2) ... annotated @odata.foreignKey4 and odataFormat: structured
592
+
593
+ else if(isEdmPropertyRendered(elementCsn, options))
594
+ {
595
+ // CDXCORE-CDXCORE-173
596
+ // V2: filter @Core.MediaType
597
+ if ( options.isV2() && elementCsn['@Core.MediaType']) {
598
+ hasStream = elementCsn['@Core.MediaType'];
599
+ delete elementCsn['@Core.MediaType'];
600
+ // CDXCORE-CDXCORE-177:
601
+ // V2: don't render element but add attribute 'm:HasStream="true' to EntityType
602
+ // V4: render property type 'Edm.Stream'
603
+ streamProps.push(elementName);
604
+
605
+ } else {
606
+ collectUsedType(elementCsn);
607
+ props.push(new Edm.Property(v, { Name: elementName }, elementCsn));
608
+ }
609
+ }
610
+
611
+ });
612
+ if(options.isV2()) {
613
+ if(streamProps.length > 1) { // TODO: why not mention 2.0 in text?
614
+ error(null, ['definitions', elementsCsn.name], { names: streamProps, version: '2.0', anno: '@Core.MediaType' },
615
+ 'Expected only one element to be annotated with $(ANNO) for OData $(VERSION) but found $(NAMES)');
616
+ }
617
+ else if(streamProps.length === 1) {
618
+ info(null, ['definitions', elementsCsn.name], { id: streamProps[0], version: '2.0', anno: '@Core.MediaType' },
619
+ 'Property $(ID) annotated with $(ANNO) is removed from EDM for OData $(VERSION)');
620
+ }
621
+ }
622
+ return [ props, hasStream ];
623
+ }
624
+
625
+ function createTerm(termCsn) {
626
+ const attributes = { Name: termCsn.name.replace(schemaNamePrefix, '') };
627
+ const term = new Edm.Term(v, attributes, termCsn);
628
+ Schema.append(term);
629
+ }
630
+
631
+ // V4 <TypeDefintion>
632
+ function createTypeDefinitionV4(typeCsn)
633
+ {
634
+ // derived types are already resolved to base types
635
+ const attributes = { Name: typeCsn.name.replace(schemaNamePrefix, '') };
636
+ if(!edmUtils.isODataSimpleIdentifier(attributes.Name))
637
+ message('odata-spec-violation-id', ['definitions', typeCsn.name], { id: attributes.Name });
638
+
639
+ const typeDef = new Edm.TypeDefinition(v, attributes, typeCsn );
640
+ edmTypeCompatibilityCheck(typeDef, [ 'definitions', typeCsn.name ]);
641
+ Schema.append(typeDef);
642
+ }
643
+
471
644
  // add bound/unbound actions/functions for V4
472
645
  function createActionV4(actionCsn, _name, entityCsn=undefined)
473
646
  {
@@ -528,7 +701,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
528
701
  const pLoc = [ ...loc, 'params', p._edmAttributes.Name ];
529
702
  if(!edmUtils.isODataSimpleIdentifier(parameterName))
530
703
  message('odata-spec-violation-id', pLoc, { id: parameterName });
531
-
704
+ collectUsedType(parameterCsn);
532
705
  edmTypeCompatibilityCheck(p, pLoc);
533
706
  actionNode.append(p);
534
707
  });
@@ -536,6 +709,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
536
709
  // return type if any
537
710
  if(actionCsn.returns) {
538
711
  actionNode._returnType = new Edm.ReturnType(v, actionCsn.returns);
712
+ collectUsedType(actionCsn.returns);
539
713
  edmTypeCompatibilityCheck(actionNode._returnType, [ ...loc, 'returns' ]);
540
714
  // if binding type matches return type add attribute EntitySetPath
541
715
  if(entityCsn != undefined && fullQualified(entityCsn.name) === actionNode._returnType._type) {
@@ -617,6 +791,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
617
791
  actionCsn.params && Object.entries(actionCsn.params).forEach(([parameterName, parameterCsn]) => {
618
792
  const pLoc = [...loc, 'params', parameterName];
619
793
  const param = new Edm.Parameter(v, { Name: parameterName }, parameterCsn, 'In' );
794
+ collectUsedType(parameterCsn);
620
795
  edmTypeCompatibilityCheck(param, pLoc);
621
796
  if(!edmUtils.isODataSimpleIdentifier(parameterName))
622
797
  message('odata-spec-violation-id', pLoc, { id: parameterName });
@@ -644,6 +819,7 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
644
819
  const returns = action.returns.items || action.returns;
645
820
  let type = returns.type;
646
821
  if (type) {
822
+ collectUsedType(action.returns);
647
823
  if (!isBuiltinType(type) && csn.definitions[type].kind !== 'entity' && csn.definitions[type].kind !== 'type') {
648
824
  message('odata-spec-violation-returns', returnsLoc, { kind: action.kind, version: '2.0' });
649
825
  }
@@ -671,147 +847,21 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
671
847
  }
672
848
  }
673
849
 
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
850
  /*
801
- * addAssociation() constructs a V2 association.
802
- * In V4 all this has been simplified very much, the only thing actually left over is
803
- * <ReferentialConstriant> that is then a sub element to <NavigationProperty>.
804
- * However, referential constraints are substantially different to its V2 counterpart,
805
- * so it is better to reimplement proper V4 construction of<NavigationProperty> in a separate
806
- * function.
807
- *
808
- * This method does:
809
- * rewrite <NavigationProperty> attributes to be V2 compliant
810
- * add <Association> elements to the schema
811
- * add <End>, <ReferentialConstraint>, <Dependent> and <Principal> sub elements to <Association>
812
- * add <AssociationSet> to the EntityContainer for each <Association>
851
+ addAssociation() constructs a V2 association.
852
+ In V4 all this has been simplified very much, the only thing actually left over is
853
+ <ReferentialConstriant> that is then a sub element to <NavigationProperty>.
854
+ However, referential constraints are substantially different to its V2 counterpart,
855
+ so it is better to reimplement proper V4 construction of<NavigationProperty> in a separate
856
+ function.
857
+
858
+ This method does:
859
+ rewrite <NavigationProperty> attributes to be V2 compliant
860
+ add <Association> elements to the schema
861
+ add <End>, <ReferentialConstraint>, <Dependent> and <Principal> sub elements to <Association>
862
+ add <AssociationSet> to the EntityContainer for each <Association>
813
863
  */
814
- function addAssociation(navigationProperty)
864
+ function addAssociationV2(navigationProperty)
815
865
  {
816
866
  let constraints = navigationProperty._csn._constraints;
817
867
  let parentName = navigationProperty._csn._edmParentCsn.name.replace(schemaNamePrefix, '');
@@ -930,8 +980,8 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
930
980
  }
931
981
 
932
982
  // generate the Edm.Annotations tree and append it to the corresponding schema
933
- function addAnnotations() {
934
- let { annos, usedVocabularies } = translate.csn2annotationEdm(reqDefs, serviceCsn.name, Edm, options, messageFunctions);
983
+ function addAnnotations(xServiceRefs) {
984
+ let { annos, usedVocabularies, xrefs } = translate.csn2annotationEdm(reqDefs, csn.vocabularies, serviceCsn.name, Edm, options, messageFunctions);
935
985
  // distribute edm:Annotations into the schemas
936
986
  // Distribute each anno into Schema
937
987
  annos.forEach(anno => {
@@ -939,21 +989,24 @@ function csn2edmAll(_csn, _options, serviceNames=undefined) {
939
989
  // if no target schema has been found, it's a service annotation that applies to the service schema
940
990
  if(targetSchema === undefined)
941
991
  targetSchema = serviceCsn.name;
942
- if(targetSchema) {
943
- if(targetSchema !== serviceCsn.name) {
944
- const newTarget = anno._edmAttributes.Target.replace(serviceCsn.name + '.', '');
945
- anno.setEdmAttribute('Target', newTarget);
946
- }
947
- edm._service._schemas[targetSchema]._annotations.push(anno);
992
+ if(targetSchema !== serviceCsn.name) {
993
+ const newTarget = anno._edmAttributes.Target.replace(serviceCsn.name + '.', '');
994
+ anno.setEdmAttribute('Target', newTarget);
948
995
  }
996
+ edm._service._schemas[targetSchema]._annotations.push(anno);
949
997
  });
950
998
  annos = [];
951
- // add references for the used vocabularies
952
- usedVocabularies.forEach(voc => {
953
- let r = new Edm.Reference(v, voc.ref);
954
- r.append(new Edm.Include(v, voc.inc))
955
- edm._defaultRefs.push(r);
956
- })
999
+
1000
+ // create service cross reference and merge it into xServiceRefs
1001
+ xrefs.forEach(xr => {
1002
+ if(xr !== serviceCsn.name) {
1003
+ const art = edmUtils.createSchemaRef(allServices, xr);
1004
+ if(xServiceRefs[art.inc.Namespace] === undefined)
1005
+ xServiceRefs[art.inc.Namespace] = art;
1006
+ }
1007
+ });
1008
+ // merge vocabulary cross references into xServiceRefs
1009
+ usedVocabularies.forEach(art => xServiceRefs[art.inc.Namespace] = art);
957
1010
  }
958
1011
 
959
1012
  function edmTypeCompatibilityCheck(p, pLoc) {