@sap/cds 5.4.3 → 5.5.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 (228) hide show
  1. package/CHANGELOG.md +239 -2
  2. package/apis/ql.d.ts +17 -15
  3. package/app/index.js +1 -1
  4. package/bin/build/buildTaskEngine.js +26 -42
  5. package/bin/build/buildTaskFactory.js +6 -10
  6. package/bin/build/buildTaskHandler.js +2 -4
  7. package/bin/build/buildTaskProvider.js +3 -1
  8. package/bin/build/buildTaskProviderFactory.js +9 -15
  9. package/bin/build/constants.js +15 -3
  10. package/bin/build/index.js +5 -4
  11. package/bin/build/mtaUtil.js +8 -11
  12. package/bin/build/provider/buildTaskHandlerEdmx.js +63 -6
  13. package/bin/build/provider/buildTaskHandlerInternal.js +2 -34
  14. package/bin/build/provider/buildTaskProviderInternal.js +16 -42
  15. package/bin/build/provider/fiori/index.js +13 -24
  16. package/bin/build/provider/hana/2migration.js +17 -15
  17. package/bin/build/provider/hana/2tabledata.js +52 -48
  18. package/bin/build/provider/hana/index.js +27 -25
  19. package/bin/build/provider/hana/migrationtable.js +91 -67
  20. package/bin/build/provider/java-cf/index.js +14 -24
  21. package/bin/build/provider/mtx/index.js +12 -14
  22. package/bin/build/provider/node-cf/index.js +18 -32
  23. package/bin/cds.js +5 -5
  24. package/bin/serve.js +29 -23
  25. package/bin/version.js +0 -1
  26. package/lib/compile/etc/_localized.js +4 -9
  27. package/lib/compile/for/sql.js +5 -2
  28. package/lib/compile/parse.js +25 -17
  29. package/lib/compile/to/srvinfo.js +2 -1
  30. package/lib/connect/bindings.js +2 -1
  31. package/lib/connect/index.js +48 -49
  32. package/lib/core/classes.js +1 -1
  33. package/lib/core/reflect.js +10 -2
  34. package/lib/deploy.js +26 -23
  35. package/lib/env/defaults.js +13 -6
  36. package/lib/env/index.js +73 -78
  37. package/lib/env/requires.js +38 -19
  38. package/lib/index.js +9 -10
  39. package/lib/lazy.js +2 -2
  40. package/lib/log/index.js +33 -45
  41. package/lib/log/service/index.js +2 -2
  42. package/lib/ql/CREATE.js +14 -9
  43. package/lib/ql/DELETE.js +6 -5
  44. package/lib/ql/DROP.js +12 -9
  45. package/lib/ql/INSERT.js +40 -16
  46. package/lib/ql/Query.js +67 -40
  47. package/lib/ql/SELECT.js +162 -127
  48. package/lib/ql/UPDATE.js +74 -42
  49. package/lib/ql/Whereable.js +77 -87
  50. package/lib/ql/index.js +36 -24
  51. package/lib/ql/parse.js +35 -0
  52. package/lib/req/context.js +44 -8
  53. package/lib/req/locale.js +7 -7
  54. package/lib/serve/Service-api.js +21 -14
  55. package/lib/serve/Service-dispatch.js +28 -12
  56. package/lib/serve/Transaction.js +22 -10
  57. package/lib/serve/index.js +16 -11
  58. package/lib/utils/axios.js +23 -16
  59. package/lib/utils/data.js +35 -0
  60. package/lib/utils/tests.js +27 -18
  61. package/libx/_runtime/audit/generic/personal/access.js +81 -0
  62. package/libx/_runtime/audit/generic/personal/constants.js +4 -0
  63. package/libx/_runtime/audit/generic/personal/index.js +50 -0
  64. package/libx/_runtime/audit/generic/personal/modification.js +138 -0
  65. package/libx/_runtime/audit/generic/personal/utils.js +186 -0
  66. package/libx/_runtime/audit/utils/v2.js +10 -4
  67. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +5 -5
  68. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +6 -7
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +5 -7
  70. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +5 -7
  71. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +2 -3
  72. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +4 -0
  73. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +7 -4
  74. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +59 -8
  75. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +11 -1
  76. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +6 -10
  77. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +3 -46
  78. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +2 -5
  79. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/createToCQN.js +2 -2
  80. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +4 -3
  81. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -2
  82. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
  83. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +1 -1
  84. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +2 -2
  85. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +16 -18
  86. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/EdmEntityType.js +6 -3
  87. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/format/RepresentationKind.js +4 -1
  88. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/core/OdataRequest.js +1 -0
  89. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/SerializerFactory.js +15 -2
  90. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/OperationValidator.js +1 -0
  91. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
  92. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +8 -1
  93. package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +6 -1
  94. package/libx/_runtime/cds-services/adapter/odata-v4/utils/omitValues.js +12 -5
  95. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -7
  96. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +7 -7
  97. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +14 -18
  98. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +13 -13
  99. package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +0 -1
  100. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +2 -1
  101. package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +2 -2
  102. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -4
  103. package/libx/_runtime/cds-services/adapter/rest/utils/result.js +4 -2
  104. package/libx/_runtime/cds-services/services/Service.js +40 -5
  105. package/libx/_runtime/cds-services/services/utils/columns.js +13 -7
  106. package/libx/_runtime/cds-services/services/utils/compareJson.js +88 -4
  107. package/libx/_runtime/cds-services/services/utils/differ.js +24 -6
  108. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -2
  109. package/libx/_runtime/common/composition/data.js +66 -63
  110. package/libx/_runtime/common/composition/delete.js +97 -71
  111. package/libx/_runtime/common/composition/index.js +2 -1
  112. package/libx/_runtime/common/composition/insert.js +34 -11
  113. package/libx/_runtime/common/composition/tree.js +119 -92
  114. package/libx/_runtime/common/composition/update.js +12 -1
  115. package/libx/_runtime/common/composition/utils.js +1 -3
  116. package/libx/_runtime/common/constants/draft.js +12 -1
  117. package/libx/_runtime/common/generic/auth.js +53 -31
  118. package/libx/_runtime/common/generic/crud.js +14 -13
  119. package/libx/_runtime/common/generic/input.js +23 -26
  120. package/libx/_runtime/common/generic/put.js +1 -1
  121. package/libx/_runtime/common/generic/sorting.js +16 -16
  122. package/libx/_runtime/common/i18n/index.js +1 -1
  123. package/libx/_runtime/common/i18n/messages.properties +4 -0
  124. package/libx/_runtime/common/utils/backlinks.js +12 -5
  125. package/libx/_runtime/common/utils/cqn.js +6 -1
  126. package/libx/_runtime/common/utils/cqn2cqn4sql.js +123 -108
  127. package/libx/_runtime/common/utils/csn.js +56 -4
  128. package/libx/_runtime/common/utils/data.js +0 -37
  129. package/libx/_runtime/common/utils/enrichWithKeysFromWhere.js +1 -1
  130. package/libx/_runtime/common/utils/entityFromCqn.js +7 -24
  131. package/libx/_runtime/common/utils/foreignKeyPropagations.js +39 -7
  132. package/libx/_runtime/common/utils/generateOnCond.js +11 -12
  133. package/libx/_runtime/common/utils/onlyKeysRemain.js +10 -0
  134. package/libx/_runtime/common/utils/path.js +35 -0
  135. package/libx/_runtime/common/utils/postProcessing.js +86 -0
  136. package/libx/_runtime/common/utils/quotingStyles.js +37 -26
  137. package/libx/_runtime/common/utils/resolveView.js +227 -173
  138. package/libx/_runtime/common/utils/rewriteAsterisk.js +46 -26
  139. package/libx/_runtime/common/utils/structured.js +13 -13
  140. package/libx/_runtime/common/utils/template.js +10 -5
  141. package/libx/_runtime/common/utils/templateDelimiter.js +1 -0
  142. package/libx/_runtime/common/utils/templateProcessor.js +28 -72
  143. package/libx/_runtime/common/utils/union.js +31 -0
  144. package/libx/_runtime/common/utils/unionCqnTemplate.js +184 -0
  145. package/libx/_runtime/db/Service.js +1 -1
  146. package/libx/_runtime/db/data-conversion/timestamp.js +2 -9
  147. package/libx/_runtime/db/expand/expandCQNToJoin.js +204 -297
  148. package/libx/_runtime/db/expand/index.js +3 -3
  149. package/libx/_runtime/db/expand/rawToExpanded.js +36 -7
  150. package/libx/_runtime/db/generic/index.js +1 -1
  151. package/libx/_runtime/db/generic/input.js +5 -7
  152. package/libx/_runtime/db/generic/integrity.js +1 -1
  153. package/libx/_runtime/db/generic/rewrite.js +2 -10
  154. package/libx/_runtime/db/generic/update.js +13 -5
  155. package/libx/_runtime/db/generic/virtual.js +22 -58
  156. package/libx/_runtime/db/query/delete.js +7 -4
  157. package/libx/_runtime/db/query/insert.js +6 -4
  158. package/libx/_runtime/db/query/read.js +21 -8
  159. package/libx/_runtime/db/query/run.js +4 -1
  160. package/libx/_runtime/db/query/update.js +5 -4
  161. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +35 -2
  162. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +17 -2
  163. package/libx/_runtime/db/sql-builder/InsertBuilder.js +6 -5
  164. package/libx/_runtime/db/sql-builder/ReferenceBuilder.js +10 -0
  165. package/libx/_runtime/db/sql-builder/SelectBuilder.js +35 -24
  166. package/libx/_runtime/db/sql-builder/UpdateBuilder.js +14 -4
  167. package/libx/_runtime/db/sql-builder/arrayed.js +4 -0
  168. package/libx/_runtime/db/utils/deep.js +8 -0
  169. package/libx/_runtime/db/utils/generateAliases.js +2 -1
  170. package/libx/_runtime/fiori/generic/activate.js +19 -15
  171. package/libx/_runtime/fiori/generic/before.js +3 -11
  172. package/libx/_runtime/fiori/generic/cancel.js +1 -1
  173. package/libx/_runtime/fiori/generic/delete.js +3 -1
  174. package/libx/_runtime/fiori/generic/edit.js +12 -2
  175. package/libx/_runtime/fiori/generic/new.js +5 -5
  176. package/libx/_runtime/fiori/generic/patch.js +0 -18
  177. package/libx/_runtime/fiori/generic/read.js +261 -205
  178. package/libx/_runtime/fiori/utils/delete.js +36 -7
  179. package/libx/_runtime/fiori/utils/handler.js +43 -44
  180. package/libx/_runtime/fiori/utils/where.js +30 -15
  181. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +4 -6
  182. package/libx/_runtime/hana/execute.js +3 -3
  183. package/libx/_runtime/hana/localized.js +4 -4
  184. package/libx/_runtime/hana/pool.js +29 -14
  185. package/libx/_runtime/hana/search2cqn4sql.js +2 -1
  186. package/libx/_runtime/hana/searchToContains.js +18 -14
  187. package/libx/_runtime/index.js +0 -5
  188. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +13 -5
  189. package/libx/_runtime/messaging/common-utils/naming-conventions.js +4 -1
  190. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +31 -19
  191. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -2
  192. package/libx/_runtime/messaging/enterprise-messaging.js +6 -4
  193. package/libx/_runtime/messaging/service.js +7 -6
  194. package/libx/_runtime/odata/cqn2odata.js +110 -43
  195. package/libx/_runtime/odata/index.js +26 -48
  196. package/libx/_runtime/odata/odata2cqn.js +1 -6154
  197. package/libx/_runtime/odata/odata2cqn.pegjs +559 -0
  198. package/libx/_runtime/odata/readToCqn.js +94 -64
  199. package/libx/_runtime/remote/Service.js +74 -21
  200. package/libx/_runtime/remote/cqn2odata/index.js +1 -5
  201. package/libx/_runtime/remote/utils/client.js +24 -101
  202. package/libx/_runtime/remote/utils/dataConversion.js +27 -12
  203. package/libx/_runtime/sqlite/Service.js +3 -5
  204. package/libx/_runtime/sqlite/execute.js +33 -27
  205. package/libx/_runtime/sqlite/localized.js +12 -7
  206. package/libx/_runtime/types/api.js +10 -0
  207. package/package.json +2 -2
  208. package/server.js +16 -2
  209. package/lib/ql/grammar.pegjs +0 -208
  210. package/lib/ql/parser.js +0 -1
  211. package/lib/ql/rt/DELETE.js +0 -29
  212. package/lib/ql/rt/INSERT.js +0 -23
  213. package/lib/ql/rt/Query.js +0 -84
  214. package/lib/ql/rt/SELECT.js +0 -174
  215. package/lib/ql/rt/UPDATE.js +0 -119
  216. package/lib/ql/rt/_helpers.js +0 -91
  217. package/lib/ql/rt/index.js +0 -32
  218. package/libx/_runtime/audit/generic/personal.js +0 -260
  219. package/libx/_runtime/cds-services/statements/BaseStatement.js +0 -72
  220. package/libx/_runtime/cds-services/statements/Create.js +0 -57
  221. package/libx/_runtime/cds-services/statements/Delete.js +0 -33
  222. package/libx/_runtime/cds-services/statements/Drop.js +0 -42
  223. package/libx/_runtime/cds-services/statements/Insert.js +0 -201
  224. package/libx/_runtime/cds-services/statements/Select.js +0 -826
  225. package/libx/_runtime/cds-services/statements/Update.js +0 -181
  226. package/libx/_runtime/cds-services/statements/Where.js +0 -726
  227. package/libx/_runtime/cds-services/statements/index.js +0 -25
  228. package/libx/_runtime/common/generic/resolve-mock.js +0 -9
