@sap/cds-compiler 2.12.0 → 2.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +221 -15
- package/bin/cdsc.js +125 -50
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +47 -84
- package/lib/api/options.js +5 -6
- package/lib/api/validate.js +6 -11
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +114 -18
- package/lib/base/messages.js +101 -90
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +177 -123
- package/lib/checks/annotationsOData.js +12 -33
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +6 -11
- package/lib/compiler/assert-consistency.js +6 -3
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +19 -6
- package/lib/compiler/checks.js +23 -60
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1151 -0
- package/lib/compiler/extend.js +1000 -0
- package/lib/compiler/finalize-parse-cdl.js +237 -0
- package/lib/compiler/index.js +107 -39
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1227 -0
- package/lib/compiler/propagator.js +114 -46
- package/lib/compiler/resolve.js +1521 -0
- package/lib/compiler/shared.js +126 -65
- package/lib/compiler/tweak-assocs.js +535 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -24
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +219 -100
- package/lib/edm/edm.js +302 -230
- package/lib/edm/edmPreprocessor.js +554 -419
- package/lib/edm/edmUtils.js +138 -44
- package/lib/gen/Dictionary.json +100 -19
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5765 -4480
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +15 -3
- package/lib/json/to-csn.js +126 -68
- package/lib/language/docCommentParser.js +4 -4
- package/lib/language/genericAntlrParser.js +123 -5
- package/lib/language/language.g4 +355 -156
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +486 -59
- package/lib/main.js +41 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +252 -156
- package/lib/model/csnUtils.js +384 -297
- package/lib/model/enrichCsn.js +71 -29
- package/lib/model/revealInternalProperties.js +29 -8
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +23 -18
- package/lib/optionProcessor.js +63 -26
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +897 -947
- package/lib/render/toHdbcds.js +205 -257
- package/lib/render/toSql.js +264 -225
- package/lib/render/utils/common.js +136 -25
- package/lib/render/utils/sql.js +4 -3
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/db/.eslintrc.json +3 -1
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +104 -306
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +58 -53
- package/lib/transform/db/expansion.js +60 -33
- package/lib/transform/db/flattening.js +582 -104
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +66 -13
- package/lib/transform/db/views.js +11 -7
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +109 -208
- package/lib/transform/forOdataNew.js +59 -212
- package/lib/transform/localized.js +46 -26
- package/lib/transform/odata/toFinalBaseType.js +85 -11
- package/lib/transform/odata/typesExposure.js +147 -199
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +44 -33
- package/lib/transform/translateAssocsToJoins.js +3 -20
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +172 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -290
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
- package/lib/transform/universalCsnEnricher.js +0 -237
|
@@ -2,7 +2,7 @@
|
|
|
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
|
|
|
@@ -86,21 +86,95 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
|
|
|
86
86
|
// EDMX at the moment and the reference in the OData CSN is fulfilled.
|
|
87
87
|
if (node.kind === 'event') return;
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
89
|
+
// elements have precedence over type
|
|
90
|
+
if (node.type && (!isBuiltinType(node.type) &&isExpandable(node, defName) || node.kind === 'type')) {
|
|
91
|
+
// 1. Get the final type of the node (resolve derived type chain)
|
|
92
|
+
const finalType = csnUtils.getFinalBaseType(node.type);
|
|
93
|
+
if (finalType) {
|
|
94
|
+
// The type replacement depends on whether 'node' is a definition or a member[element].
|
|
95
|
+
if (node.kind) {
|
|
96
|
+
// It is a definition and we expand to builtin type and to elements
|
|
97
|
+
// type T: S; --> Integer;
|
|
98
|
+
// type S: X; --> Integer;
|
|
99
|
+
// type X: Integer;
|
|
100
|
+
//
|
|
101
|
+
// type A: B; -> {...}
|
|
102
|
+
// type B: C; -> { ... }
|
|
103
|
+
// type C { .... };
|
|
104
|
+
if (isBuiltinType(finalType)) {
|
|
105
|
+
// use transformUrilsNew::toFinalBaseType for the moment,
|
|
106
|
+
// as it is collects along the chain of types
|
|
107
|
+
// attributes that need to be propagated
|
|
108
|
+
// enum, length, scale, etc.
|
|
109
|
+
transformers.toFinalBaseType(node);
|
|
110
|
+
// node.type = finalType;
|
|
111
|
+
}
|
|
112
|
+
else if (csnUtils.isStructured(finalType)) {
|
|
113
|
+
cloneElements(finalType);
|
|
114
|
+
}
|
|
115
|
+
else if (node.type && node.items)
|
|
99
116
|
delete node.type;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// this is a member and we expand to final base only if builtin
|
|
120
|
+
// type T: S; --> Integer;
|
|
121
|
+
// type S: X; --> Integer;
|
|
122
|
+
// type X: Integer;
|
|
123
|
+
//
|
|
124
|
+
// type {
|
|
125
|
+
// struct_elt: many A; ---> stays the same
|
|
126
|
+
// scalar_elt: T; ---> Integer;
|
|
127
|
+
// type_ref_elt: type of struct_elt;
|
|
128
|
+
// };
|
|
129
|
+
// type A: B; -> {...}
|
|
130
|
+
// type B: C; -> { ... }
|
|
131
|
+
// type C { .... };
|
|
132
|
+
if (isBuiltinType(finalType)) {
|
|
133
|
+
// use transformUrilsNew::toFinalBaseType for the moment,
|
|
134
|
+
// as it is collects along the chain of types
|
|
135
|
+
// attributes that need to be propagated
|
|
136
|
+
// enum, length, scale, etc.
|
|
137
|
+
transformers.toFinalBaseType(node);
|
|
138
|
+
// node.type = finalType;
|
|
139
|
+
}
|
|
140
|
+
else if (node.type && node.type.ref) {
|
|
141
|
+
cloneElements(finalType);
|
|
100
142
|
}
|
|
101
143
|
}
|
|
102
144
|
}
|
|
103
145
|
}
|
|
146
|
+
if (node.type && !isBuiltinType(node.type)) {
|
|
147
|
+
// handle array of defined via a named type
|
|
148
|
+
// example in actions: 'action act() return Primitive; type Primitive: array of String;'
|
|
149
|
+
const currService = csnUtils.getServiceName(defName);
|
|
150
|
+
const finalType = csnUtils.getFinalTypeDef(node.type);
|
|
151
|
+
if (finalType.items && isBuiltinType(finalType.items.type)) {
|
|
152
|
+
if (!isArtifactInService(node.type, currService) || !isV4) {
|
|
153
|
+
node.items = finalType.items;
|
|
154
|
+
delete node.type;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function cloneElements(finalType) {
|
|
160
|
+
// cloneCsn only works correctly if we start "from the top"
|
|
161
|
+
let clone;
|
|
162
|
+
// do the clone only if really needed
|
|
163
|
+
if((finalType.items && !node.items) ||
|
|
164
|
+
(finalType.elements && !node.elements))
|
|
165
|
+
clone = cloneCsnNonDict({ definitions: { 'TypeDef': finalType } }, options);
|
|
166
|
+
if (finalType.items) {
|
|
167
|
+
delete node.type;
|
|
168
|
+
if(!node.items)
|
|
169
|
+
Object.assign(node, { items: clone.definitions.TypeDef.items });
|
|
170
|
+
}
|
|
171
|
+
if (finalType.elements) {
|
|
172
|
+
if(!finalType.items)
|
|
173
|
+
delete node.type;
|
|
174
|
+
if(!node.elements)
|
|
175
|
+
Object.assign(node, { elements: clone.definitions.TypeDef.elements });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
104
178
|
}
|
|
105
179
|
|
|
106
180
|
function isExpandable(node, defName) {
|
|
@@ -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, 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
|
|
33
|
-
|
|
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)) {
|
|
29
|
+
if (def.kind === 'type' || def.kind === 'entity') {
|
|
34
30
|
forEachMember(def, (element, elementName, propertyName, path) => {
|
|
35
|
-
if (
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
: defNameWithoutServiceOrContextName(defName, serviceName).replace(/\./g, '_')}_${elementName}`;
|
|
39
|
-
exposeTypeOf(element, element.key || propertyName === 'params', elementName, defName, serviceName, artificialtName, path);
|
|
31
|
+
if (propertyName === 'elements' || propertyName === 'params') {
|
|
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, options, csnUtils, message) {
|
|
|
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, options, csnUtils, message) {
|
|
|
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, options, csnUtils, message) {
|
|
|
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, options, csnUtils, message) {
|
|
|
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
|
|
|
@@ -18,7 +18,7 @@ function isAssociationOrComposition(artifact) {
|
|
|
18
18
|
return isAssociation(artifact) || isComposition(artifact);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
function
|
|
21
|
+
function isManagedAssociation(artifact) {
|
|
22
22
|
return artifact.target !== undefined && artifact.on === undefined;
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -94,5 +94,5 @@ module.exports = {
|
|
|
94
94
|
isArtifactInSomeService,
|
|
95
95
|
isAssociationOrComposition,
|
|
96
96
|
isLocalizedArtifactInService,
|
|
97
|
-
|
|
97
|
+
isManagedAssociation,
|
|
98
98
|
}
|