@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.
Files changed (127) hide show
  1. package/CHANGELOG.md +155 -1594
  2. package/bin/cdsc.js +144 -66
  3. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  4. package/doc/CHANGELOG_BETA.md +3 -4
  5. package/doc/CHANGELOG_DEPRECATED.md +35 -1
  6. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  7. package/doc/Versioning.md +20 -1
  8. package/lib/api/.eslintrc.json +2 -2
  9. package/lib/api/main.js +237 -122
  10. package/lib/api/options.js +17 -88
  11. package/lib/api/validate.js +12 -16
  12. package/lib/base/keywords.js +216 -109
  13. package/lib/base/message-registry.js +152 -37
  14. package/lib/base/messages.js +145 -83
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +19 -0
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +11 -32
  19. package/lib/checks/arrayOfs.js +1 -34
  20. package/lib/checks/cdsPersistence.js +1 -0
  21. package/lib/checks/elements.js +6 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/queryNoDbArtifacts.js +2 -1
  25. package/lib/checks/selectItems.js +5 -1
  26. package/lib/checks/types.js +4 -2
  27. package/lib/checks/utils.js +2 -2
  28. package/lib/checks/validator.js +4 -5
  29. package/lib/compiler/assert-consistency.js +16 -10
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +98 -9
  32. package/lib/compiler/checks.js +22 -70
  33. package/lib/compiler/define.js +61 -13
  34. package/lib/compiler/extend.js +79 -14
  35. package/lib/compiler/finalize-parse-cdl.js +46 -29
  36. package/lib/compiler/index.js +100 -37
  37. package/lib/compiler/moduleLayers.js +7 -0
  38. package/lib/compiler/populate.js +19 -18
  39. package/lib/compiler/propagator.js +7 -4
  40. package/lib/compiler/resolve.js +297 -234
  41. package/lib/compiler/shared.js +107 -102
  42. package/lib/compiler/tweak-assocs.js +16 -11
  43. package/lib/compiler/utils.js +5 -0
  44. package/lib/edm/annotations/genericTranslation.js +93 -21
  45. package/lib/edm/csn2edm.js +230 -115
  46. package/lib/edm/edm.js +305 -226
  47. package/lib/edm/edmPreprocessor.js +509 -438
  48. package/lib/edm/edmUtils.js +31 -45
  49. package/lib/gen/Dictionary.json +98 -22
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +10 -30
  52. package/lib/gen/language.tokens +105 -114
  53. package/lib/gen/languageLexer.interp +1 -34
  54. package/lib/gen/languageLexer.js +889 -1007
  55. package/lib/gen/languageLexer.tokens +95 -106
  56. package/lib/gen/languageParser.js +20786 -22199
  57. package/lib/json/csnVersion.js +10 -11
  58. package/lib/json/from-csn.js +59 -51
  59. package/lib/json/to-csn.js +10 -10
  60. package/lib/language/antlrParser.js +2 -2
  61. package/lib/language/docCommentParser.js +62 -39
  62. package/lib/language/errorStrategy.js +52 -40
  63. package/lib/language/genericAntlrParser.js +348 -229
  64. package/lib/language/language.g4 +629 -653
  65. package/lib/language/multiLineStringParser.js +14 -42
  66. package/lib/language/textUtils.js +44 -0
  67. package/lib/main.d.ts +46 -43
  68. package/lib/main.js +108 -79
  69. package/lib/model/csnRefs.js +34 -7
  70. package/lib/model/csnUtils.js +337 -332
  71. package/lib/model/enrichCsn.js +1 -0
  72. package/lib/model/revealInternalProperties.js +30 -10
  73. package/lib/model/sortViews.js +32 -31
  74. package/lib/modelCompare/compare.js +6 -6
  75. package/lib/optionProcessor.js +73 -46
  76. package/lib/render/.eslintrc.json +1 -1
  77. package/lib/render/DuplicateChecker.js +4 -7
  78. package/lib/render/manageConstraints.js +70 -2
  79. package/lib/render/toCdl.js +1042 -882
  80. package/lib/render/toHdbcds.js +195 -245
  81. package/lib/render/toRename.js +44 -22
  82. package/lib/render/toSql.js +225 -241
  83. package/lib/render/utils/common.js +145 -15
  84. package/lib/render/utils/sql.js +20 -19
  85. package/lib/sql-identifier.js +6 -0
  86. package/lib/transform/db/.eslintrc.json +4 -3
  87. package/lib/transform/db/associations.js +2 -2
  88. package/lib/transform/db/cdsPersistence.js +5 -15
  89. package/lib/transform/db/constraints.js +4 -2
  90. package/lib/transform/db/expansion.js +22 -16
  91. package/lib/transform/db/flattening.js +109 -80
  92. package/lib/transform/db/transformExists.js +7 -7
  93. package/lib/transform/db/views.js +9 -6
  94. package/lib/transform/draft/.eslintrc.json +2 -2
  95. package/lib/transform/draft/db.js +6 -6
  96. package/lib/transform/draft/odata.js +6 -7
  97. package/lib/transform/forHanaNew.js +62 -48
  98. package/lib/transform/forOdataNew.js +49 -50
  99. package/lib/transform/localized.js +31 -20
  100. package/lib/transform/odata/toFinalBaseType.js +16 -14
  101. package/lib/transform/odata/typesExposure.js +146 -198
  102. package/lib/transform/odata/utils.js +1 -38
  103. package/lib/transform/transformUtilsNew.js +67 -84
  104. package/lib/transform/translateAssocsToJoins.js +7 -3
  105. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  106. package/lib/transform/universalCsn/coreComputed.js +16 -9
  107. package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
  108. package/lib/utils/file.js +3 -3
  109. package/lib/utils/moduleResolve.js +13 -6
  110. package/lib/utils/timetrace.js +20 -21
  111. package/package.json +35 -4
  112. package/share/messages/message-explanations.json +2 -1
  113. package/share/messages/syntax-expected-integer.md +37 -0
  114. package/doc/ApiMigration.md +0 -237
  115. package/doc/CommandLineMigration.md +0 -58
  116. package/doc/ErrorMessages.md +0 -175
  117. package/doc/FioriAnnotations.md +0 -94
  118. package/doc/ODataTransformation.md +0 -273
  119. package/lib/backends.js +0 -529
  120. package/lib/fix_antlr4-8_warning.js +0 -56
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -296
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
@@ -2,12 +2,12 @@
2
2
 
