@sap/cds-compiler 2.11.2 → 2.13.6
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 +175 -2
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +10 -8
- package/bin/cdsc.js +23 -17
- package/bin/cdsse.js +2 -2
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +25 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +32 -79
- package/lib/api/options.js +3 -2
- package/lib/api/validate.js +2 -1
- package/lib/backends.js +16 -26
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +10 -19
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +75 -9
- package/lib/base/messages.js +31 -35
- package/lib/base/model.js +2 -62
- package/lib/base/optionProcessorHelper.js +246 -183
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +94 -0
- package/lib/checks/types.js +1 -1
- package/lib/checks/unknownMagic.js +1 -1
- package/lib/checks/validator.js +12 -7
- package/lib/compiler/assert-consistency.js +12 -8
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/builtins.js +42 -21
- package/lib/compiler/checks.js +46 -12
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +46 -39
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +113 -47
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +100 -65
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +215 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -25
- package/lib/edm/annotations/preprocessAnnotations.js +3 -3
- package/lib/edm/csn2edm.js +10 -9
- package/lib/edm/edm.js +19 -20
- package/lib/edm/edmPreprocessor.js +166 -95
- package/lib/edm/edmUtils.js +127 -34
- package/lib/gen/Dictionary.json +92 -43
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -82
- package/lib/gen/languageLexer.interp +18 -1
- package/lib/gen/languageLexer.js +925 -847
- package/lib/gen/languageLexer.tokens +78 -74
- package/lib/gen/languageParser.js +5434 -4298
- package/lib/json/from-csn.js +59 -17
- package/lib/json/to-csn.js +189 -71
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +3 -3
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +144 -53
- package/lib/language/language.g4 +424 -200
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +550 -61
- package/lib/main.js +38 -11
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +322 -198
- package/lib/model/csnUtils.js +226 -370
- package/lib/model/enrichCsn.js +124 -69
- package/lib/model/revealInternalProperties.js +29 -7
- package/lib/model/sortViews.js +10 -2
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +8 -3
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +36 -33
- package/lib/render/toCdl.js +174 -275
- package/lib/render/toHdbcds.js +203 -122
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +161 -82
- package/lib/render/utils/common.js +22 -8
- package/lib/render/utils/sql.js +10 -7
- 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/braceExpression.js +4 -2
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +212 -0
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +187 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +61 -56
- package/lib/transform/db/expansion.js +50 -29
- package/lib/transform/db/flattening.js +556 -106
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +103 -28
- package/lib/transform/db/views.js +92 -44
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +9 -7
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +98 -783
- package/lib/transform/forOdataNew.js +22 -175
- package/lib/transform/localized.js +36 -32
- package/lib/transform/odata/generateForeignKeyElements.js +3 -3
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +47 -33
- package/lib/transform/translateAssocsToJoins.js +13 -30
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/file.js +8 -3
- package/lib/utils/objectUtils.js +30 -0
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2349
- package/lib/compiler/resolver.js +0 -2922
- package/lib/transform/db/helpers.js +0 -58
- package/lib/transform/universalCsnEnricher.js +0 -67
package/lib/model/csnUtils.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
const {
|
|
3
|
+
const { csnRefs, implicitAs } = require('../model/csnRefs');
|
|
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,
|
|
@@ -61,6 +62,11 @@ function getUtils(model) {
|
|
|
61
62
|
effectiveType,
|
|
62
63
|
get$combined,
|
|
63
64
|
getOrigin,
|
|
65
|
+
getQueryPrimarySource,
|
|
66
|
+
targetAspect,
|
|
67
|
+
getColumn,
|
|
68
|
+
getElement,
|
|
69
|
+
initDefinition
|
|
64
70
|
};
|
|
65
71
|
|
|
66
72
|
/**
|
|
@@ -70,135 +76,156 @@ function getUtils(model) {
|
|
|
70
76
|
* @returns {object}
|
|
71
77
|
*/
|
|
72
78
|
function get$combined(query) {
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
return getSources(query);
|
|
80
|
+
}
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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);
|
|
90
97
|
|
|
91
|
-
|
|
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);
|
|
92
103
|
}
|
|
93
|
-
else if (query.SELECT) {
|
|
94
|
-
|
|
95
|
-
return walkArgs(query.SELECT.from.args);
|
|
96
|
-
}
|
|
97
|
-
else if (query.SELECT.from.ref) {
|
|
98
|
-
let art = artifactRef(query.SELECT.from);
|
|
104
|
+
else if (query.SELECT.from.ref) {
|
|
105
|
+
let art = artifactRef(query.SELECT.from);
|
|
99
106
|
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
if(art.target)
|
|
108
|
+
art = artifactRef(art.target);
|
|
102
109
|
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
if(isSubquery && !query.SELECT.elements)
|
|
111
|
+
throw new ModelError('Expected subquery to have .elements');
|
|
105
112
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
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 );
|
|
116
|
+
}
|
|
117
|
+
else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
|
|
118
|
+
return getSources(query.SELECT.from, true);
|
|
113
119
|
}
|
|
120
|
+
}
|
|
114
121
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
for (const arg of args) {
|
|
118
|
-
if (arg.args) {
|
|
119
|
-
elements = mergeElementMaps(elements, walkArgs(arg.args));
|
|
120
|
-
}
|
|
121
|
-
else if (arg.ref) {
|
|
122
|
-
const art = artifactRef(arg);
|
|
123
|
-
elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
|
|
124
|
-
}
|
|
125
|
-
else if (arg.SELECT || arg.SET) {
|
|
126
|
-
elements = mergeElementMaps(elements, getSources(arg));
|
|
127
|
-
}
|
|
128
|
-
}
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
129
124
|
|
|
130
|
-
|
|
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));
|
|
131
137
|
}
|
|
138
|
+
}
|
|
132
139
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Merge two maps of elements together
|
|
137
|
-
*
|
|
138
|
-
* @param {object} mapA Map a - will be returned
|
|
139
|
-
* @param {object} mapB Map b - will not be returned
|
|
140
|
-
* @returns {object} mapA
|
|
141
|
-
*/
|
|
142
|
-
function mergeElementMaps(mapA, mapB) {
|
|
143
|
-
for (const elementName in mapB) {
|
|
144
|
-
if (!mapA[elementName])
|
|
145
|
-
mapA[elementName] = [];
|
|
146
|
-
|
|
147
|
-
mapB[elementName].forEach(e => mapA[elementName].push(e));
|
|
148
|
-
}
|
|
140
|
+
return elements;
|
|
141
|
+
}
|
|
149
142
|
|
|
150
|
-
|
|
151
|
-
|
|
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] = [];
|
|
152
154
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
*
|
|
156
|
-
* @param {any} existingMap map to merge into - will be returned
|
|
157
|
-
* @param {object} elements elements to merge into the map
|
|
158
|
-
* @param {CSN.Location} $location $location of the elements - where they come from
|
|
159
|
-
* @param {any} [parent] Name of the parent of the elements, alias before ref
|
|
160
|
-
* @param {any} [error_parent] Parent name to use for error messages, ref before alias
|
|
161
|
-
* @returns {object} existingMap
|
|
162
|
-
*/
|
|
163
|
-
function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
|
|
164
|
-
for (const elementName in elements) {
|
|
165
|
-
const element = elements[elementName];
|
|
166
|
-
if (!existingMap[elementName])
|
|
167
|
-
existingMap[elementName] = [];
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
existingMap[elementName].push({
|
|
171
|
-
element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
|
|
172
|
-
});
|
|
173
|
-
}
|
|
155
|
+
mapB[elementName].forEach(e => mapA[elementName].push(e));
|
|
156
|
+
}
|
|
174
157
|
|
|
175
|
-
|
|
176
|
-
|
|
158
|
+
return mapA;
|
|
159
|
+
}
|
|
160
|
+
|
|
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] = [];
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
existingMap[elementName].push({
|
|
179
|
+
element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
|
|
180
|
+
});
|
|
177
181
|
}
|
|
178
182
|
|
|
179
|
-
|
|
183
|
+
return existingMap;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
180
187
|
* Return the name part of the artifact name - no namespace etc.
|
|
181
188
|
* @param {string|object} name Absolute name of the artifact
|
|
182
189
|
*/
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
190
|
+
function getBaseName(name) {
|
|
191
|
+
if (!name)
|
|
192
|
+
return name;
|
|
186
193
|
|
|
187
|
-
|
|
188
|
-
|
|
194
|
+
if (name.id)
|
|
195
|
+
return name.id.substring( name.id.lastIndexOf('.')+1 );
|
|
196
|
+
|
|
197
|
+
return name.substring( name.lastIndexOf('.')+1 )
|
|
198
|
+
}
|
|
189
199
|
|
|
190
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Return the left-most, primary source of the given query.
|
|
202
|
+
* @param {*} query Definition's query object
|
|
203
|
+
*/
|
|
204
|
+
function getQueryPrimarySource(query) {
|
|
205
|
+
if (!query)
|
|
206
|
+
return undefined;
|
|
207
|
+
else if (query.SELECT) {
|
|
208
|
+
return getQueryPrimarySource(query.SELECT);
|
|
209
|
+
} else if (query.SET) {
|
|
210
|
+
return getQueryPrimarySource(query.SET);
|
|
211
|
+
} else if (query.from) {
|
|
212
|
+
return getQueryPrimarySource(query.from);
|
|
213
|
+
} else if (query.ref) {
|
|
214
|
+
return query;
|
|
215
|
+
} else if (query.args) {
|
|
216
|
+
return getQueryPrimarySource(query.args[0]);
|
|
191
217
|
}
|
|
218
|
+
return undefined;
|
|
192
219
|
}
|
|
193
220
|
|
|
194
221
|
|
|
195
222
|
/**
|
|
196
223
|
* Create an object to track visited objects identified by a unique string.
|
|
197
|
-
* @param {string} [
|
|
224
|
+
* @param {string} [initialId] Initial entry (optional)
|
|
198
225
|
*/
|
|
199
|
-
function createVisited(
|
|
226
|
+
function createVisited(initialId) {
|
|
200
227
|
let visited = Object.create(null);
|
|
201
|
-
check(
|
|
228
|
+
check(initialId);
|
|
202
229
|
return { check };
|
|
203
230
|
|
|
204
231
|
/**
|
|
@@ -209,7 +236,7 @@ function getUtils(model) {
|
|
|
209
236
|
function check(id) {
|
|
210
237
|
if (!id) return;
|
|
211
238
|
if (visited[id]) {
|
|
212
|
-
throw new
|
|
239
|
+
throw new ModelError('Circular dependency');
|
|
213
240
|
}
|
|
214
241
|
visited[id] = true;
|
|
215
242
|
}
|
|
@@ -223,14 +250,14 @@ function getUtils(model) {
|
|
|
223
250
|
if (model.definitions[defName])
|
|
224
251
|
return model.definitions[defName]
|
|
225
252
|
else
|
|
226
|
-
throw new
|
|
253
|
+
throw new ModelError(`Nonexistent definition in the model: '${defName}'`);
|
|
227
254
|
}
|
|
228
255
|
|
|
229
256
|
/**
|
|
230
257
|
* Returns true if an artifact is a structured type
|
|
231
258
|
* or a typedef of a structured type.
|
|
232
259
|
*
|
|
233
|
-
* @param {
|
|
260
|
+
* @param {object} obj
|
|
234
261
|
*/
|
|
235
262
|
function isStructured(obj) {
|
|
236
263
|
return obj.elements ||
|
|
@@ -276,7 +303,7 @@ function getUtils(model) {
|
|
|
276
303
|
|
|
277
304
|
// Return true if 'node' is a managed association element
|
|
278
305
|
// TODO: what about elements having a type, which (finally) is an assoc?
|
|
279
|
-
function
|
|
306
|
+
function isManagedAssociation(node) {
|
|
280
307
|
return node.target !== undefined && node.on === undefined && node.keys;
|
|
281
308
|
}
|
|
282
309
|
|
|
@@ -397,7 +424,7 @@ function getUtils(model) {
|
|
|
397
424
|
function getServiceName(artifactName) {
|
|
398
425
|
for(;;) {
|
|
399
426
|
let idx = artifactName.lastIndexOf('.');
|
|
400
|
-
if (idx
|
|
427
|
+
if (idx === -1) return null;
|
|
401
428
|
artifactName = artifactName.substring(0, idx);
|
|
402
429
|
let artifact = model.definitions[artifactName];
|
|
403
430
|
if (artifact && artifact.kind === 'service') {
|
|
@@ -514,7 +541,7 @@ function getUtils(model) {
|
|
|
514
541
|
if (cycleCheck) {
|
|
515
542
|
let visited = path.length? type + ':' + path.join('.') : type;
|
|
516
543
|
if (cycleCheck[visited])
|
|
517
|
-
throw new
|
|
544
|
+
throw new ModelError('Circular type chain on type ' + type);
|
|
518
545
|
else
|
|
519
546
|
cycleCheck[visited] = true;
|
|
520
547
|
}
|
|
@@ -627,7 +654,7 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
|
|
|
627
654
|
}
|
|
628
655
|
|
|
629
656
|
path = [...path]; // Copy
|
|
630
|
-
const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', '
|
|
657
|
+
const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'actions', 'params']);
|
|
631
658
|
propsWithMembers.forEach((prop) => forEachGeneric( construct, prop, callback, path, iterateOptions ));
|
|
632
659
|
}
|
|
633
660
|
|
|
@@ -684,45 +711,6 @@ function forEachGeneric( obj, prop, callback, path = [], iterateOptions = {}) {
|
|
|
684
711
|
}
|
|
685
712
|
}
|
|
686
713
|
|
|
687
|
-
/**
|
|
688
|
-
* For each property named 'ref' in 'node' (recursively), call callback(ref, node, path)
|
|
689
|
-
*
|
|
690
|
-
* @param {object} node
|
|
691
|
-
* @param {refCallback|refCallback[]} callback
|
|
692
|
-
* @param {CSN.Path} path
|
|
693
|
-
*/
|
|
694
|
-
function forEachRef(node, callback, path = []) {
|
|
695
|
-
if (node === null || typeof node !== 'object') {
|
|
696
|
-
// Primitive node
|
|
697
|
-
return;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
if(node._ignore){
|
|
701
|
-
return;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
if(Array.isArray(node)){
|
|
705
|
-
for (let i = 0; i < node.length; i++) {
|
|
706
|
-
// Descend recursively
|
|
707
|
-
forEachRef(node[i], callback, path.concat([i]));
|
|
708
|
-
}
|
|
709
|
-
} else {
|
|
710
|
-
for (let name in node) {
|
|
711
|
-
if (!Object.hasOwnProperty.call( node, name ))
|
|
712
|
-
continue;
|
|
713
|
-
// If ref found within a non-dictionary, call callback
|
|
714
|
-
if (name === 'ref' && Object.getPrototypeOf(node)) {
|
|
715
|
-
if(Array.isArray(callback))
|
|
716
|
-
callback.forEach(cb => cb( node.ref, node, path ));
|
|
717
|
-
else
|
|
718
|
-
callback( node.ref, node, path );
|
|
719
|
-
}
|
|
720
|
-
// Descend recursively
|
|
721
|
-
forEachRef(node[name], callback, path.concat([name]));
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
|
|
726
714
|
// Like Object.assign() but copies also non enumerable properties
|
|
727
715
|
function assignAll(target, ...sources) {
|
|
728
716
|
sources.forEach(source => {
|
|
@@ -808,39 +796,6 @@ function forAllQueries(query, callback, path = []){
|
|
|
808
796
|
}
|
|
809
797
|
}
|
|
810
798
|
|
|
811
|
-
function forAllElements(artifact, artifactName, cb, includeActions = false){
|
|
812
|
-
if(artifact.elements) {
|
|
813
|
-
cb(artifact, artifact.elements, ['definitions', artifactName, 'elements']);
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
if(includeActions && artifact.actions) {
|
|
817
|
-
Object.entries(artifact.actions).forEach( ([actionName, action]) => {
|
|
818
|
-
const path = ['definitions', artifactName, 'actions', actionName];
|
|
819
|
-
if(action.params) {
|
|
820
|
-
Object.entries(action.params).forEach( ([paramName, param]) => {
|
|
821
|
-
if(param.elements)
|
|
822
|
-
cb(param, param.elements, path.concat(['params', paramName, 'elements']));
|
|
823
|
-
});
|
|
824
|
-
}
|
|
825
|
-
if(action.returns && action.returns.elements)
|
|
826
|
-
cb(action.returns, action.returns.elements,path.concat(['returns', 'elements']));
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
if(artifact.query) {
|
|
831
|
-
forAllQueries(artifact.query, (q, p) => {
|
|
832
|
-
const s = q.SELECT;
|
|
833
|
-
if(s) {
|
|
834
|
-
if(s.elements) {
|
|
835
|
-
cb(s, s.elements, [...p, 'elements']);
|
|
836
|
-
} else if(s.$elements) { // huh?, is just refloc output
|
|
837
|
-
cb(s, s.$elements, [...p, '$elements']);
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
}, ['definitions', artifactName, 'query'])
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
799
|
/**
|
|
845
800
|
* Compare a given annotation value with an expectation value and return
|
|
846
801
|
*
|
|
@@ -859,11 +814,14 @@ function forAllElements(artifact, artifactName, cb, includeActions = false){
|
|
|
859
814
|
* @param {CSN.Artifact} artifact
|
|
860
815
|
* @param {string} annotationName Name of the annotation (including the at-sign)
|
|
861
816
|
* @param {any} expected
|
|
817
|
+
* @param {boolean} caseInsensitive
|
|
862
818
|
* @returns {boolean}
|
|
863
819
|
*/
|
|
864
|
-
function hasAnnotationValue(artifact, annotationName, expected = true) {
|
|
820
|
+
function hasAnnotationValue(artifact, annotationName, expected = true, caseInsensitive = false) {
|
|
865
821
|
if(expected === false)
|
|
866
822
|
return artifact[annotationName] === expected || artifact[annotationName] === null;
|
|
823
|
+
else if (typeof artifact[annotationName] === 'string' && caseInsensitive === true)
|
|
824
|
+
return artifact[annotationName].toLowerCase() === expected.toLowerCase();
|
|
867
825
|
else
|
|
868
826
|
return artifact[annotationName] === expected;
|
|
869
827
|
}
|
|
@@ -900,48 +858,48 @@ function isEdmPropertyRendered(elementCsn, options) {
|
|
|
900
858
|
|
|
901
859
|
/**
|
|
902
860
|
* Return the resulting database name for (absolute) 'artifactName', depending on the current naming
|
|
903
|
-
*
|
|
861
|
+
* mode.
|
|
904
862
|
*
|
|
905
|
-
* - For the 'hdbcds' naming
|
|
863
|
+
* - For the 'hdbcds' naming mode, this means converting '.' to '::' on
|
|
906
864
|
* the border between namespace and top-level artifact and correctly replacing some '.' with '_'.
|
|
907
|
-
* - For the 'plain' naming
|
|
908
|
-
* - For the 'quoted' naming
|
|
865
|
+
* - For the 'plain' naming mode, it means converting all '.' to '_' and upper-casing.
|
|
866
|
+
* - For the 'quoted' naming mode, this means correctly replacing some '.' with '_'.
|
|
909
867
|
*
|
|
910
868
|
* If the old function signature is used - with a namespace as the third argument - the result might be wrong,
|
|
911
869
|
* since the '.' -> '_' conversion for quoted/hdbcds is missing.
|
|
912
870
|
*
|
|
913
|
-
* @param {string} artifactName The name of the artifact
|
|
914
|
-
* @param {('plain'|'quoted'|'hdbcds')}
|
|
871
|
+
* @param {string} artifactName The fully qualified name of the artifact
|
|
872
|
+
* @param {('plain'|'quoted'|'hdbcds')} sqlMapping The naming mode to use
|
|
915
873
|
* @param {CSN.Model|string|undefined} csn
|
|
916
|
-
* @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming
|
|
874
|
+
* @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming mode.
|
|
917
875
|
*/
|
|
918
|
-
function getArtifactDatabaseNameOf(artifactName,
|
|
876
|
+
function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn) {
|
|
919
877
|
if(csn && typeof csn === 'object' && csn.definitions)
|
|
920
|
-
if (
|
|
921
|
-
return getResultingName(csn,
|
|
878
|
+
if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds') {
|
|
879
|
+
return getResultingName(csn, sqlMapping, artifactName);
|
|
922
880
|
}
|
|
923
|
-
else if (
|
|
881
|
+
else if (sqlMapping === 'plain') {
|
|
924
882
|
return artifactName.replace(/\./g, '_').toUpperCase();
|
|
925
883
|
} else {
|
|
926
|
-
throw new Error('Unknown naming
|
|
884
|
+
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
927
885
|
}
|
|
928
886
|
else {
|
|
929
887
|
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.`);
|
|
930
|
-
if (
|
|
888
|
+
if (sqlMapping === 'hdbcds') {
|
|
931
889
|
if (csn) {
|
|
932
890
|
const namespace = String(csn);
|
|
933
891
|
return `${namespace}::${artifactName.substring(namespace.length + 1)}`;
|
|
934
892
|
}
|
|
935
893
|
return artifactName;
|
|
936
894
|
}
|
|
937
|
-
else if (
|
|
895
|
+
else if (sqlMapping === 'plain') {
|
|
938
896
|
return artifactName.replace(/\./g, '_').toUpperCase();
|
|
939
897
|
}
|
|
940
|
-
else if (
|
|
898
|
+
else if (sqlMapping === 'quoted') {
|
|
941
899
|
return artifactName;
|
|
942
900
|
}
|
|
943
901
|
else {
|
|
944
|
-
throw new Error('Unknown naming
|
|
902
|
+
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
945
903
|
}
|
|
946
904
|
}
|
|
947
905
|
}
|
|
@@ -1027,135 +985,29 @@ function getUnderscoredName(startIndex, parts, csn) {
|
|
|
1027
985
|
|
|
1028
986
|
|
|
1029
987
|
/**
|
|
1030
|
-
* Return the resulting database element name for 'elemName', depending on the current
|
|
1031
|
-
*
|
|
1032
|
-
* - For the 'hdbcds' naming
|
|
1033
|
-
* - For the 'plain' naming
|
|
1034
|
-
* - For the 'quoted' naming
|
|
1035
|
-
* No other naming
|
|
988
|
+
* Return the resulting database element name for 'elemName', depending on the current
|
|
989
|
+
* naming mode.
|
|
990
|
+
* - For the 'hdbcds' naming mode, this is just 'elemName'.
|
|
991
|
+
* - For the 'plain' naming mode, it means converting all '.' to '_' and upper-casing.
|
|
992
|
+
* - For the 'quoted' naming mode, it means converting all '.' to '_'.
|
|
993
|
+
* No other naming modes are accepted!
|
|
1036
994
|
*
|
|
1037
|
-
* @param {string} elemName
|
|
1038
|
-
* @param {('plain'|'quoted'|'hdbcds')}
|
|
1039
|
-
* @returns {string} The resulting database element name for 'elemName', depending on the current naming
|
|
995
|
+
* @param {string} elemName The name of the element
|
|
996
|
+
* @param {('plain'|'quoted'|'hdbcds')} sqlMapping The naming mode to use
|
|
997
|
+
* @returns {string} The resulting database element name for 'elemName', depending on the current naming mode.
|
|
1040
998
|
*/
|
|
1041
|
-
function getElementDatabaseNameOf(elemName,
|
|
1042
|
-
if (
|
|
999
|
+
function getElementDatabaseNameOf(elemName, sqlMapping) {
|
|
1000
|
+
if (sqlMapping === 'hdbcds') {
|
|
1043
1001
|
return elemName;
|
|
1044
1002
|
}
|
|
1045
|
-
else if (
|
|
1003
|
+
else if (sqlMapping === 'plain') {
|
|
1046
1004
|
return elemName.replace(/\./g, '_').toUpperCase();
|
|
1047
1005
|
}
|
|
1048
|
-
else if (
|
|
1006
|
+
else if (sqlMapping === 'quoted') {
|
|
1049
1007
|
return elemName.replace(/\./g, '_');
|
|
1050
1008
|
}
|
|
1051
1009
|
else {
|
|
1052
|
-
throw new Error('Unknown naming
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
/**
|
|
1058
|
-
* Loop through the model, applying the custom transformations on the node's matching.
|
|
1059
|
-
*
|
|
1060
|
-
* Each transformer gets:
|
|
1061
|
-
* - the parent having the property
|
|
1062
|
-
* - the name of the property
|
|
1063
|
-
* - the value of the property
|
|
1064
|
-
* - the path to the property
|
|
1065
|
-
*
|
|
1066
|
-
* @param {object} csn CSN to enrich in-place
|
|
1067
|
-
* @param {object} customTransformers Map of prop to transform and function to apply
|
|
1068
|
-
* @param {Function[]} [artifactTransformers=[]] Transformations to run on the artifacts, like forEachDefinition
|
|
1069
|
-
* @param {Boolean} [skipIgnore=true] Wether to skip _ignore elements or not
|
|
1070
|
-
* @param {object} [options={}] "skipArtifact": (artifact, name) => Boolean to skip certain artifacts, drillRef: boolean - whether to drill into infix/args
|
|
1071
|
-
* @returns {object} CSN with transformations applied
|
|
1072
|
-
*/
|
|
1073
|
-
function applyTransformations( csn, customTransformers={}, artifactTransformers=[], skipIgnore = true, options = {} ) {
|
|
1074
|
-
const transformers = {
|
|
1075
|
-
elements: dictionary,
|
|
1076
|
-
definitions: dictionary,
|
|
1077
|
-
actions: dictionary,
|
|
1078
|
-
params: dictionary,
|
|
1079
|
-
enum: dictionary,
|
|
1080
|
-
mixin: dictionary,
|
|
1081
|
-
ref: pathRef,
|
|
1082
|
-
//type: simpleRef,
|
|
1083
|
-
//target: simpleRef,
|
|
1084
|
-
//includes: simpleRef,
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
const csnPath = [];
|
|
1088
|
-
if (csn.definitions)
|
|
1089
|
-
definitions( csn, 'definitions', csn.definitions );
|
|
1090
|
-
return csn;
|
|
1091
|
-
|
|
1092
|
-
function standard( parent, prop, node ) {
|
|
1093
|
-
if (!node || typeof node !== 'object' || !{}.propertyIsEnumerable.call( parent, prop ) || (typeof prop === 'string' && prop.startsWith('@')) || (skipIgnore && node._ignore))
|
|
1094
|
-
return;
|
|
1095
|
-
|
|
1096
|
-
csnPath.push( prop );
|
|
1097
|
-
|
|
1098
|
-
if (Array.isArray(node)) {
|
|
1099
|
-
node.forEach( (n, i) => standard( node, i, n ) );
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
else {
|
|
1103
|
-
for (let name of Object.getOwnPropertyNames( node )) {
|
|
1104
|
-
const trans = transformers[name] || standard;
|
|
1105
|
-
if(customTransformers[name])
|
|
1106
|
-
customTransformers[name](node, name, node[name], csnPath, parent, prop);
|
|
1107
|
-
|
|
1108
|
-
trans( node, name, node[name], csnPath );
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
csnPath.pop();
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
function dictionary( node, prop, dict ) {
|
|
1115
|
-
// Allow skipping dicts like actions in forHanaNew
|
|
1116
|
-
if(options.skipDict && options.skipDict[prop])
|
|
1117
|
-
return;
|
|
1118
|
-
csnPath.push( prop );
|
|
1119
|
-
for (let name of Object.getOwnPropertyNames( dict )) {
|
|
1120
|
-
standard( dict, name, dict[name] );
|
|
1121
|
-
}
|
|
1122
|
-
if (!Object.prototype.propertyIsEnumerable.call( node, prop ))
|
|
1123
|
-
setProp(node, '$' + prop, dict);
|
|
1124
|
-
csnPath.pop();
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
function definitions( node, prop, dict ) {
|
|
1128
|
-
csnPath.push( prop );
|
|
1129
|
-
for (let name of Object.getOwnPropertyNames( dict )) {
|
|
1130
|
-
const skip = options && options.skipArtifact && options.skipArtifact(dict[name], name) || false;
|
|
1131
|
-
if(!skip) {
|
|
1132
|
-
artifactTransformers.forEach(fn => fn(dict, name, dict[name]));
|
|
1133
|
-
standard( dict, name, dict[name] );
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
if (!Object.prototype.propertyIsEnumerable.call( node, prop ))
|
|
1137
|
-
setProp(node, '$' + prop, dict);
|
|
1138
|
-
csnPath.pop();
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
//Keep looping through the pathRef
|
|
1142
|
-
function pathRef( node, prop, path ) {
|
|
1143
|
-
csnPath.push( prop );
|
|
1144
|
-
path.forEach( function step( s, i ) {
|
|
1145
|
-
if (s && typeof s === 'object') {
|
|
1146
|
-
csnPath.push( i );
|
|
1147
|
-
if(options.drillRef) {
|
|
1148
|
-
standard(path, i, s);
|
|
1149
|
-
} else {
|
|
1150
|
-
if (s.args)
|
|
1151
|
-
standard( s, 'args', s.args );
|
|
1152
|
-
if (s.where)
|
|
1153
|
-
standard( s, 'where', s.where );
|
|
1154
|
-
}
|
|
1155
|
-
csnPath.pop();
|
|
1156
|
-
}
|
|
1157
|
-
} );
|
|
1158
|
-
csnPath.pop();
|
|
1010
|
+
throw new Error('Unknown naming mode: ' + sqlMapping);
|
|
1159
1011
|
}
|
|
1160
1012
|
}
|
|
1161
1013
|
|
|
@@ -1233,7 +1085,7 @@ function setDependencies( csn ) {
|
|
|
1233
1085
|
* @returns {boolean}
|
|
1234
1086
|
*/
|
|
1235
1087
|
function isPersistedOnDatabase(art) {
|
|
1236
|
-
return !(
|
|
1088
|
+
return !('entity' === art.kind && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
|
|
1237
1089
|
}
|
|
1238
1090
|
|
|
1239
1091
|
/**
|
|
@@ -1301,10 +1153,10 @@ function mergeOptions(...optionsObjects) {
|
|
|
1301
1153
|
}
|
|
1302
1154
|
// Check against improper overwriting
|
|
1303
1155
|
if (isObject(left) && !Array.isArray(left) && (Array.isArray(right) || isScalar(right))) {
|
|
1304
|
-
throw new
|
|
1156
|
+
throw new ModelError(`Cannot overwrite structured option "${name}" with array or scalar value`);
|
|
1305
1157
|
}
|
|
1306
1158
|
if ((isScalar(left) && typeof left !== 'boolean' || Array.isArray(left)) && isObject(right) && !Array.isArray(right)) {
|
|
1307
|
-
throw new
|
|
1159
|
+
throw new ModelError(`Cannot overwrite non-boolean scalar or array option "${name}" with structured value`);
|
|
1308
1160
|
}
|
|
1309
1161
|
|
|
1310
1162
|
// Copy or overwrite properties from right to left
|
|
@@ -1338,38 +1190,6 @@ function mergeOptions(...optionsObjects) {
|
|
|
1338
1190
|
}
|
|
1339
1191
|
}
|
|
1340
1192
|
|
|
1341
|
-
// Return the name of the top-level artifact surrounding the artifact 'name'
|
|
1342
|
-
// in 'model'.
|
|
1343
|
-
// We define "top-level artifact" to be an artifact that has either no parent or only
|
|
1344
|
-
// ancestors of kind 'namespace'. Note that it is possible for a non-top-level artifact
|
|
1345
|
-
// to have a namespace as parent and e.g. a context as grandparent (weird but true).
|
|
1346
|
-
// Will return the artifact 'name' if it is a top-level artifact itself, and 'undefined'
|
|
1347
|
-
// if there is no artifact surrounding 'name' in the model
|
|
1348
|
-
// TODO: to be checked by author: still intended behaviour with 'cds' prefix?
|
|
1349
|
-
// TODO: Can this be replaced by getRootArtifactName? Or maybe not rely on namespace-hacking...
|
|
1350
|
-
// FIXME: This only works with namespace-hacking, i.e. adding them as artifacts...
|
|
1351
|
-
function getTopLevelArtifactNameOf(name, model) {
|
|
1352
|
-
let dotIdx = name.indexOf('.');
|
|
1353
|
-
if (dotIdx == -1) {
|
|
1354
|
-
// No '.' in the name, i.e. no parent - this is a top-level artifact (if it exists)
|
|
1355
|
-
return model.definitions[name] ? name : undefined;
|
|
1356
|
-
}
|
|
1357
|
-
// If the first name part is not in the model, there is nothing to find
|
|
1358
|
-
if (!model.definitions[name.substring(0, dotIdx)]) {
|
|
1359
|
-
return undefined;
|
|
1360
|
-
}
|
|
1361
|
-
// Skip forward through '.'s until finding a non-namespace
|
|
1362
|
-
while (dotIdx != -1 && (!model.definitions[name.substring(0, dotIdx)] || model.definitions[name.substring(0, dotIdx)].kind === 'namespace')) {
|
|
1363
|
-
dotIdx = name.indexOf('.', dotIdx + 1);
|
|
1364
|
-
}
|
|
1365
|
-
if (dotIdx == -1) {
|
|
1366
|
-
// This is a top-level artifact
|
|
1367
|
-
return name;
|
|
1368
|
-
}
|
|
1369
|
-
// The skipped part of 'name' is the top-level artifact name
|
|
1370
|
-
return name.substring(0, dotIdx);
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
1193
|
/**
|
|
1374
1194
|
* If the artifact with the name given is part of a context (or multiple), return the top-most context.
|
|
1375
1195
|
* Else, return the artifact itself. Namespaces are not of concern here.
|
|
@@ -1491,7 +1311,7 @@ function forEachPath(node, callback) {
|
|
|
1491
1311
|
* @returns {boolean}
|
|
1492
1312
|
*/
|
|
1493
1313
|
function hasValidSkipOrExists(artifact) {
|
|
1494
|
-
return
|
|
1314
|
+
return artifact.kind === 'entity' &&
|
|
1495
1315
|
(hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true))
|
|
1496
1316
|
|
|
1497
1317
|
}
|
|
@@ -1560,7 +1380,7 @@ function getServiceNames(csn) {
|
|
|
1560
1380
|
}
|
|
1561
1381
|
|
|
1562
1382
|
/**
|
|
1563
|
-
* Check
|
|
1383
|
+
* Check whether the artifact is @cds.persistence.skip
|
|
1564
1384
|
*
|
|
1565
1385
|
* @param {CSN.Artifact} artifact
|
|
1566
1386
|
* @returns {Boolean}
|
|
@@ -1579,8 +1399,8 @@ function isSkipped(artifact) {
|
|
|
1579
1399
|
function walkCsnPath(csn, path) {
|
|
1580
1400
|
/** @type {object} */
|
|
1581
1401
|
let obj = csn;
|
|
1582
|
-
for(
|
|
1583
|
-
obj = obj[
|
|
1402
|
+
for(const segment of path){
|
|
1403
|
+
obj = obj[segment];
|
|
1584
1404
|
}
|
|
1585
1405
|
|
|
1586
1406
|
return obj;
|
|
@@ -1589,16 +1409,16 @@ function walkCsnPath(csn, path) {
|
|
|
1589
1409
|
/**
|
|
1590
1410
|
* If provided, get the replacement string for the given magic variable ref.
|
|
1591
1411
|
* No validation is done that the ref is actually magic!
|
|
1592
|
-
*
|
|
1593
|
-
* @param {array} ref
|
|
1594
|
-
* @param {CSN.Options} options
|
|
1595
|
-
* @returns {string|null}
|
|
1412
|
+
*
|
|
1413
|
+
* @param {array} ref
|
|
1414
|
+
* @param {CSN.Options} options
|
|
1415
|
+
* @returns {string|null}
|
|
1596
1416
|
*/
|
|
1597
1417
|
function getVariableReplacement(ref, options) {
|
|
1598
1418
|
if(options && options.variableReplacements) {
|
|
1599
1419
|
let replacement = options.variableReplacements;
|
|
1600
|
-
for(
|
|
1601
|
-
replacement = replacement[
|
|
1420
|
+
for(const segment of ref) {
|
|
1421
|
+
replacement = replacement[segment];
|
|
1602
1422
|
if(replacement === undefined)
|
|
1603
1423
|
return null;
|
|
1604
1424
|
}
|
|
@@ -1614,6 +1434,42 @@ function getVariableReplacement(ref, options) {
|
|
|
1614
1434
|
}
|
|
1615
1435
|
}
|
|
1616
1436
|
|
|
1437
|
+
/**
|
|
1438
|
+
*
|
|
1439
|
+
* @param {object} obj
|
|
1440
|
+
* @param {*} other
|
|
1441
|
+
* @param {boolean} noExtendedProps
|
|
1442
|
+
* @returns {boolean} returns equality
|
|
1443
|
+
*
|
|
1444
|
+
* noExtendedProps remove '$', '_' and '@' properties from
|
|
1445
|
+
* the comparision. This eliminates false negatives such as
|
|
1446
|
+
* mismatching $locations or @odata.foreignKey4.
|
|
1447
|
+
*/
|
|
1448
|
+
function isDeepEqual(obj, other, noExtendedProps) {
|
|
1449
|
+
let objectKeys = Object.keys(obj);
|
|
1450
|
+
let otherKeys = Object.keys(other);
|
|
1451
|
+
|
|
1452
|
+
if(noExtendedProps) {
|
|
1453
|
+
objectKeys = objectKeys.filter(k => !['@', '$', '_'].includes(k[0]));
|
|
1454
|
+
otherKeys = otherKeys.filter(k => !['@', '$', '_'].includes(k[0]));
|
|
1455
|
+
}
|
|
1456
|
+
if (objectKeys.length !== otherKeys.length)
|
|
1457
|
+
return false;
|
|
1458
|
+
|
|
1459
|
+
for (let key of objectKeys) {
|
|
1460
|
+
const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object')
|
|
1461
|
+
&& (other[key] !== null && typeof other[key] === 'object');
|
|
1462
|
+
|
|
1463
|
+
if (areValuesObjects) {
|
|
1464
|
+
if (!isDeepEqual(obj[key], other[key], noExtendedProps))
|
|
1465
|
+
return false;
|
|
1466
|
+
} else if (obj[key] !== other[key]) {
|
|
1467
|
+
return false;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return true;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1617
1473
|
module.exports = {
|
|
1618
1474
|
getUtils,
|
|
1619
1475
|
cloneCsn,
|
|
@@ -1624,9 +1480,7 @@ module.exports = {
|
|
|
1624
1480
|
forEachDefinition,
|
|
1625
1481
|
forEachMember,
|
|
1626
1482
|
forEachMemberRecursively,
|
|
1627
|
-
forEachRef,
|
|
1628
1483
|
forAllQueries,
|
|
1629
|
-
forAllElements,
|
|
1630
1484
|
hasAnnotationValue,
|
|
1631
1485
|
isEdmPropertyRendered,
|
|
1632
1486
|
getArtifactDatabaseNameOf,
|
|
@@ -1634,12 +1488,12 @@ module.exports = {
|
|
|
1634
1488
|
getUnderscoredName,
|
|
1635
1489
|
getElementDatabaseNameOf,
|
|
1636
1490
|
applyTransformations,
|
|
1491
|
+
applyTransformationsOnNonDictionary,
|
|
1637
1492
|
setDependencies,
|
|
1638
1493
|
isPersistedOnDatabase,
|
|
1639
1494
|
generatedByCompilerVersion,
|
|
1640
1495
|
getNormalizedQuery,
|
|
1641
1496
|
mergeOptions,
|
|
1642
|
-
getTopLevelArtifactNameOf,
|
|
1643
1497
|
getRootArtifactName,
|
|
1644
1498
|
getLastPartOfRef,
|
|
1645
1499
|
getParentNamesOf,
|
|
@@ -1654,5 +1508,7 @@ module.exports = {
|
|
|
1654
1508
|
getServiceNames,
|
|
1655
1509
|
isSkipped,
|
|
1656
1510
|
walkCsnPath,
|
|
1657
|
-
getVariableReplacement
|
|
1511
|
+
getVariableReplacement,
|
|
1512
|
+
implicitAs,
|
|
1513
|
+
isDeepEqual,
|
|
1658
1514
|
};
|