@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
@@ -0,0 +1,227 @@
1
+ 'use strict';
2
+
3
+ const { forEachDefinition, getUtils, getServiceNames } = require('../../model/csnUtils');
4
+ const { forEach } = require('../../utils/objectUtils');
5
+ const { isArtifactInSomeService, getServiceOfArtifact } = require('../odata/utils');
6
+ const { getTransformers } = require('../transformUtilsNew');
7
+ const { ModelError } = require('../../base/error');
8
+ const { makeMessageFunction } = require('../../base/messages');
9
+
10
+ /**
11
+ * - Generate artificial draft fields if requested
12
+ *
13
+ * - Check associations for:
14
+ * - exposed associations do not point to non-exposed targets
15
+ * - structured types must not contain associations for OData V2
16
+ * - Element must not be an 'array of' for OData V2 TODO: move to the validator
17
+ * - Perform checks for exposed non-abstract entities and views - check media type and key-ness
18
+ *
19
+ * @param {CSN.Model} csn
20
+ * @param {CSN.Options} options
21
+ * @param {Array} [services] Will be calculated JIT if not provided
22
+ * @returns {CSN.Model} Returns the transformed input model
23
+ * @todo should be done by the compiler - Check associations for valid foreign keys
24
+ * @todo check if needed at all: Remove '$projection' from paths in the element's ON-condition
25
+ */
26
+ function generateDrafts(csn, options, services) {
27
+ const {
28
+ createForeignKeyElement,
29
+ createAndAddDraftAdminDataProjection, createScalarElement,
30
+ createAssociationElement, createAssociationPathComparison,
31
+ addElement, createAction, assignAction,
32
+ resetAnnotation,
33
+ } = getTransformers(csn, options);
34
+
35
+ const {
36
+ getFinalType,
37
+ getServiceName,
38
+ hasAnnotationValue,
39
+ getFinalBaseType,
40
+ getFinalTypeDef,
41
+ } = getUtils(csn);
42
+
43
+ const { error, info } = makeMessageFunction(csn, options, 'for.odata');
44
+
45
+ if (!services)
46
+ services = getServiceNames(csn);
47
+
48
+ const visitedArtifacts = Object.create(null);
49
+ // @ts-ignore
50
+ const externalServices = services.filter(serviceName => csn.definitions[serviceName]['@cds.external']);
51
+ // @ts-ignore
52
+ const isExternalServiceMember = (_art, name) => externalServices.includes(getServiceName(name));
53
+
54
+ forEachDefinition(csn, (def, defName) => {
55
+ // Generate artificial draft fields for entities/views if requested, ignore if not part of a service
56
+ if (def.kind === 'entity' && def['@odata.draft.enabled'] && isArtifactInSomeService(defName, services))
57
+ generateDraftForOdata(def, defName, def);
58
+
59
+ visitedArtifacts[defName] = true;
60
+ }, { skipArtifact: isExternalServiceMember });
61
+
62
+ return csn;
63
+ /**
64
+ * Generate all that is required in ODATA for draft enablement of 'artifact' into the artifact,
65
+ * into its transitively reachable composition targets, and into the model.
66
+ * 'rootArtifact' is the root artifact where composition traversal started.
67
+ *
68
+ * Constraints
69
+ * Draft Root: Exactly one PK of type UUID
70
+ * Draft Node: One PK of type UUID + 0..1 PK of another type
71
+ * Draft Node: Must not be reachable from multiple draft roots
72
+ *
73
+ * @param {CSN.Artifact} artifact
74
+ * @param {string} artifactName
75
+ * @param {CSN.Artifact} rootArtifact artifact where composition traversal started
76
+ */
77
+ function generateDraftForOdata(artifact, artifactName, rootArtifact) {
78
+ // Sanity check
79
+ // @ts-ignore
80
+ if (!isArtifactInSomeService(artifactName, services))
81
+ throw new ModelError(`Expecting artifact to be part of a service: ${JSON.stringify(artifact)}`);
82
+
83
+ // Nothing to do if already draft-enabled (composition traversal may have circles)
84
+ if ((artifact['@Common.DraftRoot.PreparationAction'] || artifact['@Common.DraftNode.PreparationAction']) &&
85
+ artifact.actions && artifact.actions.draftPrepare)
86
+ return;
87
+
88
+
89
+ // Generate the DraftAdministrativeData projection into the service, unless there is already one
90
+ // @ts-ignore
91
+ const draftAdminDataProjectionName = `${getServiceOfArtifact(artifactName, services)}.DraftAdministrativeData`;
92
+ let draftAdminDataProjection = csn.definitions[draftAdminDataProjectionName];
93
+ if (!draftAdminDataProjection) {
94
+ // @ts-ignore
95
+ draftAdminDataProjection = createAndAddDraftAdminDataProjection(getServiceOfArtifact(artifactName, services));
96
+ }
97
+ // Report an error if it is not an entity or not what we expect
98
+ if (draftAdminDataProjection.kind !== 'entity' || !draftAdminDataProjection.elements.DraftUUID) {
99
+ error(null, [ 'definitions', draftAdminDataProjectionName ], { name: draftAdminDataProjectionName },
100
+ 'Generated entity $(NAME) conflicts with existing artifact');
101
+ }
102
+ // Generate the annotations describing the draft actions (only draft roots can be activated/edited)
103
+ if (artifact === rootArtifact) {
104
+ resetAnnotation(artifact, '@Common.DraftRoot.ActivationAction', 'draftActivate', info, [ 'definitions', draftAdminDataProjectionName ]);
105
+ resetAnnotation(artifact, '@Common.DraftRoot.EditAction', 'draftEdit', info, [ 'definitions', draftAdminDataProjectionName ]);
106
+ resetAnnotation(artifact, '@Common.DraftRoot.PreparationAction', 'draftPrepare', info, [ 'definitions', draftAdminDataProjectionName ]);
107
+ }
108
+ else {
109
+ resetAnnotation(artifact, '@Common.DraftNode.PreparationAction', 'draftPrepare', info, [ 'definitions', draftAdminDataProjectionName ]);
110
+ }
111
+
112
+ Object.values(artifact.elements || {}).forEach( (elem) => {
113
+ // Make all non-key elements nullable
114
+ if (elem.notNull && elem.key !== true)
115
+ delete elem.notNull;
116
+ });
117
+ // Generate the additional elements into the draft-enabled artifact
118
+
119
+ // key IsActiveEntity : Boolean default true
120
+ const isActiveEntity = createScalarElement('IsActiveEntity', 'cds.Boolean', true, true, false);
121
+ isActiveEntity.IsActiveEntity['@UI.Hidden'] = true;
122
+ addElement(isActiveEntity, artifact, artifactName);
123
+
124
+ // HasActiveEntity : Boolean default false
125
+ const hasActiveEntity = createScalarElement('HasActiveEntity', 'cds.Boolean', false, false, true);
126
+ hasActiveEntity.HasActiveEntity['@UI.Hidden'] = true;
127
+ addElement(hasActiveEntity, artifact, artifactName);
128
+
129
+ // HasDraftEntity : Boolean default false;
130
+ const hasDraftEntity = createScalarElement('HasDraftEntity', 'cds.Boolean', false, false, true);
131
+ hasDraftEntity.HasDraftEntity['@UI.Hidden'] = true;
132
+ addElement(hasDraftEntity, artifact, artifactName);
133
+
134
+ // @odata.contained: true
135
+ // DraftAdministrativeData : Association to one DraftAdministrativeData;
136
+ const draftAdministrativeData = createAssociationElement('DraftAdministrativeData', draftAdminDataProjectionName, true);
137
+ draftAdministrativeData.DraftAdministrativeData.cardinality = { max: 1 };
138
+ draftAdministrativeData.DraftAdministrativeData['@odata.contained'] = true;
139
+ draftAdministrativeData.DraftAdministrativeData['@UI.Hidden'] = true;
140
+ addElement(draftAdministrativeData, artifact, artifactName);
141
+
142
+ // Note that we need to do the ODATA transformation steps for managed associations
143
+ // (foreign key field generation, generatedFieldName) by hand, because the corresponding
144
+ // transformation steps have already been done on all artifacts when we come here)
145
+ let uuidDraftKey = draftAdministrativeData.DraftAdministrativeData.keys.filter(key => key.ref && key.ref.length === 1 && key.ref[0] === 'DraftUUID');
146
+ if (uuidDraftKey && uuidDraftKey[0]) {
147
+ uuidDraftKey = uuidDraftKey[0]; // filter returns an array, but it has only one element
148
+ const path = [ 'definitions', artifactName, 'elements', 'DraftAdministrativeData', 'keys', 0 ];
149
+ createForeignKeyElement(draftAdministrativeData.DraftAdministrativeData, 'DraftAdministrativeData', uuidDraftKey, artifact, artifactName, path);
150
+ }
151
+ // SiblingEntity : Association to one <artifact> on (... IsActiveEntity unequal, all other key fields equal ...)
152
+ const siblingEntity = createAssociationElement('SiblingEntity', artifactName, false);
153
+ siblingEntity.SiblingEntity.cardinality = { max: 1 };
154
+ addElement(siblingEntity, artifact, artifactName);
155
+ // ... on SiblingEntity.IsActiveEntity != IsActiveEntity ...
156
+ siblingEntity.SiblingEntity.on = createAssociationPathComparison('SiblingEntity', 'IsActiveEntity', '!=', 'IsActiveEntity');
157
+
158
+ // Iterate elements
159
+ // TODO: Iterative vs recursive? What is more likely: Super deep nesting or cycles via malicious CSN?
160
+ if (artifact.elements) {
161
+ // No need to reverse the stack, not order dependent
162
+ const stack = [ artifact ];
163
+ while (stack.length > 0) {
164
+ const { elements } = stack.pop();
165
+ forEach(elements, (elemName, elem) => {
166
+ if (elemName !== 'IsActiveEntity' && elem.key) {
167
+ // Amend the ON-condition above:
168
+ // ... and SiblingEntity.<keyfield> = <keyfield> ... (for all key fields except 'IsActiveEntity')
169
+ const cond = createAssociationPathComparison('SiblingEntity', elemName, '=', elemName);
170
+ cond.push('and');
171
+ cond.push(...siblingEntity.SiblingEntity.on);
172
+ siblingEntity.SiblingEntity.on = cond;
173
+ }
174
+
175
+ // Draft-enable the targets of composition elements (draft nodes), too
176
+ // TODO rewrite
177
+ if (elem.target && elem.type && getFinalType(elem.type) === 'cds.Composition') {
178
+ const draftNode = csn.definitions[elem.target];
179
+
180
+ // Ignore if that is our own draft root
181
+ if (draftNode !== rootArtifact) {
182
+ // Report error when the draft node has @odata.draft.enabled itself
183
+ if (hasAnnotationValue(draftNode, '@odata.draft.enabled', true)) {
184
+ error('ref-unexpected-draft-enabled', [ 'definitions', artifactName, 'elements', elemName ], { anno: '@odata.draft.enabled' });
185
+ }
186
+ // Ignore composition if it's target is not part of a service or explicitly draft disabled
187
+ else if (!getServiceName(elem.target) || hasAnnotationValue(draftNode, '@odata.draft.enabled', false)) {
188
+ return;
189
+ }
190
+ else {
191
+ // Generate draft stuff into the target
192
+ generateDraftForOdata(draftNode, elem.target, rootArtifact);
193
+ }
194
+ }
195
+ }
196
+ else if (elem.elements) { // anonymous structure
197
+ stack.push(elem);
198
+ }
199
+ else if (elem.type) { // types - possibly structured
200
+ const typeDef = elem.type.ref ? getFinalBaseType(elem.type) : getFinalTypeDef(elem.type);
201
+ if (typeDef.elements)
202
+ stack.push(typeDef);
203
+ }
204
+ });
205
+ }
206
+ }
207
+
208
+
209
+ // Generate the actions into the draft-enabled artifact (only draft roots can be activated/edited)
210
+
211
+ // action draftPrepare (SideEffectsQualifier: String) return <artifact>;
212
+ const draftPrepare = createAction('draftPrepare', artifactName, 'SideEffectsQualifier', 'cds.String');
213
+ assignAction(draftPrepare, artifact);
214
+
215
+ if (artifact === rootArtifact) {
216
+ // action draftActivate() return <artifact>;
217
+ const draftActivate = createAction('draftActivate', artifactName);
218
+ assignAction(draftActivate, artifact);
219
+
220
+ // action draftEdit (PreserveChanges: Boolean) return <artifact>;
221
+ const draftEdit = createAction('draftEdit', artifactName, 'PreserveChanges', 'cds.Boolean');
222
+ assignAction(draftEdit, artifact);
223
+ }
224
+ }
225
+ }
226
+
227
+ module.exports = generateDrafts;