@@ -1,6 +1,7 @@
1
1
  const { getNavigationIfStruct } = require('./structured')
2
2
  const getColumns = require('../../db/utils/columns')
3
3
  const _isAsteriskCol = col => col === '*' || (col.ref && col.ref[0] === '*')
4
+ const cds = require('../../../../libx/_runtime/cds')
4
5
 
5
6
  const _isDraft = req => {
6
7
  return (
@@ -10,43 +11,62 @@ const _isDraft = req => {
10
11
  )
11
12
  }
12
13
 
13
- const _rewriteExpandAsterisks = (expand, entity, refs) => {
14
+ const _getColumns = target => {
15
+ const columns = getColumns(target)
16
+ return columns.map(col => ({ ref: [col.name] }))
17
+ }
18
+
19
+ const _rewriteExpandAsterisks = (column, entity, refs) => {
14
20
  const navigation = getNavigationIfStruct(entity, refs)
15
- const targetEntity = navigation._target
21
+ const targetEntity = navigation && navigation._target
16
22
 
17
- expand.forEach(col => {
18
- if (col.ref && col.expand) {
19
- _rewriteExpandAsterisks(col.expand, targetEntity, col.ref)
20
- }
21
- })
23
+ if (Array.isArray(column.expand) && targetEntity) {
24
+ column.expand.forEach(col => {
25
+ if (col.ref && col.expand) {
26
+ _rewriteExpandAsterisks(col, targetEntity, col.ref)
27
+ }
28
+ })
29
+ }
30
+
31
+ if (column.expand === '*') {
32
+ column.expand = _getColumns(targetEntity)
33
+ return
34
+ }
22
35
 
23
- const asteriskColumnIndex = expand.findIndex(col => _isAsteriskCol(col))
36
+ if (Array.isArray(column.expand)) {
37
+ const asteriskColumnIndex = column.expand.findIndex(col => _isAsteriskCol(col))
38
+ if (asteriskColumnIndex === -1) return // * not found
24
39
 
25
- if (asteriskColumnIndex !== -1) {
26
- expand.splice(asteriskColumnIndex, 1)
27
- getColumns(targetEntity).forEach(col => {
28
- expand.push({ ref: [col.name] })
40
+ column.expand.splice(asteriskColumnIndex, 1)
41
+ const columns = getColumns(targetEntity)
42
+ columns.forEach(col => {
43
+ column.expand.push({ ref: [col.name] })
29
44
  })
30
45
  }
31
46
  }
32
47
 
33
48
  const _rewriteAsterisks = req => {
34
- if (!_isDraft(req) && req.query.SELECT && req.query.SELECT.columns) {
35
- const asteriskColumnIndex = req.query.SELECT.columns.findIndex(col => _isAsteriskCol(col))
36
-
37
- if (asteriskColumnIndex !== -1) {
38
- req.query.SELECT.columns.splice(asteriskColumnIndex, 1)
39
- getColumns(req.target).forEach(col => {
40
- req.query.SELECT.columns.push({ ref: [col.name] })
41
- })
42
- }
49
+ if (_isDraft(req) || !req.query.SELECT) return
43
50
 
44
- req.query.SELECT.columns.forEach(col => {
45
- if (col.ref && col.ref[0] !== 'DraftAdministrativeData' && col.expand && req.target) {
46
- _rewriteExpandAsterisks(col.expand, req.target, col.ref)
47
- }
48
- })
51
+ if (cds.env.features.odata_new_parser && !req.query.SELECT.columns) {
52
+ req.query.SELECT.columns = _getColumns(req.target)
53
+ return
49
54
  }
55
+
56
+ if (!req.query.SELECT.columns) return
57
+ const columnsIncludesAsterisk = req.query.SELECT.columns.some(col => _isAsteriskCol(col))
58
+
59
+ if (columnsIncludesAsterisk) {
60
+ const expandColumns = req.query.SELECT.columns.filter(column => column.expand)
61
+ const columns = _getColumns(req.target)
62
+ req.query.SELECT.columns = expandColumns.length ? [...columns, ...expandColumns] : columns
63
+ }
64
+
65
+ req.query.SELECT.columns.forEach(col => {
66
+ if (col.ref && col.ref[0] !== 'DraftAdministrativeData' && col.expand && req.target) {
67
+ _rewriteExpandAsterisks(col, req.target, col.ref)
68
+ }
69
+ })
50
70
  }
51
71
 
52
72
  module.exports = _rewriteAsterisks
@@ -36,6 +36,7 @@ const _flattenStructuredInExpand = (column, { _target: expandedEntity }) => {
36
36
  continue
37
37
  }
38
38
 
39
+ if (!expandElement.ref) continue
39
40
  const propertyName = expandElement.ref[expandElement.ref.length - 1]
40
41
  const element = expandedEntity.elements[expandElement.ref[0]] // TODO alias
41
42
  if (!element) continue
@@ -56,7 +57,7 @@ const _flattenStructuredInExpand = (column, { _target: expandedEntity }) => {
56
57
  column.orderBy = orderBy
57
58
  }
58
59
  column.where = flattenStructuredWhereHaving(column.where, expandedEntity)
59
- column.expand = column.expand.filter(e => !toBeDeleted.includes(e.ref[e.ref.length - 1]))
60
+ column.expand = column.expand.filter(e => !e.ref || !toBeDeleted.includes(e.ref[e.ref.length - 1]))
60
61
  column.expand.push(...flattenedElements)
61
62
  }
62
63
 
@@ -175,7 +176,7 @@ const _transformStructToFlatWhereHaving = ([first, op, second], resArray, struct
175
176
  } catch (e) {
176
177
  /* since val === string */
177
178
  }
178
- if (flattenedElement && structData === val) {
179
+ if (flattenedElement && (structData === val || `${structData}` === val)) {
179
180
  flattenedElement.ref.unshift(...nav)
180
181
  resArray.push(flattenedElement, op, { val })
181
182
  } else {
@@ -208,10 +209,16 @@ const flattenStructuredWhereHaving = (filterArray, csnEntity, model) => {
208
209
  for (let i = 0; i < filterArray.length; i++) {
209
210
  if (OPERATIONS.includes(filterArray[i + 1])) {
210
211
  const refElement = filterArray[i].ref ? filterArray[i] : filterArray[i + 2]
211
- const { element, idx } = _structFromRef(refElement.ref, csnEntity, model)
212
+ // copy for processing
213
+ const ref = refElement.ref && refElement.ref.map(ele => ele)
214
+ // is ref[0] an alias? -> remove
215
+ const isAliased = ref && ref.length > 1 && !csnEntity.elements[ref[0]]
216
+ if (isAliased) ref.shift()
217
+ const { element, idx } = _structFromRef(ref, csnEntity, model)
212
218
  // REVISIT: We cannot make the simple distinction between ref and others
213
219
  // for xpr, subselect, we need to call this method recursively
214
220
  if (element) {
221
+ if (isAliased) refElement.ref.shift()
215
222
  // REVISIT: This does not support operator like "between", "in" or a different order of elements like val,op,ref or expressions like ref,op,val+val
216
223
  _transformStructToFlatWhereHaving(filterArray.slice(i, i + 3), newFilterArray, element, idx)
217
224
  i += 2 // skip next two entries e.g. ('=', '{struct:{int:1}}')
@@ -228,17 +235,10 @@ const _entityFromRef = ref => {
228
235
  if (ref) return ref[0].id || ref[0]
229
236
  }
230
237
  const getNavigationIfStruct = (entity, ref) => {
231
- const element = entity.elements[_entityFromRef(ref)]
232
-
238
+ const element = entity && entity.elements && entity.elements[_entityFromRef(ref)]
233
239
  if (!element) return
234
-
235
- if (element._target) return element
236
-
237
- if (ref.length > 1) {
238
- return getNavigationIfStruct(element, ref.slice(1))
239
- }
240
-
241
- return element.elements[_entityFromRef(ref)]
240
+ if (ref.length > 1) return getNavigationIfStruct(element._target || element, ref.slice(1))
241
+ return element
242
242
  }
243
243
 
244
244
  const _flattenColumns = (SELECT, flattenedElements, toBeDeleted, csnEntity) => {
@@ -1,3 +1,5 @@
1
+ const DELIMITER = require('./templateDelimiter')
2
+
1
3
  const _addSubTemplate = (templateElements, elementName, subTemplate) => {
2
4
  if (subTemplate.elements.size > 0) {
3
5
  const t = templateElements.get(elementName)
@@ -48,7 +50,7 @@ const _getNextTarget = (model, element, currentPath = []) => {
48
50
  }
49
51
  } else if (_isInlineStructured(element)) {
50
52
  return {
51
- nextTargetName: [...currentPath, element.name].join(' '),
53
+ nextTargetName: [...currentPath, element.name].join(DELIMITER),
52
54
  nextTarget: element.items || element
53
55
  }
54
56
  }
@@ -73,7 +75,7 @@ function _getTemplate(model, cache, target, { pick, ignore }, parent = null, ent
73
75
  const templateElements = new Map()
74
76
  const template = { target, elements: templateElements }
75
77
  const currentPath = [...targetPath, target.name]
76
- entityMap.set(currentPath.join(' '), { template })
78
+ entityMap.set(currentPath.join(DELIMITER), { template })
77
79
 
78
80
  if (target.elements) {
79
81
  for (const elementName in target.elements) {
@@ -82,7 +84,7 @@ function _getTemplate(model, cache, target, { pick, ignore }, parent = null, ent
82
84
 
83
85
  _pick(pick, element, target, parent, templateElements, elementName)
84
86
  if (element.items) {
85
- _pick(pick, element.items, target, parent, templateElements, ['_itemsOf', elementName].join(' '))
87
+ _pick(pick, element.items, target, parent, templateElements, ['_itemsOf', elementName].join(DELIMITER))
86
88
  }
87
89
 
88
90
  const { nextTargetName, nextTarget } = _getNextTarget(model, element, currentPath)
@@ -121,16 +123,19 @@ const getCache = (anything, cache, newCacheFn) => {
121
123
  module.exports = (usecase, tx, target, ...args) => {
122
124
  // get model first as it may be added to tx (cf. "_ensureModel")
123
125
  const model = tx.model
126
+ if (!model) return
124
127
  // double-check with get target from model
125
128
  // since target might come from anywhere like via cqn etc
126
- const root = target && model && model.definitions[target.name]
129
+ if (!target) return
130
+ const root = (model && model.definitions[target.name]) || (target.elements && target)
131
+ if (!root) return
127
132
  // tx could be the service itself
128
133
  // prefer ApplicationService (i.e., tx.context._tx.__proto__)
129
134
  // REVISIT: context._tx is not a stable API -> pls do not rely on that
130
135
  const service = tx.context
131
136
  ? (tx.context._tx && Object.getPrototypeOf(tx.context._tx)) || Object.getPrototypeOf(tx)
132
137
  : tx
133
- if (!model || !service || !root) return
138
+ if (!service) return
134
139
  // cache templates at service for garbage collection
135
140
  if (!service._templateCache) service._templateCache = new Map()
136
141
  // model can be also a subset from tx
@@ -0,0 +1 @@
1
+ module.exports = '|$|'
@@ -1,86 +1,56 @@
1
- const _formatRowContext = ({ tKey, keyNames, row }) => {
2
- const keyValuePairs = keyNames.map(key => key + '=' + row[key])
3
- return tKey + '(' + keyValuePairs.join(',') + ')'
1
+ const DELIMITER = require('./templateDelimiter')
2
+
3
+ const _formatRowContext = (tKey, keyNames, row) => {
4
+ const keyValuePairs = keyNames.map(key => `${key}=${row[key]}`)
5
+ const keyValuePairsSerialized = keyValuePairs.join(',')
6
+ return `${tKey}(${keyValuePairsSerialized})`
4
7
  }
5
8
 
6
- const _processElement = args => {
7
- const { processFn, row, key, elements, picked = {}, complex = false, isRoot, pathOptions } = args
9
+ const _processElement = (processFn, row, key, elements, picked = {}, complex = false, isRoot, pathOptions) => {
8
10
  const { segments: pathSegments } = pathOptions
9
11
  const element = elements[key]
10
12
  const { plain } = picked
11
13
 
12
14
  if (plain) {
13
- if (!complex && pathSegments) {
14
- pathSegments.push(key)
15
- }
15
+ if (!complex && pathSegments) pathSegments.push(key)
16
16
 
17
- // skip processing if row is undefined, it is not mandatory, and null is allowed
18
- // where should this go? `if (row && (row[key] !== undefined || element._isMandatory || element.notNull))`
19
- processFn(row, key, element, plain, isRoot, pathSegments)
17
+ /**
18
+ * @type import('../../types/api').templateProcessorProcessFnArgs
19
+ */
20
+ const elementInfo = { row, key, element, plain, isRoot, pathSegments }
21
+ processFn(elementInfo)
20
22
  }
21
23
  }
22
24
 
23
- const _processRow = ({ processFn, row, template, tKey, tValue, isRoot, pathOptions }) => {
25
+ const _processRow = (processFn, row, template, tKey, tValue, isRoot, pathOptions) => {
24
26
  const { template: subTemplate, picked } = tValue
25
- const key = tKey.split(' ').pop()
26
-
27
- const args = {
28
- processFn,
29
- row,
30
- key,
31
- elements: template.target.elements,
32
- picked,
33
- complex: !!subTemplate,
34
- isRoot,
35
- pathOptions
36
- }
27
+ const key = tKey.split(DELIMITER).pop()
37
28
 
38
- _processElement(args)
29
+ _processElement(processFn, row, key, template.target.elements, picked, !!subTemplate, isRoot, pathOptions)
39
30
 
40
31
  // process deep
41
32
  if (subTemplate) {
42
33
  let subRows = row && row[key]
43
34
  subRows = Array.isArray(subRows) ? subRows : [subRows]
44
- const complexArgs = {
45
- processFn,
46
- rows: subRows,
47
- template: subTemplate,
48
- tKey: key,
49
- pathOptions
50
- }
51
-
52
- _processComplex(complexArgs)
35
+ _processComplex(processFn, subRows, subTemplate, key, pathOptions)
53
36
  }
54
37
  }
55
38
 
56
- const _processComplex = ({ processFn, rows, template, tKey, pathOptions }) => {
39
+ const _processComplex = (processFn, rows, template, tKey, pathOptions) => {
57
40
  if (rows.length === 0) return
58
- const { extraKeys, rowKeysGenerator, segments: pathSegments, includeKeyValues } = pathOptions
41
+
42
+ const segments = pathOptions.segments
59
43
  let keyNames
60
44
 
61
45
  for (const row of rows) {
62
46
  if (row == null) continue
47
+ const args = { processFn, row, template, isRoot: false, pathOptions }
63
48
 
64
- let pathSegment
65
- let complexPathSegments
66
-
67
- if (includeKeyValues) {
49
+ if (pathOptions.includeKeyValues) {
68
50
  keyNames = keyNames || (template.target.keys && Object.keys(template.target.keys)) || []
69
- if (typeof rowKeysGenerator === 'function') rowKeysGenerator({ keyNames, row, template })
70
- const rowWithExtraKeys = { ...row, ...extraKeys }
71
- pathSegment = _formatRowContext({ tKey, keyNames, row: rowWithExtraKeys })
72
- complexPathSegments = [...pathSegments, pathSegment]
73
- pathOptions.segments = complexPathSegments
74
- } else {
75
- pathOptions.segments.push(tKey)
76
- }
77
-
78
- const args = {
79
- processFn,
80
- row,
81
- template,
82
- isRoot: false,
83
- pathOptions
51
+ pathOptions.rowKeysGenerator(keyNames, row, template)
52
+ const pathSegment = _formatRowContext(tKey, keyNames, { ...row, ...pathOptions.extraKeys })
53
+ args.pathOptions.segments = segments ? [...segments, pathSegment] : [pathSegment]
84
54
  }
85
55
 
86
56
  templateProcessor(args)
@@ -91,25 +61,11 @@ const _processComplex = ({ processFn, rows, template, tKey, pathOptions }) => {
91
61
  * @param {import("../../types/api").TemplateProcessor} args
92
62
  */
93
63
  const templateProcessor = ({ processFn, row, template, isRoot = true, pathOptions = {} }) => {
94
- let { segments: pathSegments } = pathOptions
95
- pathSegments = pathSegments || []
64
+ const segments = pathOptions.segments && [...pathOptions.segments]
96
65
 
97
66
  for (const [tKey, tValue] of template.elements) {
98
- if (pathSegments) {
99
- pathOptions.segments = [...pathSegments]
100
- }
101
-
102
- const args = {
103
- processFn,
104
- row,
105
- template,
106
- tKey,
107
- tValue,
108
- isRoot,
109
- pathOptions
110
- }
111
-
112
- _processRow(args)
67
+ if (segments) pathOptions.segments = [...segments]
68
+ _processRow(processFn, row, template, tKey, tValue, isRoot, pathOptions)
113
69
  }
114
70
  }
115
71
 
@@ -0,0 +1,31 @@
1
+ const CQN_TEMPLATE_STRING = JSON.stringify(require('./unionCqnTemplate'))
2
+
3
+ function getCQNUnionFrom(columns, active, draft, keys, user) {
4
+ let cqn = CQN_TEMPLATE_STRING
5
+
6
+ const activeCols = []
7
+ const draftCols = []
8
+ for (const col of columns) {
9
+ activeCols.push(`{ "ref": ["${col}"] }`)
10
+ draftCols.push(`{ "ref": ["${draft}", "${col}"] }`)
11
+ }
12
+ cqn = cqn.replace(/"%%ACTIVE_COLUMNS%%"/g, activeCols.join(', '))
13
+ cqn = cqn.replace(/"%%DRAFT_COLUMNS%%"/g, draftCols.join(', '))
14
+
15
+ cqn = cqn.replace(/%%ACTIVE%%/g, active)
16
+ cqn = cqn.replace(/%%DRAFT%%/g, draft)
17
+
18
+ const keyCondition = []
19
+ for (const key of keys) {
20
+ keyCondition.push(`{ "ref": ["${active}", "${key}"] }, "=", { "ref": ["${draft}", "${key}"] }`)
21
+ }
22
+ cqn = cqn.replace(/"%%KEYS%%"/g, keyCondition.join(', "and", '))
23
+
24
+ cqn = cqn.replace(/%%USER%%/g, user)
25
+
26
+ return JSON.parse(cqn)
27
+ }
28
+
29
+ module.exports = {
30
+ getCQNUnionFrom
31
+ }
@@ -0,0 +1,184 @@
1
+ module.exports = {
2
+ SET: {
3
+ op: 'union',
4
+ all: true,
5
+ args: [
6
+ {
7
+ SELECT: {
8
+ from: {
9
+ join: 'inner',
10
+ args: [
11
+ {
12
+ ref: ['%%DRAFT%%']
13
+ },
14
+ {
15
+ ref: ['DRAFT.DraftAdministrativeData'],
16
+ as: 'filterAdmin'
17
+ }
18
+ ],
19
+ on: [
20
+ {
21
+ ref: ['%%DRAFT%%', 'DraftAdministrativeData_DraftUUID']
22
+ },
23
+ '=',
24
+ {
25
+ ref: ['filterAdmin', 'DraftUUID']
26
+ }
27
+ ]
28
+ },
29
+ where: [
30
+ {
31
+ ref: ['filterAdmin', 'InProcessByUser']
32
+ },
33
+ '=',
34
+ {
35
+ val: '%%USER%%'
36
+ }
37
+ ],
38
+ columns: [
39
+ '%%DRAFT_COLUMNS%%',
40
+ {
41
+ ref: ['IsActiveEntity'],
42
+ cast: {
43
+ type: 'cds.Boolean'
44
+ }
45
+ },
46
+ {
47
+ ref: ['HasActiveEntity'],
48
+ cast: {
49
+ type: 'cds.Boolean'
50
+ }
51
+ },
52
+ {
53
+ ref: ['HasDraftEntity'],
54
+ cast: {
55
+ type: 'cds.Boolean'
56
+ }
57
+ },
58
+ {
59
+ ref: ['DraftAdministrativeData_DraftUUID']
60
+ }
61
+ ]
62
+ }
63
+ },
64
+ {
65
+ SELECT: {
66
+ from: {
67
+ ref: ['%%ACTIVE%%']
68
+ },
69
+ columns: [
70
+ '%%ACTIVE_COLUMNS%%',
71
+ {
72
+ val: true,
73
+ as: 'IsActiveEntity',
74
+ cast: {
75
+ type: 'cds.Boolean'
76
+ }
77
+ },
78
+ {
79
+ val: false,
80
+ as: 'HasActiveEntity',
81
+ cast: {
82
+ type: 'cds.Boolean'
83
+ }
84
+ },
85
+ {
86
+ xpr: [
87
+ 'case',
88
+ 'when',
89
+ {
90
+ SELECT: {
91
+ from: {
92
+ ref: ['%%DRAFT%%']
93
+ },
94
+ columns: [
95
+ {
96
+ val: 1
97
+ }
98
+ ],
99
+ where: ['(', '%%KEYS%%', ')']
100
+ },
101
+ as: 'HasDraftEntity',
102
+ cast: {
103
+ type: 'cds.Boolean'
104
+ }
105
+ },
106
+ 'IS NOT NULL',
107
+ 'then',
108
+ 'true',
109
+ 'else',
110
+ 'false',
111
+ 'end'
112
+ ],
113
+ as: 'HasDraftEntity',
114
+ cast: {
115
+ type: 'cds.Boolean'
116
+ }
117
+ },
118
+ {
119
+ SELECT: {
120
+ from: {
121
+ ref: ['%%DRAFT%%']
122
+ },
123
+ columns: [
124
+ {
125
+ ref: ['DraftAdministrativeData_DraftUUID']
126
+ }
127
+ ],
128
+ where: ['(', '%%KEYS%%', ')']
129
+ }
130
+ }
131
+ ],
132
+ where: [
133
+ 'not exists',
134
+ {
135
+ SELECT: {
136
+ from: {
137
+ join: 'inner',
138
+ args: [
139
+ {
140
+ ref: ['%%DRAFT%%']
141
+ },
142
+ {
143
+ ref: ['DRAFT.DraftAdministrativeData'],
144
+ as: 'filterAdmin'
145
+ }
146
+ ],
147
+ on: [
148
+ {
149
+ ref: ['%%DRAFT%%', 'DraftAdministrativeData_DraftUUID']
150
+ },
151
+ '=',
152
+ {
153
+ ref: ['filterAdmin', 'DraftUUID']
154
+ }
155
+ ]
156
+ },
157
+ columns: [
158
+ {
159
+ val: 1
160
+ }
161
+ ],
162
+ where: [
163
+ '(',
164
+ {
165
+ ref: ['filterAdmin', 'InProcessByUser']
166
+ },
167
+ '=',
168
+ {
169
+ val: '%%USER%%'
170
+ },
171
+ ')',
172
+ 'and',
173
+ '(',
174
+ '%%KEYS%%',
175
+ ')'
176
+ ]
177
+ }
178
+ }
179
+ ]
180
+ }
181
+ }
182
+ ]
183
+ }
184
+ }
@@ -106,7 +106,7 @@ class DatabaseService extends cds.Service {
106
106
  return {
107
107
  from: (...args) => {
108
108
  const streamQuery = SELECT.from(...args)
109
- if (!streamQuery.SELECT.columns || streamQuery.SELECT.columns.length !== 0) {
109
+ if (query && (!streamQuery.SELECT.columns || streamQuery.SELECT.columns.length !== 0)) {
110
110
  streamQuery.columns([query])
111
111
  }
112
112
  delete streamQuery.SELECT.one
@@ -1,13 +1,6 @@
1
1
  function timestampToISO(ts) {
2
- if (typeof ts === 'number') {
3
- return new Date(ts).toISOString()
4
- }
5
-
6
- // REVISIT: instanceof or object
7
- if (ts instanceof Date) {
8
- return ts.toISOString()
9
- }
10
-
2
+ if (typeof ts === 'number') return new Date(ts).toISOString()
3
+ if (ts instanceof Date) return ts.toISOString()
11
4
  return ts
12
5
  }
13
6