@sap/cds 5.8.4 → 5.9.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.
Files changed (248) hide show
  1. package/CHANGELOG.md +198 -77
  2. package/app/fiori/preview.js +16 -11
  3. package/app/fiori/routes.js +15 -8
  4. package/app/index.js +1 -1
  5. package/bin/build/buildTaskFactory.js +3 -3
  6. package/bin/build/buildTaskProviderFactory.js +1 -1
  7. package/bin/build/constants.js +1 -1
  8. package/bin/build/provider/buildTaskHandlerEdmx.js +12 -7
  9. package/bin/build/provider/buildTaskHandlerInternal.js +1 -1
  10. package/bin/build/provider/buildTaskProviderInternal.js +8 -2
  11. package/bin/build/provider/hana/2migration.js +27 -24
  12. package/bin/build/provider/hana/index.js +17 -18
  13. package/bin/build/provider/hana/migrationtable.js +9 -10
  14. package/bin/build/provider/java-cf/index.js +4 -5
  15. package/bin/build/provider/node-cf/index.js +99 -6
  16. package/bin/cds.js +17 -18
  17. package/bin/deploy/to-hana/cfUtil.js +16 -19
  18. package/bin/deploy/to-hana/hana.js +7 -24
  19. package/bin/deploy/to-hana/hdiDeployUtil.js +8 -4
  20. package/bin/mtx/in-cds.js +2 -2
  21. package/bin/serve.js +10 -3
  22. package/bin/utils/modules.js +7 -0
  23. package/bin/version.js +56 -3
  24. package/lib/compile/cdsc.js +7 -2
  25. package/lib/compile/etc/_localized.js +37 -25
  26. package/lib/compile/etc/csv.js +8 -8
  27. package/lib/compile/for/drafts.js +9 -0
  28. package/lib/compile/for/java.js +16 -0
  29. package/lib/compile/for/nodejs.js +12 -0
  30. package/lib/compile/index.js +3 -0
  31. package/lib/compile/minify.js +16 -2
  32. package/lib/compile/parse.js +2 -2
  33. package/lib/compile/resolve.js +35 -18
  34. package/lib/compile/to/json.js +3 -1
  35. package/lib/compile/to/sql.js +2 -2
  36. package/lib/compile/to/srvinfo.js +4 -2
  37. package/lib/connect/bindings.js +1 -1
  38. package/lib/connect/index.js +3 -4
  39. package/lib/core/entities.js +15 -14
  40. package/lib/core/index.js +39 -36
  41. package/lib/core/reflect.js +4 -2
  42. package/lib/deploy.js +114 -127
  43. package/lib/env/defaults.js +1 -0
  44. package/lib/env/index.js +165 -165
  45. package/lib/env/presets.js +1 -0
  46. package/lib/env/requires.js +121 -50
  47. package/lib/index.js +2 -0
  48. package/lib/log/format/kibana.js +2 -2
  49. package/lib/ql/SELECT.js +10 -0
  50. package/lib/ql/parse.js +1 -0
  51. package/lib/req/cds-context.js +4 -1
  52. package/lib/req/context.js +50 -56
  53. package/lib/req/event.js +1 -6
  54. package/lib/req/locale.js +6 -5
  55. package/lib/req/request.js +2 -0
  56. package/lib/req/user.js +7 -5
  57. package/lib/serve/Service-api.js +10 -7
  58. package/lib/serve/Service-dispatch.js +9 -11
  59. package/lib/serve/Service-methods.js +30 -41
  60. package/lib/serve/Transaction.js +10 -7
  61. package/lib/serve/adapters.js +11 -9
  62. package/lib/serve/factory.js +14 -9
  63. package/lib/serve/index.js +28 -15
  64. package/lib/utils/data.js +1 -1
  65. package/lib/utils/index.js +27 -30
  66. package/lib/utils/resources/index.js +101 -0
  67. package/lib/utils/resources/tar.js +71 -0
  68. package/lib/utils/resources/utils.js +11 -0
  69. package/libx/_runtime/audit/Service.js +36 -39
  70. package/libx/_runtime/audit/generic/personal/access.js +3 -4
  71. package/libx/_runtime/audit/generic/personal/modification.js +3 -4
  72. package/libx/_runtime/audit/utils/v2.js +1 -2
  73. package/libx/_runtime/auth/index.js +126 -84
  74. package/libx/_runtime/auth/strategies/JWT.js +12 -19
  75. package/libx/_runtime/auth/strategies/dummy.js +1 -5
  76. package/libx/_runtime/auth/strategies/dwc.js +11 -9
  77. package/libx/_runtime/auth/strategies/mock.js +0 -4
  78. package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
  79. package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
  80. package/libx/_runtime/auth/utils.js +22 -1
  81. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
  82. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +8 -3
  83. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
  84. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
  85. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
  86. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
  87. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
  88. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
  89. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
  90. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
  91. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
  92. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
  93. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +2 -0
  94. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
  95. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
  96. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
  97. package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +56 -0
  98. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
  99. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
  100. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
  101. package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
  102. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
  103. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
  104. package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
  105. package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
  106. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
  107. package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
  108. package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
  109. package/libx/_runtime/cds-services/services/Service.js +40 -0
  110. package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
  111. package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
  112. package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
  113. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
  114. package/libx/_runtime/cds-services/util/assert.js +20 -14
  115. package/libx/_runtime/cds.js +9 -1
  116. package/libx/_runtime/common/aspects/any.js +5 -0
  117. package/libx/_runtime/common/aspects/entity.js +25 -7
  118. package/libx/_runtime/common/aspects/utils.js +2 -2
  119. package/libx/_runtime/common/composition/data.js +6 -0
  120. package/libx/_runtime/common/composition/insert.js +3 -2
  121. package/libx/_runtime/common/composition/tree.js +4 -10
  122. package/libx/_runtime/common/composition/update.js +4 -4
  123. package/libx/_runtime/common/constants/draft.js +29 -26
  124. package/libx/_runtime/common/error/constants.js +2 -2
  125. package/libx/_runtime/common/error/frontend.js +7 -15
  126. package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
  127. package/libx/_runtime/common/generic/auth/constants.js +20 -0
  128. package/libx/_runtime/common/generic/auth/expand.js +54 -0
  129. package/libx/_runtime/common/generic/auth/index.js +32 -0
  130. package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
  131. package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
  132. package/libx/_runtime/common/generic/auth/requires.js +34 -0
  133. package/libx/_runtime/common/generic/auth/restrict.js +298 -0
  134. package/libx/_runtime/common/generic/auth/restrictions.js +85 -0
  135. package/libx/_runtime/common/generic/auth/utils.js +213 -0
  136. package/libx/_runtime/common/generic/crud.js +8 -6
  137. package/libx/_runtime/common/generic/etag.js +1 -1
  138. package/libx/_runtime/common/generic/input.js +35 -35
  139. package/libx/_runtime/common/generic/sorting.js +2 -3
  140. package/libx/_runtime/common/generic/temporal.js +2 -2
  141. package/libx/_runtime/common/i18n/messages.properties +1 -1
  142. package/libx/_runtime/common/toggles/handler.js +21 -0
  143. package/libx/_runtime/common/utils/copy.js +10 -1
  144. package/libx/_runtime/common/utils/cqn2cqn4sql.js +111 -35
  145. package/libx/_runtime/common/utils/csn.js +63 -1
  146. package/libx/_runtime/common/utils/dollar.js +10 -1
  147. package/libx/_runtime/common/utils/draft.js +46 -7
  148. package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
  149. package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
  150. package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
  151. package/libx/_runtime/common/utils/generateOnCond.js +4 -1
  152. package/libx/_runtime/common/utils/quotingStyles.js +2 -0
  153. package/libx/_runtime/common/utils/resolveStructured.js +25 -9
  154. package/libx/_runtime/common/utils/resolveView.js +4 -1
  155. package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
  156. package/libx/_runtime/common/utils/structured.js +33 -37
  157. package/libx/_runtime/common/utils/template.js +17 -8
  158. package/libx/_runtime/common/utils/templateProcessor.js +28 -28
  159. package/libx/_runtime/db/data-conversion/post-processing.js +118 -412
  160. package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
  161. package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
  162. package/libx/_runtime/db/generic/index.js +1 -3
  163. package/libx/_runtime/db/generic/input.js +5 -10
  164. package/libx/_runtime/db/generic/rewrite.js +5 -2
  165. package/libx/_runtime/db/generic/structured.js +2 -2
  166. package/libx/_runtime/db/query/delete.js +2 -2
  167. package/libx/_runtime/db/query/insert.js +1 -1
  168. package/libx/_runtime/db/query/update.js +9 -14
  169. package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
  170. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +8 -8
  171. package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
  172. package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
  173. package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
  174. package/libx/_runtime/db/utils/columns.js +3 -3
  175. package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
  176. package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
  177. package/libx/_runtime/extensibility/mps/index.js +5 -0
  178. package/libx/_runtime/extensibility/mps/service.js +111 -0
  179. package/libx/_runtime/extensibility/mps/tar.js +42 -0
  180. package/libx/_runtime/extensibility/mps/utils.js +11 -0
  181. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
  182. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
  183. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
  184. package/libx/_runtime/extensibility/uiflex/index.js +54 -0
  185. package/libx/_runtime/extensibility/uiflex/service.js +276 -0
  186. package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
  187. package/libx/_runtime/fiori/generic/activate.js +2 -2
  188. package/libx/_runtime/fiori/generic/before.js +4 -4
  189. package/libx/_runtime/fiori/generic/new.js +3 -3
  190. package/libx/_runtime/fiori/generic/patch.js +1 -1
  191. package/libx/_runtime/fiori/generic/read.js +58 -66
  192. package/libx/_runtime/fiori/generic/readOverDraft.js +74 -16
  193. package/libx/_runtime/fiori/utils/handler.js +6 -13
  194. package/libx/_runtime/fiori/utils/where.js +6 -5
  195. package/libx/_runtime/hana/Service.js +4 -10
  196. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +1 -1
  197. package/libx/_runtime/hana/driver.js +2 -2
  198. package/libx/_runtime/hana/execute.js +45 -75
  199. package/libx/_runtime/hana/pool.js +1 -1
  200. package/libx/_runtime/hana/streaming.js +2 -1
  201. package/libx/_runtime/index.js +6 -6
  202. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
  203. package/libx/_runtime/messaging/Outbox.js +2 -2
  204. package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
  205. package/libx/_runtime/messaging/common-utils/connections.js +5 -7
  206. package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
  207. package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
  208. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
  209. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
  210. package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
  211. package/libx/_runtime/messaging/file-based.js +5 -5
  212. package/libx/_runtime/messaging/message-queuing.js +14 -12
  213. package/libx/_runtime/messaging/outbox/utils.js +18 -19
  214. package/libx/_runtime/messaging/redis-messaging.js +91 -0
  215. package/libx/_runtime/messaging/service.js +8 -6
  216. package/libx/_runtime/remote/Service.js +44 -8
  217. package/libx/_runtime/remote/utils/client.js +24 -19
  218. package/libx/_runtime/remote/utils/data.js +11 -11
  219. package/libx/_runtime/sqlite/Service.js +6 -9
  220. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
  221. package/libx/_runtime/types/api.js +10 -2
  222. package/libx/common/utils/ucsn.js +109 -0
  223. package/libx/gql/resolvers/crud/update.js +5 -0
  224. package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
  225. package/libx/gql/schema/typeDefMap.js +2 -2
  226. package/libx/odata/afterburner.js +110 -16
  227. package/libx/odata/cqn2odata.js +24 -27
  228. package/libx/odata/grammar.pegjs +9 -1
  229. package/libx/odata/parseToCqn.js +39 -0
  230. package/libx/odata/parser.js +1 -1
  231. package/libx/rest/RestAdapter.js +9 -1
  232. package/libx/rest/middleware/input.js +54 -0
  233. package/libx/rest/middleware/operation.js +14 -1
  234. package/libx/rest/middleware/parse.js +11 -7
  235. package/package.json +2 -2
  236. package/server.js +34 -19
  237. package/srv/audit-log.cds +2 -2
  238. package/srv/flex.cds +8 -2
  239. package/srv/flex.js +1 -1
  240. package/srv/mps.cds +23 -0
  241. package/srv/mps.js +1 -0
  242. package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
  243. package/libx/_runtime/common/generic/auth.js +0 -874
  244. package/libx/_runtime/common/toggles/alpha.js +0 -43
  245. package/libx/_runtime/db/generic/arrayed.js +0 -33
  246. package/libx/_runtime/fiori/uiflex/index.js +0 -35
  247. package/libx/_runtime/fiori/uiflex/service.js +0 -150
  248. package/libx/rest/utils/data.js +0 -60
