@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/model/csnUtils.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { csnRefs } = require('../model/csnRefs');
|
|
3
|
+
const { csnRefs, implicitAs } = require('../model/csnRefs');
|
|
4
4
|
const { applyTransformations, applyTransformationsOnNonDictionary } = require('../transform/db/applyTransformations');
|
|
5
5
|
const { isBuiltinType } = require('../compiler/builtins.js')
|
|
6
6
|
const { sortCsn, cloneCsnDictionary: _cloneCsnDictionary } = require('../json/to-csn');
|
|
7
|
+
const { ModelError } = require("../base/error");
|
|
7
8
|
const version = require('../../package.json').version;
|
|
8
9
|
|
|
9
10
|
// Low-level utility functions to work with compact CSN.
|
|
@@ -36,15 +37,15 @@ const version = require('../../package.json').version;
|
|
|
36
37
|
* Get utility functions for a given CSN.
|
|
37
38
|
* @param {CSN.Model} model (Compact) CSN model
|
|
38
39
|
*/
|
|
39
|
-
function getUtils(model) {
|
|
40
|
-
const { artifactRef, inspectRef, effectiveType, getOrigin } = csnRefs(model);
|
|
40
|
+
function getUtils(model, universalReady) {
|
|
41
|
+
const { artifactRef, inspectRef, effectiveType, getOrigin, targetAspect, getColumn, getElement, initDefinition } = csnRefs(model, universalReady);
|
|
41
42
|
|
|
42
43
|
return {
|
|
43
44
|
getCsnDef,
|
|
44
45
|
isStructured,
|
|
45
46
|
getFinalType,
|
|
46
47
|
getFinalTypeDef,
|
|
47
|
-
|
|
48
|
+
isManagedAssociation,
|
|
48
49
|
isAssocOrComposition,
|
|
49
50
|
isAssociation,
|
|
50
51
|
isComposition,
|
|
@@ -62,6 +63,10 @@ function getUtils(model) {
|
|
|
62
63
|
get$combined,
|
|
63
64
|
getOrigin,
|
|
64
65
|
getQueryPrimarySource,
|
|
66
|
+
targetAspect,
|
|
67
|
+
getColumn,
|
|
68
|
+
getElement,
|
|
69
|
+
initDefinition
|
|
65
70
|
};
|
|
66
71
|
|
|
67
72
|
/**
|
|
@@ -71,125 +76,125 @@ function getUtils(model) {
|
|
|
71
76
|
* @returns {object}
|
|
72
77
|
*/
|
|
73
78
|
function get$combined(query) {
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
return getSources(query);
|
|
80
|
+
}
|
|
76
81
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Get the union of all elements from the from clause
|
|
84
|
+
* - descend into unions, following the lead query
|
|
85
|
+
* - merge all queries in case of joins
|
|
86
|
+
* - follow subqueries
|
|
87
|
+
*
|
|
88
|
+
* @param {CSN.Query} query Query to check
|
|
89
|
+
* @param {boolean} [isSubquery]
|
|
90
|
+
* @returns {object} Map of sources
|
|
91
|
+
*/
|
|
92
|
+
function getSources(query, isSubquery=false) {
|
|
93
|
+
// Remark CW: better just a while along query.SET.args[0]
|
|
94
|
+
if (query.SET) {
|
|
95
|
+
if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
|
|
96
|
+
return mergeElementsIntoMap(Object.create(null), query.SET.args[0].SELECT.elements, query.SET.args[0].$location);
|
|
91
97
|
|
|
92
|
-
|
|
98
|
+
return getSources(query.SET.args[0], isSubquery);
|
|
99
|
+
}
|
|
100
|
+
else if (query.SELECT) {
|
|
101
|
+
if (query.SELECT.from.args) {
|
|
102
|
+
return walkArgs(query.SELECT.from.args);
|
|
93
103
|
}
|
|
94
|
-
else if (query.SELECT) {
|
|
95
|
-
|
|
96
|
-
return walkArgs(query.SELECT.from.args);
|
|
97
|
-
}
|
|
98
|
-
else if (query.SELECT.from.ref) {
|
|
99
|
-
let art = artifactRef(query.SELECT.from);
|
|
104
|
+
else if (query.SELECT.from.ref) {
|
|
105
|
+
let art = artifactRef(query.SELECT.from);
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
|
|
107
|
+
if(art.target)
|
|
108
|
+
art = artifactRef(art.target);
|
|
103
109
|
|
|
104
|
-
|
|
105
|
-
|
|
110
|
+
if(isSubquery && !query.SELECT.elements)
|
|
111
|
+
throw new ModelError('Expected subquery to have .elements');
|
|
106
112
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
|
|
112
|
-
return getSources(query.SELECT.from, true);
|
|
113
|
-
}
|
|
113
|
+
return mergeElementsIntoMap(Object.create(null), isSubquery ? query.SELECT.elements : art.elements, art.$location,
|
|
114
|
+
query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
|
|
115
|
+
query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
|
|
114
116
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
let elements = Object.create(null);
|
|
118
|
-
for (const arg of args) {
|
|
119
|
-
if (arg.args) {
|
|
120
|
-
elements = mergeElementMaps(elements, walkArgs(arg.args));
|
|
121
|
-
}
|
|
122
|
-
else if (arg.ref) {
|
|
123
|
-
const art = artifactRef(arg);
|
|
124
|
-
elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
|
|
125
|
-
}
|
|
126
|
-
else if (arg.SELECT || arg.SET) {
|
|
127
|
-
elements = mergeElementMaps(elements, getSources(arg));
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return elements;
|
|
117
|
+
else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
|
|
118
|
+
return getSources(query.SELECT.from, true);
|
|
132
119
|
}
|
|
120
|
+
}
|
|
133
121
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Merge two maps of elements together
|
|
138
|
-
*
|
|
139
|
-
* @param {object} mapA Map a - will be returned
|
|
140
|
-
* @param {object} mapB Map b - will not be returned
|
|
141
|
-
* @returns {object} mapA
|
|
142
|
-
*/
|
|
143
|
-
function mergeElementMaps(mapA, mapB) {
|
|
144
|
-
for (const elementName in mapB) {
|
|
145
|
-
if (!mapA[elementName])
|
|
146
|
-
mapA[elementName] = [];
|
|
147
|
-
|
|
148
|
-
mapB[elementName].forEach(e => mapA[elementName].push(e));
|
|
149
|
-
}
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
150
124
|
|
|
151
|
-
|
|
125
|
+
function walkArgs(args) {
|
|
126
|
+
let elements = Object.create(null);
|
|
127
|
+
for (const arg of args) {
|
|
128
|
+
if (arg.args) {
|
|
129
|
+
elements = mergeElementMaps(elements, walkArgs(arg.args));
|
|
130
|
+
}
|
|
131
|
+
else if (arg.ref) {
|
|
132
|
+
const art = artifactRef(arg);
|
|
133
|
+
elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
|
|
134
|
+
}
|
|
135
|
+
else if (arg.SELECT || arg.SET) {
|
|
136
|
+
elements = mergeElementMaps(elements, getSources(arg));
|
|
152
137
|
}
|
|
138
|
+
}
|
|
153
139
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
*
|
|
157
|
-
* @param {any} existingMap map to merge into - will be returned
|
|
158
|
-
* @param {object} elements elements to merge into the map
|
|
159
|
-
* @param {CSN.Location} $location $location of the elements - where they come from
|
|
160
|
-
* @param {any} [parent] Name of the parent of the elements, alias before ref
|
|
161
|
-
* @param {any} [error_parent] Parent name to use for error messages, ref before alias
|
|
162
|
-
* @returns {object} existingMap
|
|
163
|
-
*/
|
|
164
|
-
function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
|
|
165
|
-
for (const elementName in elements) {
|
|
166
|
-
const element = elements[elementName];
|
|
167
|
-
if (!existingMap[elementName])
|
|
168
|
-
existingMap[elementName] = [];
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
existingMap[elementName].push({
|
|
172
|
-
element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
|
|
173
|
-
});
|
|
174
|
-
}
|
|
140
|
+
return elements;
|
|
141
|
+
}
|
|
175
142
|
|
|
176
|
-
|
|
177
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Merge two maps of elements together
|
|
145
|
+
*
|
|
146
|
+
* @param {object} mapA Map a - will be returned
|
|
147
|
+
* @param {object} mapB Map b - will not be returned
|
|
148
|
+
* @returns {object} mapA
|
|
149
|
+
*/
|
|
150
|
+
function mergeElementMaps(mapA, mapB) {
|
|
151
|
+
for (const elementName in mapB) {
|
|
152
|
+
if (!mapA[elementName])
|
|
153
|
+
mapA[elementName] = [];
|
|
154
|
+
|
|
155
|
+
mapB[elementName].forEach(e => mapA[elementName].push(e));
|
|
178
156
|
}
|
|
179
157
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
* @param {string|object} name Absolute name of the artifact
|
|
183
|
-
*/
|
|
184
|
-
function getBaseName(name) {
|
|
185
|
-
if (!name)
|
|
186
|
-
return name;
|
|
158
|
+
return mapA;
|
|
159
|
+
}
|
|
187
160
|
|
|
188
|
-
|
|
189
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Merge elements into an existing map
|
|
163
|
+
*
|
|
164
|
+
* @param {any} existingMap map to merge into - will be returned
|
|
165
|
+
* @param {object} elements elements to merge into the map
|
|
166
|
+
* @param {CSN.Location} $location $location of the elements - where they come from
|
|
167
|
+
* @param {any} [parent] Name of the parent of the elements, alias before ref
|
|
168
|
+
* @param {any} [error_parent] Parent name to use for error messages, ref before alias
|
|
169
|
+
* @returns {object} existingMap
|
|
170
|
+
*/
|
|
171
|
+
function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
|
|
172
|
+
for (const elementName in elements) {
|
|
173
|
+
const element = elements[elementName];
|
|
174
|
+
if (!existingMap[elementName])
|
|
175
|
+
existingMap[elementName] = [];
|
|
190
176
|
|
|
191
|
-
|
|
177
|
+
|
|
178
|
+
existingMap[elementName].push({
|
|
179
|
+
element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
|
|
180
|
+
});
|
|
192
181
|
}
|
|
182
|
+
|
|
183
|
+
return existingMap;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Return the name part of the artifact name - no namespace etc.
|
|
188
|
+
* @param {string|object} name Absolute name of the artifact
|
|
189
|
+
*/
|
|
190
|
+
function getBaseName(name) {
|
|
191
|
+
if (!name)
|
|
192
|
+
return name;
|
|
193
|
+
|
|
194
|
+
if (name.id)
|
|
195
|
+
return name.id.substring( name.id.lastIndexOf('.')+1 );
|
|
196
|
+
|
|
197
|
+
return name.substring( name.lastIndexOf('.')+1 )
|
|
193
198
|
}
|
|
194
199
|
|
|
195
200
|
/**
|
|
@@ -216,11 +221,11 @@ function getUtils(model) {
|
|
|
216
221
|
|
|
217
222
|
/**
|
|
218
223
|
* Create an object to track visited objects identified by a unique string.
|
|
219
|
-
* @param {string} [
|
|
224
|
+
* @param {string} [initialId] Initial entry (optional)
|
|
220
225
|
*/
|
|
221
|
-
function createVisited(
|
|
226
|
+
function createVisited(initialId) {
|
|
222
227
|
let visited = Object.create(null);
|
|
223
|
-
check(
|
|
228
|
+
check(initialId);
|
|
224
229
|
return { check };
|
|
225
230
|
|
|
226
231
|
/**
|
|
@@ -231,7 +236,7 @@ function getUtils(model) {
|
|
|
231
236
|
function check(id) {
|
|
232
237
|
if (!id) return;
|
|
233
238
|
if (visited[id]) {
|
|
234
|
-
throw new
|
|
239
|
+
throw new ModelError('Circular dependency');
|
|
235
240
|
}
|
|
236
241
|
visited[id] = true;
|
|
237
242
|
}
|
|
@@ -245,7 +250,7 @@ function getUtils(model) {
|
|
|
245
250
|
if (model.definitions[defName])
|
|
246
251
|
return model.definitions[defName]
|
|
247
252
|
else
|
|
248
|
-
throw new
|
|
253
|
+
throw new ModelError(`Nonexistent definition in the model: '${defName}'`);
|
|
249
254
|
}
|
|
250
255
|
|
|
251
256
|
/**
|
|
@@ -298,7 +303,7 @@ function getUtils(model) {
|
|
|
298
303
|
|
|
299
304
|
// Return true if 'node' is a managed association element
|
|
300
305
|
// TODO: what about elements having a type, which (finally) is an assoc?
|
|
301
|
-
function
|
|
306
|
+
function isManagedAssociation(node) {
|
|
302
307
|
return node.target !== undefined && node.on === undefined && node.keys;
|
|
303
308
|
}
|
|
304
309
|
|
|
@@ -442,13 +447,13 @@ function getUtils(model) {
|
|
|
442
447
|
* The transformer functions are called with the following signature:
|
|
443
448
|
* transformer(value, node, resultNode, key)
|
|
444
449
|
*
|
|
445
|
-
* @param {any}
|
|
450
|
+
* @param {any} rootNode Node to transform
|
|
446
451
|
* @param {any} transformers Object defining transformer functions
|
|
447
452
|
* @returns {object}
|
|
448
453
|
*/
|
|
449
|
-
function cloneWithTransformations(
|
|
454
|
+
function cloneWithTransformations(rootNode, transformers) {
|
|
450
455
|
|
|
451
|
-
return transformNode(
|
|
456
|
+
return transformNode(rootNode);
|
|
452
457
|
|
|
453
458
|
// This general transformation function will be applied to each node recursively
|
|
454
459
|
function transformNode(node) {
|
|
@@ -536,7 +541,7 @@ function getUtils(model) {
|
|
|
536
541
|
if (cycleCheck) {
|
|
537
542
|
let visited = path.length? type + ':' + path.join('.') : type;
|
|
538
543
|
if (cycleCheck[visited])
|
|
539
|
-
throw new
|
|
544
|
+
throw new ModelError('Circular type chain on type ' + type);
|
|
540
545
|
else
|
|
541
546
|
cycleCheck[visited] = true;
|
|
542
547
|
}
|
|
@@ -583,7 +588,7 @@ function getUtils(model) {
|
|
|
583
588
|
* @param {object} csn Top-level CSN. You can pass non-dictionary values.
|
|
584
589
|
* @param {CSN.Options} options CSN Options, only used for `dictionaryPrototype`, `testMode`, and `testSortCsn`
|
|
585
590
|
*/
|
|
586
|
-
function
|
|
591
|
+
function cloneCsnNonDict(csn, options) {
|
|
587
592
|
return sortCsn(csn, options);
|
|
588
593
|
}
|
|
589
594
|
|
|
@@ -591,7 +596,7 @@ function cloneCsn(csn, options) {
|
|
|
591
596
|
* Deeply clone the given CSN dictionary and return it.
|
|
592
597
|
* Note that annotations are only copied shallowly.
|
|
593
598
|
* This function does _not_ sort the given dictionary.
|
|
594
|
-
* See
|
|
599
|
+
* See cloneCsnNonDict() if you want sorted definitions.
|
|
595
600
|
*
|
|
596
601
|
* @param {object} csn
|
|
597
602
|
* @param {CSN.Options} options Only cloneOptions.dictionaryPrototype is
|
|
@@ -627,8 +632,10 @@ function forEachDefinition( csn, callback, iterateOptions = {} ) {
|
|
|
627
632
|
* @param {CSN.Path} [path]
|
|
628
633
|
* @param {boolean} [ignoreIgnore]
|
|
629
634
|
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
635
|
+
* @param constructCallback
|
|
630
636
|
*/
|
|
631
|
-
function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {}
|
|
637
|
+
function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
|
|
638
|
+
constructCallback = (_construct, _prop, _path) => {}) {
|
|
632
639
|
// Allow processing _ignored elements if requested
|
|
633
640
|
if (ignoreIgnore && construct._ignore) {
|
|
634
641
|
return;
|
|
@@ -636,8 +643,7 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
|
|
|
636
643
|
|
|
637
644
|
// `items` itself is a structure that can contain "elements", and more.
|
|
638
645
|
if (construct.items) {
|
|
639
|
-
|
|
640
|
-
forEachMember( construct.items, callback, [...path, 'items'], ignoreIgnore, iterateOptions );
|
|
646
|
+
forEachMember( construct.items, callback, [...path, 'items'], ignoreIgnore, iterateOptions, constructCallback );
|
|
641
647
|
}
|
|
642
648
|
|
|
643
649
|
// Unlike XSN, we don't make "returns" a "params" in the callback.
|
|
@@ -645,12 +651,43 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
|
|
|
645
651
|
// `elements` of the return type (if structured).
|
|
646
652
|
// TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
|
|
647
653
|
if (construct.returns && !iterateOptions.elementsOnly) {
|
|
648
|
-
forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions );
|
|
654
|
+
forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions, constructCallback );
|
|
649
655
|
}
|
|
650
656
|
|
|
651
657
|
path = [...path]; // Copy
|
|
652
|
-
const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', '
|
|
653
|
-
propsWithMembers.forEach((prop) =>
|
|
658
|
+
const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'actions', 'params']);
|
|
659
|
+
propsWithMembers.forEach((prop) => {
|
|
660
|
+
forEachGeneric( construct, prop, callback, path, iterateOptions );
|
|
661
|
+
if (construct[prop]) {
|
|
662
|
+
if (Array.isArray(constructCallback))
|
|
663
|
+
constructCallback.forEach(cb => cb(construct, prop, path));
|
|
664
|
+
else
|
|
665
|
+
constructCallback(construct, prop, path);
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Call `forEachMember` and then apply `forEachMember` on queries.
|
|
672
|
+
*
|
|
673
|
+
* @param {CSN.Artifact} construct
|
|
674
|
+
* @param {genericCallback|genericCallback[]} callback
|
|
675
|
+
* @param {CSN.Path} [path]
|
|
676
|
+
* @param {boolean} [ignoreIgnore]
|
|
677
|
+
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
678
|
+
* @param {constructCallback|constructCallback[]} callback
|
|
679
|
+
*/
|
|
680
|
+
function forEachMemberWithQuery( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
|
|
681
|
+
constructCallback = (_construct, _prop, _path) => {}) {
|
|
682
|
+
forEachMember(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
|
|
683
|
+
if (construct.query) {
|
|
684
|
+
forAllQueries(construct.query, (q, p) => {
|
|
685
|
+
const s = q.SELECT;
|
|
686
|
+
if(s) {
|
|
687
|
+
forEachMember(s, callback, p, ignoreIgnore, iterateOptions);
|
|
688
|
+
}
|
|
689
|
+
}, [ ...path, 'query' ]);
|
|
690
|
+
}
|
|
654
691
|
}
|
|
655
692
|
|
|
656
693
|
/**
|
|
@@ -662,16 +699,43 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
|
|
|
662
699
|
* @param {CSN.Path} [path]
|
|
663
700
|
* @param {boolean} [ignoreIgnore]
|
|
664
701
|
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
702
|
+
* @param {constructCallback|constructCallback[]} callback
|
|
665
703
|
*/
|
|
666
|
-
function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {}
|
|
667
|
-
|
|
704
|
+
function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
|
|
705
|
+
constructCallback = (_construct, _prop, _path) => {}) {
|
|
706
|
+
forEachMember( construct, ( member, memberName, prop, subpath, parent ) => {
|
|
668
707
|
if(Array.isArray(callback))
|
|
669
|
-
callback.forEach(cb => cb( member, memberName, prop, subpath,
|
|
708
|
+
callback.forEach(cb => cb( member, memberName, prop, subpath, parent ));
|
|
670
709
|
else
|
|
671
|
-
callback( member, memberName, prop, subpath,
|
|
710
|
+
callback( member, memberName, prop, subpath, parent );
|
|
672
711
|
// Descend into nested members, too
|
|
673
|
-
forEachMemberRecursively( member, callback, subpath, ignoreIgnore, iterateOptions);
|
|
674
|
-
}, path, ignoreIgnore, iterateOptions);
|
|
712
|
+
forEachMemberRecursively( member, callback, subpath, ignoreIgnore, iterateOptions, constructCallback);
|
|
713
|
+
}, path, ignoreIgnore, iterateOptions, constructCallback);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Apply function `callback(member, memberName)` to each member in `construct`,
|
|
718
|
+
* recursively (i.e. also for sub-elements of elements).
|
|
719
|
+
* Recursively iterate over elements of `construct` query.
|
|
720
|
+
*
|
|
721
|
+
* @param {CSN.Artifact} construct
|
|
722
|
+
* @param {genericCallback|genericCallback[]} callback
|
|
723
|
+
* @param {CSN.Path} [path]
|
|
724
|
+
* @param {boolean} [ignoreIgnore]
|
|
725
|
+
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
726
|
+
* @param {constructCallback|constructCallback[]} callback
|
|
727
|
+
*/
|
|
728
|
+
function forEachMemberRecursivelyWithQuery( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
|
|
729
|
+
constructCallback = (_construct, _prop, _path) => {}) {
|
|
730
|
+
forEachMemberRecursively(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
|
|
731
|
+
if(construct.query) {
|
|
732
|
+
forAllQueries(construct.query, (q, p) => {
|
|
733
|
+
const s = q.SELECT;
|
|
734
|
+
if(s) {
|
|
735
|
+
forEachMemberRecursively(s, callback, p, ignoreIgnore, iterateOptions);
|
|
736
|
+
}
|
|
737
|
+
}, [ ...path, 'query' ]);
|
|
738
|
+
}
|
|
675
739
|
}
|
|
676
740
|
|
|
677
741
|
/**
|
|
@@ -680,14 +744,14 @@ function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=tr
|
|
|
680
744
|
* the following arguments: the object, the name, and -if it is a duplicate-
|
|
681
745
|
* the array index and the array containing all duplicates.
|
|
682
746
|
*
|
|
683
|
-
* @param {object}
|
|
747
|
+
* @param {object} construct
|
|
684
748
|
* @param {string} prop
|
|
685
749
|
* @param {genericCallback|genericCallback[]} callback
|
|
686
750
|
* @param {CSN.Path} path
|
|
687
751
|
* @param {object} iterateOptions can be used to skip certain kinds from being iterated
|
|
688
752
|
*/
|
|
689
|
-
function forEachGeneric(
|
|
690
|
-
const dict =
|
|
753
|
+
function forEachGeneric( construct, prop, callback, path = [], iterateOptions = {}) {
|
|
754
|
+
const dict = construct[prop];
|
|
691
755
|
for (const name in dict) {
|
|
692
756
|
if (!Object.prototype.hasOwnProperty.call(dict, name))
|
|
693
757
|
continue;
|
|
@@ -696,65 +760,26 @@ function forEachGeneric( obj, prop, callback, path = [], iterateOptions = {}) {
|
|
|
696
760
|
|| (iterateOptions.skipArtifact && typeof iterateOptions.skipArtifact === 'function'
|
|
697
761
|
&& iterateOptions.skipArtifact(dictObj, name)))
|
|
698
762
|
continue;
|
|
699
|
-
|
|
763
|
+
executeCallbacks( dictObj, name );
|
|
700
764
|
}
|
|
701
|
-
function
|
|
765
|
+
function executeCallbacks(o, name ) {
|
|
702
766
|
if (Array.isArray(callback))
|
|
703
|
-
callback.forEach(cb => cb( o, name, prop, path.concat([prop, name])));
|
|
767
|
+
callback.forEach(cb => cb( o, name, prop, path.concat([prop, name]), construct ));
|
|
704
768
|
else
|
|
705
|
-
callback( o, name, prop, path.concat([prop, name]))
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
/**
|
|
710
|
-
* For each property named 'ref' in 'node' (recursively), call callback(ref, node, path)
|
|
711
|
-
*
|
|
712
|
-
* @param {object} node
|
|
713
|
-
* @param {refCallback|refCallback[]} callback
|
|
714
|
-
* @param {CSN.Path} path
|
|
715
|
-
*/
|
|
716
|
-
function forEachRef(node, callback, path = []) {
|
|
717
|
-
if (node === null || typeof node !== 'object') {
|
|
718
|
-
// Primitive node
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
if(node._ignore){
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
if(Array.isArray(node)){
|
|
727
|
-
for (let i = 0; i < node.length; i++) {
|
|
728
|
-
// Descend recursively
|
|
729
|
-
forEachRef(node[i], callback, path.concat([i]));
|
|
730
|
-
}
|
|
731
|
-
} else {
|
|
732
|
-
for (let name in node) {
|
|
733
|
-
if (!Object.hasOwnProperty.call( node, name ))
|
|
734
|
-
continue;
|
|
735
|
-
// If ref found within a non-dictionary, call callback
|
|
736
|
-
if (name === 'ref' && Object.getPrototypeOf(node)) {
|
|
737
|
-
if(Array.isArray(callback))
|
|
738
|
-
callback.forEach(cb => cb( node.ref, node, path ));
|
|
739
|
-
else
|
|
740
|
-
callback( node.ref, node, path );
|
|
741
|
-
}
|
|
742
|
-
// Descend recursively
|
|
743
|
-
forEachRef(node[name], callback, path.concat([name]));
|
|
744
|
-
}
|
|
769
|
+
callback( o, name, prop, path.concat([prop, name]), construct )
|
|
745
770
|
}
|
|
746
771
|
}
|
|
747
772
|
|
|
748
773
|
// Like Object.assign() but copies also non enumerable properties
|
|
749
774
|
function assignAll(target, ...sources) {
|
|
750
775
|
sources.forEach(source => {
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
return
|
|
776
|
+
const descriptors = Object.getOwnPropertyNames(source).reduce((propertyDescriptors, current) => {
|
|
777
|
+
propertyDescriptors[current] = Object.getOwnPropertyDescriptor(source, current);
|
|
778
|
+
return propertyDescriptors;
|
|
754
779
|
}, {});
|
|
755
780
|
// by default, Object.assign copies enumerable Symbols too
|
|
756
781
|
Object.getOwnPropertySymbols(source).forEach(sym => {
|
|
757
|
-
|
|
782
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, sym);
|
|
758
783
|
if (descriptor.enumerable) {
|
|
759
784
|
descriptors[sym] = descriptor;
|
|
760
785
|
}
|
|
@@ -765,101 +790,68 @@ function assignAll(target, ...sources) {
|
|
|
765
790
|
}
|
|
766
791
|
|
|
767
792
|
/**
|
|
768
|
-
* @param {CSN.Query}
|
|
769
|
-
* @param {queryCallback|queryCallback[]}
|
|
793
|
+
* @param {CSN.Query} mainQuery
|
|
794
|
+
* @param {queryCallback|queryCallback[]} queryCallback
|
|
770
795
|
* @param {CSN.Path} path
|
|
771
796
|
*/
|
|
772
|
-
function forAllQueries(
|
|
773
|
-
return traverseQuery(
|
|
774
|
-
function traverseQuery(
|
|
775
|
-
if (
|
|
797
|
+
function forAllQueries(mainQuery, queryCallback, path = []){
|
|
798
|
+
return traverseQuery(mainQuery, queryCallback, path);
|
|
799
|
+
function traverseQuery( query, callback, queryPath ) {
|
|
800
|
+
if (query.SELECT) {
|
|
776
801
|
// The projection is turned into a normalized query - there
|
|
777
802
|
// is no real SELECT, it is fake
|
|
778
803
|
if(!(path.length === 3 && path[2] === 'projection'))
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
804
|
+
queryPath.push('SELECT');
|
|
805
|
+
executeCallbacks();
|
|
806
|
+
query = query.SELECT;
|
|
782
807
|
}
|
|
783
|
-
else if (
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
808
|
+
else if (query.SET) {
|
|
809
|
+
queryPath.push('SET');
|
|
810
|
+
executeCallbacks();
|
|
811
|
+
query = query.SET;
|
|
787
812
|
}
|
|
788
813
|
|
|
789
|
-
if (
|
|
790
|
-
traverseFrom(
|
|
814
|
+
if (query.from)
|
|
815
|
+
traverseFrom( query.from, callback, queryPath.concat(['from']) );
|
|
791
816
|
|
|
792
817
|
for (const prop of ['args', 'xpr', 'columns', 'where', 'having']) {
|
|
793
818
|
// all properties which could have sub queries (directly or indirectly)
|
|
794
|
-
const expr =
|
|
819
|
+
const expr = query[prop];
|
|
795
820
|
if (expr && typeof expr === 'object') {
|
|
796
821
|
if(Array.isArray(expr)){
|
|
797
822
|
for(let i = 0; i < expr.length; i++){
|
|
798
|
-
traverseQuery(expr[i], callback,
|
|
823
|
+
traverseQuery(expr[i], callback, queryPath.concat([prop, i]));
|
|
799
824
|
}
|
|
800
825
|
} else {
|
|
801
826
|
for(const argName of Object.keys( expr )){
|
|
802
|
-
traverseQuery(expr[argName], callback,
|
|
827
|
+
traverseQuery(expr[argName], callback, queryPath.concat([prop, argName]))
|
|
803
828
|
}
|
|
804
829
|
}
|
|
805
830
|
}
|
|
806
831
|
}
|
|
807
|
-
function
|
|
832
|
+
function executeCallbacks() {
|
|
808
833
|
if(Array.isArray(callback))
|
|
809
|
-
callback.forEach(cb => cb(
|
|
834
|
+
callback.forEach(cb => cb( query, queryPath ));
|
|
810
835
|
else
|
|
811
|
-
callback(
|
|
836
|
+
callback( query, queryPath );
|
|
812
837
|
}
|
|
813
838
|
}
|
|
814
839
|
|
|
815
840
|
/**
|
|
816
841
|
* @param {CSN.QueryFrom} from
|
|
817
842
|
* @param {Function} callback
|
|
818
|
-
* @param {CSN.Path}
|
|
843
|
+
* @param {CSN.Path} csnPath
|
|
819
844
|
*/
|
|
820
|
-
function traverseFrom( from, callback,
|
|
845
|
+
function traverseFrom( from, callback, csnPath = [] ) {
|
|
821
846
|
if (from.ref) // ignore
|
|
822
847
|
return;
|
|
823
848
|
else if (from.args){ // join
|
|
824
849
|
for(let i = 0; i < from.args.length; i++){
|
|
825
|
-
traverseFrom(from.args[i], callback,
|
|
850
|
+
traverseFrom(from.args[i], callback, csnPath.concat(['args', i]));
|
|
826
851
|
}
|
|
827
852
|
}
|
|
828
853
|
else
|
|
829
|
-
traverseQuery( from, callback,
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
function forAllElements(artifact, artifactName, cb, includeActions = false){
|
|
834
|
-
if(artifact.elements) {
|
|
835
|
-
cb(artifact, artifact.elements, ['definitions', artifactName, 'elements']);
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
if(includeActions && artifact.actions) {
|
|
839
|
-
Object.entries(artifact.actions).forEach( ([actionName, action]) => {
|
|
840
|
-
const path = ['definitions', artifactName, 'actions', actionName];
|
|
841
|
-
if(action.params) {
|
|
842
|
-
Object.entries(action.params).forEach( ([paramName, param]) => {
|
|
843
|
-
if(param.elements)
|
|
844
|
-
cb(param, param.elements, path.concat(['params', paramName, 'elements']));
|
|
845
|
-
});
|
|
846
|
-
}
|
|
847
|
-
if(action.returns && action.returns.elements)
|
|
848
|
-
cb(action.returns, action.returns.elements,path.concat(['returns', 'elements']));
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
if(artifact.query) {
|
|
853
|
-
forAllQueries(artifact.query, (q, p) => {
|
|
854
|
-
const s = q.SELECT;
|
|
855
|
-
if(s) {
|
|
856
|
-
if(s.elements) {
|
|
857
|
-
cb(s, s.elements, [...p, 'elements']);
|
|
858
|
-
} else if(s.$elements) { // huh?, is just refloc output
|
|
859
|
-
cb(s, s.$elements, [...p, '$elements']);
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
}, ['definitions', artifactName, 'query'])
|
|
854
|
+
traverseQuery( from, callback, csnPath ); // sub query in FROM
|
|
863
855
|
}
|
|
864
856
|
}
|
|
865
857
|
|
|
@@ -925,48 +917,48 @@ function isEdmPropertyRendered(elementCsn, options) {
|
|
|
925
917
|
|
|
926
918
|
/**
|
|
927
919
|
* Return the resulting database name for (absolute) 'artifactName', depending on the current naming
|
|
928
|
-
*
|
|
920
|
+
* mode.
|
|
929
921
|
*
|
|
930
|
-
* - For the 'hdbcds' naming
|
|
922
|
+
* - For the 'hdbcds' naming mode, this means converting '.' to '::' on
|
|
931
923
|
* the border between namespace and top-level artifact and correctly replacing some '.' with '_'.
|
|
932
|
-
* - For the 'plain' naming
|
|
933
|
-
* - For the 'quoted' naming
|
|
924
|
+
* - For the 'plain' naming mode, it means converting all '.' to '_' and upper-casing.
|
|
925
|
+
* - For the 'quoted' naming mode, this means correctly replacing some '.' with '_'.
|
|
934
926
|
*
|
|
935
927
|
* If the old function signature is used - with a namespace as the third argument - the result might be wrong,
|
|
936
928
|
* since the '.' -> '_' conversion for quoted/hdbcds is missing.
|
|
937
929
|
*
|
|
938
|
-
* @param {string} artifactName The name of the artifact
|
|
939
|
-
* @param {('plain'|'quoted'|'hdbcds')}
|
|
930
|
+
* @param {string} artifactName The fully qualified name of the artifact
|
|
931
|
+
* @param {('plain'|'quoted'|'hdbcds')} sqlMapping The naming mode to use
|
|
940
932
|
* @param {CSN.Model|string|undefined} csn
|
|
941
|
-
* @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming
|
|
933
|
+
* @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming mode.
|
|
942
934
|
*/
|
|
943
|
-
function getArtifactDatabaseNameOf(artifactName,
|
|
935
|
+
function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn) {
|
|
944
936
|
if(csn && typeof csn === 'object' && csn.definitions)
|
|
945
|
-
if (
|
|
946
|
-
return getResultingName(csn,
|
|
937
|
+
if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds') {
|
|
938
|
+
return getResultingName(csn, sqlMapping, artifactName);
|
|
947
939
|
}
|
|
948
|
-
else if (
|
|
940
|
+
else if (sqlMapping === 'plain') {
|
|
949
941
|
return artifactName.replace(/\./g, '_').toUpperCase();
|
|
950
942
|
} else {
|
|
951
|
-
throw new Error('Unknown naming
|
|
943
|
+
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
952
944
|
}
|
|
953
945
|
else {
|
|
954
946
|
console.error(`This invocation of "getArtifactCdsPersistenceName" is deprecated, as it doesn't produce correct output with definition names containing dots - please provide a CSN as the third parameter.`);
|
|
955
|
-
if (
|
|
947
|
+
if (sqlMapping === 'hdbcds') {
|
|
956
948
|
if (csn) {
|
|
957
949
|
const namespace = String(csn);
|
|
958
950
|
return `${namespace}::${artifactName.substring(namespace.length + 1)}`;
|
|
959
951
|
}
|
|
960
952
|
return artifactName;
|
|
961
953
|
}
|
|
962
|
-
else if (
|
|
954
|
+
else if (sqlMapping === 'plain') {
|
|
963
955
|
return artifactName.replace(/\./g, '_').toUpperCase();
|
|
964
956
|
}
|
|
965
|
-
else if (
|
|
957
|
+
else if (sqlMapping === 'quoted') {
|
|
966
958
|
return artifactName;
|
|
967
959
|
}
|
|
968
960
|
else {
|
|
969
|
-
throw new Error('Unknown naming
|
|
961
|
+
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
970
962
|
}
|
|
971
963
|
}
|
|
972
964
|
}
|
|
@@ -1034,7 +1026,7 @@ function getUnderscoredName(startIndex, parts, csn) {
|
|
|
1034
1026
|
|
|
1035
1027
|
return result;
|
|
1036
1028
|
} else if(art && art.kind === 'service') {
|
|
1037
|
-
// inside services, we
|
|
1029
|
+
// inside services, we immediately turn . into _
|
|
1038
1030
|
const prefix = parts.slice(0, i).join('.');
|
|
1039
1031
|
const suffix = parts.slice(i).join('_');
|
|
1040
1032
|
const result = [];
|
|
@@ -1052,29 +1044,29 @@ function getUnderscoredName(startIndex, parts, csn) {
|
|
|
1052
1044
|
|
|
1053
1045
|
|
|
1054
1046
|
/**
|
|
1055
|
-
* Return the resulting database element name for 'elemName', depending on the current
|
|
1056
|
-
*
|
|
1057
|
-
* - For the 'hdbcds' naming
|
|
1058
|
-
* - For the 'plain' naming
|
|
1059
|
-
* - For the 'quoted' naming
|
|
1060
|
-
* No other naming
|
|
1047
|
+
* Return the resulting database element name for 'elemName', depending on the current
|
|
1048
|
+
* naming mode.
|
|
1049
|
+
* - For the 'hdbcds' naming mode, this is just 'elemName'.
|
|
1050
|
+
* - For the 'plain' naming mode, it means converting all '.' to '_' and upper-casing.
|
|
1051
|
+
* - For the 'quoted' naming mode, it means converting all '.' to '_'.
|
|
1052
|
+
* No other naming modes are accepted!
|
|
1061
1053
|
*
|
|
1062
|
-
* @param {string} elemName
|
|
1063
|
-
* @param {('plain'|'quoted'|'hdbcds')}
|
|
1064
|
-
* @returns {string} The resulting database element name for 'elemName', depending on the current naming
|
|
1054
|
+
* @param {string} elemName The name of the element
|
|
1055
|
+
* @param {('plain'|'quoted'|'hdbcds')} sqlMapping The naming mode to use
|
|
1056
|
+
* @returns {string} The resulting database element name for 'elemName', depending on the current naming mode.
|
|
1065
1057
|
*/
|
|
1066
|
-
function getElementDatabaseNameOf(elemName,
|
|
1067
|
-
if (
|
|
1058
|
+
function getElementDatabaseNameOf(elemName, sqlMapping) {
|
|
1059
|
+
if (sqlMapping === 'hdbcds') {
|
|
1068
1060
|
return elemName;
|
|
1069
1061
|
}
|
|
1070
|
-
else if (
|
|
1062
|
+
else if (sqlMapping === 'plain') {
|
|
1071
1063
|
return elemName.replace(/\./g, '_').toUpperCase();
|
|
1072
1064
|
}
|
|
1073
|
-
else if (
|
|
1065
|
+
else if (sqlMapping === 'quoted') {
|
|
1074
1066
|
return elemName.replace(/\./g, '_');
|
|
1075
1067
|
}
|
|
1076
1068
|
else {
|
|
1077
|
-
throw new Error('Unknown naming
|
|
1069
|
+
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
1078
1070
|
}
|
|
1079
1071
|
}
|
|
1080
1072
|
|
|
@@ -1152,7 +1144,7 @@ function setDependencies( csn ) {
|
|
|
1152
1144
|
* @returns {boolean}
|
|
1153
1145
|
*/
|
|
1154
1146
|
function isPersistedOnDatabase(art) {
|
|
1155
|
-
return !(
|
|
1147
|
+
return !('entity' === art.kind && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
|
|
1156
1148
|
}
|
|
1157
1149
|
|
|
1158
1150
|
/**
|
|
@@ -1206,44 +1198,44 @@ function mergeOptions(...optionsObjects) {
|
|
|
1206
1198
|
|
|
1207
1199
|
// Recursively used for scalars, too
|
|
1208
1200
|
function mergeTwo(left, right, name) {
|
|
1209
|
-
let
|
|
1201
|
+
let intermediateResult;
|
|
1210
1202
|
// Copy left as far as required
|
|
1211
1203
|
if (Array.isArray(left)) {
|
|
1212
1204
|
// Shallow-copy left array
|
|
1213
|
-
|
|
1205
|
+
intermediateResult = left.slice();
|
|
1214
1206
|
} else if (isObject(left)) {
|
|
1215
1207
|
// Deep-copy left object (unless empty)
|
|
1216
|
-
|
|
1208
|
+
intermediateResult = Object.keys(left).length ? mergeTwo({}, left, name) : {};
|
|
1217
1209
|
} else {
|
|
1218
1210
|
// Just use left scalar
|
|
1219
|
-
|
|
1211
|
+
intermediateResult = left;
|
|
1220
1212
|
}
|
|
1221
1213
|
// Check against improper overwriting
|
|
1222
1214
|
if (isObject(left) && !Array.isArray(left) && (Array.isArray(right) || isScalar(right))) {
|
|
1223
|
-
throw new
|
|
1215
|
+
throw new ModelError(`Cannot overwrite structured option "${name}" with array or scalar value`);
|
|
1224
1216
|
}
|
|
1225
1217
|
if ((isScalar(left) && typeof left !== 'boolean' || Array.isArray(left)) && isObject(right) && !Array.isArray(right)) {
|
|
1226
|
-
throw new
|
|
1218
|
+
throw new ModelError(`Cannot overwrite non-boolean scalar or array option "${name}" with structured value`);
|
|
1227
1219
|
}
|
|
1228
1220
|
|
|
1229
1221
|
// Copy or overwrite properties from right to left
|
|
1230
1222
|
if (Array.isArray(right)) {
|
|
1231
1223
|
// Shallow-copy right array
|
|
1232
|
-
|
|
1224
|
+
intermediateResult = right.slice();
|
|
1233
1225
|
} else if (isObject(right)) {
|
|
1234
1226
|
// Object overwrites undefined, scalars and arrays
|
|
1235
|
-
if (
|
|
1236
|
-
|
|
1227
|
+
if (intermediateResult === undefined || isScalar(intermediateResult) || Array.isArray(intermediateResult)) {
|
|
1228
|
+
intermediateResult = {};
|
|
1237
1229
|
}
|
|
1238
1230
|
// Deep-copy right object into result
|
|
1239
1231
|
for (let key of Object.keys(right)) {
|
|
1240
|
-
|
|
1232
|
+
intermediateResult[key] = mergeTwo(intermediateResult[key], right[key], `${name}.${key}`);
|
|
1241
1233
|
}
|
|
1242
1234
|
} else {
|
|
1243
1235
|
// Right scalar wins (unless undefined)
|
|
1244
|
-
|
|
1236
|
+
intermediateResult = (right !== undefined) ? right : intermediateResult;
|
|
1245
1237
|
}
|
|
1246
|
-
return
|
|
1238
|
+
return intermediateResult;
|
|
1247
1239
|
}
|
|
1248
1240
|
|
|
1249
1241
|
// Return true if 'o' is a non-null object or array
|
|
@@ -1331,19 +1323,73 @@ function getParentNamesOf(name) {
|
|
|
1331
1323
|
}
|
|
1332
1324
|
|
|
1333
1325
|
|
|
1334
|
-
|
|
1335
|
-
|
|
1326
|
+
/**
|
|
1327
|
+
* Copy all annotations from 'fromNode' to 'toNode'.
|
|
1328
|
+
*
|
|
1329
|
+
* Overwrite existing ones only if 'overwrite' is true.
|
|
1330
|
+
*
|
|
1331
|
+
* @param {object} fromNode
|
|
1332
|
+
* @param {object} toNode
|
|
1333
|
+
* @param {boolean} [overwrite]
|
|
1334
|
+
*/
|
|
1335
|
+
function copyAnnotations(fromNode, toNode, overwrite = false) {
|
|
1336
|
+
// Ignore if no toNode (in case of errors)
|
|
1337
|
+
if (!toNode)
|
|
1338
|
+
return;
|
|
1339
|
+
|
|
1340
|
+
const annotations = Object.keys(fromNode).filter(key => key.startsWith('@'));
|
|
1341
|
+
|
|
1342
|
+
for (const anno of annotations) {
|
|
1343
|
+
if (toNode[anno] === undefined || overwrite) {
|
|
1344
|
+
toNode[anno] = fromNode[anno];
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
|
|
1350
|
+
/**
|
|
1351
|
+
* Same as `copyAnnotations()` but also copies the
|
|
1352
|
+
* annotation-like property `doc`.
|
|
1353
|
+
*
|
|
1354
|
+
* Overwrite existing ones only if 'overwrite' is true.
|
|
1355
|
+
*
|
|
1356
|
+
* @param {object} fromNode
|
|
1357
|
+
* @param {object} toNode
|
|
1358
|
+
* @param {boolean} [overwrite]
|
|
1359
|
+
*/
|
|
1360
|
+
function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
|
|
1336
1361
|
// Ignore if no toNode (in case of errors)
|
|
1337
|
-
if (!toNode)
|
|
1362
|
+
if (!toNode)
|
|
1338
1363
|
return;
|
|
1364
|
+
|
|
1365
|
+
const annotations = Object.keys(fromNode)
|
|
1366
|
+
.filter(key => key.startsWith('@') || key === 'doc');
|
|
1367
|
+
|
|
1368
|
+
for (const anno of annotations) {
|
|
1369
|
+
if (toNode[anno] === undefined || overwrite) {
|
|
1370
|
+
toNode[anno] = fromNode[anno];
|
|
1371
|
+
}
|
|
1339
1372
|
}
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* Applies annotations from `csn.extensions` to definitions, i.e. top-level artifacts.
|
|
1377
|
+
* Does _not_ apply element/param/action/... annotations.
|
|
1378
|
+
* `config.filter` can be used to only copy annotations for those definitions,
|
|
1379
|
+
* for which the filter returns true.
|
|
1380
|
+
*
|
|
1381
|
+
* @param {CSN.Model} csn
|
|
1382
|
+
* @param {{overwrite?: boolean, filter?: (name: string) => boolean}} config
|
|
1383
|
+
*/
|
|
1384
|
+
function applyDefinitionAnnotationsFromExtensions(csn, config) {
|
|
1385
|
+
if (!csn.extensions)
|
|
1386
|
+
return;
|
|
1387
|
+
|
|
1388
|
+
const filter = config.filter || ((_name) => true);
|
|
1389
|
+
for (const ext of csn.extensions) {
|
|
1390
|
+
const name = ext.annotate || ext.extend;
|
|
1391
|
+
if (name && csn.definitions[name] && filter(name)) {
|
|
1392
|
+
copyAnnotationsAndDoc(ext, csn.definitions[name], config.overwrite);
|
|
1347
1393
|
}
|
|
1348
1394
|
}
|
|
1349
1395
|
}
|
|
@@ -1378,7 +1424,7 @@ function forEachPath(node, callback) {
|
|
|
1378
1424
|
* @returns {boolean}
|
|
1379
1425
|
*/
|
|
1380
1426
|
function hasValidSkipOrExists(artifact) {
|
|
1381
|
-
return
|
|
1427
|
+
return artifact.kind === 'entity' &&
|
|
1382
1428
|
(hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true))
|
|
1383
1429
|
|
|
1384
1430
|
}
|
|
@@ -1447,7 +1493,7 @@ function getServiceNames(csn) {
|
|
|
1447
1493
|
}
|
|
1448
1494
|
|
|
1449
1495
|
/**
|
|
1450
|
-
* Check
|
|
1496
|
+
* Check whether the artifact is @cds.persistence.skip
|
|
1451
1497
|
*
|
|
1452
1498
|
* @param {CSN.Artifact} artifact
|
|
1453
1499
|
* @returns {Boolean}
|
|
@@ -1501,19 +1547,57 @@ function getVariableReplacement(ref, options) {
|
|
|
1501
1547
|
}
|
|
1502
1548
|
}
|
|
1503
1549
|
|
|
1550
|
+
/**
|
|
1551
|
+
*
|
|
1552
|
+
* @param {object} obj
|
|
1553
|
+
* @param {*} other
|
|
1554
|
+
* @param {boolean} noExtendedProps
|
|
1555
|
+
* @returns {boolean} returns equality
|
|
1556
|
+
*
|
|
1557
|
+
* noExtendedProps remove '$', '_' and '@' properties from
|
|
1558
|
+
* the comparison. This eliminates false negatives such as
|
|
1559
|
+
* mismatching $locations or @odata.foreignKey4.
|
|
1560
|
+
*/
|
|
1561
|
+
function isDeepEqual(obj, other, noExtendedProps) {
|
|
1562
|
+
let objectKeys = Object.keys(obj);
|
|
1563
|
+
let otherKeys = Object.keys(other);
|
|
1564
|
+
|
|
1565
|
+
if(noExtendedProps) {
|
|
1566
|
+
objectKeys = objectKeys.filter(k => !['@', '$', '_'].includes(k[0]));
|
|
1567
|
+
otherKeys = otherKeys.filter(k => !['@', '$', '_'].includes(k[0]));
|
|
1568
|
+
}
|
|
1569
|
+
if (objectKeys.length !== otherKeys.length)
|
|
1570
|
+
return false;
|
|
1571
|
+
|
|
1572
|
+
for (let key of objectKeys) {
|
|
1573
|
+
const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object')
|
|
1574
|
+
&& (other[key] !== null && typeof other[key] === 'object');
|
|
1575
|
+
|
|
1576
|
+
if (areValuesObjects) {
|
|
1577
|
+
if (!isDeepEqual(obj[key], other[key], noExtendedProps))
|
|
1578
|
+
return false;
|
|
1579
|
+
} else if (obj[key] !== other[key]) {
|
|
1580
|
+
return false;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
return true;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1504
1586
|
module.exports = {
|
|
1505
1587
|
getUtils,
|
|
1506
|
-
cloneCsn,
|
|
1588
|
+
cloneCsn: cloneCsnNonDict, // Umbrella relies on this name
|
|
1589
|
+
cloneCsnNonDict,
|
|
1507
1590
|
cloneCsnDictionary,
|
|
1508
1591
|
isBuiltinType,
|
|
1509
1592
|
assignAll,
|
|
1593
|
+
applyDefinitionAnnotationsFromExtensions,
|
|
1510
1594
|
forEachGeneric,
|
|
1511
1595
|
forEachDefinition,
|
|
1512
1596
|
forEachMember,
|
|
1597
|
+
forEachMemberWithQuery,
|
|
1513
1598
|
forEachMemberRecursively,
|
|
1514
|
-
|
|
1599
|
+
forEachMemberRecursivelyWithQuery,
|
|
1515
1600
|
forAllQueries,
|
|
1516
|
-
forAllElements,
|
|
1517
1601
|
hasAnnotationValue,
|
|
1518
1602
|
isEdmPropertyRendered,
|
|
1519
1603
|
getArtifactDatabaseNameOf,
|
|
@@ -1533,6 +1617,7 @@ module.exports = {
|
|
|
1533
1617
|
getParentNameOf,
|
|
1534
1618
|
getLastPartOf,
|
|
1535
1619
|
copyAnnotations,
|
|
1620
|
+
copyAnnotationsAndDoc,
|
|
1536
1621
|
isAspect,
|
|
1537
1622
|
forEachPath,
|
|
1538
1623
|
hasValidSkipOrExists,
|
|
@@ -1541,5 +1626,7 @@ module.exports = {
|
|
|
1541
1626
|
getServiceNames,
|
|
1542
1627
|
isSkipped,
|
|
1543
1628
|
walkCsnPath,
|
|
1544
|
-
getVariableReplacement
|
|
1629
|
+
getVariableReplacement,
|
|
1630
|
+
implicitAs,
|
|
1631
|
+
isDeepEqual,
|
|
1545
1632
|
};
|