@sap/cds 5.4.6 → 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 +208 -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 +44 -55
  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 +4 -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 +6 -22
  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 +102 -101
  127. package/libx/_runtime/common/utils/csn.js +47 -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 +223 -171
  138. package/libx/_runtime/common/utils/rewriteAsterisk.js +46 -26
  139. package/libx/_runtime/common/utils/structured.js +6 -12
  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 +22 -30
  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 +13 -20
  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 +241 -189
  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 +2 -2
  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 +23 -24
  205. package/libx/_runtime/sqlite/localized.js +12 -7
  206. package/libx/_runtime/types/api.js +10 -0
  207. package/package.json +1 -1
  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,7 +1,10 @@
1
1
  const cds = require('../../cds')
2
2
  const { SELECT } = cds.ql
3
3
 
4
- const { DRAFT_COLUMNS, DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
4
+ const cqn2cqn4sql = require('../../common/utils/cqn2cqn4sql')
5
+ const { getElementDeep } = require('../../common/utils/csn')
6
+
7
+ const { DRAFT_COLUMNS, DRAFT_COLUMNS_MAP, SCENARIO } = require('../../common/constants/draft')
5
8
  const {
6
9
  adaptStreamCQN,
7
10
  addColumnAlias,
@@ -10,21 +13,39 @@ const {
10
13
  ensureNoDraftsSuffix,
11
14
  ensureUnlocalized,
12
15
  getEnrichedCQN,
13
- removeAnnotationWhere,
14
16
  removeDraftUUIDIfNecessary,
15
17
  replaceRefWithDraft,
16
18
  hasKeyInWhere,
17
- filterKeys,
18
- getKeyProperty
19
+ filterKeys
19
20
  } = require('../utils/handler')
20
21
  const { deleteCondition, readAndDeleteKeywords, removeIsActiveEntityRecursively } = require('../utils/where')
22
+
21
23
  const { getColumns } = require('../../cds-services/services/utils/columns')
22
- const cqn2cqn4sql = require('../../common/utils/cqn2cqn4sql')
23
- const { removeVirtuals, postProcessVirtuals } = require('../../db/generic/virtual')
24
- const { getElementDeep } = require('../../common/utils/csn')
25
24
 
26
- // REVISIT: remove everywhere
27
- const SYMBOL_FROM_ANNOTATION = Symbol.for('sap.cds.FROM_ANNOTATION')
25
+ // append where with clauses from @restrict
26
+ const _getWhereWithAppendedDraftRestrictions = (where = [], req, scenarioAlias, model) => {
27
+ if (req.query._draftRestrictions) {
28
+ for (const each of req.query._draftRestrictions) {
29
+ if (where.length) where.push('and')
30
+
31
+ // REVISIT: remove with cds^6
32
+ // adjust alias of @restrict where "exists (select ...)"
33
+ if (scenarioAlias && model)
34
+ each
35
+ .filter(e => e.SELECT && e.SELECT.from && e.SELECT.where)
36
+ .forEach(e => {
37
+ const entity = model.definitions[e.SELECT.from.ref[0]]
38
+ e.SELECT.where = e.SELECT.where.map(w => {
39
+ if (w.ref && w.ref.length === 1 && !entity.elements[w.ref[0]]) w.ref.unshift(scenarioAlias)
40
+ return w
41
+ })
42
+ })
43
+
44
+ where.push(...each)
45
+ }
46
+ }
47
+ return where
48
+ }
28
49
 
29
50
  const _isTrue = val => val === true || val === 'true'
30
51
 
@@ -114,7 +135,8 @@ const _getDraftPropertiesDetermineDraft = (req, where, tableName, calcDraftUUID
114
135
 
115
136
  const hasDraftQuery = SELECT.from(tableName, [{ val: 1 }])
116
137
  if (where && where.length > 0) {
117
- hasDraftQuery.where(removeAnnotationWhere(where))
138
+ // clone where to protect from later modification
139
+ hasDraftQuery.where([...where])
118
140
  }
119
141
 
120
142
  let draftUUIDColumn
@@ -159,13 +181,9 @@ function _copyCQNPartial(partial) {
159
181
  }
160
182
 
161
183
  function _copyArray(array) {
162
- const copy = array.map(entry => {
184
+ return array.map(entry => {
163
185
  return typeof entry === 'object' && !(entry instanceof String) ? _copyCQNPartial(entry) : entry
164
186
  })
165
- if (array[SYMBOL_FROM_ANNOTATION] === true) {
166
- copy[SYMBOL_FROM_ANNOTATION] = true
167
- }
168
- return copy
169
187
  }
170
188
 
171
189
  const _whereContainsKeys = (req, whereDraft) => {
@@ -277,12 +295,12 @@ const _draftAdminTable = req => {
277
295
  }
278
296
 
279
297
  return {
280
- cqn: getEnrichedCQN(cqn, req.query.SELECT, removeAnnotationWhere(req.query.SELECT.where)),
281
- scenario: 'DRAFT_ADMIN'
298
+ cqn: getEnrichedCQN(cqn, req.query.SELECT, req.query.SELECT.where),
299
+ scenario: SCENARIO.DRAFT_ADMIN
282
300
  }
283
301
  }
284
302
 
285
- const _allInactive = (req, columns, model) => {
303
+ const _allInactive = (req, columns) => {
286
304
  const table = {
287
305
  ref: [ensureDraftsSuffix(req.query.SELECT.from.ref[0])],
288
306
  as: req.query.SELECT.from.as || 'drafts'
@@ -293,17 +311,6 @@ const _allInactive = (req, columns, model) => {
293
311
  _getDefaultDraftProperties({ hasDraft: false, isActive: false, withDraftUUID: false })
294
312
  )
295
313
 
296
- // determine name of primary key to use in HasActiveEntity expression
297
- const pk = req.target['@assert.unique.locale']
298
- ? req.target['@assert.unique.locale'].map(o => o['=']).find(k => k !== 'locale')
299
- : getKeyProperty(req.target.keys)
300
-
301
- const xpr = {
302
- xpr: ['case', 'when', `active.${pk}`, 'IS NOT NULL', 'then', 'true', 'else', 'false', 'end'],
303
- as: 'HasActiveEntity',
304
- cast: { type: 'cds.Boolean' }
305
- }
306
-
307
314
  const ids = filterKeys(req.target.keys)
308
315
  const isCount = columns.some(element => element.func === 'count')
309
316
 
@@ -312,7 +319,7 @@ const _allInactive = (req, columns, model) => {
312
319
  if (isCount) {
313
320
  cqn.columns(...outerMostColumns)
314
321
  } else {
315
- cqn.columns(...outerMostColumns.filter(o => o.as !== 'HasActiveEntity'), xpr)
322
+ cqn.columns(...outerMostColumns.filter(o => o.as !== 'HasActiveEntity'), { ref: ['HasActiveEntity'] })
316
323
  cqn.leftJoin(ensureNoDraftsSuffix(table.ref[0]) + ' as active').on(`${table.as}.${ids[0]} = active.${ids[0]}`)
317
324
 
318
325
  for (let i = 1; i < ids.length; i++) {
@@ -322,9 +329,8 @@ const _allInactive = (req, columns, model) => {
322
329
  }
323
330
 
324
331
  cqn.where(req.query.SELECT.where)
325
- removeAnnotationWhere(cqn.SELECT.where)
326
332
 
327
- return { cqn: getEnrichedCQN(cqn, req.query.SELECT, []), scenario: 'ALL_INACTIVE' }
333
+ return { cqn: getEnrichedCQN(cqn, req.query.SELECT, []), scenario: SCENARIO.ALL_INACTIVE }
328
334
  }
329
335
 
330
336
  const _setRefAlias = (ref, as) => {
@@ -362,7 +368,7 @@ const _buildOrderBy = (query, columns, table) => {
362
368
  }
363
369
  }
364
370
 
365
- const _allActive = (req, columns) => {
371
+ const _allActive = (req, columns, model) => {
366
372
  const { table } = _getTableName(req)
367
373
  if (!table.as) {
368
374
  table.as = 'active'
@@ -406,12 +412,20 @@ const _allActive = (req, columns) => {
406
412
  }
407
413
  }
408
414
 
415
+ const scenarioAlias = 'active'
416
+
417
+ req.query.SELECT.where = _getWhereWithAppendedDraftRestrictions(req.query.SELECT.where, req, scenarioAlias, model)
418
+
409
419
  if (req.query.SELECT.where) {
410
420
  _buildWhere(req.query.SELECT.where, table)
411
421
  }
422
+
412
423
  _buildOrderBy(req.query, cqn.SELECT.columns, table)
413
424
 
414
- return { cqn: getEnrichedCQN(cqn, req.query.SELECT, req.query.SELECT.where, 'active'), scenario: 'ALL_ACTIVE' }
425
+ return {
426
+ cqn: getEnrichedCQN(cqn, req.query.SELECT, req.query.SELECT.where, scenarioAlias),
427
+ scenario: SCENARIO.ALL_ACTIVE
428
+ }
415
429
  }
416
430
 
417
431
  const _active = (req, draftWhere, columns) => {
@@ -421,7 +435,9 @@ const _active = (req, draftWhere, columns) => {
421
435
 
422
436
  const cqn = SELECT.from(table).columns(...outerMostColumns)
423
437
 
424
- return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: 'ACTIVE' }
438
+ draftWhere = _getWhereWithAppendedDraftRestrictions(draftWhere, req)
439
+
440
+ return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: SCENARIO.ACTIVE }
425
441
  }
426
442
 
427
443
  const _activeWithoutDraft = (req, draftWhere, columns) => {
@@ -449,7 +465,9 @@ const _activeWithoutDraft = (req, draftWhere, columns) => {
449
465
  .columns(...outerMostColumns)
450
466
  .where(['not exists', subSelect])
451
467
 
452
- return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: 'ACTIVE_WITHOUT_DRAFT' }
468
+ draftWhere = _getWhereWithAppendedDraftRestrictions(draftWhere, req)
469
+
470
+ return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: SCENARIO.ACTIVE_WITHOUT_DRAFT }
453
471
  }
454
472
 
455
473
  const _draftOfWhichIAmOwner = (req, draftWhere, columns) => {
@@ -469,9 +487,7 @@ const _draftOfWhichIAmOwner = (req, draftWhere, columns) => {
469
487
  ])
470
488
  .where(_inProcessByUserWhere(req.user.id))
471
489
 
472
- removeAnnotationWhere(draftWhere)
473
-
474
- return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: 'DRAFT_WHICH_OWNER' }
490
+ return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: SCENARIO.DRAFT_WHICH_OWNER }
475
491
  }
476
492
 
477
493
  const _activeWithDraftInProcess = (req, draftWhere, columns, isLocked) => {
@@ -504,7 +520,7 @@ const _activeWithDraftInProcess = (req, draftWhere, columns, isLocked) => {
504
520
  'and',
505
521
  {
506
522
  func: 'seconds_between',
507
- args: [{ ref: ['filterAdmin', 'LastChangeDateTime'] }, { ref: ['CURRENT_TIMESTAMP'] }]
523
+ args: [{ ref: ['filterAdmin', 'LastChangeDateTime'] }, 'CURRENT_TIMESTAMP']
508
524
  },
509
525
  isLocked ? '<' : '>',
510
526
  { val: DRAFT_CANCEL_TIMEOUT_IN_SEC }
@@ -515,11 +531,13 @@ const _activeWithDraftInProcess = (req, draftWhere, columns, isLocked) => {
515
531
  subSelect
516
532
  )
517
533
 
534
+ subSelect.SELECT.where = _getWhereWithAppendedDraftRestrictions(subSelect.SELECT.where, req)
535
+
518
536
  const outerMostColumns = _getOuterMostColumns(columns, draftColumns)
519
537
 
520
538
  const cqn = SELECT.from(active.table).columns(outerMostColumns).where(['exists', subSelect])
521
539
 
522
- return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: 'DRAFT_IN_PROCESS' }
540
+ return { cqn: getEnrichedCQN(cqn, req.query.SELECT, draftWhere), scenario: SCENARIO.DRAFT_IN_PROCESS }
523
541
  }
524
542
 
525
543
  const _alignAliasForUnion = (table, as, select) => {
@@ -536,37 +554,27 @@ const _alignAliasForUnion = (table, as, select) => {
536
554
  return select
537
555
  }
538
556
 
539
- const _isKeyElement = (element, keys) => element.ref && keys.includes(element.ref[element.ref.length - 1])
540
- const _valExists = (i, length, where) => i < length - 2 && where[i + 2].val
541
-
542
- const _findKeysInWhereAndAddToResult = (where, keys, result) => {
543
- for (let i = 0, length = where.length; i < length; i++) {
544
- const element = where[i]
545
- if (_isKeyElement(element, keys) && _valExists(i, length, where)) {
546
- result[element.ref[element.ref.length - 1]] = where[i + 2].val
547
- i = i + 2
548
- }
549
- }
550
- }
551
-
552
- const _findKeysInQuery = (query, keys) => {
553
- const result = {}
554
- if (query.SELECT && query.SELECT.where) {
555
- const indexExists = query.SELECT.where.indexOf('exists')
556
- if (indexExists !== -1) {
557
- return _findKeysInQuery(query.SELECT.where[indexExists + 1], keys)
558
- }
559
-
560
- _findKeysInWhereAndAddToResult(query.SELECT.where, keys, result)
561
- }
562
- return result
557
+ const _findJoinInQuery = (query, parentAlias) => {
558
+ const targetAlias = query.SELECT.from.as
559
+ const isTargetRef = el => targetAlias && el.ref && el.ref.length > 1 && el.ref[0] === targetAlias
560
+ if (query.SELECT && query.SELECT.where)
561
+ return query.SELECT.where.reduce((links, el, idx, where) => {
562
+ if (el.ref && el.ref[0] === parentAlias && el.ref[el.ref.length - 1] !== 'IsActiveEntity') {
563
+ if (where[idx - 1] && where[idx - 1] === '=' && isTargetRef(where[idx - 2])) {
564
+ if (links.length) links.push('and')
565
+ links.push(el, '=', where[idx - 2])
566
+ }
567
+ if (where[idx + 1] && where[idx + 1] === '=' && isTargetRef(where[idx + 2])) {
568
+ if (links.length) links.push('and')
569
+ links.push(el, '=', where[idx + 2])
570
+ }
571
+ }
572
+ return links
573
+ }, [])
574
+ return []
563
575
  }
564
576
 
565
- const _isFiltered = where => {
566
- return where.some(element => {
567
- return !(element in ['(', ')']) && element[SYMBOL_FROM_ANNOTATION] !== true
568
- })
569
- }
577
+ const _isFiltered = where => where.some(element => !(element in ['(', ')']))
570
578
 
571
579
  const _isDraftField = element => element.ref && element.ref.length > 1 && element.ref[0] === 'DraftAdministrativeData'
572
580
 
@@ -614,40 +622,112 @@ const _getWhereForActive = where => {
614
622
  return activeWhere
615
623
  }
616
624
 
617
- const _siblingEntity = (req, columns, model) => {
618
- const unlocalizedEntity = model.definitions[ensureUnlocalized(req.target.name)]
619
- const keys = _findKeysInQuery(req.query, Object.keys(unlocalizedEntity.keys))
620
- const isSiblingActive = !keys.IsActiveEntity
621
-
622
- const targetTable = isSiblingActive ? ensureNoDraftsSuffix(req.target.name) : ensureDraftsSuffix(req.target.name)
623
- const columnCqnPartial = columns.map(col => {
624
- const colName = col.ref ? col.ref[col.ref.length - 1] : col
625
- return { ref: ['target', colName], as: colName }
626
- })
627
- columnCqnPartial.push({ ref: ['draftAdmin', 'InProcessByUser'], as: 'draftAdmin_inProcessByUser' })
628
- const cqn = SELECT.from(`${targetTable} as target`).columns(...columnCqnPartial)
625
+ const _siblingEntity = ({ query, target, nav, params }, columns, model, draftAdminAlias, parentQuery, siblingIndex) => {
626
+ const parentLinks = parentQuery ? _findJoinInQuery(query, parentQuery.SELECT.from.as) : []
627
+ const keys = (nav[siblingIndex + 1].where && (params[siblingIndex] || params[0])) || {}
628
+ const siblingQuery = query.SELECT.where[query.SELECT.where.indexOf('exists') + 1]
629
+ const onCond = _findJoinInQuery(siblingQuery, target.as)
630
+ const siblingAlias = siblingQuery.SELECT.from.as
631
+ const subScenario = _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, model, onCond)
632
+ const isSiblingDraft = subScenario
633
+ ? subScenario.isSiblingActive || subScenario.scenario === 'ACTIVE' || subScenario.scenario === 'ALL_ACTIVE'
634
+ : keys.IsActiveEntity && keys.IsActiveEntity !== 'false'
635
+ const { table } = _getTableName({ query, target }, isSiblingDraft)
636
+ const cqn = SELECT.from(table)
637
+ if (siblingIndex === 0) {
638
+ const columnCqnPartial = columns.map(col => {
639
+ const colName = col.ref ? col.ref[col.ref.length - 1] : col
640
+ const ref = col.ref ? [table.as, ...col.ref] : [table.as, colName]
641
+ return Object.assign({}, col, { ref })
642
+ })
643
+ columnCqnPartial.push({ ref: ['draftAdmin', 'InProcessByUser'], as: 'draftAdmin_inProcessByUser' })
644
+ cqn.columns(...columnCqnPartial)
645
+ } else {
646
+ cqn.columns([{ val: 1 }])
647
+ }
629
648
 
630
- if (isSiblingActive) {
649
+ if (isSiblingDraft) {
631
650
  cqn
632
- .join(ensureDraftsSuffix(req.target.name), 'target_draft')
633
- .on(
634
- Object.keys(keys)
635
- .map(k => `target.${k} = target_draft.${k}`)
636
- .join(' AND ')
637
- )
651
+ .join(ensureNoDraftsSuffix(target.name), siblingAlias)
652
+ .on(onCond)
638
653
  .join('DRAFT.DraftAdministrativeData', 'draftAdmin')
639
- .on('target_draft.DraftAdministrativeData_DraftUUID = draftAdmin.DraftUUID')
654
+ .on(`${table.as}.DraftAdministrativeData_DraftUUID = draftAdmin.DraftUUID`)
640
655
  } else {
641
656
  cqn
657
+ .join(ensureDraftsSuffix(target.name), siblingAlias)
658
+ .on(onCond)
642
659
  .join('DRAFT.DraftAdministrativeData', 'draftAdmin')
643
- .on('target.DraftAdministrativeData_DraftUUID = draftAdmin.DraftUUID')
660
+ .on(`${siblingAlias}.DraftAdministrativeData_DraftUUID = draftAdmin.DraftUUID`)
644
661
  }
645
662
 
646
663
  for (const key in keys) {
647
- if (key !== 'IsActiveEntity') cqn.where([{ ref: ['target', key] }, '=', { val: keys[key] }])
664
+ if (key !== 'IsActiveEntity') cqn.where([{ ref: [table.as, key] }, '=', { val: keys[key] }])
665
+ }
666
+ if (subScenario) {
667
+ cqn.where(['exists', subScenario.cqn])
648
668
  }
669
+ // in DraftAdminData scenario parent is linked via join
670
+ if (draftAdminAlias) {
671
+ cqn.where([{ ref: [draftAdminAlias, 'DraftUUID'] }, '=', { ref: ['draftAdmin', 'DraftUUID'] }])
672
+ } else if (parentLinks.length) {
673
+ cqn.where('(', ...parentLinks, ')')
674
+ }
675
+
676
+ return { cqn, scenario: SCENARIO.SIBLING_ENTITY, isSiblingActive: !isSiblingDraft }
677
+ }
649
678
 
650
- return { cqn, scenario: 'SIBLING_ENTITY', isSiblingActive: isSiblingActive }
679
+ function _siblingSubScenario(nav, siblingIndex, siblingQuery, target, params, model, onCond) {
680
+ if (nav[siblingIndex + 1].where) return
681
+ let subScenario
682
+ const subNav = nav.slice(siblingIndex + 1)
683
+ const subSiblingIndex = subNav.indexOf('SiblingEntity')
684
+ const subReq = { query: siblingQuery, target: model.definitions[target.name], params: [...params].reverse() }
685
+ if (subSiblingIndex > -1) {
686
+ subScenario = _getSiblingScenario(subReq, [{ val: 1 }], model, subSiblingIndex, subNav, params)
687
+ if (subSiblingIndex > 0) {
688
+ const subQuery = SELECT.from(siblingQuery.SELECT.from).columns([{ val: 1 }])
689
+ _mergeSiblingIntoCQN(subQuery, subScenario, subSiblingIndex - 1)
690
+ subQuery.where(onCond)
691
+ subScenario.cqn = subQuery
692
+ }
693
+ } else {
694
+ subReq.query = SELECT.from(siblingQuery.SELECT.from).columns([{ val: 1 }])
695
+ const existsIdx = siblingQuery.SELECT.where.indexOf('exists')
696
+ if (existsIdx > -1) subReq.query.where(siblingQuery.SELECT.where.slice(existsIdx, existsIdx + 2))
697
+ const subReqOrig = { query: { SELECT: { from: { ref: [...subNav].reverse() } } } }
698
+ subScenario = _generateCQN(subReqOrig, subReq, [{ val: 1 }], model)
699
+ subScenario.cqn.where(onCond)
700
+ }
701
+ return subScenario
702
+ }
703
+
704
+ const _getSiblingScenario = (req, columns, model, siblingIndex, nav) => {
705
+ const draftAdminAlias = _isDraftAdminScenario(req) && req.query.SELECT.from.as
706
+ const params = [...req.params].reverse()
707
+ const _getSiblingQueryFromWhere = (query, queryIndex, parentQuery) => {
708
+ if (query.SELECT && query.SELECT.where) {
709
+ const indexExists = query.SELECT.where.indexOf('exists')
710
+ if (indexExists > -1 && queryIndex > 0) {
711
+ return _getSiblingQueryFromWhere(query.SELECT.where[indexExists + 1], queryIndex - 1, query)
712
+ }
713
+ }
714
+ const target = { name: query.SELECT.from.ref[0].id || query.SELECT.from.ref[0], as: query.SELECT.from.as }
715
+ return _siblingEntity({ query, target, params, nav }, columns, model, draftAdminAlias, parentQuery, siblingIndex)
716
+ }
717
+ return _getSiblingQueryFromWhere(req.query, siblingIndex)
718
+ }
719
+
720
+ const _mergeSiblingIntoCQN = (cqn, { cqn: siblingCQN }, siblingIndex) => {
721
+ const _replaceWhereExists = (query, _siblingIndex) => {
722
+ if (query.SELECT && query.SELECT.where) {
723
+ const indexExists = query.SELECT.where.indexOf('exists')
724
+ if (indexExists > -1) {
725
+ if (_siblingIndex > 0) return _replaceWhereExists(query.SELECT.where[indexExists + 1], _siblingIndex - 1)
726
+ query.SELECT.where.splice(indexExists + 1, 1, siblingCQN)
727
+ }
728
+ }
729
+ }
730
+ return _replaceWhereExists(cqn, siblingIndex)
651
731
  }
652
732
 
653
733
  const _getDraftDoc = (req, draftName, draftWhere) => {
@@ -706,7 +786,7 @@ const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere) => {
706
786
  const activeDocs = getEnrichedCQN(SELECT.from(req.target), req.query.SELECT, draftActiveWhere, undefined, false)
707
787
 
708
788
  _replaceDraftAlias(draftWhere)
709
- const draftDocs = _getDraftDoc(req, draftName, removeAnnotationWhere(draftWhere))
789
+ const draftDocs = _getDraftDoc(req, draftName, draftWhere)
710
790
 
711
791
  const union = SELECT.from({ SET: { op: 'union', all: true, args: [draftDocs, activeDocs] } })
712
792
  if (req.query.SELECT.count) union.SELECT.count = true
@@ -809,6 +889,8 @@ const _excludeActiveDraftExists = (req, draftWhere, columns) => {
809
889
  subSelect.where([{ ref: [ensureNoDraftsSuffix(req.target.name), key] }, '=', { ref: [draftName, key] }])
810
890
  }
811
891
 
892
+ draftWhere = _getWhereWithAppendedDraftRestrictions(draftWhere, req)
893
+
812
894
  draftWhere = removeIsActiveEntityRecursively(draftWhere)
813
895
  const cqn = _getUnionCQN(req, draftName, columns, subSelect, draftWhere)
814
896
  cqn.SELECT.from.as = name
@@ -821,7 +903,7 @@ const _excludeActiveDraftExists = (req, draftWhere, columns) => {
821
903
  }
822
904
  }
823
905
 
824
- return { cqn: cqn, scenario: 'UNION' }
906
+ return { cqn: cqn, scenario: SCENARIO.UNION }
825
907
  }
826
908
 
827
909
  const _readDraftParameters = where => {
@@ -865,37 +947,46 @@ const _validatedActive = (req, draftWhere, draftParameters, columns) =>
865
947
  const _validatedDraftOfWhichIAmOwner = (req, draftWhere, draftParameters, columns) =>
866
948
  _isValidDraftOfWhichIAmOwner(draftParameters.isActiveEntity) && _draftOfWhichIAmOwner(req, draftWhere, columns)
867
949
 
868
- const _draftInSubSelect = where => {
950
+ const _draftInSubSelect = (where, req) => {
869
951
  return where.some(({ SELECT }) => {
870
952
  if (SELECT && SELECT.where) {
871
953
  const isActiveEntity = readAndDeleteKeywords(['IsActiveEntity'], SELECT.where, false)
872
954
  if (isActiveEntity) {
873
- return _isFalse(isActiveEntity.value.val)
955
+ const isFalse = _isFalse(isActiveEntity.value.val)
956
+ if (isFalse) SELECT.where = _getWhereWithAppendedDraftRestrictions(SELECT.where, req)
957
+ return isFalse
874
958
  }
875
959
 
876
- return _draftInSubSelect(SELECT.where)
960
+ return _draftInSubSelect(SELECT.where, req)
877
961
  }
878
962
 
879
963
  return false
880
964
  })
881
965
  }
882
966
 
883
- const _generateCQN = (reqOriginal, req, columns, serviceName, model) => {
884
- // REVISIT: get rid of getUrlObject
885
- if (req.getUrlObject && req.getUrlObject().path.endsWith('SiblingEntity')) {
886
- return _siblingEntity(req, columns, model)
967
+ const _isDraftAdminScenario = req =>
968
+ req.target.query && req.target.query._target && req.target.query._target.name === 'DRAFT.DraftAdministrativeData'
969
+
970
+ // eslint-disable-next-line complexity
971
+ const _generateCQN = (reqOriginal, req, columns, model) => {
972
+ const nav = [...reqOriginal.query.SELECT.from.ref].reverse() || []
973
+ const siblingIndex = nav.indexOf('SiblingEntity')
974
+ let siblingScenario
975
+ if (siblingIndex > -1) {
976
+ siblingScenario = _getSiblingScenario(req, columns, model, siblingIndex, nav)
977
+ if (siblingIndex === 0) {
978
+ return siblingScenario
979
+ } else {
980
+ _mergeSiblingIntoCQN(req.query, siblingScenario, siblingIndex - 1)
981
+ }
887
982
  }
888
983
 
889
- if (
890
- req.target.query &&
891
- req.target.query._target &&
892
- req.target.query._target.name === 'DRAFT.DraftAdministrativeData'
893
- ) {
984
+ if (_isDraftAdminScenario(req)) {
894
985
  return _draftAdminTable(req)
895
986
  }
896
987
 
897
988
  if (!req.query.SELECT.where || !_isFiltered(req.query.SELECT.where)) {
898
- return _allActive(req, columns)
989
+ return _allActive(req, columns, model)
899
990
  }
900
991
 
901
992
  if (req.query.SELECT.where.length === 1 && req.query.SELECT.where[0].xpr) {
@@ -915,8 +1006,11 @@ const _generateCQN = (reqOriginal, req, columns, serviceName, model) => {
915
1006
  }
916
1007
 
917
1008
  if (!draftParameters.isActiveEntity) {
918
- if (_draftInSubSelect(req.query.SELECT.where)) {
919
- return _allInactive(req, columns, model)
1009
+ // _draftInSubSelect adds draft restrictions in case check is truthy
1010
+ // -> not nice but works for now and we don't need to go in recursively again
1011
+ if (_draftInSubSelect(req.query.SELECT.where, req) || (siblingScenario && !siblingScenario.isSiblingActive)) {
1012
+ // this is only the case when navigating into tree
1013
+ return _allInactive(req, columns)
920
1014
  }
921
1015
  return _allActive(req, columns)
922
1016
  }
@@ -943,52 +1037,39 @@ const _getColumns = ({ query: { SELECT } }, model) => {
943
1037
  )
944
1038
  : getColumns(model.definitions[ensureNoDraftsSuffix(SELECT.from.ref[0])], {
945
1039
  onlyNames: true,
946
- removeIgnore: true,
947
- filterVirtual: true
1040
+ removeIgnore: true
948
1041
  })
949
1042
  }
950
1043
 
951
- const _isIsActiveEntity = element =>
952
- element.ref &&
953
- (element.ref[0] === 'IsActiveEntity' || (element.ref.length > 1 && element.ref[1] === 'IsActiveEntity'))
1044
+ const _isIsActiveEntity = element => element.ref && element.ref[element.ref.length - 1] === 'IsActiveEntity'
954
1045
 
955
1046
  const _adaptSubSelects = ({ SELECT: { from, where } }, scenario) => {
956
- if (!where) {
957
- return
958
- }
959
-
960
- let indexDel = -1
1047
+ if (!where) return
961
1048
 
962
1049
  if (scenario === 'ALL_INACTIVE') {
963
1050
  replaceRefWithDraft(from.ref)
964
1051
  }
965
- let _removeAnnotationWhere = false
966
1052
 
967
- for (let i = 0, len = where.length; i < len; i++) {
1053
+ for (let i = 0; i < where.length; i++) {
968
1054
  const element = where[i]
969
1055
 
970
- if (_isIsActiveEntity(element) && len > i + 2) {
971
- if (scenario !== 'ALL_INACTIVE' && _isFalse(where[i + 2].val)) {
972
- replaceRefWithDraft(from.ref)
973
- _removeAnnotationWhere = true
974
- // read from draft table because draft has draftUUID
975
- } else if (scenario === 'DRAFT_ADMIN' && !_isFalse(where[i + 2].val)) {
1056
+ if (_isIsActiveEntity(element) && where.length > i + 2) {
1057
+ if (
1058
+ (scenario !== 'ALL_INACTIVE' && _isFalse(where[i + 2].val)) ||
1059
+ (scenario === SCENARIO.DRAFT_ADMIN && !_isFalse(where[i + 2].val))
1060
+ ) {
976
1061
  replaceRefWithDraft(from.ref)
977
- _removeAnnotationWhere = true
978
1062
  }
979
1063
 
980
- indexDel = i
981
- }
982
-
983
- if (element.SELECT) {
1064
+ if (!_isIsActiveEntity(where[i + 2])) {
1065
+ i = deleteCondition(i, where) - 1
1066
+ } else {
1067
+ i = i + 3 < where.length ? i + 2 : i + 3
1068
+ }
1069
+ } else if (element.SELECT) {
984
1070
  _adaptSubSelects(element, scenario)
985
1071
  }
986
1072
  }
987
-
988
- if (indexDel !== -1) {
989
- deleteCondition(indexDel, where)
990
- }
991
- if (_removeAnnotationWhere) removeAnnotationWhere(where)
992
1073
  }
993
1074
 
994
1075
  const _calculateDraftAdminColumns = (result, user) => {
@@ -1013,16 +1094,6 @@ const _adaptDraftColumnsForSiblingEntity = (result, isSiblingActive) => {
1013
1094
  result.HasActiveEntity = !isSiblingActive
1014
1095
  }
1015
1096
 
1016
- const _addAlias = (whereElement, aliases) => {
1017
- if (whereElement.ref) {
1018
- const table = whereElement.ref[0].replace(/\./g, '_')
1019
- const alias = aliases.get(table)
1020
- if (alias) {
1021
- whereElement.ref.splice(0, 1, aliases.get(table))
1022
- }
1023
- }
1024
- }
1025
-
1026
1097
  const _collectAliases = (from, aliases) => {
1027
1098
  if (from) {
1028
1099
  if (from.ref && from.as) {
@@ -1041,39 +1112,13 @@ const _collectAliases = (from, aliases) => {
1041
1112
  }
1042
1113
  }
1043
1114
 
1044
- const _adaptAliasForSubSelect = (subselect, aliases) => {
1045
- if (subselect.where) {
1046
- for (const whereElement of subselect.where) {
1047
- if (whereElement.ref) {
1048
- _addAlias(whereElement, aliases)
1049
- }
1050
-
1051
- if (whereElement.SELECT) {
1052
- _adaptAliasForSubSelect(whereElement.SELECT, aliases)
1053
- }
1054
- }
1055
- }
1056
- }
1057
-
1058
1115
  const _adaptAnnotationAliases = cqn => {
1059
1116
  const aliases = new Map()
1060
1117
  _collectAliases(cqn.SELECT.from, aliases)
1061
-
1062
- if (cqn.SELECT.where) {
1063
- for (const whereElement of cqn.SELECT.where) {
1064
- if (
1065
- whereElement.SELECT &&
1066
- whereElement.SELECT.from.ref &&
1067
- whereElement.SELECT.from.ref[SYMBOL_FROM_ANNOTATION] === true
1068
- ) {
1069
- _adaptAliasForSubSelect(whereElement.SELECT, aliases)
1070
- }
1071
- }
1072
- }
1073
1118
  }
1074
1119
 
1075
1120
  const calculateDraftTimeout = (scenario, result, deleteLastChangeDateTime) => {
1076
- if (scenario === 'DRAFT_ADMIN') {
1121
+ if (scenario === SCENARIO.DRAFT_ADMIN) {
1077
1122
  if (!draftIsLocked(result[0].LastChangeDateTime)) {
1078
1123
  result[0].InProcessByUser = ''
1079
1124
  }
@@ -1097,12 +1142,12 @@ const calculateDraftTimeout = (scenario, result, deleteLastChangeDateTime) => {
1097
1142
  }
1098
1143
 
1099
1144
  const enhanceQueryForTimeoutIfNeeded = (scenario, columns = []) => {
1100
- if (scenario !== 'DRAFT_ADMIN') {
1145
+ if (scenario !== SCENARIO.DRAFT_ADMIN) {
1101
1146
  const draftAdmin = columns.find(col => col.ref && col.ref[col.ref.length - 1] === 'DraftAdministrativeData')
1102
1147
  columns = (draftAdmin && draftAdmin.expand) || []
1103
1148
  }
1104
- const inProcessByUser = columns.find(col => col.ref[col.ref.length - 1] === 'InProcessByUser')
1105
- const lastChangeDateTime = columns.find(col => col.ref[col.ref.length - 1] === 'LastChangeDateTime')
1149
+ const inProcessByUser = columns.find(col => col.ref && col.ref[col.ref.length - 1] === 'InProcessByUser')
1150
+ const lastChangeDateTime = columns.find(col => col.ref && col.ref[col.ref.length - 1] === 'LastChangeDateTime')
1106
1151
  if (inProcessByUser && !lastChangeDateTime) {
1107
1152
  columns.push({ ref: [...inProcessByUser.ref.slice(0, inProcessByUser.ref.length - 1), 'LastChangeDateTime'] })
1108
1153
  return true
@@ -1114,7 +1159,11 @@ const enhanceQueryForTimeoutIfNeeded = (scenario, columns = []) => {
1114
1159
  const _getLocalizedEntity = (model, target, user) => {
1115
1160
  const prefix = 'localized'
1116
1161
  let localizedEntity
1117
- if (user.locale === 'de' || user.locale === 'fr') {
1162
+ /*
1163
+ * REVISIT: in case of not sqlite, model.definitions[`${prefix}.${user.locale}.${target.name}`] is undefined
1164
+ * and the fallback lookup model.definitions[`${prefix}.${target.name}`] gets the entity -> bad coding
1165
+ */
1166
+ if (cds.env.i18n.for_sqlite.includes(user.locale)) {
1118
1167
  localizedEntity = model.definitions[`${prefix}.${user.locale}.${target.name}`]
1119
1168
  }
1120
1169
  return localizedEntity || model.definitions[`${prefix}.${target.name}`]
@@ -1125,6 +1174,7 @@ const _getLocalizedEntity = (model, target, user) => {
1125
1174
  *
1126
1175
  * @param req
1127
1176
  */
1177
+ // eslint-disable-next-line complexity
1128
1178
  const _handler = async function (req) {
1129
1179
  // handle localized here as it was previously handled for req.target
1130
1180
  req.target = _getLocalizedEntity(this.model, req.target, req.user) || req.target
@@ -1138,18 +1188,22 @@ const _handler = async function (req) {
1138
1188
  // do not clone with Object.assign as that would skip all non-enumerable properties
1139
1189
  const reqClone = { __proto__: req, query: _copyCQNPartial(sqlQuery) }
1140
1190
 
1191
+ // ensure draft restrictions are copied to new query
1192
+ reqClone.query._draftRestrictions = req.query._draftRestrictions
1193
+
1141
1194
  if (req.query._streaming) {
1142
1195
  adaptStreamCQN(reqClone.query)
1143
1196
  reqClone.query._streaming = true
1144
1197
  return cds.tx(req).run(reqClone.query)
1145
1198
  }
1146
- const virtuals = removeVirtuals(reqClone, this.model)
1147
- const cqnScenario = _generateCQN(req, reqClone, _getColumns(reqClone, this.model), this.name, this.model)
1199
+
1200
+ const cqnScenario = _generateCQN(req, reqClone, _getColumns(reqClone, this.model), this.model)
1148
1201
 
1149
1202
  if (!cqnScenario) {
1150
1203
  req.reject(400)
1151
1204
  return
1152
1205
  }
1206
+
1153
1207
  const enhacnedWithLastChangeDateTime = enhanceQueryForTimeoutIfNeeded(
1154
1208
  cqnScenario.scenario,
1155
1209
  cqnScenario.cqn.SELECT.columns
@@ -1161,13 +1215,14 @@ const _handler = async function (req) {
1161
1215
 
1162
1216
  // unlocalize for db and after handlers as it was before
1163
1217
  req.target = this.model.definitions[ensureUnlocalized(req.target.name)]
1164
- const result = await cds.tx(req).run(cqnScenario.cqn)
1218
+
1219
+ const result = await cds.tx(req).send({ query: cqnScenario.cqn, target: req.target })
1165
1220
 
1166
1221
  const resultAsArray = Array.isArray(result) ? result : result ? [result] : []
1167
1222
 
1168
1223
  removeDraftUUIDIfNecessary(resultAsArray, req)
1169
1224
 
1170
- if (cqnScenario.scenario === 'DRAFT_ADMIN') {
1225
+ if (cqnScenario.scenario === SCENARIO.DRAFT_ADMIN) {
1171
1226
  if (!result || (Array.isArray(result) && !result.length)) return result
1172
1227
 
1173
1228
  _calculateDraftAdminColumns(resultAsArray[0], req.user.id)
@@ -1175,12 +1230,9 @@ const _handler = async function (req) {
1175
1230
 
1176
1231
  calculateDraftTimeout(cqnScenario.scenario, resultAsArray, enhacnedWithLastChangeDateTime)
1177
1232
 
1178
- if (cqnScenario.scenario === 'SIBLING_ENTITY') {
1233
+ if (cqnScenario.scenario === SCENARIO.SIBLING_ENTITY) {
1179
1234
  if (!result || (Array.isArray(result) && !result.length)) return result
1180
-
1181
- if (resultAsArray[0].draftAdmin_inProcessByUser !== req.user.id) {
1182
- return []
1183
- }
1235
+ if (resultAsArray[0].draftAdmin_inProcessByUser !== req.user.id) return []
1184
1236
 
1185
1237
  delete resultAsArray[0].draftAdmin_inProcessByUser
1186
1238
  _adaptDraftColumnsForSiblingEntity(resultAsArray[0], cqnScenario.isSiblingActive)
@@ -1191,7 +1243,7 @@ const _handler = async function (req) {
1191
1243
  row.DraftAdministrativeData && _calculateDraftAdminColumns(row.DraftAdministrativeData, req.user.id)
1192
1244
  })
1193
1245
  }
1194
- postProcessVirtuals(virtuals, resultAsArray)
1246
+
1195
1247
  return result
1196
1248
  }
1197
1249