@sap/cds-compiler 2.11.4 → 2.13.8

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 (133) hide show
  1. package/CHANGELOG.md +159 -1
  2. package/bin/cds_update_identifiers.js +7 -7
  3. package/bin/cdsc.js +22 -23
  4. package/bin/cdsse.js +2 -2
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +25 -6
  7. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  8. package/doc/NameResolution.md +21 -16
  9. package/lib/api/main.js +30 -63
  10. package/lib/api/options.js +5 -5
  11. package/lib/api/validate.js +0 -5
  12. package/lib/backends.js +15 -23
  13. package/lib/base/dictionaries.js +0 -8
  14. package/lib/base/error.js +26 -0
  15. package/lib/base/keywords.js +7 -17
  16. package/lib/base/location.js +9 -4
  17. package/lib/base/message-registry.js +52 -2
  18. package/lib/base/messages.js +16 -26
  19. package/lib/base/model.js +2 -62
  20. package/lib/base/optionProcessorHelper.js +246 -183
  21. package/lib/checks/.eslintrc.json +2 -0
  22. package/lib/checks/actionsFunctions.js +2 -1
  23. package/lib/checks/annotationsOData.js +1 -1
  24. package/lib/checks/cdsPersistence.js +2 -1
  25. package/lib/checks/enricher.js +17 -1
  26. package/lib/checks/foreignKeys.js +4 -4
  27. package/lib/checks/invalidTarget.js +3 -1
  28. package/lib/checks/managedInType.js +4 -4
  29. package/lib/checks/managedWithoutKeys.js +3 -1
  30. package/lib/checks/queryNoDbArtifacts.js +1 -3
  31. package/lib/checks/selectItems.js +4 -4
  32. package/lib/checks/sql-snippets.js +94 -0
  33. package/lib/checks/types.js +1 -1
  34. package/lib/checks/validator.js +12 -7
  35. package/lib/compiler/assert-consistency.js +10 -6
  36. package/lib/compiler/base.js +0 -1
  37. package/lib/compiler/builtins.js +8 -6
  38. package/lib/compiler/checks.js +46 -12
  39. package/lib/compiler/cycle-detector.js +1 -1
  40. package/lib/compiler/define.js +1103 -0
  41. package/lib/compiler/extend.js +983 -0
  42. package/lib/compiler/finalize-parse-cdl.js +231 -0
  43. package/lib/compiler/index.js +33 -14
  44. package/lib/compiler/kick-start.js +190 -0
  45. package/lib/compiler/moduleLayers.js +4 -4
  46. package/lib/compiler/populate.js +1226 -0
  47. package/lib/compiler/propagator.js +113 -47
  48. package/lib/compiler/resolve.js +1433 -0
  49. package/lib/compiler/shared.js +76 -38
  50. package/lib/compiler/tweak-assocs.js +529 -0
  51. package/lib/compiler/utils.js +204 -33
  52. package/lib/edm/.eslintrc.json +5 -0
  53. package/lib/edm/annotations/genericTranslation.js +38 -25
  54. package/lib/edm/annotations/preprocessAnnotations.js +3 -3
  55. package/lib/edm/csn2edm.js +10 -9
  56. package/lib/edm/edm.js +19 -20
  57. package/lib/edm/edmPreprocessor.js +166 -95
  58. package/lib/edm/edmUtils.js +127 -34
  59. package/lib/gen/Dictionary.json +92 -43
  60. package/lib/gen/language.checksum +1 -1
  61. package/lib/gen/language.interp +11 -1
  62. package/lib/gen/language.tokens +86 -82
  63. package/lib/gen/languageLexer.interp +18 -1
  64. package/lib/gen/languageLexer.js +925 -847
  65. package/lib/gen/languageLexer.tokens +78 -74
  66. package/lib/gen/languageParser.js +5434 -4298
  67. package/lib/json/from-csn.js +59 -17
  68. package/lib/json/to-csn.js +143 -71
  69. package/lib/language/antlrParser.js +3 -3
  70. package/lib/language/docCommentParser.js +3 -3
  71. package/lib/language/genericAntlrParser.js +144 -54
  72. package/lib/language/language.g4 +424 -203
  73. package/lib/language/multiLineStringParser.js +536 -0
  74. package/lib/main.d.ts +472 -61
  75. package/lib/main.js +38 -11
  76. package/lib/model/api.js +3 -1
  77. package/lib/model/csnRefs.js +321 -204
  78. package/lib/model/csnUtils.js +224 -263
  79. package/lib/model/enrichCsn.js +97 -40
  80. package/lib/model/revealInternalProperties.js +27 -6
  81. package/lib/model/sortViews.js +2 -1
  82. package/lib/modelCompare/compare.js +17 -12
  83. package/lib/optionProcessor.js +7 -6
  84. package/lib/render/DuplicateChecker.js +1 -1
  85. package/lib/render/manageConstraints.js +36 -33
  86. package/lib/render/toCdl.js +174 -275
  87. package/lib/render/toHdbcds.js +201 -115
  88. package/lib/render/toRename.js +7 -10
  89. package/lib/render/toSql.js +149 -75
  90. package/lib/render/utils/common.js +22 -8
  91. package/lib/render/utils/sql.js +10 -7
  92. package/lib/render/utils/stringEscapes.js +111 -0
  93. package/lib/sql-identifier.js +1 -1
  94. package/lib/transform/.eslintrc.json +5 -0
  95. package/lib/transform/braceExpression.js +4 -2
  96. package/lib/transform/db/.eslintrc.json +2 -0
  97. package/lib/transform/db/applyTransformations.js +35 -12
  98. package/lib/transform/db/assertUnique.js +1 -1
  99. package/lib/transform/db/associations.js +187 -0
  100. package/lib/transform/db/cdsPersistence.js +150 -0
  101. package/lib/transform/db/constraints.js +61 -56
  102. package/lib/transform/db/expansion.js +50 -29
  103. package/lib/transform/db/flattening.js +552 -105
  104. package/lib/transform/db/groupByOrderBy.js +3 -1
  105. package/lib/transform/db/temporal.js +236 -0
  106. package/lib/transform/db/transformExists.js +94 -28
  107. package/lib/transform/db/views.js +5 -4
  108. package/lib/transform/draft/.eslintrc.json +38 -0
  109. package/lib/transform/{db/draft.js → draft/db.js} +9 -7
  110. package/lib/transform/draft/odata.js +227 -0
  111. package/lib/transform/forHanaNew.js +94 -801
  112. package/lib/transform/forOdataNew.js +22 -175
  113. package/lib/transform/localized.js +36 -32
  114. package/lib/transform/odata/generateForeignKeyElements.js +3 -3
  115. package/lib/transform/odata/referenceFlattener.js +95 -89
  116. package/lib/transform/odata/structureFlattener.js +1 -1
  117. package/lib/transform/odata/toFinalBaseType.js +86 -12
  118. package/lib/transform/odata/typesExposure.js +5 -5
  119. package/lib/transform/odata/utils.js +2 -2
  120. package/lib/transform/transformUtilsNew.js +47 -33
  121. package/lib/transform/translateAssocsToJoins.js +10 -27
  122. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  123. package/lib/transform/universalCsn/coreComputed.js +170 -0
  124. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  125. package/lib/transform/universalCsn/utils.js +63 -0
  126. package/lib/utils/file.js +2 -1
  127. package/lib/utils/objectUtils.js +30 -0
  128. package/lib/utils/timetrace.js +8 -2
  129. package/package.json +1 -1
  130. package/share/messages/README.md +26 -0
  131. package/lib/compiler/definer.js +0 -2340
  132. package/lib/compiler/resolver.js +0 -2988
  133. package/lib/transform/universalCsnEnricher.js +0 -67
