@sap/cds 5.8.2 → 5.9.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 (252) hide show
  1. package/CHANGELOG.md +214 -78
  2. package/app/fiori/preview.js +16 -11
  3. package/app/fiori/routes.js +3 -0
  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 +20 -17
  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 +12 -5
  22. package/bin/utils/modules.js +7 -0
  23. package/bin/version.js +56 -3
  24. package/lib/compile/cdsc.js +26 -3
  25. package/lib/compile/etc/_localized.js +36 -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/for/odata.js +1 -1
  31. package/lib/compile/index.js +3 -0
  32. package/lib/compile/minify.js +16 -2
  33. package/lib/compile/parse.js +2 -2
  34. package/lib/compile/resolve.js +35 -18
  35. package/lib/compile/to/json.js +3 -1
  36. package/lib/compile/to/sql.js +2 -2
  37. package/lib/compile/to/srvinfo.js +4 -2
  38. package/lib/connect/index.js +1 -1
  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 +120 -49
  47. package/lib/index.js +1 -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 +7 -5
  62. package/lib/serve/index.js +24 -12
  63. package/lib/utils/data.js +1 -1
  64. package/lib/utils/index.js +27 -30
  65. package/lib/utils/resources/index.js +101 -0
  66. package/lib/utils/resources/tar.js +71 -0
  67. package/lib/utils/resources/utils.js +11 -0
  68. package/libx/_runtime/audit/Service.js +36 -39
  69. package/libx/_runtime/audit/generic/personal/access.js +3 -4
  70. package/libx/_runtime/audit/generic/personal/modification.js +3 -4
  71. package/libx/_runtime/audit/utils/v2.js +1 -2
  72. package/libx/_runtime/auth/index.js +126 -84
  73. package/libx/_runtime/auth/strategies/JWT.js +12 -19
  74. package/libx/_runtime/auth/strategies/dummy.js +1 -5
  75. package/libx/_runtime/auth/strategies/dwc.js +11 -9
  76. package/libx/_runtime/auth/strategies/mock.js +0 -4
  77. package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
  78. package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
  79. package/libx/_runtime/auth/utils.js +22 -1
  80. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
  81. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -2
  82. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
  83. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +13 -0
  84. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
  85. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
  86. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
  87. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
  88. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
  89. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
  90. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
  91. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
  92. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriHelper.js +1 -1
  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/deserializer/ResourceJsonDeserializer.js +5 -6
  95. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
  96. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/UriHelper.js +4 -1
  97. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
  98. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
  99. package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +50 -0
  100. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
  101. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
  102. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
  103. package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
  104. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
  105. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
  106. package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
  107. package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
  108. package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
  109. package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
  110. package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
  111. package/libx/_runtime/cds-services/services/Service.js +40 -0
  112. package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
  113. package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
  114. package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
  115. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
  116. package/libx/_runtime/cds-services/services/utils/restrictions.js +78 -0
  117. package/libx/_runtime/cds-services/util/assert.js +20 -14
  118. package/libx/_runtime/cds.js +9 -1
  119. package/libx/_runtime/common/aspects/any.js +5 -0
  120. package/libx/_runtime/common/aspects/entity.js +25 -7
  121. package/libx/_runtime/common/aspects/utils.js +2 -2
  122. package/libx/_runtime/common/composition/data.js +6 -0
  123. package/libx/_runtime/common/composition/insert.js +3 -2
  124. package/libx/_runtime/common/composition/tree.js +4 -10
  125. package/libx/_runtime/common/composition/update.js +4 -4
  126. package/libx/_runtime/common/constants/draft.js +29 -26
  127. package/libx/_runtime/common/error/constants.js +2 -2
  128. package/libx/_runtime/common/error/frontend.js +7 -15
  129. package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
  130. package/libx/_runtime/common/generic/auth/constants.js +20 -0
  131. package/libx/_runtime/common/generic/auth/expand.js +54 -0
  132. package/libx/_runtime/common/generic/auth/index.js +32 -0
  133. package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
  134. package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
  135. package/libx/_runtime/common/generic/auth/requires.js +34 -0
  136. package/libx/_runtime/common/generic/auth/restrict.js +296 -0
  137. package/libx/_runtime/common/generic/auth/utils.js +213 -0
  138. package/libx/_runtime/common/generic/crud.js +14 -10
  139. package/libx/_runtime/common/generic/etag.js +1 -1
  140. package/libx/_runtime/common/generic/input.js +35 -35
  141. package/libx/_runtime/common/generic/sorting.js +2 -3
  142. package/libx/_runtime/common/generic/temporal.js +2 -2
  143. package/libx/_runtime/common/i18n/index.js +2 -31
  144. package/libx/_runtime/common/i18n/messages.properties +1 -1
  145. package/libx/_runtime/common/toggles/handler.js +21 -0
  146. package/libx/_runtime/common/utils/copy.js +10 -1
  147. package/libx/_runtime/common/utils/cqn2cqn4sql.js +100 -29
  148. package/libx/_runtime/common/utils/csn.js +63 -1
  149. package/libx/_runtime/common/utils/dollar.js +10 -1
  150. package/libx/_runtime/common/utils/draft.js +46 -7
  151. package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
  152. package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
  153. package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
  154. package/libx/_runtime/common/utils/generateOnCond.js +9 -6
  155. package/libx/_runtime/common/utils/quotingStyles.js +2 -0
  156. package/libx/_runtime/common/utils/resolveStructured.js +25 -9
  157. package/libx/_runtime/common/utils/resolveView.js +4 -1
  158. package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
  159. package/libx/_runtime/common/utils/structured.js +33 -37
  160. package/libx/_runtime/common/utils/template.js +17 -8
  161. package/libx/_runtime/common/utils/templateProcessor.js +28 -28
  162. package/libx/_runtime/db/data-conversion/post-processing.js +118 -417
  163. package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
  164. package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
  165. package/libx/_runtime/db/generic/index.js +1 -3
  166. package/libx/_runtime/db/generic/input.js +5 -10
  167. package/libx/_runtime/db/generic/rewrite.js +5 -2
  168. package/libx/_runtime/db/generic/structured.js +2 -2
  169. package/libx/_runtime/db/query/delete.js +2 -2
  170. package/libx/_runtime/db/query/insert.js +1 -1
  171. package/libx/_runtime/db/query/update.js +9 -14
  172. package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
  173. package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
  174. package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
  175. package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
  176. package/libx/_runtime/db/utils/columns.js +3 -3
  177. package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
  178. package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
  179. package/libx/_runtime/extensibility/mps/index.js +5 -0
  180. package/libx/_runtime/extensibility/mps/service.js +111 -0
  181. package/libx/_runtime/extensibility/mps/tar.js +42 -0
  182. package/libx/_runtime/extensibility/mps/utils.js +11 -0
  183. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
  184. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
  185. package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
  186. package/libx/_runtime/extensibility/uiflex/index.js +54 -0
  187. package/libx/_runtime/extensibility/uiflex/service.js +276 -0
  188. package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
  189. package/libx/_runtime/fiori/generic/activate.js +2 -2
  190. package/libx/_runtime/fiori/generic/before.js +4 -4
  191. package/libx/_runtime/fiori/generic/new.js +3 -3
  192. package/libx/_runtime/fiori/generic/patch.js +1 -1
  193. package/libx/_runtime/fiori/generic/read.js +58 -66
  194. package/libx/_runtime/fiori/generic/readOverDraft.js +71 -16
  195. package/libx/_runtime/fiori/utils/handler.js +6 -13
  196. package/libx/_runtime/fiori/utils/where.js +6 -5
  197. package/libx/_runtime/hana/Service.js +4 -10
  198. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +2 -2
  199. package/libx/_runtime/hana/driver.js +2 -2
  200. package/libx/_runtime/hana/execute.js +29 -75
  201. package/libx/_runtime/hana/pool.js +1 -1
  202. package/libx/_runtime/hana/streaming.js +2 -1
  203. package/libx/_runtime/index.js +6 -6
  204. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
  205. package/libx/_runtime/messaging/Outbox.js +2 -2
  206. package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
  207. package/libx/_runtime/messaging/common-utils/connections.js +5 -7
  208. package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
  209. package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
  210. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
  211. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
  212. package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
  213. package/libx/_runtime/messaging/file-based.js +5 -5
  214. package/libx/_runtime/messaging/message-queuing.js +14 -12
  215. package/libx/_runtime/messaging/outbox/utils.js +18 -19
  216. package/libx/_runtime/messaging/redis-messaging.js +91 -0
  217. package/libx/_runtime/messaging/service.js +8 -6
  218. package/libx/_runtime/remote/Service.js +44 -8
  219. package/libx/_runtime/remote/utils/client.js +25 -13
  220. package/libx/_runtime/remote/utils/data.js +11 -11
  221. package/libx/_runtime/sqlite/Service.js +6 -9
  222. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
  223. package/libx/_runtime/types/api.js +10 -2
  224. package/libx/common/utils/ucsn.js +109 -0
  225. package/libx/gql/resolvers/crud/create.js +6 -1
  226. package/libx/gql/resolvers/crud/delete.js +6 -1
  227. package/libx/gql/resolvers/crud/read.js +6 -1
  228. package/libx/gql/resolvers/crud/update.js +9 -1
  229. package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
  230. package/libx/gql/schema/typeDefMap.js +2 -2
  231. package/libx/odata/afterburner.js +110 -16
  232. package/libx/odata/grammar.pegjs +9 -1
  233. package/libx/odata/parseToCqn.js +39 -0
  234. package/libx/odata/parser.js +1 -1
  235. package/libx/rest/RestAdapter.js +9 -1
  236. package/libx/rest/middleware/input.js +54 -0
  237. package/libx/rest/middleware/operation.js +14 -1
  238. package/libx/rest/middleware/parse.js +11 -7
  239. package/package.json +1 -1
  240. package/server.js +34 -19
  241. package/srv/audit-log.cds +2 -2
  242. package/srv/flex.cds +8 -2
  243. package/srv/flex.js +1 -1
  244. package/srv/mps.cds +23 -0
  245. package/srv/mps.js +1 -0
  246. package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
  247. package/libx/_runtime/common/generic/auth.js +0 -874
  248. package/libx/_runtime/common/toggles/alpha.js +0 -43
  249. package/libx/_runtime/db/generic/arrayed.js +0 -33
  250. package/libx/_runtime/fiori/uiflex/index.js +0 -35
  251. package/libx/_runtime/fiori/uiflex/service.js +0 -150
  252. package/libx/rest/utils/data.js +0 -60