@@ -1,50 +1,7 @@
1
- const cds = require('../../cds')
2
-
3
- const { ensureUnlocalized, ensureNoDraftsSuffix } = require('../../common/utils/draft')
4
-
5
- /**
6
- * Check if the value is a function or reference to private function.
7
- * Return if any of the cases are true.
8
- *
9
- * @param {string|Function} fn -
10
- * @returns {Function}
11
- * @throws Error if no valid parameter fn provided
12
- * @private
13
- */
14
- const _getMethod = fn => {
15
- if (typeof fn === 'function') {
16
- return fn
17
- }
18
-
19
- throw new Error(`Method "${fn}" does not exist.`)
20
- }
21
-
22
- /**
23
- * Get the entity name from CQN and check if it exists at CSN.
24
- *
25
- * @param {object} csn - Reflected CSN
26
- * @param {string} from - Entity name
27
- * @returns {string|undefined}
28
- * @private
29
- */
30
- const _getEntityName = (csn, from) => {
31
- if (!from.ref || typeof csn !== 'object') {
32
- return
33
- }
34
-
35
- let entityName = from.ref[0].id ? from.ref[0].id : from.ref[0]
36
-
37
- if (csn.definitions && csn.definitions[entityName]) {
38
- return entityName
39
- }
40
-
41
- // REVISIT: Find better solution
42
- entityName = entityName.replace(/_drafts$/i, '')
43
-
44
- if (csn.definitions && csn.definitions[entityName]) {
45
- return entityName
46
- }
47
- }
1
+ const { getEntityNameFromCQN, traverseFroms } = require('../../common/utils/entityFromCqn')
2
+ const { ensureNoDraftsSuffix } = require('../../common/utils/draft')
3
+ const { proxifyIfFlattened } = require('../../../common/utils/ucsn')
4
+ const cds = require('../../../../lib')
48
5
 