@@ -7,7 +7,7 @@ const { getUtils,
7
7
  cloneCsn,
8
8
  forEachDefinition,
9
9
  forEachMemberRecursively,
10
- forEachRef,
10
+ applyTransformationsOnNonDictionary,
11
11
  getArtifactDatabaseNameOf,
12
12
  getElementDatabaseNameOf,
13
13
  isAspect,
@@ -16,7 +16,7 @@ const { getUtils,
16
16
  } = require('../model/csnUtils');
17
17
  const { checkCSNVersion } = require('../json/csnVersion');
18
18
  const validate = require('../checks/validator');
19
- const { isArtifactInSomeService, getServiceOfArtifact, isLocalizedArtifactInService } = require('./odata/utils');
19
+ const { isArtifactInSomeService, isLocalizedArtifactInService } = require('./odata/utils');
20
20
  const ReferenceFlattener = require('./odata/referenceFlattener');
21
21
  const { flattenCSN } = require('./odata/structureFlattener');
22
22
  const generateForeignKeys = require('./odata/generateForeignKeyElements');
@@ -24,7 +24,8 @@ const expandStructKeysInAssociations = require('./odata/expandStructKeysInAssoci
24
24
  const expandToFinalBaseType = require('./odata/toFinalBaseType');
25
25
  const { timetrace } = require('../utils/timetrace');
26
26
  const { attachPath } = require('./odata/attachPath');
27
- const enrichUniversalCsn = require('./universalCsnEnricher');
27
+ const enrichUniversalCsn = require('./universalCsn/universalCsnEnricher');
28
+ const generateDrafts = require('./draft/odata');
28
29
 
29
30
  const { addLocalizationViews } = require('./localized');
30
31
 
@@ -75,7 +76,7 @@ function transform4odataWithCsn(inputModel, options) {
75
76
  // copy the model as we don't want to change the input model
76
77
  let csn = cloneCsn(inputModel, options);
77
78
 
78
- const { error, warning, info, throwWithError } = makeMessageFunction(csn, options, 'for.odata');
79
+ const { message, error, warning, info, throwWithError } = makeMessageFunction(csn, options, 'for.odata');
79
80
  throwWithError();
80
81
 
81
82
  // the new transformer works only with new CSN
@@ -84,22 +85,16 @@ function transform4odataWithCsn(inputModel, options) {
84
85
  const transformers = transformUtils.getTransformers(csn, options, '_');
85
86
  const {
86
87
  addDefaultTypeFacets,
87
- createForeignKeyElement,
88
- createAndAddDraftAdminDataProjection, createScalarElement,
89
- createAssociationElement, createAssociationPathComparison,
90
- addElement, createAction, assignAction,
91
88
  extractValidFromToKeyElement,
92
89
  checkAssignment, checkMultipleAssignments,
93
- recurseElements, setAnnotation, resetAnnotation, renameAnnotation,
90
+ recurseElements, setAnnotation, renameAnnotation,
94
91
  expandStructsInExpression
95
92
  } = transformers;
96
93
 
97
94
  const csnUtils = getUtils(csn);
98
95
  const {
99
96
  getCsnDef,
100
- getFinalType,
101
97
  getServiceName,
102
- hasAnnotationValue,
103
98
  isAssocOrComposition,
104
99
  isAssociation,
105
100
  isStructured,
@@ -133,7 +128,7 @@ function transform4odataWithCsn(inputModel, options) {
133
128
  addLocalizationViews(csn, options, acceptLocalizedView);
134
129
 
135
130
  validate.forOdata(csn, {
136
- error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, getFinalBaseType, isAspect, isExternalServiceMember
131
+ message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, getFinalBaseType, isAspect, isExternalServiceMember
137
132
  });
138
133
 
139
134
 
@@ -191,10 +186,10 @@ function transform4odataWithCsn(inputModel, options) {
191
186
  generateForeignKeys(csn, options, referenceFlattener, csnUtils, error, isExternalServiceMember);
192
187
 
193
188
  // Apply default type facets as set by options
194
- // Flatten on-conditions in unmanaged associations
189
+ // Flatten on-conditions in unmanaged associations
195
190
  /* FIXME (HJB): Is this comment still correct? processOnCond only strips $self
196
191
  We should not remove $self prefixes in structured OData to not
197
- interfer with path resolution
192
+ interfer with path resolution
198
193
  */
199
194
  // This must be done before all the draft logic as all
200
195
  // composition targets are annotated with @odata.draft.enabled in this step
@@ -209,19 +204,7 @@ function transform4odataWithCsn(inputModel, options) {
209
204
  // - structured types must not contain associations for OData V2
210
205
  // - Element must not be an 'array of' for OData V2 TODO: move to the validator
211
206
  // - Perform checks for exposed non-abstract entities and views - check media type and key-ness
212
- let visitedArtifacts = Object.create(null);
213
- forEachDefinition(csn, (def, defName) => {
214
- if (def.kind === 'entity' || def.kind === 'view') {
215
- // Generate artificial draft fields if requested
216
- if (def['@odata.draft.enabled']) {
217
- // Ignore if not part of a service
218
- if (isArtifactInSomeService(defName, services)) {
219
- generateDraftForOdata(def, defName, def, visitedArtifacts);
220
- }
221
- }
222
- }
223
- visitedArtifacts[defName] = true;
224
- }, { skipArtifact: isExternalServiceMember });
207
+ generateDrafts(csn, options, services)
225
208
 
226
209
  // Deal with all kind of annotations manipulations here
227
210
  forEachDefinition(csn, (def, defName) => {
@@ -303,8 +286,9 @@ function transform4odataWithCsn(inputModel, options) {
303
286
  // If @Core.Computed is explicitly set, don't overwrite it!
304
287
  if (node['@Core.Computed'] !== undefined) return;
305
288
 
306
- // For @odata.on.insert/update, also add @Core.Computed
307
- if (node['@odata.on.insert'] || node['@odata.on.update'])
289
+ // For @odata.on.insert/update, also add @Core.Computed
290
+ // @odata.on is deprecated, use @cds.on {update|insert} instead
291
+ if(['@odata.on.insert', '@odata.on.update', '@cds.on.insert', '@cds.on.update'].some(a => node[a]))
308
292
  node['@Core.Computed'] = true;
309
293
  }
310
294
 
@@ -341,7 +325,7 @@ function transform4odataWithCsn(inputModel, options) {
341
325
  // but '@Core.Immutable' for everything else.
342
326
  if (!(node['@readonly'] && node['@insertonly'])) {
343
327
  if (name === '@readonly' && node[name] !== null) {
344
- if (node.kind === 'entity' || node.kind === 'view') {
328
+ if (node.kind === 'entity') {
345
329
  setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
346
330
  setAnnotation(node, '@Capabilities.InsertRestrictions.Insertable', false);
347
331
  setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
@@ -351,7 +335,7 @@ function transform4odataWithCsn(inputModel, options) {
351
335
  }
352
336
  // @insertonly is effective on entities/queries only
353
337
  else if (name === '@insertonly' && node[name] !== null) {
354
- if (node.kind === 'entity' || node.kind === 'view') {
338
+ if (node.kind === 'entity') {
355
339
  setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
356
340
  setAnnotation(node, '@Capabilities.ReadRestrictions.Readable', false);
357
341
  setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
@@ -422,151 +406,14 @@ function transform4odataWithCsn(inputModel, options) {
422
406
  // removes leading $self in on-conditions's references
423
407
  function removeLeadingDollarSelfInOnCondition(assoc) {
424
408
  if (!assoc.on) return; // nothing to do
425
- forEachRef(assoc, (ref, node) => {
426
- // remove leading $self when at the begining of a ref
427
- if (ref.length > 1 && ref[0] === '$self')
428
- node.ref.splice(0, 1);
429
- });
430
- }
431
- }
432
-
433
- // Generate all that is required in ODATA for draft enablement of 'artifact' into the artifact,
434
- // into its transitively reachable composition targets, and into the model.
435
- // 'rootArtifact' is the root artifact where composition traversal started.
436
-
437
- // Constraints
438
- // Draft Root: Exactly one PK of type UUID
439
- // Draft Node: One PK of type UUID + 0..1 PK of another type
440
- // Draft Node: Must not be reachable from multiple draft roots
441
- function generateDraftForOdata(artifact, artifactName, rootArtifact, visitedArtifacts) {
442
- // Sanity check
443
- // @ts-ignore
444
- if (!isArtifactInSomeService(artifactName, services)) {
445
- throw new Error('Expecting artifact to be part of a service: ' + JSON.stringify(artifact));
446
- }
447
- // Nothing to do if already draft-enabled (composition traversal may have circles)
448
- if ((artifact['@Common.DraftRoot.PreparationAction'] || artifact['@Common.DraftNode.PreparationAction'])
449
- && artifact.actions && artifact.actions.draftPrepare) {
450
- return;
451
- }
452
-
453
- // Generate the DraftAdministrativeData projection into the service, unless there is already one
454
- // @ts-ignore
455
- let draftAdminDataProjectionName = `${getServiceOfArtifact(artifactName, services)}.DraftAdministrativeData`;
456
- let draftAdminDataProjection = csn.definitions[draftAdminDataProjectionName];
457
- if (!draftAdminDataProjection) {
458
- // @ts-ignore
459
- draftAdminDataProjection = createAndAddDraftAdminDataProjection(getServiceOfArtifact(artifactName, services));
460
- }
461
- // Report an error if it is not an entity or not what we expect
462
- if (draftAdminDataProjection.kind !== 'entity' || !draftAdminDataProjection.elements['DraftUUID']) {
463
- error(null, ['definitions', draftAdminDataProjectionName], { name: draftAdminDataProjectionName },
464
- `Generated entity $(NAME) conflicts with existing artifact`);
465
- }
466
- // Generate the annotations describing the draft actions (only draft roots can be activated/edited)
467
- if (artifact == rootArtifact) {
468
- resetAnnotation(artifact, '@Common.DraftRoot.ActivationAction', 'draftActivate', info, ['definitions', draftAdminDataProjectionName]);
469
- resetAnnotation(artifact, '@Common.DraftRoot.EditAction', 'draftEdit', info, ['definitions', draftAdminDataProjectionName]);
470
- resetAnnotation(artifact, '@Common.DraftRoot.PreparationAction', 'draftPrepare', info, ['definitions', draftAdminDataProjectionName]);
471
- } else {
472
- resetAnnotation(artifact, '@Common.DraftNode.PreparationAction', 'draftPrepare', info, ['definitions', draftAdminDataProjectionName]);
473
- }
474
-
475
- artifact.elements && Object.values(artifact.elements).forEach( elem => {
476
- // Make all non-key elements nullable
477
- if (elem.notNull && elem.key !== true) {
478
- delete elem.notNull;
479
- }
480
- });
481
- // Generate the additional elements into the draft-enabled artifact
482
-
483
- // key IsActiveEntity : Boolean default true
484
- let isActiveEntity = createScalarElement('IsActiveEntity', 'cds.Boolean', true, true, false);
485
- isActiveEntity.IsActiveEntity['@UI.Hidden'] = true;
486
- addElement(isActiveEntity, artifact, artifactName);
487
-
488
- // HasActiveEntity : Boolean default false
489
- let hasActiveEntity = createScalarElement('HasActiveEntity', 'cds.Boolean', false, false, true);
490
- hasActiveEntity.HasActiveEntity['@UI.Hidden'] = true;
491
- addElement(hasActiveEntity, artifact, artifactName);
492
-
493
- // HasDraftEntity : Boolean default false;
494
- let hasDraftEntity = createScalarElement('HasDraftEntity', 'cds.Boolean', false, false, true);
495
- hasDraftEntity.HasDraftEntity['@UI.Hidden'] = true;
496
- addElement(hasDraftEntity, artifact, artifactName);
497
-
498
- // @odata.contained: true
499
- // DraftAdministrativeData : Association to one DraftAdministrativeData;
500
- let draftAdministrativeData = createAssociationElement('DraftAdministrativeData', draftAdminDataProjectionName, true);
501
- draftAdministrativeData.DraftAdministrativeData.cardinality = { max: 1, };
502
- draftAdministrativeData.DraftAdministrativeData['@odata.contained'] = true;
503
- draftAdministrativeData.DraftAdministrativeData['@UI.Hidden'] = true;
504
- addElement(draftAdministrativeData, artifact, artifactName);
505
-
506
- // Note that we need to do the ODATA transformation steps for managed associations
507
- // (foreign key field generation, generatedFieldName) by hand, because the corresponding
508
- // transformation steps have already been done on all artifacts when we come here)
509
- let uuidDraftKey = draftAdministrativeData.DraftAdministrativeData.keys.filter(key => key.ref && key.ref.length === 1 && key.ref[0] === 'DraftUUID');
510
- if (uuidDraftKey && uuidDraftKey[0]) {
511
- uuidDraftKey = uuidDraftKey[0]; // filter returns an array, but it has only one element
512
- let path = ['definitions', artifactName, 'elements', 'DraftAdministrativeData', 'keys', 0];
513
- createForeignKeyElement(draftAdministrativeData.DraftAdministrativeData, 'DraftAdministrativeData', uuidDraftKey, artifact, artifactName, path);
514
- }
515
- // SiblingEntity : Association to one <artifact> on (... IsActiveEntity unequal, all other key fields equal ...)
516
- let siblingEntity = createAssociationElement('SiblingEntity', artifactName, false);
517
- siblingEntity.SiblingEntity.cardinality = { max: 1 };
518
- addElement(siblingEntity, artifact, artifactName);
519
- // ... on SiblingEntity.IsActiveEntity != IsActiveEntity ...
520
- siblingEntity.SiblingEntity.on = createAssociationPathComparison('SiblingEntity', 'IsActiveEntity', '!=', 'IsActiveEntity');
521
-
522
- // Iterate elements
523
- artifact.elements && Object.entries(artifact.elements).forEach( ([elemName, elem]) => {
524
- if (elemName !== 'IsActiveEntity' && elem.key) {
525
- // Amend the ON-condition above:
526
- // ... and SiblingEntity.<keyfield> = <keyfield> ... (for all key fields except 'IsActiveEntity')
527
- let cond = createAssociationPathComparison('SiblingEntity', elemName, '=', elemName);
528
- cond.push('and');
529
- cond.push(...siblingEntity.SiblingEntity.on);
530
- siblingEntity.SiblingEntity.on = cond;
531
- }
532
-
533
- // Draft-enable the targets of composition elements (draft nodes), too
534
- // TODO rewrite
535
- if (elem.target && elem.type && getFinalType(elem.type) === 'cds.Composition') {
536
- let draftNode = csn.definitions[elem.target];
537
-
538
- // Ignore if that is our own draft root
539
- if (draftNode != rootArtifact) {
540
- // Report error when the draft node has @odata.draft.enabled itself
541
- if (hasAnnotationValue(draftNode, '@odata.draft.enabled', true)) {
542
- error(null, ['definitions', artifactName, 'elements', elemName], 'Composition in draft-enabled entity can\'t lead to another entity with “@odata.draft.enabled”');
543
- }
544
- // Ignore composition if not part of a service or explicitly draft disabled
545
- else if (!getServiceName(elem.target) || hasAnnotationValue(draftNode, '@odata.draft.enabled', false)) {
546
- return;
547
- }
548
- else {
549
- // Generate draft stuff into the target
550
- generateDraftForOdata(draftNode, elem.target, rootArtifact, visitedArtifacts);
551
- }
409
+ // TODO: Shouldn't this only run on the on-condition and not the whole assoc-node?
410
+ applyTransformationsOnNonDictionary({ assoc }, 'assoc', {
411
+ ref: (node, prop, ref) => {
412
+ // remove leading $self when at the begining of a ref
413
+ if (ref.length > 1 && ref[0] === '$self')
414
+ node.ref.splice(0, 1);
552
415
  }
553
- }
554
- });
555
-
556
- // Generate the actions into the draft-enabled artifact (only draft roots can be activated/edited)
557
-
558
- // action draftPrepare (SideEffectsQualifier: String) return <artifact>;
559
- let draftPrepare = createAction('draftPrepare', artifactName, 'SideEffectsQualifier', 'cds.String');
560
- assignAction(draftPrepare, artifact);
561
-
562
- if (artifact == rootArtifact) {
563
- // action draftActivate() return <artifact>;
564
- let draftActivate = createAction('draftActivate', artifactName);
565
- assignAction(draftActivate, artifact);
566
-
567
- // action draftEdit (PreserveChanges: Boolean) return <artifact>;
568
- let draftEdit = createAction('draftEdit', artifactName, 'PreserveChanges', 'cds.Boolean');
569
- assignAction(draftEdit, artifact);
416
+ });
570
417
  }
571
418
  }
572
419
 
@@ -5,14 +5,12 @@ const { setProp } = require('../base/model');
5
5
  const { hasErrors } = require('../base/messages');
6
6
  const { cloneCsnDictionary } = require('../model/csnUtils');
7
7
  const { cleanSymbols } = require('../base/cleanSymbols.js');
8
- const { rejectManagedAssociationsAndStructuresForHdbcsNames } = require('../checks/selectItems');
9
8
  const {
10
9
  cloneCsn,
11
10
  forEachDefinition,
12
11
  forEachGeneric,
13
12
  forAllQueries,
14
13
  sortCsnDefinitionsForTests,
15
- getUtils,
16
14
  } = require('../model/csnUtils');
17
15
 
18
16
  /**
@@ -50,7 +48,7 @@ const _targetFor = Symbol('_targetFor');
50
48
  * We have three kinds of localized convenience views:
51
49
  *
52
50
  * 1. "direct ones" using coalesce() for the table entities with localized
53
- * elements: as projection on the original (created in definer.js)
51
+ * elements: as projection on the original (created in extend.js)
54
52
  * 2. for table entities with associations to entities which have a localized
55
53
  * convenience views or redirections thereon: as projection on the original
56
54
  * 3. for view entities with associations to entities which have a localized
@@ -76,27 +74,17 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
76
74
  if (hasErrors(options.messages))
77
75
  return csn;
78
76
 
79
- if (hasExistingLocalizationViews(csn, options))
77
+ const messageFunctions = makeMessageFunction(csn, options);
78
+ if (hasExistingLocalizationViews(csn, options, messageFunctions))
80
79
  return csn;
81
80
 
82
- const { info, error } = makeMessageFunction(csn, options);
83
-
84
81
  const noCoalesce = (options.localizedLanguageFallback === 'none' ||
85
82
  options.localizedWithoutCoalesce);
86
83
 
87
84
  createDirectConvenienceViews(); // 1
88
85
  createTransitiveConvenienceViews(); // 2 + 3
89
86
 
90
- forEachDefinition(csn, (definition, artName, prop, path) => {
91
- cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor)
92
- if(definition.query) {
93
- // reject managed association and structure publishing for to-hdbcds.hdbcds
94
- const that = { csnUtils: getUtils(csn), options, error };
95
- rejectManagedAssociationsAndStructuresForHdbcsNames.call(that, definition, path)
96
- }
97
- });
98
-
99
-
87
+ forEachDefinition(csn, definition => cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor));
100
88
 
101
89
  sortCsnDefinitionsForTests(csn, options);
102
90
  return csn;
@@ -113,7 +101,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
113
101
  return;
114
102
 
115
103
  if (isInLocalizedNamespace(artName))
116
- // We already issued a warning for it in warnAboutExistingLocalizationViews()
104
+ // We already issued a warning for it in hasExistingLocalizationViews()
117
105
  return;
118
106
 
119
107
  const localized = getLocalizedTextElements( artName );
@@ -138,13 +126,13 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
138
126
 
139
127
  if (csn.definitions[viewName]) {
140
128
  // Already exists, skip creation.
141
- info( null, artPath, null, 'Convenience view can\'t be created due to conflicting names' );
129
+ messageFunctions.info( null, artPath, null, 'Convenience view can\'t be created due to conflicting names' );
142
130
  return;
143
131
  }
144
132
 
145
133
  art[_hasLocalizedView] = viewName;
146
134
 
147
- if(acceptLocalizedView && !acceptLocalizedView(viewName, artName))
135
+ if (acceptLocalizedView && !acceptLocalizedView(viewName, artName))
148
136
  return;
149
137
 
150
138
  let view;
@@ -350,7 +338,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
350
338
  if (elem.key || elem.$key || elem.localized)
351
339
  textElements.push( elemName );
352
340
 
353
- // TODO: Already warned about in definer.js
341
+ // TODO: Already warned about in extend.js
354
342
  // if (elem.key && isLocalized)
355
343
  // warning( 'localized-key', path, {}, 'Keyword "localized" is ignored for primary keys' );
356
344
  }, artPath);
@@ -360,7 +348,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
360
348
  return null;
361
349
 
362
350
  if (!isEntityPreprocessed( art )) {
363
- info( null, artPath, { name: artName },
351
+ messageFunctions.info( null, artPath, { name: artName },
364
352
  'Skipped creation of convenience view for $(NAME) because the artifact is missing localization elements' );
365
353
  return null;
366
354
  }
@@ -369,13 +357,13 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
369
357
  const textsEntity = csn.definitions[textsName];
370
358
 
371
359
  if (!textsEntity) {
372
- info( null, artPath, { name: artName },
360
+ messageFunctions.info( null, artPath, { name: artName },
373
361
  'Skipped creation of convenience view for $(NAME) because its texts entity could not be found' );
374
362
  return null;
375
363
  }
376
364
 
377
365
  if (!isValidTextsEntity( textsEntity )) {
378
- info( null, [ 'definitions', textsName ], { name: artName },
366
+ messageFunctions.info( null, [ 'definitions', textsName ], { name: artName },
379
367
  'Skipped creation of convenience view for $(NAME) because its texts entity does not appear to be valid' );
380
368
  return null;
381
369
  }
@@ -696,18 +684,32 @@ function copyPersistenceAnnotations(target, source) {
696
684
  *
697
685
  * @param {CSN.Model} csn
698
686
  * @param {CSN.Options} options
687
+ * @param {object} messageFunctions
699
688
  */
700
- function hasExistingLocalizationViews(csn, options) {
689
+ function hasExistingLocalizationViews(csn, options, messageFunctions) {
701
690
  if (!csn || !csn.definitions)
702
691
  return false;
703
- const firstLocalizedView = Object.keys(csn.definitions).find(isInLocalizedNamespace);
704
- if (firstLocalizedView) {
705
- const { info } = makeMessageFunction(csn, options);
706
- info( null, [ 'definitions', firstLocalizedView ], {},
707
- 'Input CSN already contains expansions for localized data' );
708
- return true;
692
+
693
+ let hasExistingViews = false;
694
+ let hasNonViews = false;
695
+
696
+ for (const name in csn.definitions) {
697
+ const art = csn.definitions[name];
698
+ if (isInLocalizedNamespace(name) || name === 'localized') {
699
+ if (!art.query && !art.projection) {
700
+ if (!name.endsWith('.texts')) {
701
+ hasNonViews = true;
702
+ messageFunctions.error('reserved-namespace-localized', ['definitions', name], {},
703
+ 'The namespace "localized" is reserved for localization views');
704
+ }
705
+ } else if (!hasExistingViews) {
706
+ hasExistingViews = true;
707
+ messageFunctions.info( null, [ 'definitions', name ], {},
708
+ 'Input CSN already contains localization views, no further ones will be created' );
709
+ }
710
+ }
709
711
  }
710
- return false;
712
+ return hasExistingViews || hasNonViews;
711
713
  }
712
714
 
713
715
  /**
@@ -739,9 +741,10 @@ function isEntityPreprocessed(entity) {
739
741
 
740
742
  /**
741
743
  * @param {string} name
744
+ * @returns {boolean}
742
745
  */
743
746
  function isInLocalizedNamespace(name) {
744
- return name.startsWith('localized.');
747
+ return name === 'localized' || name.startsWith('localized.');
745
748
  }
746
749
 
747
750
  /**
@@ -749,6 +752,7 @@ function isInLocalizedNamespace(name) {
749
752
  *
750
753
  * @param {CSN.Model} csn
751
754
  * @param {string} artifactName
755
+ * @returns {boolean}
752
756
  */
753
757
  function hasLocalizedConvenienceView(csn, artifactName) {
754
758
  return !isInLocalizedNamespace(artifactName) && !!csn.definitions[`localized.${ artifactName }`];
@@ -37,7 +37,7 @@ module.exports = function (csn, options, referenceFlattener, csnUtils, error, is
37
37
  sortedAssociations.forEach(item => {
38
38
  const { definitionName, structuralNodeName, elementName, element, parent, path } = item;
39
39
 
40
- if (csnUtils.isManagedAssociationElement(element) && element.keys) {
40
+ if (csnUtils.isManagedAssociation(element) && element.keys) {
41
41
  if (flatKeys) // tackling the ref value in assoc.keys
42
42
  takeoverForeignKeysOfTargetAssociations(element, path, generatedForeignKeyNamesForPath);
43
43
  // TODO: move in separate function
@@ -201,13 +201,13 @@ module.exports = function (csn, options, referenceFlattener, csnUtils, error, is
201
201
  // Transfer selected type properties from target key element
202
202
  // FIXME: There is currently no other way but to treat the annotation '@odata.Type' as a type property.
203
203
  for (let prop of ['type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type']) {
204
- if (fkArtifact[prop] != undefined) {
204
+ if (fkArtifact[prop] !== undefined) {
205
205
  foreignKeyElement[prop] = fkArtifact[prop];
206
206
  }
207
207
  }
208
208
  // If the association is non-fkArtifact resp. key, so should be the foreign key field
209
209
  for (let prop of ['notNull', 'key']) {
210
- if (assoc[prop] != undefined) {
210
+ if (assoc[prop] !== undefined) {
211
211
  foreignKeyElement[prop] = assoc[prop];
212
212
  }
213
213
  }