@sap/cds-compiler 2.13.8 → 3.0.0
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 +155 -1594
- package/bin/cdsc.js +144 -66
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +237 -122
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +12 -16
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +152 -37
- package/lib/base/messages.js +145 -83
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +4 -5
- package/lib/compiler/assert-consistency.js +16 -10
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +98 -9
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +61 -13
- package/lib/compiler/extend.js +79 -14
- package/lib/compiler/finalize-parse-cdl.js +46 -29
- package/lib/compiler/index.js +100 -37
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +19 -18
- package/lib/compiler/propagator.js +7 -4
- package/lib/compiler/resolve.js +297 -234
- package/lib/compiler/shared.js +107 -102
- package/lib/compiler/tweak-assocs.js +16 -11
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/annotations/genericTranslation.js +93 -21
- package/lib/edm/csn2edm.js +230 -115
- package/lib/edm/edm.js +305 -226
- package/lib/edm/edmPreprocessor.js +509 -438
- package/lib/edm/edmUtils.js +31 -45
- package/lib/gen/Dictionary.json +98 -22
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +10 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20786 -22199
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +59 -51
- package/lib/json/to-csn.js +10 -10
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +62 -39
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +348 -229
- package/lib/language/language.g4 +629 -653
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +46 -43
- package/lib/main.js +108 -79
- package/lib/model/csnRefs.js +34 -7
- package/lib/model/csnUtils.js +337 -332
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +30 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +73 -46
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +1042 -882
- package/lib/render/toHdbcds.js +195 -245
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +225 -241
- package/lib/render/utils/common.js +145 -15
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +4 -3
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +4 -2
- package/lib/transform/db/expansion.js +22 -16
- package/lib/transform/db/flattening.js +109 -80
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +9 -6
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +62 -48
- package/lib/transform/forOdataNew.js +49 -50
- package/lib/transform/localized.js +31 -20
- package/lib/transform/odata/toFinalBaseType.js +16 -14
- package/lib/transform/odata/typesExposure.js +146 -198
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +67 -84
- package/lib/transform/translateAssocsToJoins.js +7 -3
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +16 -9
- package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
- package/lib/utils/file.js +3 -3
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
- 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
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
forEachDefinition, forEachMemberRecursively,
|
|
5
|
-
isBuiltinType, cloneCsnDictionary,
|
|
5
|
+
isBuiltinType, cloneCsnDictionary, cloneCsnNonDict,
|
|
6
6
|
} = require('../../model/csnUtils');
|
|
7
7
|
const { isArtifactInSomeService, isArtifactInService } = require('./utils');
|
|
8
8
|
|
|
9
9
|
function expandToFinalBaseType(csn, transformers, csnUtils, services, options, isExternalServiceMember) {
|
|
10
|
-
const isV4 = options.
|
|
10
|
+
const isV4 = options.odataVersion === 'v4';
|
|
11
11
|
forEachDefinition(csn, (def, defName) => {
|
|
12
12
|
// Unravel derived type chains to final one for elements, actions, action parameters (propagating annotations)
|
|
13
13
|
forEachMemberRecursively(def, (member) => {
|
|
@@ -87,9 +87,9 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
87
87
|
if (node.kind === 'event') return;
|
|
88
88
|
|
|
89
89
|
// elements have precedence over type
|
|
90
|
-
if (node.type && (!isBuiltinType(node.type) &&isExpandable(node, defName) || node.kind === 'type')) {
|
|
90
|
+
if (node.type && (!isBuiltinType(node.type) && isExpandable(node, defName) || node.kind === 'type')) {
|
|
91
91
|
// 1. Get the final type of the node (resolve derived type chain)
|
|
92
|
-
const finalType = csnUtils.
|
|
92
|
+
const finalType = csnUtils.getFinalBaseTypeWithProps(node.type);
|
|
93
93
|
if (finalType) {
|
|
94
94
|
// The type replacement depends on whether 'node' is a definition or a member[element].
|
|
95
95
|
if (node.kind) {
|
|
@@ -97,11 +97,11 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
97
97
|
// type T: S; --> Integer;
|
|
98
98
|
// type S: X; --> Integer;
|
|
99
99
|
// type X: Integer;
|
|
100
|
-
//
|
|
100
|
+
//
|
|
101
101
|
// type A: B; -> {...}
|
|
102
102
|
// type B: C; -> { ... }
|
|
103
103
|
// type C { .... };
|
|
104
|
-
if (isBuiltinType(finalType)) {
|
|
104
|
+
if (isBuiltinType(finalType.type)) {
|
|
105
105
|
// use transformUrilsNew::toFinalBaseType for the moment,
|
|
106
106
|
// as it is collects along the chain of types
|
|
107
107
|
// attributes that need to be propagated
|
|
@@ -120,7 +120,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
120
120
|
// type T: S; --> Integer;
|
|
121
121
|
// type S: X; --> Integer;
|
|
122
122
|
// type X: Integer;
|
|
123
|
-
//
|
|
123
|
+
//
|
|
124
124
|
// type {
|
|
125
125
|
// struct_elt: many A; ---> stays the same
|
|
126
126
|
// scalar_elt: T; ---> Integer;
|
|
@@ -129,8 +129,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
129
129
|
// type A: B; -> {...}
|
|
130
130
|
// type B: C; -> { ... }
|
|
131
131
|
// type C { .... };
|
|
132
|
-
if (isBuiltinType(finalType)) {
|
|
133
|
-
// use
|
|
132
|
+
if (isBuiltinType(finalType.type)) {
|
|
133
|
+
// use transformUtilsNew::toFinalBaseType for the moment,
|
|
134
134
|
// as it is collects along the chain of types
|
|
135
135
|
// attributes that need to be propagated
|
|
136
136
|
// enum, length, scale, etc.
|
|
@@ -148,7 +148,9 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
148
148
|
// example in actions: 'action act() return Primitive; type Primitive: array of String;'
|
|
149
149
|
const currService = csnUtils.getServiceName(defName);
|
|
150
150
|
const finalType = csnUtils.getFinalTypeDef(node.type);
|
|
151
|
-
if (finalType.items &&
|
|
151
|
+
if (finalType.items &&
|
|
152
|
+
(isBuiltinType(finalType.items.type) || isBuiltinType(csnUtils.getFinalBaseTypeWithProps(finalType.items.type)?.type)))
|
|
153
|
+
{
|
|
152
154
|
if (!isArtifactInService(node.type, currService) || !isV4) {
|
|
153
155
|
node.items = finalType.items;
|
|
154
156
|
delete node.type;
|
|
@@ -162,7 +164,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
162
164
|
// do the clone only if really needed
|
|
163
165
|
if((finalType.items && !node.items) ||
|
|
164
166
|
(finalType.elements && !node.elements))
|
|
165
|
-
clone =
|
|
167
|
+
clone = cloneCsnNonDict({ definitions: { 'TypeDef': finalType } }, options);
|
|
166
168
|
if (finalType.items) {
|
|
167
169
|
delete node.type;
|
|
168
170
|
if(!node.items)
|
|
@@ -184,8 +186,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
184
186
|
function isUserDefinedBuiltinFromTheCurrService(node, defName) {
|
|
185
187
|
// in V4 we should use TypeDefinitions whenever possible, thus in case the final type of a field is
|
|
186
188
|
// a builtin from the service - do not expand to the final base type
|
|
187
|
-
let finalBaseType = csnUtils.
|
|
188
|
-
// if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.
|
|
189
|
+
let finalBaseType = csnUtils.getFinalBaseTypeWithProps(node.type).type;
|
|
190
|
+
// if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.getFinalBaseTypeWithProps(finalBaseType.items);
|
|
189
191
|
const currService = csnUtils.getServiceName(defName);
|
|
190
192
|
return node.type && !node.type.ref
|
|
191
193
|
&& isBuiltinType(finalBaseType) && !csnUtils.isAssocOrComposition(finalBaseType)
|
|
@@ -193,4 +195,4 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
193
195
|
}
|
|
194
196
|
}
|
|
195
197
|
|
|
196
|
-
module.exports = expandToFinalBaseType;
|
|
198
|
+
module.exports = expandToFinalBaseType;
|
|
@@ -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
|
-
const isMultiSchema = options.
|
|
17
|
+
const isMultiSchema = options.odataVersion === 'v4' && (options.odataProxies || options.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
|
|
|
@@ -1,38 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
forEachDefinition,
|
|
3
|
-
forEachMemberRecursively,
|
|
4
|
-
} = require('../../model/csnUtils');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// Return true if 'artifact' has an association type
|
|
8
|
-
function isAssociation(artifact) {
|
|
9
|
-
return (artifact.type === 'cds.Association' || artifact.type === 'Association');
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Return true if 'artifact' has a composition type
|
|
13
|
-
function isComposition(artifact) {
|
|
14
|
-
return (artifact.type === 'cds.Composition' || artifact.type === 'Composition')
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function isAssociationOrComposition(artifact) {
|
|
18
|
-
return isAssociation(artifact) || isComposition(artifact);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function isManagedAssociation(artifact) {
|
|
22
|
-
return artifact.target !== undefined && artifact.on === undefined;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function forEachManagedAssociation(csn, callback, isExternalServiceMember) {
|
|
26
|
-
|
|
27
|
-
forEachDefinition(csn, (def) => {
|
|
28
|
-
forEachMemberRecursively(def, (element) => {
|
|
29
|
-
if (isAssociationOrComposition(element) && !element.on) {
|
|
30
|
-
callback(element)
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
-
}, { skipArtifact: isExternalServiceMember });
|
|
34
|
-
|
|
35
|
-
}
|
|
1
|
+
'use strict';
|
|
36
2
|
|
|
37
3
|
/**
|
|
38
4
|
* Return the definition name, without the prefixed service name
|
|
@@ -87,12 +53,9 @@ function isLocalizedArtifactInService(artName, services) {
|
|
|
87
53
|
}
|
|
88
54
|
|
|
89
55
|
module.exports = {
|
|
90
|
-
forEachManagedAssociation,
|
|
91
56
|
defNameWithoutServiceOrContextName,
|
|
92
57
|
getServiceOfArtifact,
|
|
93
58
|
isArtifactInService,
|
|
94
59
|
isArtifactInSomeService,
|
|
95
|
-
isAssociationOrComposition,
|
|
96
60
|
isLocalizedArtifactInService,
|
|
97
|
-
isManagedAssociation,
|
|
98
61
|
}
|