@@ -1,30 +1,21 @@
1
1
  const resolveStructured = require('./resolveStructured')
2
2
  const { ensureNoDraftsSuffix } = require('../../common/utils/draft')
3
+ const { traverseFroms } = require('../../common/utils/entityFromCqn')
3
4
  // TODO move to commons as also used in cqn2cqn4sql
4
- const OPERATIONS = ['=', '>', '<', '!=', '<>', '>=', '<=', 'like', 'between', 'in', 'not in']
5
-
6
- const _getEntityNames = from => {
7
- if (from.ref) {
8
- return [ensureNoDraftsSuffix(from.ref[0])]
9
- }
10
-
11
- if (from.SET) {
12
- return Array.from(
13
- from.SET.args.reduce((set, elem) => {
14
- for (const entityName of _getEntityNames(elem.SELECT.from)) {
15
- set.add(entityName)
16
- }
17
- return set
18
- }, new Set())
19
- )
20
- }
21
-
22
- if (Array.isArray(from.args)) {
23
- // TODO this only considers first level refs and not from sub selects
24
- return from.args.filter(arg => arg.ref).map(arg => ensureNoDraftsSuffix(arg.ref[0]))
25
- }
26
-
27
- return []
5
+ const OPERATIONS_MAP = ['=', '>', '<', '!=', '<>', '>=', '<=', 'like', 'between', 'in', 'not in'].reduce((acc, cur) => {
6
+ acc[cur] = 1
7
+ return acc
8
+ }, {})
9
+
10
+ const _getEntityNamesAndIds = from => {
11
+ const nameAndIds = []
12
+ traverseFroms(from, from => {
13
+ const nameAndId = { name: ensureNoDraftsSuffix(from.ref[0].id || from.ref[0]), id: from.as || from.ref[0] }
14
+ if (nameAndIds.some(x => x.name === nameAndId.name)) return // no duplicates
15
+ nameAndIds.push(nameAndId)
16
+ })
17
+
18
+ return nameAndIds
28
19
  }
29
20
 
30
21
  const _flattenStructuredInExpand = (column, { _target: expandedEntity }) => {
@@ -117,8 +108,8 @@ const _filterForStructProperty = (structElement, structData, op, prefix = '', na
117
108
  )
118
109
  } else {
119
110
  if (element.isAssociation) continue
120
- if (element['@odata.foreignKey4']) {
121
- const assocName = element['@odata.foreignKey4']
111
+ const assocName = element._foreignKey4
112
+ if (assocName) {
122
113
  const assoc = structElement.elements[assocName]
123
114
  if (assoc.is2one && !assoc.on) {
124
115
  for (const key in assoc._target.keys) {
@@ -214,7 +205,7 @@ const flattenStructuredWhereHaving = (filterArray, csnEntity, model) => {
214
205
 
215
206
  const newFilterArray = []
216
207
  for (let i = 0; i < filterArray.length; i++) {
217
- if (OPERATIONS.includes(filterArray[i + 1])) {
208
+ if (filterArray[i + 1] in OPERATIONS_MAP) {
218
209
  const refElement = filterArray[i].ref ? filterArray[i] : filterArray[i + 2]
219
210
 
220
211
  // copy for processing
@@ -254,42 +245,47 @@ const getNavigationIfStruct = (entity, ref) => {
254
245
  return element
255
246
  }
256
247
 
257
- const _flattenColumns = (SELECT, flattenedElements, toBeDeleted, csnEntity) => {
248
+ const _flattenColumns = (SELECT, flattenedElements, toBeDeleted, csnEntity, tableId) => {
258
249
  for (const column of SELECT.columns) {
259
250
  if (!column.ref) continue
260
251
 
261
- // TODO aliases are not working right now
262
- const structName = column.ref[0]
252
+ // might begin with table id
253
+ const cleanedUpRef = column.ref.length > 1 && column.ref[0] === tableId ? column.ref.slice(1) : column.ref
254
+ const structName = cleanedUpRef[0]
263
255
 
264
256
  const element = csnEntity.elements[structName]
265
257
  if (!element) continue
266
258
 
267
259
  if (column.expand) {
268
- _flattenStructuredInExpand(column, getNavigationIfStruct(csnEntity, column.ref))
260
+ _flattenStructuredInExpand(column, getNavigationIfStruct(csnEntity, cleanedUpRef))
269
261
  continue
270
262
  }
271
263
 
272
264
  if (element._isStructured) {
273
265
  toBeDeleted.push(structName) // works with aliases?
274
266
  flattenedElements.push(
275
- ...resolveStructured({ structName, structProperties: column.ref.slice(1) }, element.elements)
267
+ ...resolveStructured({ structName, structProperties: cleanedUpRef.slice(1) }, element.elements)
276
268
  )
277
269
  }
270
+ if (cleanedUpRef.length < column.ref.length) {
271
+ flattenedElements.forEach(e => e.ref.unshift(tableId))
272
+ }
278
273
  }
279
274
  }
280
275
 
281
276
  const flattenStructuredSelect = ({ SELECT }, model) => {
282
- const entityNames = _getEntityNames(SELECT.from) // TODO consider alias for custom CQNs?
277
+ const entityNamesAndIds = _getEntityNamesAndIds(SELECT.from)
283
278
 
284
- for (const entityName of entityNames) {
285
- const entity = model.definitions[entityName]
279
+ for (const entityNameAndId of entityNamesAndIds) {
280
+ const entity = model.definitions[entityNameAndId.name]
281
+ const tableId = entityNameAndId.id
286
282
 
287
283
  if (Array.isArray(SELECT.columns) && SELECT.columns.length > 0) {
288
284
  const flattenedElements = []
289
285
  const toBeDeleted = []
290
- _flattenColumns(SELECT, flattenedElements, toBeDeleted, entity)
286
+ _flattenColumns(SELECT, flattenedElements, toBeDeleted, entity, tableId)
291
287
  SELECT.columns = SELECT.columns.filter(column => {
292
- const columnName = column.ref ? column.ref[0] : column.as
288
+ const columnName = column.ref ? (column.ref[0] === tableId ? column.ref[1] : column.ref[0]) : column.as
293
289
  return (columnName && !toBeDeleted.includes(columnName)) || column.func || column.expand || 'val' in column
294
290
  })
295
291
  if (flattenedElements.length) SELECT.columns.push(...flattenedElements)
@@ -73,15 +73,25 @@ const _getNextTarget = (model, element, currentPath = []) => {
73
73
  * @param {array} [targetPath=[]]
74
74
  */
75
75
  function _getTemplate(model, cache, targetEntity, callbacks, parent = null, _entityMap = new Map(), targetPath = []) {
76
- const { pick, ignore } = callbacks
76
+ const { pick, ignore, flatAccess } = callbacks
77
77
  const templateElements = new Map()
78
78
  const template = { target: targetEntity, elements: templateElements }
79
79
  const currentPath = [...targetPath, targetEntity.name]
80
80
  _entityMap.set(currentPath.join(DELIMITER), { template })
81
- if (!targetEntity.elements) return template
81
+ const elements = targetEntity.elements || targetEntity.params
82
+ if (!elements) return template
83
+
84
+ if (flatAccess) {
85
+ if (targetEntity._flat2struct) {
86
+ for (const elementName in targetEntity._flat2struct) {
87
+ const element = targetEntity._flat2struct[elementName]
88
+ _pick(pick, element, targetEntity, parent, templateElements, elementName)
89
+ }
90
+ }
91
+ }
82
92
 
83
- for (const elementName in targetEntity.elements) {
84
- const element = targetEntity.elements[elementName]
93
+ for (const elementName in elements) {
94
+ const element = elements[elementName]
85
95
  if (ignore && ignore(element, targetEntity, parent)) continue
86
96
 
87
97
  _pick(pick, element, targetEntity, parent, templateElements, elementName)
@@ -99,12 +109,11 @@ function _getTemplate(model, cache, targetEntity, callbacks, parent = null, _ent
99
109
  // For associations and _typed_ structured elements, there's a (cacheable) target,
100
110
  // inline structures must be handled separately.
101
111
  const subTemplate = _isInlineStructured(element)
102
- ? _getTemplate(model, cache, nextTarget, { pick, ignore }, targetEntity, _entityMap, currentPath)
103
- : cache.for(nextTarget, getTemplate(model, { pick, ignore }, targetEntity, _entityMap))
112
+ ? _getTemplate(model, cache, nextTarget, callbacks, targetEntity, _entityMap, currentPath)
113
+ : cache.for(nextTarget, getTemplate(model, callbacks, targetEntity, _entityMap))
104
114
  _addSubTemplate(templateElements, elementName, subTemplate)
105
115
  }
106
116
  }
107
-
108
117
  return template
109
118
  }
110
119
 
@@ -131,7 +140,7 @@ module.exports = (usecase, tx, target, ...args) => {
131
140
  // double-check with get target from model
132
141
  // since target might come from anywhere like via cqn etc
133
142
  if (!target) return
134
- const root = (model && model.definitions[target.name]) || (target.elements && target)
143
+ const root = (model && model.definitions[target.name]) || target
135
144
  if (!root) return
136
145
 
137
146
  // tx could be the service itself
@@ -6,54 +6,57 @@ const _formatRowContext = (tKey, keyNames, row) => {
6
6
  return `${tKey}(${keyValuePairsSerialized})`
7
7
  }
8
8
 
9
- const _processElement = (processFn, row, key, elements, picked = {}, complex = false, isRoot, pathOptions) => {
10
- const { segments: pathSegments } = pathOptions
11
- const element = elements[key]
9
+ const _processElement = (processFn, row, key, target, picked = {}, isRoot, path) => {
10
+ const element = (target.elements || target.params)[key]
12
11
  const { plain } = picked
13
12
 
14
- if (plain) {
15
- if (!complex && pathSegments) pathSegments.push(key)
16
-
17
- /**
18
- * @type import('../../types/api').templateProcessorProcessFnArgs
19
- */
20
- const elementInfo = { row, key, element, plain, isRoot, pathSegments }
21
- processFn(elementInfo)
13
+ if (!plain) return
14
+ /**
15
+ * @type import('../../types/api').templateProcessorProcessFnArgs
16
+ */
17
+ const elementInfo = { row, key, element, target, plain, isRoot, path }
18
+ if (!element && target._flat2struct && target._flat2struct[key]) {
19
+ elementInfo.path = path.slice(0)
20
+ elementInfo.path.push(...target._flat2struct[key].map(key => ({ key })))
22
21
  }
22
+ processFn(elementInfo)
23
23
  }
24
24
 
25
25
  const _processRow = (processFn, row, template, tKey, tValue, isRoot, pathOptions) => {
26
26
  const { template: subTemplate, picked } = tValue
27
27
  const key = tKey.split(DELIMITER).pop()
28
28
 
29
- _processElement(processFn, row, key, template.target.elements, picked, !!subTemplate, isRoot, pathOptions)
29
+ _processElement(processFn, row, key, template.target, picked, isRoot, pathOptions.path)
30
30
 
31
31
  // process deep
32
- if (subTemplate) {
33
- let subRows = row && row[key]
34
- subRows = Array.isArray(subRows) ? subRows : [subRows]
35
- _processComplex(processFn, subRows, subTemplate, key, pathOptions)
32
+ if (subTemplate && typeof row === 'object' && row) {
33
+ _processComplex(processFn, row, subTemplate, key, pathOptions)
36
34
  }
37
35
  }
38
36
 
39
- const _processComplex = (processFn, rows, template, tKey, pathOptions) => {
37
+ const _processComplex = (processFn, row, template, key, pathOptions) => {
38
+ const value = row && row[key]
39
+ const _is2many = Array.isArray(value)
40
+ const rows = _is2many ? value : [value]
40
41
  if (rows.length === 0) return
42
+ const keyNames = (template.target.keys && Object.keys(template.target.keys)) || []
41
43
 
42
- const segments = pathOptions.segments
43
- let keyNames
44
-
45
- for (const row of rows) {
44
+ for (let idx = 0; idx < rows.length; idx++) {
45
+ const row = rows[idx]
46
46
  if (row == null) continue
47
47
  const args = { processFn, row, template, isRoot: false, pathOptions }
48
48
 
49
+ let url
49
50
  if (pathOptions.includeKeyValues) {
50
- keyNames = keyNames || (template.target.keys && Object.keys(template.target.keys)) || []
51
- pathOptions.rowKeysGenerator(keyNames, row, template)
52
- const pathSegment = _formatRowContext(tKey, keyNames, { ...row, ...pathOptions.extraKeys })
53
- args.pathOptions.segments = segments ? [...segments, pathSegment] : [pathSegment]
51
+ if (pathOptions.rowKeysGenerator) pathOptions.rowKeysGenerator(keyNames, row, template)
52
+ url = _formatRowContext(key, keyNames, Object.assign({}, row, pathOptions.extraKeys))
54
53
  }
55
54
 
55
+ if (pathOptions.path) pathOptions.path.push({ key, idx: _is2many && idx, url })
56
+
56
57
  templateProcessor(args)
58
+
59
+ if (pathOptions.path) pathOptions.path.pop()
57
60
  }
58
61
  }
59
62
 
@@ -61,10 +64,7 @@ const _processComplex = (processFn, rows, template, tKey, pathOptions) => {
61
64
  * @param {import("../../types/api").TemplateProcessor} args
62
65
  */
63
66
  const templateProcessor = ({ processFn, row, template, isRoot = true, pathOptions = {} }) => {
64
- const segments = pathOptions.segments && [...pathOptions.segments]
65
-
66
67
  for (const [tKey, tValue] of template.elements) {
67
- if (segments) pathOptions.segments = [...segments]
68
68
  _processRow(processFn, row, template, tKey, tValue, isRoot, pathOptions)
69
69
  }
70
70
  }