49
6
  const _refs = (refs, as) => {
50
7
  const arr = []
@@ -90,114 +47,135 @@ const _extractRefs = (from, as) => {
90
47
  return [ref]
91
48
  }
92
49
 
93
- // REVISIT: check why we need includeAlias for some draft cases (check AFC tests)
94
- const _addMapperFunction = (elements, toService, key, type, alias, includeAlias) => {
95
- if (!toService.has(type)) {
96
- return
97
- }
98
-
99
- const convertFunction = _getMethod(toService.get(type))
100
-
101
- // ambiguous cases will lead to SQL syntax errors anyway, so no need for a check
102
- elements.set(key, convertFunction)
103
- if (includeAlias && alias) {
104
- elements.set(`${alias}_${key}`, convertFunction)
50
+ const _getCastFunction = ({ type }) => {
51
+ switch (type) {
52
+ case 'cds.Boolean':
53
+ return Boolean
54
+ case 'cds.Integer':
55
+ return Number
56
+ default:
57
+ return String
105
58
  }
106
59
  }
107
60
 
108
- const _filterUnique = (value, index, arr) => {
109
- return arr.indexOf(value) === index
110
- }
111
-
112
- const _addMapperFunction4struct = (elements, toService, parent, struct, alias, includeAlias) => {
113
- for (const k in struct.elements) {
114
- const current = struct.elements[k]
115
- if (current._isStructured) {
116
- _addMapperFunction4struct(elements, toService, `${parent}_${k}`, current, alias, includeAlias)
117
- } else {
118
- _addMapperFunction(elements, toService, `${parent}_${k}`, current.type, alias, includeAlias)
119
- }
61
+ const _structElement = (definition, structPath) => {
62
+ if (definition.elements) {
63
+ return _structElement(definition.elements[structPath[0]], structPath.slice(1))
120
64
  }
121
- }
122
65
 
123
- /**
124
- * Get a map of all possible elements and their mappers.
125
- *
126
- * @param {Map} toService - Mapping instructions for data conversions based on CDS data types
127
- * @param {object} csn - Reflected CSN
128
- * @param {object} cqn - CQN that is used to query the DB.
129
- * @returns {Map<any, any>}
130
- * @private
131
- */
132
- const _getElementCombinations = (toService, csn, cqn, includeAlias = false) => {
133
- const elements = new Map()
66
+ return definition
67
+ }
134
68
 
135
- // Get list of available and relevant entities as defined by from
136
- for (const from of _extractRefs(cqn.SELECT.from).filter(_filterUnique)) {
137
- const entityName = _getEntityName(csn, from)
69
+ const _getNestedElement = (entity, key) => {
70
+ if (entity.elements[key]) return entity.elements[key]
138
71
 
139
- if (!entityName) {
140
- continue
141
- }
72
+ const structPath = entity._flat2struct[key]
73
+ if (!structPath) return
142
74
 
143
- const entity = csn.definitions[ensureUnlocalized(entityName)]
144
- for (const key in entity.elements) {
145
- const element = entity.elements[key]
146
- if (element._isStructured) {
147
- _addMapperFunction4struct(elements, toService, key, element, from.as, includeAlias)
148
- } else {
149
- if ('SET' in cqn.SELECT.from) {
150
- _addMapperFunction(elements, toService, key, element.type, cqn.SELECT.from.as, includeAlias)
151
- } else {
152
- _addMapperFunction(elements, toService, key, element.type, from.as, includeAlias)
153
- }
154
- }
155
- }
156
- }
75
+ return _structElement(entity, structPath)
76
+ }
157
77
 
158
- return elements
78
+ const _structure = csnEntity => (value, key, row, unaliasedKey) => {
79
+ proxifyIfFlattened(csnEntity, row)
80
+ const effectiveKey = unaliasedKey || key
81
+ delete row[effectiveKey]
82
+ row[effectiveKey] = value
159
83
  }
160
84
 
161
- const _getCastFunction = ({ type }) => {
162
- switch (type) {
163
- case 'cds.Boolean':
164
- return Boolean
165
- case 'cds.Integer':
166
- return Number
167
- default:
168
- return String
85
+ const _addConverter = (mapper, name, converter) => {
86
+ if (mapper.has(name)) {
87
+ const oldConverter = mapper.get(name)
88
+ mapper.set(name, (val, key, row, unaliasedKey) => {
89
+ oldConverter(val, key, row, unaliasedKey)
90
+ converter(row[unaliasedKey || key], key, row, unaliasedKey)
91
+ })
92
+ } else {
93
+ mapper.set(name, converter)
169
94
  }
170
95
  }
171
96
 
172
97
  /**
173
98
  * Get a map of to be converted elements and their conversion functions.
174
99
  *
175
- * @param {Map} toService - Mapping instructions for data conversions based on CDS data types
100
+ * @param {Map} conversionMap - Mapping instructions for data conversions based on CDS data types
176
101
  * @param {object} csn - Reflected CSN
177
102
  * @param {object} cqn - CQN that is used to query the DB.
178
103
  * @returns {Map<any, any>}
179
104
  * @private
180
105
  */
181
- const _getMapperForListedElements = (toService, csn, cqn) => {
182
- const elements = _getElementCombinations(toService, csn, cqn, true)
106
+ // eslint-disable-next-line complexity
107
+ const _getMapperForListedElements = (conversionMap, csn, cqn) => {
183
108
  const mapper = new Map()
184
109
 
185
- for (const element of cqn.SELECT.columns) {
186
- if (element.ref) {
187
- const identifier = element.ref.length === 1 ? element.ref[0] : undefined
188
- const name = element.as ? element.as : identifier
110
+ const alias2entity = {}
111
+ if (cqn.SELECT.from.args) {
112
+ for (const each of cqn.SELECT.from.args) {
113
+ const { entityName, alias } = getEntityNameFromCQN(each)
114
+ let as = each.as || alias
115
+ if (as) as = ensureNoDraftsSuffix(as) // > in comp2one, the alias gets suffixed with "_drafts"
116
+ alias2entity[as] =
117
+ csn.definitions[ensureNoDraftsSuffix(entityName)] || csn.definitions[entityName.replace(/\.drafts$/, '')]
118
+ }
119
+ // additionally, for deeply nested args, find pairs of single-element ref + as and build alias to entity mapping
120
+ const ref2as = []
121
+ traverseFroms(cqn.SELECT.from, from => ref2as.push({ ref: from.ref, as: from.as }))
122
+ for (const each of ref2as) {
123
+ alias2entity[each.as] =
124
+ csn.definitions[ensureNoDraftsSuffix(each.ref[0])] || csn.definitions[each.ref[0].replace(/\.drafts$/, '')]
125
+ }
126
+ } else {
127
+ const { entityName } = getEntityNameFromCQN(cqn)
128
+ alias2entity[0] = csn.definitions[ensureNoDraftsSuffix(entityName)]
129
+ }
130
+
131
+ for (const col of cqn.SELECT.columns) {
132
+ if (col.cast) {
133
+ const name = col.as ? col.as : col.ref[col.ref.length - 1]
134
+ _addConverter(mapper, name, (val, key, row, unaliasedKey) => {
135
+ row[unaliasedKey || key] = _getCastFunction(col.cast)(val)
136
+ })
137
+ continue
138
+ }
139
+ if (col.ref) {
140
+ const name = col.ref[col.ref.length - 1]
141
+
142
+ const entity =
143
+ col.ref.length > 1
144
+ ? alias2entity[col.ref[0]] || alias2entity[0]
145
+ : alias2entity[0] || alias2entity[getEntityNameFromCQN(cqn).alias]
146
+ if (!entity) {
147
+ // REVISIT: should not be necessary, but HasActiveEntity is not always prefixed with alias
148
+ if (col.ref.length === 1 && col.ref[0] === 'HasActiveEntity')
149
+ _addConverter(mapper, col.as ? col.as : name, (val, key, row, unaliasedKey) => {
150
+ row[unaliasedKey || key] = conversionMap.get('cds.Boolean')(val)
151
+ })
152
+ continue
153
+ }
154
+
155
+ const element = _getNestedElement(entity, name)
156
+ if (!element) continue
157
+
158
+ // REVISIT: _type is undefined for cds.hana.CLOB etc.
159
+ const type = element._type || element.type
160
+ if (conversionMap.has(type)) {
161
+ _addConverter(mapper, col.as ? col.as : name, (val, key, row, unaliasedKey) => {
162
+ row[unaliasedKey || key] = conversionMap.get(type)(val)
163
+ })
164
+ } else if (element.items) {
165
+ _addConverter(mapper, col.as ? col.as : name, (val, key, row, unaliasedKey) => {
166
+ const effectiveKey = unaliasedKey || key
167
+ row[effectiveKey] = JSON.parse(val)
168
+ }) // > arrayed elements
169
+ }
189
170
 
190
- if (element.cast) {
191
- mapper.set(name, _getCastFunction(element.cast))
192
- } else if (elements.has(name)) {
193
- mapper.set(name, elements.get(name))
194
- } else if (elements.has(identifier)) {
195
- mapper.set(name, elements.get(identifier))
196
- } else if (elements.has(element.ref.join('_'))) {
197
- mapper.set(name || element.ref[element.ref.length - 1], elements.get(element.ref.join('_')))
171
+ if (
172
+ cds.env.features.ucsn_struct_conversion &&
173
+ element.parent &&
174
+ element.parent._isStructured &&
175
+ !element._isStructured
176
+ ) {
177
+ _addConverter(mapper, col.as ? col.as : name, _structure(entity))
198
178
  }
199
- } else if (element.as && element.cast) {
200
- mapper.set(element.as, _getCastFunction(element.cast))
201
179
  }
202
180
  }
203
181
 
@@ -207,143 +185,25 @@ const _getMapperForListedElements = (toService, csn, cqn) => {
207
185
  /**
208
186
  * Based on CSN and CQN get a map on how to map the result.
209
187
  *
210
- * @param {Map} toService - Mapping instructions for data conversions based on CDS data types
188
+ * @param {Map} conversionMap - Mapping instructions for data conversions based on CDS data types
211
189
  * @param {object} csn - Reflected CSN
212
190
  * @param {object} cqn - CQN that is used to query the DB.
213
191
  * @returns {Map<any, any>}
214
192
  * @private
215
193
  */
216
- const getPostProcessMapper = (toService, csn = {}, cqn = {}) => {
194
+ const getPostProcessMapper = (conversionMap, csn = {}, cqn = {}) => {
217
195
  // No mapper defined or irrelevant as no READ request
218
196
  if (!Object.prototype.hasOwnProperty.call(cqn, 'SELECT')) {
219
197
  return new Map()
220
198
  }
221
199
 
222
- if (Array.isArray(cqn.SELECT.columns) && cqn.SELECT.columns.length !== 0 && !cqn.SELECT.columns.includes('*')) {
223
- return _getMapperForListedElements(toService, csn, cqn)
224
- }
225
-
226
- // No element/column specified
227
- return _getElementCombinations(toService, csn, cqn)
228
- }
229
-
230
- const _getCombineStructureConvert = (structure, columnName, propName, fn) => {
231
- const length = structure.length
232
-
233
- return row => {
234
- if (row[columnName] === undefined) {
235
- return
236
- }
237
-
238
- if (!row[structure[0]]) {
239
- row[structure[0]] = {}
240
- }
241
-
242
- let subObj = row[structure[0]]
243
-
244
- for (let i = 1; i < length; i++) {
245
- subObj = subObj[structure[i]] = {}
246
- }
247
-
248
- subObj[propName] = fn ? fn(row[columnName]) : row[columnName]
249
-
250
- delete row[columnName]
251
- }
252
- }
253
-
254
- const _getCombineRenameConvert = (columnName, propName, fn) => {
255
- return row => {
256
- if (row[columnName] === undefined) {
257
- return
258
- }
259
-
260
- row[propName] = fn ? fn(row[columnName]) : row[columnName]
261
- delete row[columnName]
262
- }
263
- }
264
-
265
- const _getConvert = (columnName, fn) => {
266
- return row => {
267
- row[columnName] = fn(row[columnName])
268
- }
269
- }
270
-
271
- const _getRemoveMapper = (mapper, propName) => {
272
- if (mapper) {
273
- const fn = mapper.get(propName)
274
- mapper.delete(propName)
275
-
276
- return fn
277
- }
200
+ return cqn.SELECT.columns ? _getMapperForListedElements(conversionMap, csn, cqn) : new Map()
278
201
  }
279
202
 
280
- const _propertyMapper = (dataMapper, propertyMapper, objStructMapper, mapper) => {
281
- if (!propertyMapper) {
282
- return
283
- }
284
-
285
- for (const [columnName, propName] of propertyMapper.entries()) {
286
- const fn = _getRemoveMapper(dataMapper, propName)
287
- const structure = _getRemoveMapper(objStructMapper, propName)
288
-
289
- mapper.push(
290
- structure
291
- ? _getCombineStructureConvert(structure, columnName, propName, fn)
292
- : _getCombineRenameConvert(columnName, propName, fn)
293
- )
294
- }
295
- }
296
-
297
- const _objStructMapper = (dataMapper, propertyMapper, objStructMapper, mapper) => {
298
- if (!objStructMapper) {
299
- return
300
- }
301
-
302
- for (const [propName, structure] of objStructMapper.entries()) {
303
- mapper.push(_getCombineStructureConvert(structure, propName, propName, _getRemoveMapper(dataMapper, propName)))
304
- }
305
- }
306
-
307
- const _dataMapper = (dataMapper, propertyMapper, objStructMapper, mapper) => {
308
- if (!dataMapper) {
309
- return
310
- }
311
-
312
- for (const [columnName, converter] of dataMapper.entries()) {
313
- mapper.push(_getConvert(columnName, converter))
314
- }
315
- }
316
-
317
- /**
318
- * Generate the mapper per row up front, so that we do not have to iterate over possibly three mappers
319
- *
320
- * @param dataMapper
321
- * @param propertyMapper
322
- * @param objStructMapper
323
- * @returns {Array}
324
- * @private
325
- */
326
- const _combineMappers = (dataMapper, propertyMapper, objStructMapper) => {
327
- const mapper = []
328
-
329
- // Technical names + optionally structure and/or type conversions
330
- _propertyMapper(dataMapper, propertyMapper, objStructMapper, mapper)
331
-
332
- // Deep structures + optionally type conversions
333
- _objStructMapper(dataMapper, propertyMapper, objStructMapper, mapper)
334
-
335
- // type conversion
336
- _dataMapper(dataMapper, propertyMapper, objStructMapper, mapper)
337
-
338
- return mapper
339
- }
340
-
341
- const _processRow = (mapper, mapperCount, row) => {
342
- // REVISIT: when is this the case?
203
+ const _processRow = (mapper, row) => {
343
204
  if (!row) return
344
-
345
- for (let i = 0; i < mapperCount; i++) {
346
- mapper[i](row)
205
+ for (const [columnName, converter] of mapper.entries()) {
206
+ converter(row[columnName], columnName, row)
347
207
  }
348
208
  }
349
209
 
@@ -351,179 +211,25 @@ const _processRow = (mapper, mapperCount, row) => {
351
211
  * Post process the result as given by the db driver.
352
212
  *
353
213
  * @param {*} result - The result as returned by the db driver.
354
- * @param {Map} dataMapper - Instructions, how to transform.
214
+ * @param {Map} mapper - Instructions, how to transform.
355
215
  * @param {Map} propertyMapper - Instructions, how to rename properties.
356
216
  * @param {Map} objStructMapper - Instructions, how to rename properties.
357
217
  * @returns {*}
358
218
  * @private
359
219
  */
360
- const postProcess = (result, dataMapper, propertyMapper, objStructMapper) => {
361
- const mapper = _combineMappers(dataMapper, propertyMapper, objStructMapper)
362
- const mapperCount = mapper.length
220
+ const postProcess = (result, mapper = new Map()) => {
221
+ if (mapper.size === 0) return result
363
222
 
364
- if (mapperCount === 0) {
365
- return result
366
- }
367
-
368
- if (Array.isArray(result)) {
369
- for (let i = 0, length = result.length; i < length; i++) {
370
- _processRow(mapper, mapperCount, result[i])
371
- }
372
- } else {
373
- _processRow(mapper, mapperCount, result)
374
- }
223
+ if (Array.isArray(result)) for (const each of result) _processRow(mapper, each)
224
+ else _processRow(mapper, result)
375
225
 
376
226
  return result
377
227
  }
378
228
 
379
- const _checkExpressionsAmbiguousNaming = (csn, entity, element) => {
380
- if (!entity.as || !Array.isArray(element.ref)) {
381
- return
382
- }
383
-
384
- if (
385
- entity.as === element.ref[0] &&
386
- csn.definitions[entity.ref[0]] &&
387
- csn.definitions[entity.ref[0]].elements[element.ref[1]] &&
388
- csn.definitions[entity.ref[0]].elements[element.ref[0]] &&
389
- csn.definitions[entity.ref[0]].elements[element.ref[0]].isAssociation
390
- ) {
391
- throw new Error(`Ambiguous entity property and alias name: "${entity.as}"`)
392
- }
393
- }
394
-
395
- const _checkColumnsAmbiguousNaming = (csn, entity, columns) => {
396
- for (const element of columns) {
397
- _checkExpressionsAmbiguousNaming(csn, entity, element)
398
- }
399
- }
400
-
401
- const _checkJoinAmbiguousNaming = (csn, select) => {
402
- for (const subSelect of select.from.args) {
403
- if (Array.isArray(select.columns)) {
404
- _checkColumnsAmbiguousNaming(csn, subSelect, select.columns)
405
- }
406
- }
407
- }
408
-
409
- const _checkSelectAmbiguousNaming = (csn, select) => {
410
- if (Array.isArray(select.columns)) {
411
- _checkColumnsAmbiguousNaming(csn, select.from, select.columns)
412
- }
413
- }
414
-
415
- const _checkRecursiveSelectAmbiguousNaming = (csn, select) => {
416
- if (select.from.SELECT) {
417
- _checkRecursiveSelectAmbiguousNaming(csn, select.from.SELECT)
418
- } else if (select.from.join) {
419
- _checkJoinAmbiguousNaming(csn, select)
420
- } else if (select.from.as) {
421
- // Check innermost select statement
422
- _checkSelectAmbiguousNaming(csn, select)
423
- }
424
- }
425
-
426
- const getStructMapper = (csn, cqn, propertyMapper) => {
427
- if (csn && cqn.SELECT) {
428
- _checkRecursiveSelectAmbiguousNaming(csn, cqn.SELECT)
429
- }
430
-
431
- // REVISIT still needed?
432
-
433
- return new Map()
434
- }
435
-
436
- const _addToMap = (map, key) => {
437
- const allUpperKey = key.toUpperCase()
438
-
439
- if (key === allUpperKey) {
440
- return
441
- }
442
-
443
- map.set(allUpperKey, key)
444
- }
445
-
446
- const _addColumn = (map, from, element) => {
447
- _addToMap(map, element)
448
-
449
- if (from.ref[0] && from.ref[0].id) {
450
- _addToMap(map, `${from.ref[0].id}.${element}`)
451
- } else {
452
- if (from.ref) {
453
- _addToMap(map, `${from.ref.join('.')}.${element}`)
454
- }
455
- }
456
-
457
- if (from.as) {
458
- _addToMap(map, `${from.as}.${element}`)
459
- }
460
- }
461
-
462
- const _addAssociationToMap = (map, from, element) => {
463
- const assocs = element.keys ? element.keys.map(key => `${element.name}_${key.ref[0]}`) : []
464
- for (const assocName of assocs) {
465
- _addColumn(map, from, assocName)
466
- }
467
- }
468
-
469
- const _addComplexTypeToMap = (map, from, name, element) => {
470
- for (const complexName in element.elements) {
471
- _addColumn(map, from, `${name}_${complexName}`)
472
- }
473
- }
474
-
475
- const _getKeyMapForAllElements = (csn, cqn) => {
476
- const map = new Map()
477
-
478
- // Get list of available and relevant entities as defined by from
479
- for (const from of _extractRefs(cqn.SELECT.from)) {
480
- const entityName = _getEntityName(csn, from)
481
-
482
- if (!entityName) {
483
- continue
484
- }
485
-
486
- for (const name in csn.definitions[entityName].elements) {
487
- const element = csn.definitions[entityName].elements[name]
488
-
489
- if (element.isAssociation) {
490
- _addAssociationToMap(map, from, element)
491
- } else if (element._isStructured) {
492
- _addComplexTypeToMap(map, from, name, element)
493
- } else {
494
- _addColumn(map, from, name)
495
- }
496
- }
497
- }
498
-
499
- return map
500
- }
501
-
502
- /**
503
- * Provide a map with to be renamed properties.
504
- *
505
- * @param {object} csn - Reflected CSN
506
- * @param {object} cqn - CQN that is used to query the DB.
507
- * @returns {Map<any, any>}
508
- */
509
- const getPropertyMapper = (csn, cqn) => {
510
- if (
511
- cds.env.sql.names === 'plain' &&
512
- cqn.SELECT &&
513
- (cqn.SELECT.columns === undefined || (cqn.SELECT.columns && cqn.SELECT.columns.includes('*')))
514
- ) {
515
- return _getKeyMapForAllElements(csn, cqn)
516
- }
517
-
518
- return new Map()
519
- }
520
-
521
229
  /*
522
230
  * this module is required by cds-pg. -> in case of incompatible changes, we should let them know.
523
231
  */
524
232
  module.exports = {
525
- getPropertyMapper,
526
233
  getPostProcessMapper,
527
- getStructMapper,
528
234
  postProcess
529
235
  }