@sap/cds-compiler 2.13.8 → 2.15.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 +128 -4
- package/bin/cdsc.js +112 -37
- package/lib/api/main.js +63 -22
- package/lib/api/options.js +2 -3
- package/lib/api/validate.js +6 -6
- package/lib/base/message-registry.js +100 -17
- package/lib/base/messages.js +85 -64
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/validator.js +2 -4
- package/lib/compiler/assert-consistency.js +1 -0
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +11 -0
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +59 -11
- package/lib/compiler/extend.js +20 -3
- package/lib/compiler/finalize-parse-cdl.js +26 -20
- package/lib/compiler/index.js +75 -26
- package/lib/compiler/populate.js +36 -17
- package/lib/compiler/propagator.js +4 -1
- package/lib/compiler/resolve.js +104 -16
- package/lib/compiler/shared.js +61 -27
- package/lib/compiler/tweak-assocs.js +7 -1
- package/lib/edm/annotations/genericTranslation.js +93 -21
- package/lib/edm/csn2edm.js +216 -98
- package/lib/edm/edm.js +305 -226
- package/lib/edm/edmPreprocessor.js +499 -423
- package/lib/edm/edmUtils.js +22 -22
- package/lib/gen/Dictionary.json +98 -22
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageParser.js +4636 -4368
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +3 -2
- package/lib/json/to-csn.js +0 -2
- package/lib/language/docCommentParser.js +2 -2
- package/lib/language/genericAntlrParser.js +47 -2
- package/lib/language/language.g4 +59 -27
- package/lib/main.d.ts +19 -1
- package/lib/main.js +6 -0
- package/lib/model/csnRefs.js +33 -6
- package/lib/model/csnUtils.js +193 -75
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +2 -2
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +62 -26
- package/lib/render/toCdl.js +844 -679
- package/lib/render/toHdbcds.js +189 -243
- package/lib/render/toSql.js +180 -198
- package/lib/render/utils/common.js +131 -15
- package/lib/transform/db/.eslintrc.json +1 -1
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/constraints.js +3 -1
- package/lib/transform/db/expansion.js +15 -10
- package/lib/transform/db/flattening.js +94 -64
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +6 -3
- package/lib/transform/forHanaNew.js +43 -26
- package/lib/transform/forOdataNew.js +43 -42
- package/lib/transform/localized.js +12 -7
- package/lib/transform/odata/toFinalBaseType.js +8 -6
- package/lib/transform/odata/typesExposure.js +145 -197
- package/lib/transform/transformUtilsNew.js +9 -12
- package/lib/transform/translateAssocsToJoins.js +5 -1
- package/lib/transform/universalCsn/coreComputed.js +5 -3
- package/lib/transform/universalCsn/universalCsnEnricher.js +27 -5
- package/lib/utils/moduleResolve.js +13 -6
- package/package.json +1 -1
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- 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 -296
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
|
@@ -8,35 +8,29 @@
|
|
|
8
8
|
|
|
9
9
|
const { setProp } = require('../../base/model');
|
|
10
10
|
const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
|
|
11
|
-
const {
|
|
11
|
+
const { cloneCsnNonDict, isBuiltinType, forEachDefinition, forEachMember } = require('../../model/csnUtils');
|
|
12
12
|
const { copyAnnotations } = require('../../model/csnUtils');
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
* @param {CSN.Model} csn
|
|
16
|
-
* @param {function} whatsMyServiceName
|
|
17
|
-
* @param {CSN.Options} options
|
|
18
|
-
* @param {*} csnUtils
|
|
19
|
-
* @param {object} message message object with { error } function
|
|
20
|
-
*/
|
|
21
|
-
function typesExposure(csn, whatsMyServiceName, autoexposeSchemaName, options, csnUtils, message) {
|
|
14
|
+
function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackSchemaName, options, csnUtils, message) {
|
|
22
15
|
const { error } = message;
|
|
23
16
|
// are we working with OData proxies or cross-service refs
|
|
24
17
|
const isMultiSchema = options.toOdata.version === 'v4' && (options.toOdata.odataProxies || options.toOdata.odataXServiceRefs);
|
|
25
18
|
// collect in this variable all the newly exposed types
|
|
26
|
-
const exposedStructTypes = [];
|
|
27
19
|
const schemas = Object.create(null);
|
|
20
|
+
const exposedTypes = Object.create(null);
|
|
28
21
|
// walk through the definitions of the given CSN and expose types where needed
|
|
29
22
|
forEachDefinition(csn, (def, defName, propertyName, path) => {
|
|
30
23
|
// we do expose types only for definition from inside services
|
|
31
24
|
const serviceName = whatsMyServiceName(defName, false);
|
|
32
|
-
if
|
|
25
|
+
// run type exposure only on requested services if not in multi schema mode
|
|
26
|
+
// multi schema mode requires a proper type exposure for all services as a prerequisite
|
|
27
|
+
// for the proxy exposure
|
|
28
|
+
if (serviceName && requestedServiceNames.includes(serviceName)) {
|
|
33
29
|
if (def.kind === 'type' || def.kind === 'entity') {
|
|
34
30
|
forEachMember(def, (element, elementName, propertyName, path) => {
|
|
35
31
|
if (propertyName === 'elements' || propertyName === 'params') {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
: defNameWithoutServiceOrContextName(defName, serviceName).replace(/\./g, '_')}_${elementName}`;
|
|
39
|
-
exposeTypeOf(element, element.key || propertyName === 'params', elementName, defName, serviceName, artificialtName, path);
|
|
32
|
+
const newTypeName = getNewTypeName(element, elementName, defName, serviceName);
|
|
33
|
+
exposeTypeOf(element, element.key || propertyName === 'params', elementName, defName, serviceName, newTypeName, path);
|
|
40
34
|
}
|
|
41
35
|
}, path);
|
|
42
36
|
}
|
|
@@ -55,46 +49,26 @@ function typesExposure(csn, whatsMyServiceName, autoexposeSchemaName, options, c
|
|
|
55
49
|
});
|
|
56
50
|
|
|
57
51
|
return schemas;
|
|
58
|
-
/**
|
|
59
|
-
* General function used for exposing a type of given element
|
|
60
|
-
* @param {object} node
|
|
61
|
-
* @param {string} memberName
|
|
62
|
-
* @param {string} service
|
|
63
|
-
* @param {string} artificialName
|
|
64
|
-
* @param {CSN.Path} path
|
|
65
|
-
*/
|
|
66
|
-
function exposeTypeOf(node, isKey, memberName, defName, service, artificialName, path) {
|
|
67
|
-
if (isArrayed(node))
|
|
68
|
-
exposeArrayOfTypeOf(node, isKey, memberName, defName, service, artificialName, path);
|
|
69
|
-
else if (csnUtils.isStructured(node))
|
|
70
|
-
exposeStructTypeOf(node, isKey, memberName, defName, service, artificialName, path);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Check if a node is arrayed
|
|
75
|
-
* @param {object} node
|
|
76
|
-
*/
|
|
77
|
-
function isArrayed(node) {
|
|
78
|
-
return node.items || (node.type && csnUtils.getFinalTypeDef(node.type).items);
|
|
79
|
-
}
|
|
80
52
|
|
|
81
|
-
|
|
53
|
+
/**
|
|
82
54
|
* If an 'action' uses structured types as parameters or return values that are not exposed in 'service'
|
|
83
55
|
* (because the types are anonymous or have a definition outside of 'service'),
|
|
84
56
|
* create equivalent types in 'service' and make 'action' use them instead,
|
|
85
57
|
* @param {Object} action
|
|
86
58
|
* @param {String} actionName
|
|
87
|
-
* @param {String}
|
|
59
|
+
* @param {String} serviceName
|
|
88
60
|
*/
|
|
89
|
-
function exposeTypesOfAction(action, actionName, defName,
|
|
61
|
+
function exposeTypesOfAction(action, actionName, defName, serviceName, path) {
|
|
90
62
|
if (action.returns) {
|
|
91
63
|
const artificialName = `return_${actionName.replace(/\./g, '_')}`;
|
|
92
|
-
|
|
64
|
+
const newTypeName = getNewTypeName(action.returns, undefined, artificialName, serviceName);
|
|
65
|
+
exposeTypeOf(action.returns, false, actionName, defName, serviceName, newTypeName, path.concat(['returns']));
|
|
93
66
|
}
|
|
94
67
|
|
|
95
68
|
action.params && Object.entries(action.params).forEach(([paramName, param]) => {
|
|
96
|
-
const artificialName = `param_${actionName.replace(/\./g, '_')}_${paramName}`;
|
|
97
|
-
|
|
69
|
+
const artificialName = `param_${actionName.replace(/\./g, '_')}`;//_${paramName}`;
|
|
70
|
+
const newTypeName = getNewTypeName(param, paramName, artificialName, serviceName);
|
|
71
|
+
exposeTypeOf(param, false, actionName, defName, serviceName, newTypeName, path.concat(['params', paramName]));
|
|
98
72
|
});
|
|
99
73
|
}
|
|
100
74
|
|
|
@@ -104,93 +78,139 @@ function typesExposure(csn, whatsMyServiceName, autoexposeSchemaName, options, c
|
|
|
104
78
|
* for a value of the 'node.type' property.
|
|
105
79
|
* @param {Object} node
|
|
106
80
|
* @param {String} memberName
|
|
107
|
-
* @param {String}
|
|
108
|
-
* @param {String}
|
|
81
|
+
* @param {String} serviceName
|
|
82
|
+
* @param {String} newTypeName
|
|
109
83
|
*/
|
|
110
|
-
function
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (isExposableStructure(node)) {
|
|
117
|
-
let typeDef = node.type ? csnUtils.getCsnDef(node.type) : /* structure|anonymous type */ node;
|
|
118
|
-
let newTypeId = node.type ? `${isMultiSchema ? node.type : node.type.replace(/\./g, '_')}` : artificialName;
|
|
119
|
-
let newTypeFullName =
|
|
84
|
+
function exposeTypeOf(node, isKey, memberName, defName, serviceName, newTypeName, path, parentName) {
|
|
85
|
+
const { isExposable, typeDef, typeName, elements, isAnonymous } = isTypeExposable(node);
|
|
86
|
+
if (isExposable) {
|
|
87
|
+
// this is the name used to register the new type in csn.definitions
|
|
88
|
+
let fullQualifiedNewTypeName =
|
|
120
89
|
isMultiSchema
|
|
121
|
-
? node.type
|
|
122
|
-
|
|
90
|
+
? (node.type || (node.items && node.items.type)
|
|
91
|
+
? getTypeNameInMultiSchema(node.type|| (node.items && node.items.type), serviceName)
|
|
92
|
+
: getAnonymousTypeNameInMultiSchema(newTypeName, parentName || defName))
|
|
93
|
+
: `${serviceName}.${newTypeName}`;
|
|
123
94
|
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
let newTypeElements = (node.type && node.elements) ? node.elements : typeDef.elements;
|
|
128
|
-
// if node and typeDef are identical, we're anonymous
|
|
129
|
-
isAnonymous = node === typeDef;
|
|
130
|
-
// if we've left the anonymous world, we're no longer in a key def
|
|
131
|
-
if (!isAnonymous)
|
|
95
|
+
// as soon as we leave of the anonymous world,
|
|
96
|
+
// we're no longer in a key def => don't set notNull:true on named types
|
|
97
|
+
if (!isAnonymous && isKey)
|
|
132
98
|
isKey = false;
|
|
133
99
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
100
|
+
// check if that type is already defined
|
|
101
|
+
let newType = csn.definitions[fullQualifiedNewTypeName];
|
|
102
|
+
if (newType) {
|
|
103
|
+
// error, if it was not exposed by us
|
|
104
|
+
if (!exposedTypes[fullQualifiedNewTypeName]) {
|
|
105
|
+
error(null, path, `Cannot create artificial type "${fullQualifiedNewTypeName}" for "${memberName}" because the name is already used`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
138
108
|
}
|
|
109
|
+
else {
|
|
110
|
+
/* Expose new structured type
|
|
111
|
+
* Treat items.elements as ordinary elements for now.
|
|
112
|
+
*/
|
|
113
|
+
newType = createNewStructType(elements);
|
|
114
|
+
if (node.$location)
|
|
115
|
+
setProp(newType, '$location', node.$location);
|
|
139
116
|
|
|
140
|
-
|
|
141
|
-
|
|
117
|
+
csn.definitions[fullQualifiedNewTypeName] = newType;
|
|
118
|
+
exposedTypes[fullQualifiedNewTypeName] = 1;
|
|
142
119
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
120
|
+
// Recurse into elements of 'type' (if any) and expose them as well (is needed)
|
|
121
|
+
newType.elements && Object.entries(newType.elements).forEach(([elemName, newElem]) => {
|
|
122
|
+
if (node.elements && node.elements[elemName].$location)
|
|
123
|
+
setProp(newElem, '$location', node.elements[elemName].$location);
|
|
124
|
+
defName = typeDef.kind === 'type' ? typeName : defName;
|
|
125
|
+
exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
|
|
126
|
+
getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName);
|
|
127
|
+
});
|
|
128
|
+
copyAnnotations(typeDef, newType);
|
|
129
|
+
// if the origin type had items, add items to exposed type
|
|
130
|
+
if(typeDef.kind === 'type') {
|
|
131
|
+
if(typeDef.items) {
|
|
132
|
+
newType.items = { elements: newType.elements };
|
|
133
|
+
delete newType.elements;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// adjust current node to new type
|
|
138
|
+
if(node.items) {
|
|
139
|
+
delete node.items.elements;
|
|
140
|
+
delete node.type;
|
|
141
|
+
node.items.type = fullQualifiedNewTypeName;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
delete node.elements;
|
|
145
|
+
node.type = fullQualifiedNewTypeName;
|
|
146
|
+
}
|
|
158
147
|
}
|
|
159
148
|
|
|
160
|
-
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
149
|
+
/**
|
|
150
|
+
* Check if the node's type can be exposed:
|
|
151
|
+
* 1) If it's an anonymous structured type (items.elements || elements)
|
|
152
|
+
* 2) If it's a named type resolve to the final type definition and
|
|
153
|
+
* check if this is a structured type
|
|
154
|
+
* Returns an object that indicates
|
|
155
|
+
* - wether or not the type needs exposure
|
|
156
|
+
* - the elements dictionary that needs to be cloned
|
|
157
|
+
* - the typeDef, either the resolved type def or the node itself
|
|
158
|
+
* - if structured type was anonymously defined
|
|
159
|
+
* @param {object} node
|
|
160
|
+
* @returns {object} { isExposable, typeDef, typeName, elements, isAnonymous }
|
|
166
161
|
*/
|
|
167
|
-
function
|
|
168
|
-
let
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
162
|
+
function isTypeExposable(node) {
|
|
163
|
+
let typeName = undefined;
|
|
164
|
+
let typeDef = node;
|
|
165
|
+
let elements = (node.items && node.items.elements || node.elements)
|
|
166
|
+
// anonymous structured type
|
|
167
|
+
if(elements)
|
|
168
|
+
return { isExposable: true, typeDef, typeName, elements, isAnonymous: true };
|
|
169
|
+
// named type, resolve the type to inspect it
|
|
170
|
+
let type = node.items && node.items.type || node.type;
|
|
171
|
+
if(type) {
|
|
172
|
+
typeName = (type.ref && csnUtils.getFinalType(type)) || type;
|
|
173
|
+
typeDef = csnUtils.getFinalTypeDef(typeName);
|
|
174
|
+
const rc = { isExposable: true, typeDef, typeName, isAnonymous: false };
|
|
175
|
+
if(!isBuiltinType(typeName) && !isArtifactInService(typeName, serviceName)) {
|
|
176
|
+
while(!isBuiltinType(typeName)) {
|
|
177
|
+
typeDef = csnUtils.getFinalTypeDef(typeName);
|
|
178
|
+
if(typeDef) {
|
|
179
|
+
if((rc.elements = (typeDef.items && typeDef.items.elements || typeDef.elements)) !== undefined)
|
|
180
|
+
return rc;
|
|
181
|
+
type = typeDef.items && typeDef.items.type || typeDef.type;
|
|
182
|
+
typeName = (type.ref && csnUtils.getFinalType(type)) || type;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
throw Error(`Please debug me: ${typeName} not found`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return { isExposable: false };
|
|
172
191
|
}
|
|
173
192
|
|
|
193
|
+
|
|
174
194
|
/**
|
|
175
195
|
* Calculate the new type name that will be exposed in multi schema,
|
|
176
196
|
* in case that the element has a named type.
|
|
177
197
|
*
|
|
178
198
|
* @param {string} typeName type of the element
|
|
179
|
-
* @param {string}
|
|
199
|
+
* @param {string} serviceName current service name
|
|
180
200
|
*/
|
|
181
|
-
function getTypeNameInMultiSchema(typeName,
|
|
201
|
+
function getTypeNameInMultiSchema(typeName, serviceName) {
|
|
182
202
|
const typeService = whatsMyServiceName(typeName);
|
|
183
203
|
if (typeService) {
|
|
184
204
|
// new type name without any prefixes
|
|
185
205
|
const typePlainName = defNameWithoutServiceOrContextName(typeName, typeService);
|
|
186
|
-
const newSchemaName = `${
|
|
206
|
+
const newSchemaName = `${serviceName}.${typeService}`;
|
|
187
207
|
createSchema(newSchemaName);
|
|
188
208
|
// return the new type name
|
|
189
209
|
return `${newSchemaName}.${typePlainName.replace(/\./g, '_')}`;
|
|
190
210
|
} else {
|
|
191
211
|
const typeContext = csnUtils.getContextOfArtifact(typeName);
|
|
192
212
|
const typeNamespace = csnUtils.getNamespaceOfArtifact(typeName);
|
|
193
|
-
const newSchemaName = `${
|
|
213
|
+
const newSchemaName = `${serviceName}.${typeContext || typeNamespace || fallBackSchemaName}`;
|
|
194
214
|
// new type name without any prefixes
|
|
195
215
|
const typePlainName = typeContext ? defNameWithoutServiceOrContextName(typeName, typeContext)
|
|
196
216
|
: typeName.replace(`${typeNamespace}.`, '');
|
|
@@ -209,7 +229,7 @@ function typesExposure(csn, whatsMyServiceName, autoexposeSchemaName, options, c
|
|
|
209
229
|
*/
|
|
210
230
|
function getAnonymousTypeNameInMultiSchema(typeName, parentName) {
|
|
211
231
|
let currPrefix = parentName.substring(0, parentName.lastIndexOf('.'));
|
|
212
|
-
const newSchemaName = currPrefix ||
|
|
232
|
+
const newSchemaName = currPrefix || fallBackSchemaName;
|
|
213
233
|
// new type name without any prefixes
|
|
214
234
|
const typePlainName = defNameWithoutServiceOrContextName(typeName, newSchemaName);
|
|
215
235
|
|
|
@@ -222,121 +242,49 @@ function typesExposure(csn, whatsMyServiceName, autoexposeSchemaName, options, c
|
|
|
222
242
|
* @param {string} name
|
|
223
243
|
*/
|
|
224
244
|
function createSchema(name) {
|
|
225
|
-
schemas[
|
|
245
|
+
schemas[name] = { kind: 'schema', name };
|
|
226
246
|
}
|
|
227
247
|
|
|
228
248
|
/**
|
|
229
|
-
*
|
|
230
|
-
* if it already exists).
|
|
231
|
-
* The new type has name 'typeName', elements which are 'elements'.
|
|
232
|
-
* 'parentName' is used for error reporting.x
|
|
233
|
-
* @param {String} typeName
|
|
249
|
+
* create a new structured type for 'elements'
|
|
234
250
|
* @param {Object} elements
|
|
235
|
-
* @param {String} parentName
|
|
236
251
|
*/
|
|
237
|
-
function
|
|
238
|
-
// If type already exists, reuse it (complain if not created here)
|
|
239
|
-
let type = csn.definitions[typeName];
|
|
240
|
-
if (type) {
|
|
241
|
-
if (!exposedStructTypes.includes(typeName)) {
|
|
242
|
-
error(null, path, `Cannot create artificial type "${typeName}" for "${parentName}" because the name is already used`);
|
|
243
|
-
return null;
|
|
244
|
-
}
|
|
245
|
-
return type;
|
|
246
|
-
}
|
|
247
|
-
|
|
252
|
+
function createNewStructType(elements) {
|
|
248
253
|
// Create a type with empty elements
|
|
249
|
-
type = {
|
|
254
|
+
const type = {
|
|
250
255
|
kind: 'type',
|
|
251
256
|
elements: Object.create(null),
|
|
252
257
|
};
|
|
258
|
+
setProp(type, '$exposedBy', 'typeExposure');
|
|
253
259
|
|
|
254
|
-
// Duplicate
|
|
260
|
+
// Duplicate elements
|
|
255
261
|
Object.entries(elements).forEach(([elemName, element]) => {
|
|
256
|
-
|
|
257
|
-
const path = ['definitions', typeName, 'elements', elemName];
|
|
258
|
-
error(null, path, `"${elemName}": Element name conflicts with existing element`);
|
|
259
|
-
}
|
|
260
|
-
let cloned = cloneCsn(element, options);
|
|
262
|
+
let cloned = cloneCsnNonDict(element, options);
|
|
261
263
|
// if this was an anonymous sub element of a key, mark it as not nullable
|
|
262
264
|
if(isAnonymous && isKey && !cloned.key && cloned.notNull === undefined)
|
|
263
265
|
cloned.notNull = true;
|
|
264
266
|
type.elements[elemName] = cloned;
|
|
265
267
|
});
|
|
266
|
-
|
|
267
|
-
// add to the CSN
|
|
268
|
-
csn.definitions[typeName] = type;
|
|
269
|
-
// store typeName in set of exposed struct types
|
|
270
|
-
exposedStructTypes.push(typeName);
|
|
271
268
|
return type;
|
|
272
269
|
}
|
|
273
270
|
}
|
|
274
271
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
//
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (!isArtifactInService(node.type, service)) {
|
|
292
|
-
let typeId = `${service}.${node.type.replace(/\./g, '_')}`;
|
|
293
|
-
let newType = exposeArrayedType(node.items || finalType.items, typeId);
|
|
294
|
-
// When we have in the model something like:
|
|
295
|
-
// type Foo: array of Bar; type Bar: { qux: Integer };
|
|
296
|
-
// In the type Foo we expand the first level of elements of the items like we have in CDL this:
|
|
297
|
-
// type Foo: array of { qux: Integer };
|
|
298
|
-
expandFirstLevelOfArrayed(newType);
|
|
299
|
-
node.type = typeId;
|
|
300
|
-
}
|
|
301
|
-
// case 1. - as we keep the type property, the items property is removed
|
|
302
|
-
if (node.items) delete node.items;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
function exposeArrayedType(items, typeId) {
|
|
307
|
-
let newType = csn.definitions[typeId];
|
|
308
|
-
if (newType) {
|
|
309
|
-
if (!exposedStructTypes.includes(typeId)) {
|
|
310
|
-
error(null, newType.$path, `Cannot create artificial type "${typeId}" because the name is already used`);
|
|
311
|
-
}
|
|
312
|
-
return newType;
|
|
313
|
-
}
|
|
314
|
-
// create empty type
|
|
315
|
-
newType = {
|
|
316
|
-
kind: 'type',
|
|
317
|
-
items: Object.create(null),
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// copy over the items
|
|
321
|
-
newType.items = cloneCsn(items, options);
|
|
322
|
-
csn.definitions[typeId] = newType;
|
|
323
|
-
exposedStructTypes.push(typeId);
|
|
324
|
-
return newType;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// In case we have in the model something like:
|
|
329
|
-
// type Foo: array of Bar; type Bar: { qux: Integer };
|
|
330
|
-
// In the type Foo we expand the first level of elements of the items like we have in CDL this:
|
|
331
|
-
// type Foo: array of { qux: Integer };
|
|
332
|
-
function expandFirstLevelOfArrayed(def) {
|
|
333
|
-
if (def.items.type && !isBuiltinType(def.items.type)) {
|
|
334
|
-
let finalType = csnUtils.getFinalTypeDef(def.items.type);
|
|
335
|
-
if (csnUtils.isStructured(finalType)) {
|
|
336
|
-
if (!def.items.elements) def.items.elements = cloneCsnDictionary(finalType.elements, options);
|
|
337
|
-
delete def.items.type;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
272
|
+
/*
|
|
273
|
+
* Calculate the name of the exposed type based on the information, the element can provide
|
|
274
|
+
* If the element is typed, use the type name
|
|
275
|
+
* else assume it's a de-anonymized type, concatenate the element name to the defName
|
|
276
|
+
*/
|
|
277
|
+
function getNewTypeName(element, elementName, typeNamePrefix, serviceName) {
|
|
278
|
+
// for the new type name node.type has precedence over node.items.type
|
|
279
|
+
const typeName = (!element.elements && element.type || element.items && !element.items.elements && element.items.type);
|
|
280
|
+
return typeName
|
|
281
|
+
? `${isMultiSchema
|
|
282
|
+
? typeName.split('.').pop() // use leaf element
|
|
283
|
+
: typeName.replace(/\./g, '_')}` // concatenate path
|
|
284
|
+
: ( elementName // returns has no elementName, return the precalculated prefix
|
|
285
|
+
? `${defNameWithoutServiceOrContextName(typeNamePrefix, serviceName).replace(/\./g, '_')}_${elementName}`
|
|
286
|
+
: typeNamePrefix
|
|
287
|
+
);
|
|
340
288
|
}
|
|
341
289
|
}
|
|
342
290
|
|
|
@@ -9,8 +9,9 @@ const { setProp } = require('../base/model');
|
|
|
9
9
|
const { csnRefs } = require('../model/csnRefs');
|
|
10
10
|
|
|
11
11
|
const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
|
|
12
|
-
const {
|
|
12
|
+
const { cloneCsnNonDict, cloneCsnDictionary, getUtils, isBuiltinType } = require('../model/csnUtils');
|
|
13
13
|
const { ModelError } = require("../base/error");
|
|
14
|
+
const { forEach } = require('../utils/objectUtils');
|
|
14
15
|
|
|
15
16
|
// Return the public functions of this module, with 'model' captured in a closure (for definitions, options etc).
|
|
16
17
|
// Use 'pathDelimiter' for flattened names (e.g. of struct elements or foreign key elements).
|
|
@@ -258,7 +259,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
258
259
|
result[eName] = e;
|
|
259
260
|
}
|
|
260
261
|
}
|
|
261
|
-
|
|
262
|
+
forEach(struct, (childName, childElem) => {
|
|
262
263
|
if (isStructured(childElem)) {
|
|
263
264
|
// Descend recursively into structured children
|
|
264
265
|
let grandChildElems = flattenStructuredElement(childElem, childName, elementPath, pathInCsn.concat('elements',childName));
|
|
@@ -276,7 +277,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
276
277
|
} else {
|
|
277
278
|
// Primitive child - clone it and restore its cross references
|
|
278
279
|
let flatElemName = elemName + pathDelimiter + childName;
|
|
279
|
-
let flatElem =
|
|
280
|
+
let flatElem = cloneCsnNonDict(childElem, options);
|
|
280
281
|
// Don't take over notNull from leaf elements
|
|
281
282
|
delete flatElem.notNull;
|
|
282
283
|
setProp(flatElem, '_flatElementNameWithDots', elementPath.concat(childName).join('.'));
|
|
@@ -285,7 +286,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
285
286
|
});
|
|
286
287
|
|
|
287
288
|
// Fix all collected flat elements (names, annotations, properties, origin ..)
|
|
288
|
-
|
|
289
|
+
forEach(result, (name, flatElem) => {
|
|
289
290
|
// Copy annotations from struct (not overwriting, because deep annotations should have precedence)
|
|
290
291
|
copyAnnotations(elem, flatElem, false);
|
|
291
292
|
// Copy selected type properties
|
|
@@ -413,11 +414,11 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
413
414
|
throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
|
|
414
415
|
if(finalBaseType.elements) {
|
|
415
416
|
// This changes the order - to be discussed!
|
|
416
|
-
node.elements =
|
|
417
|
+
node.elements = cloneCsnNonDict(finalBaseType, options).elements; // copy elements
|
|
417
418
|
delete node.type; // delete the type reference as edm processing does not expect it
|
|
418
419
|
} else if(finalBaseType.items) {
|
|
419
420
|
// This changes the order - to be discussed!
|
|
420
|
-
node.items =
|
|
421
|
+
node.items = cloneCsnNonDict(finalBaseType.items, options); // copy items
|
|
421
422
|
delete node.type;
|
|
422
423
|
} else {
|
|
423
424
|
node.type=finalBaseType;
|
|
@@ -431,12 +432,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
431
432
|
let typeDef = typeof node.type === 'string' ? getCsnDef(node.type) : node.type;
|
|
432
433
|
// Nothing to do if type is an array or a struct type
|
|
433
434
|
if (typeDef.items || typeDef.elements) {
|
|
434
|
-
// Expand structured types on entity elements for flat OData
|
|
435
|
-
if(!(options.transformation === 'hdbcds' || options.toSql))
|
|
436
|
-
return;
|
|
437
|
-
|
|
438
435
|
// cloneCsn only works correctly if we start "from the top"
|
|
439
|
-
const cloneTypeDef =
|
|
436
|
+
const cloneTypeDef = cloneCsnNonDict(typeDef, options);
|
|
440
437
|
// With hdbcds-hdbcds, don't resolve structured types - but propagate ".items", to turn into LargeString later on.
|
|
441
438
|
if(typeDef.items) {
|
|
442
439
|
delete node.type;
|
|
@@ -899,7 +896,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
899
896
|
let elements = artifact.elements;
|
|
900
897
|
if (elements) {
|
|
901
898
|
path.push('elements', null);
|
|
902
|
-
|
|
899
|
+
forEach(elements, (name, obj) => {
|
|
903
900
|
path[path.length - 1] = name;
|
|
904
901
|
recurseElements(obj, path, callback);
|
|
905
902
|
});
|
|
@@ -44,7 +44,7 @@ function translateAssocsToJoinsCSN(csn, options){
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// If A2J reports error - end! Continuing with a broken CSN makes no sense
|
|
47
|
-
makeMessageFunction(model, options).
|
|
47
|
+
makeMessageFunction(model, options).throwWithAnyError();
|
|
48
48
|
// FIXME: Move this somewhere more appropriate
|
|
49
49
|
const compact = compactModel(model, compileOptions);
|
|
50
50
|
return compact;
|
|
@@ -1818,6 +1818,10 @@ function walkPath(node, env)
|
|
|
1818
1818
|
// or that are parameters ($parameters or escaped paths (':')
|
|
1819
1819
|
//path.length && path[ path.length-1 ]._artifact
|
|
1820
1820
|
const art = path && path.length && path[path.length-1]._artifact;
|
|
1821
|
+
|
|
1822
|
+
// regardless of the position in the query, ignore paths that have virtual path steps
|
|
1823
|
+
if(art && path.some(ps => ps._artifact && ps._artifact.virtual && ps._artifact.virtual.val))
|
|
1824
|
+
return path;
|
|
1821
1825
|
if(art && !internalArtifactKinds.includes(art.kind))
|
|
1822
1826
|
{
|
|
1823
1827
|
if(env.callback)
|
|
@@ -137,9 +137,11 @@ function setCoreComputedOnViews(csn) {
|
|
|
137
137
|
* @returns {boolean}
|
|
138
138
|
*/
|
|
139
139
|
function needsCoreComputed(column) {
|
|
140
|
-
return column &&
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
return column &&
|
|
141
|
+
(
|
|
142
|
+
column.xpr || column.func || column.val !== undefined || column.param || column.SELECT || column.SET ||
|
|
143
|
+
column.ref && [ '$at', '$now', '$user', '$session' ].includes(column.ref[0])
|
|
144
|
+
);
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
/**
|
|
@@ -288,7 +288,11 @@ module.exports = (csn, options) => {
|
|
|
288
288
|
if (returns.target)
|
|
289
289
|
calculateForeignKeys(returns);
|
|
290
290
|
},
|
|
291
|
-
items: (parent, prop, items) =>
|
|
291
|
+
items: (parent, prop, items) => {
|
|
292
|
+
// items in items must be propagated
|
|
293
|
+
// `specialItemsRule()` does not cover this case
|
|
294
|
+
propagateMemberPropsFromOrigin(items, { '@': true, doc: true }, { items: onlyWithTypeRef });
|
|
295
|
+
},
|
|
292
296
|
elements: (parent, prop, elements) => {
|
|
293
297
|
forEachValue(elements, (e) => {
|
|
294
298
|
// within query elements we have to propagate the `enum` prop
|
|
@@ -358,13 +362,31 @@ module.exports = (csn, options) => {
|
|
|
358
362
|
return !origin;
|
|
359
363
|
}
|
|
360
364
|
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Some properties must only be copied over if the target
|
|
368
|
+
* is a type reference pointing to an element of a type or
|
|
369
|
+
* an aspect.
|
|
370
|
+
*
|
|
371
|
+
* @param {string} prop
|
|
372
|
+
* @param {CSN.Element} target
|
|
373
|
+
* @param {CSN.Element} source
|
|
374
|
+
*/
|
|
375
|
+
function onlyWithTypeRef(prop, target, source) {
|
|
376
|
+
const typeIsTypeRef = Boolean(typeof target.type === 'object' && target.type.ref);
|
|
377
|
+
if (typeIsTypeRef) {
|
|
378
|
+
const referencedArtifact = csn.definitions[target.type.ref[0]];
|
|
379
|
+
if (referencedArtifact.kind in { type: true, aspect: true })
|
|
380
|
+
target[prop] = source[prop];
|
|
381
|
+
}
|
|
382
|
+
}
|
|
361
383
|
}
|
|
362
384
|
|
|
363
385
|
/**
|
|
364
386
|
* Identify the sources of the passed object and propagate the relevant
|
|
365
387
|
* properties/annotations along it's $origin chain.
|
|
366
388
|
*
|
|
367
|
-
* @param {
|
|
389
|
+
* @param {object} art Target object for propagation
|
|
368
390
|
*/
|
|
369
391
|
function propagateOnArtifactLevel(art) {
|
|
370
392
|
// check if art was already processed by the status flag
|
|
@@ -415,7 +437,7 @@ module.exports = (csn, options) => {
|
|
|
415
437
|
copyProperties(definition.$origin, definition, getDefinitionPropagationRuleFor);
|
|
416
438
|
|
|
417
439
|
// We need to propagate .elements to type artifacts - but our direct origin might not have .elements,
|
|
418
|
-
// because they are not propagated to members. We check if our root had elements (so we know that we should have some
|
|
440
|
+
// because they are not propagated to members. We check if our root had elements (so we know that we should have some as well)
|
|
419
441
|
// and then walk the origin-chain until we find the first .elements
|
|
420
442
|
if (definition.kind === 'type' && root.elements && !definition.elements) {
|
|
421
443
|
const firstOriginWithElements = getFirstOriginWithElements(source);
|
|
@@ -580,7 +602,7 @@ module.exports = (csn, options) => {
|
|
|
580
602
|
* @param {Function} getCustomRule getter for the `memberProps` or `defProps`
|
|
581
603
|
* which shall be used for retrieving custom rules
|
|
582
604
|
* @param {object} [except=null] array of properties which should not be propagated
|
|
583
|
-
* @param {object} [force=null] Force propagation of the contained keys via rule
|
|
605
|
+
* @param {object} [force=null] Force propagation of the contained keys via a custom rule.
|
|
584
606
|
*/
|
|
585
607
|
function copyProperties(from, to, getCustomRule, except = null, force = null) {
|
|
586
608
|
const keys = Object.keys(from);
|
|
@@ -589,7 +611,7 @@ function copyProperties(from, to, getCustomRule, except = null, force = null) {
|
|
|
589
611
|
if (except && !(force && force[key]) && (key.charAt(0) in except || key in except))
|
|
590
612
|
continue;
|
|
591
613
|
if (!(key in to)) {
|
|
592
|
-
const func = force && force[key] ?
|
|
614
|
+
const func = force && force[key] ? force[key] : getCustomRule(key);
|
|
593
615
|
if (func)
|
|
594
616
|
func(key, to, from);
|
|
595
617
|
}
|