3
3
  const {
4
4
  forEachDefinition, forEachMemberRecursively,
5
- isBuiltinType, cloneCsnDictionary, cloneCsn,
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.toOdata.version === 'v4';
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.getFinalBaseType(node.type);
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 transformUrilsNew::toFinalBaseType for the moment,
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 && isBuiltinType(finalType.items.type)) {
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 = cloneCsn({ definitions: { 'TypeDef': finalType } }, options);
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.getFinalBaseType(node.type);
188
- // if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.getFinalBaseType(finalBaseType.items);
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 { cloneCsn, isBuiltinType, forEachDefinition, forEachMember, cloneCsnDictionary } = require('../../model/csnUtils');
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.toOdata.version === 'v4' && (options.toOdata.odataProxies || options.toOdata.odataXServiceRefs);
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 (serviceName) {
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 artificialtName = `${isMultiSchema ?
37
- defNameWithoutServiceOrContextName(defName, serviceName)
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} service
59
+ * @param {String} serviceName
88
60
  */
89
- function exposeTypesOfAction(action, actionName, defName, service, path) {
61
+ function exposeTypesOfAction(action, actionName, defName, serviceName, path) {
90
62
  if (action.returns) {
91
63
  const artificialName = `return_${actionName.replace(/\./g, '_')}`;
92
- exposeTypeOf(action.returns, false, actionName, defName, service, artificialName, path.concat(['returns']));
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
- exposeTypeOf(param, false, actionName, defName, service, artificialName, path.concat(['params', paramName]));
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} service
108
- * @param {String} artificialName
81
+ * @param {String} serviceName
82
+ * @param {String} newTypeName
109
83
  */
110
- function exposeStructTypeOf(node, isKey, memberName, defName, service, artificialName, path, parentName) {
111
- if (node.items) exposeStructTypeOf(node.items, isKey, memberName, defName, service, artificialName, path);
112
-
113
- // start conservative, assume we're in a named type
114
- let isAnonymous = false;
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 ? getTypeNameInMultiSchema(node.type, service) : getAnonymousTypeNameInMultiSchema(artificialName, parentName || defName)
122
- : `${service}.${newTypeId}`;
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
- // With the redirection of sub elements, the element which is of named type with an association is now expanded and contains the association
125
- // and the new target. Consequently, we now have both type and elements properties in this case, and the elements should be taken as a priority
126
- // as the correct target is there and no longer in the type definition
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
- let newType = exposeStructType(newTypeFullName, newTypeElements, memberName, path);
135
- if (!newType) {
136
- // Error already reported
137
- return;
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
- if (node.$location) setProp(newType, '$location', node.$location);
141
- setProp(newType, '$exposedBy', 'typeExposure');
117
+ csn.definitions[fullQualifiedNewTypeName] = newType;
118
+ exposedTypes[fullQualifiedNewTypeName] = 1;
142
119
 
143
- // Recurse into elements of 'type' (if any) and expose them as well (is needed)
144
- newType.elements && Object.entries(newType.elements).forEach(([elemName, newElem]) => {
145
- if (node.elements && node.elements[elemName].$location) setProp(newElem, '$location', node.elements[elemName].$location);
146
- exposeStructTypeOf(newElem,
147
- isKey,
148
- memberName,
149
- typeDef.kind === 'type' ? node.type : defName,
150
- service,
151
- isMultiSchema ? `${newTypeFullName}_${elemName}` : `${newTypeId}_${elemName}`,
152
- path,
153
- newTypeFullName);
154
- });
155
- typeDef.kind === 'type' ? copyAnnotations(typeDef, newType) : copyAnnotations(node, newType);
156
- delete node.elements;
157
- node.type = newTypeFullName;
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
- * Returns whether the 'node' is for exposing the in service.
162
- * There are 2 cases when we would like to expose a type is the service:
163
- * 1. If the node is of user-defined type which is not part of the service
164
- * 2. When we have structured element (the object has property 'elements')
165
- * @param {Object} node
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 isExposableStructure(node) {
168
- let finalNodeType = node.type ? csnUtils.getFinalType(node.type) : undefined;
169
- return finalNodeType && isArtifactInService(finalNodeType, service)
170
- ? false
171
- : csnUtils.isStructured(node);
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} service current service name
199
+ * @param {string} serviceName current service name
180
200
  */
181
- function getTypeNameInMultiSchema(typeName, service) {
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 = `${service}.${typeService}`;
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 = `${service}.${typeContext || typeNamespace || autoexposeSchemaName}`;
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 || autoexposeSchemaName;
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[`${name}`] = { kind: 'schema', name };
245
+ schemas[name] = { kind: 'schema', name };
226
246
  }
227
247
 
228
248
  /**
229
- * Expose a new type definition in the 'definitions' of the CSN and return that type(reusing such a type
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 exposeStructType(typeName, elements, parentName, path) {
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 the type's elements
260
+ // Duplicate elements
255
261
  Object.entries(elements).forEach(([elemName, element]) => {
256
- if (type.elements[elemName]) {
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
- // If a member is of type "array of <named type|anonymous type>", we expose the arrayed type,
277
- // like we expose structures in structured mode
278
- function exposeArrayOfTypeOf(node, isKey, memberName, defName, service, artificialName, path) {
279
- // if anonymously defined in place -> we always expose the type
280
- // this would be definition like 'elem: array of { ... }'
281
- // and we use the artificial name for the new type name
282
- if (node.items && !node.type) {
283
- exposeStructTypeOf(node.items, isKey, memberName, defName, service, artificialName, path.concat('items'));
284
- }
285
- // we can have both of the 'type' and 'items' in the cases:
286
- // 1. 'elem: Foo' and 'type Foo: array of Baz' and 'type Baz: { ... }'
287
- // or 2. 'elem: Foo' and type Foo: array of Integer|String|...'
288
- else if (node.type) {
289
- let finalType = csnUtils.getFinalTypeDef(node.type);
290
- if (finalType.items) {
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
- const {
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
  }