@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,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,20 +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
24
 
25
- // REVISIT: remove everywhere
26
- 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
+ }
27
49
 
28
50
  const _isTrue = val => val === true || val === 'true'
29
51
 
@@ -113,7 +135,8 @@ const _getDraftPropertiesDetermineDraft = (req, where, tableName, calcDraftUUID
113
135
 
114
136
  const hasDraftQuery = SELECT.from(tableName, [{ val: 1 }])
115
137
  if (where && where.length > 0) {
116
- hasDraftQuery.where(removeAnnotationWhere(where))
138
+ // clone where to protect from later modification
139
+ hasDraftQuery.where([...where])
117
140
  }
118
141
 
119
142
  let draftUUIDColumn
@@ -158,13 +181,9 @@ function _copyCQNPartial(partial) {
158
181
  }
159
182
 
160
183
  function _copyArray(array) {
161
- const copy = array.map(entry => {
184
+ return array.map(entry => {
162
185
  return typeof entry === 'object' && !(entry instanceof String) ? _copyCQNPartial(entry) : entry
163
186
  })
164
- if (array[SYMBOL_FROM_ANNOTATION] === true) {
165
- copy[SYMBOL_FROM_ANNOTATION] = true
166
- }
167
- return copy
168
187
  }
169
188
 
170
189
  const _whereContainsKeys = (req, whereDraft) => {
@@ -276,12 +295,12 @@ const _draftAdminTable = req => {
276
295
  }
277
296
 
278
297
  return {
279
- cqn: getEnrichedCQN(cqn, req.query.SELECT, removeAnnotationWhere(req.query.SELECT.where)),
280
- scenario: 'DRAFT_ADMIN'
298
+ cqn: getEnrichedCQN(cqn, req.query.SELECT, req.query.SELECT.where),
299
+ scenario: SCENARIO.DRAFT_ADMIN
281
300
  }
282
301
  }
283
302
 
284
- const _allInactive = (req, columns, model) => {
303
+ const _allInactive = (req, columns) => {
285
304
  const table = {
286
305
  ref: [ensureDraftsSuffix(req.query.SELECT.from.ref[0])],
287
306
  as: req.query.SELECT.from.as || 'drafts'
@@ -292,17 +311,6 @@ const _allInactive = (req, columns, model) => {
292
311
  _getDefaultDraftProperties({ hasDraft: false, isActive: false, withDraftUUID: false })
293
312
  )
294
313
 
295
- // determine name of primary key to use in HasActiveEntity expression
296
- const pk = req.target['@assert.unique.locale']
297
- ? req.target['@assert.unique.locale'].map(o => o['=']).find(k => k !== 'locale')
298
- : getKeyProperty(req.target.keys)
299
-
300
- const xpr = {
301
- xpr: ['case', 'when', `active.${pk}`, 'IS NOT NULL', 'then', 'true', 'else', 'false', 'end'],
302
- as: 'HasActiveEntity',
303
- cast: { type: 'cds.Boolean' }
304
- }
305
-
306
314
  const ids = filterKeys(req.target.keys)
307
315
  const isCount = columns.some(element => element.func === 'count')
308
316
 
@@ -311,7 +319,7 @@ const _allInactive = (req, columns, model) => {
311
319
  if (isCount) {
312
320
  cqn.columns(...outerMostColumns)
313
321
  } else {
314
- cqn.columns(...outerMostColumns.filter(o => o.as !== 'HasActiveEntity'), xpr)
322
+ cqn.columns(...outerMostColumns.filter(o => o.as !== 'HasActiveEntity'), { ref: ['HasActiveEntity'] })
315
323
  cqn.leftJoin(ensureNoDraftsSuffix(table.ref[0]) + ' as active').on(`${table.as}.${ids[0]} = active.${ids[0]}`)
316
324
 
317
325
  for (let i = 1; i < ids.length; i++) {
@@ -321,9 +329,8 @@ const _allInactive = (req, columns, model) => {
321
329
  }
322
330
 
323
331
  cqn.where(req.query.SELECT.where)
324
- removeAnnotationWhere(cqn.SELECT.where)
325
332
 
326
- return { cqn: getEnrichedCQN(cqn, req.query.SELECT, []), scenario: 'ALL_INACTIVE' }
333
+ return { cqn: getEnrichedCQN(cqn, req.query.SELECT, []), scenario: SCENARIO.ALL_INACTIVE }
327
334
  }
328
335
 
329
336
  const _setRefAlias = (ref, as) => {
@@ -361,7 +368,7 @@ const _buildOrderBy = (query, columns, table) => {
361
368
  }
362
369
  }
363
370
 
364
- const _allActive = (req, columns) => {
371
+ const _allActive = (req, columns, model) => {
365
372
  const { table } = _getTableName(req)
366
373
  if (!table.as) {
367
374
  table.as = 'active'
@@ -405,12 +412,20 @@ const _allActive = (req, columns) => {
405
412
  }
406
413
  }
407
414
 
415
+ const scenarioAlias = 'active'
416
+
417
+ req.query.SELECT.where = _getWhereWithAppendedDraftRestrictions(req.query.SELECT.where, req, scenarioAlias, model)
418
+
408
419
  if (req.query.SELECT.where) {
409
420
  _buildWhere(req.query.SELECT.where, table)
410
421
  }
422
+
411
423
  _buildOrderBy(req.query, cqn.SELECT.columns, table)
412
424
 
413
- 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
+ }
414
429
  }
415
430
 
416
431
  const _active = (req, draftWhere, columns) => {
@@ -420,7 +435,9 @@ const _active = (req, draftWhere, columns) => {
420
435
 
421
436
  const cqn = SELECT.from(table).columns(...outerMostColumns)
422
437
 
423
- 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 }
424
441
  }
425
442
 
426
443
  const _activeWithoutDraft = (req, draftWhere, columns) => {
@@ -448,7 +465,9 @@ const _activeWithoutDraft = (req, draftWhere, columns) => {
448
465
  .columns(...outerMostColumns)
449
466
  .where(['not exists', subSelect])
450
467
 
451
- 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 }
452
471
  }
453
472
 
454
473
  const _draftOfWhichIAmOwner = (req, draftWhere, columns) => {
@@ -468,9 +487,7 @@ const _draftOfWhichIAmOwner = (req, draftWhere, columns) => {
468
487
  ])
469
488
  .where(_inProcessByUserWhere(req.user.id))
470
489
 
471
- removeAnnotationWhere(draftWhere)
472
-
473
- 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 }
474
491
  }
475
492
 
476
493
  const _activeWithDraftInProcess = (req, draftWhere, columns, isLocked) => {
@@ -503,7 +520,7 @@ const _activeWithDraftInProcess = (req, draftWhere, columns, isLocked) => {
503
520
  'and',
504
521
  {
505
522
  func: 'seconds_between',
506
- args: [{ ref: ['filterAdmin', 'LastChangeDateTime'] }, { ref: ['CURRENT_TIMESTAMP'] }]
523
+ args: [{ ref: ['filterAdmin', 'LastChangeDateTime'] }, 'CURRENT_TIMESTAMP']
507
524
  },
508
525
  isLocked ? '<' : '>',
509
526
  { val: DRAFT_CANCEL_TIMEOUT_IN_SEC }
@@ -514,11 +531,13 @@ const _activeWithDraftInProcess = (req, draftWhere, columns, isLocked) => {
514
531
  subSelect
515
532
  )
516
533
 
534
+ subSelect.SELECT.where = _getWhereWithAppendedDraftRestrictions(subSelect.SELECT.where, req)
535
+
517
536
  const outerMostColumns = _getOuterMostColumns(columns, draftColumns)
518
537
 
519
538
  const cqn = SELECT.from(active.table).columns(outerMostColumns).where(['exists', subSelect])
520
539
 
521
- 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 }
522
541
  }
523
542
 
524
543
  const _alignAliasForUnion = (table, as, select) => {
@@ -535,37 +554,27 @@ const _alignAliasForUnion = (table, as, select) => {
535
554
  return select
536
555
  }
537
556
 
538
- const _isKeyElement = (element, keys) => element.ref && keys.includes(element.ref[element.ref.length - 1])
539
- const _valExists = (i, length, where) => i < length - 2 && where[i + 2].val
540
-
541
- const _findKeysInWhereAndAddToResult = (where, keys, result) => {
542
- for (let i = 0, length = where.length; i < length; i++) {
543
- const element = where[i]
544
- if (_isKeyElement(element, keys) && _valExists(i, length, where)) {
545
- result[element.ref[element.ref.length - 1]] = where[i + 2].val
546
- i = i + 2
547
- }
548
- }
549
- }
550
-
551
- const _findKeysInQuery = (query, keys) => {
552
- const result = {}
553
- if (query.SELECT && query.SELECT.where) {
554
- const indexExists = query.SELECT.where.indexOf('exists')
555
- if (indexExists !== -1) {
556
- return _findKeysInQuery(query.SELECT.where[indexExists + 1], keys)
557
- }
558
-
559
- _findKeysInWhereAndAddToResult(query.SELECT.where, keys, result)
560
- }
561
- 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 []
562
575
  }
563
576
 
564
- const _isFiltered = where => {
565
- return where.some(element => {
566
- return !(element in ['(', ')']) && element[SYMBOL_FROM_ANNOTATION] !== true
567
- })
568
- }
577
+ const _isFiltered = where => where.some(element => !(element in ['(', ')']))
569
578
 
570
579
  const _isDraftField = element => element.ref && element.ref.length > 1 && element.ref[0] === 'DraftAdministrativeData'
571
580
 
@@ -613,40 +622,112 @@ const _getWhereForActive = where => {
613
622
  return activeWhere
614
623
  }
615
624
 
616
- const _siblingEntity = (req, columns, model) => {
617
- const unlocalizedEntity = model.definitions[ensureUnlocalized(req.target.name)]
618
- const keys = _findKeysInQuery(req.query, Object.keys(unlocalizedEntity.keys))
619
- const isSiblingActive = !keys.IsActiveEntity
620
-
621
- const targetTable = isSiblingActive ? ensureNoDraftsSuffix(req.target.name) : ensureDraftsSuffix(req.target.name)
622
- const columnCqnPartial = columns.map(col => {
623
- const colName = col.ref ? col.ref[col.ref.length - 1] : col
624
- return { ref: ['target', colName], as: colName }
625
- })
626
- columnCqnPartial.push({ ref: ['draftAdmin', 'InProcessByUser'], as: 'draftAdmin_inProcessByUser' })
627
- 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
+ }
628
648
 
629
- if (isSiblingActive) {
649
+ if (isSiblingDraft) {
630
650
  cqn
631
- .join(ensureDraftsSuffix(req.target.name), 'target_draft')
632
- .on(
633
- Object.keys(keys)
634
- .map(k => `target.${k} = target_draft.${k}`)
635
- .join(' AND ')
636
- )
651
+ .join(ensureNoDraftsSuffix(target.name), siblingAlias)
652
+ .on(onCond)
637
653
  .join('DRAFT.DraftAdministrativeData', 'draftAdmin')
638
- .on('target_draft.DraftAdministrativeData_DraftUUID = draftAdmin.DraftUUID')
654
+ .on(`${table.as}.DraftAdministrativeData_DraftUUID = draftAdmin.DraftUUID`)
639
655
  } else {
640
656
  cqn
657
+ .join(ensureDraftsSuffix(target.name), siblingAlias)
658
+ .on(onCond)
641
659
  .join('DRAFT.DraftAdministrativeData', 'draftAdmin')
642
- .on('target.DraftAdministrativeData_DraftUUID = draftAdmin.DraftUUID')
660
+ .on(`${siblingAlias}.DraftAdministrativeData_DraftUUID = draftAdmin.DraftUUID`)
643
661
  }
644
662
 
645
663
  for (const key in keys) {
646
- 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])
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
+ }
678
+
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)
647
716
  }
717
+ return _getSiblingQueryFromWhere(req.query, siblingIndex)
718
+ }
648
719
 
649
- return { cqn, scenario: 'SIBLING_ENTITY', isSiblingActive: isSiblingActive }
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)
650
731
  }
651
732
 
652
733
  const _getDraftDoc = (req, draftName, draftWhere) => {
@@ -705,7 +786,7 @@ const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere) => {
705
786
  const activeDocs = getEnrichedCQN(SELECT.from(req.target), req.query.SELECT, draftActiveWhere, undefined, false)
706
787
 
707
788
  _replaceDraftAlias(draftWhere)
708
- const draftDocs = _getDraftDoc(req, draftName, removeAnnotationWhere(draftWhere))
789
+ const draftDocs = _getDraftDoc(req, draftName, draftWhere)
709
790
 
710
791
  const union = SELECT.from({ SET: { op: 'union', all: true, args: [draftDocs, activeDocs] } })
711
792
  if (req.query.SELECT.count) union.SELECT.count = true
@@ -730,17 +811,22 @@ const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere) => {
730
811
  const enrichedColumns = _getOrderByEnrichedColumns(req.query.SELECT.orderBy, columns)
731
812
 
732
813
  for (const col of enrichedColumns) {
814
+ // if we have columns for outer order by that may also be needed for joins, we need to duplicate them
815
+ const element = getElementDeep(req.target, col.ref)
816
+ if (element && element['@odata.foreignKey4']) columns.push({ ref: [...col.ref] })
817
+
733
818
  col.as = _poorMansAlias4(col)
734
819
  // add alias to outer order by
735
820
  const ob = req.query.SELECT.orderBy.find(ele => _poorMansAlias4(ele) === col.as)
736
821
  ob.ref = [col.as]
737
822
  }
738
823
 
739
- draftDocs.columns(
824
+ const draftColumns = [
740
825
  ...addColumnAlias([...columns, ...enrichedColumns], req.query.SELECT.from.as || draftName),
741
826
  ..._filterDraftColumnsBySelected(DRAFT_COLUMNS_CASTED, req.query.SELECT.columns),
742
827
  'DraftAdministrativeData_DraftUUID'
743
- )
828
+ ]
829
+ draftDocs.columns(draftColumns)
744
830
 
745
831
  const activeName = activeDocs.SELECT.from.as || (activeDocs.SELECT.from.ref && activeDocs.SELECT.from.ref[0])
746
832
 
@@ -751,19 +837,20 @@ const _getUnionCQN = (req, draftName, columns, subSelect, draftWhere) => {
751
837
  hasDraftWhere.push({ ref: [activeName, key] }, '=', { ref: [draftName, key] })
752
838
  }
753
839
 
754
- activeDocs
755
- .columns(
756
- ...columns,
757
- ...enrichedColumns,
758
- ..._filterDraftColumnsBySelected(
759
- _getDraftPropertiesDetermineDraft(req, hasDraftWhere, ensureDraftsSuffix(req.target.name), true),
760
- req.query.SELECT.columns
761
- )
840
+ const activeColumns = [
841
+ ...columns,
842
+ ...enrichedColumns,
843
+ ..._filterDraftColumnsBySelected(
844
+ _getDraftPropertiesDetermineDraft(req, hasDraftWhere, ensureDraftsSuffix(req.target.name), true),
845
+ req.query.SELECT.columns
762
846
  )
763
- .where([
764
- 'not exists',
765
- _alignAliasForUnion(ensureNoDraftsSuffix(req.target.name), req.query.SELECT.from.as, subSelect)
766
- ])
847
+ ]
848
+ activeDocs.columns(activeColumns)
849
+
850
+ activeDocs.where([
851
+ 'not exists',
852
+ _alignAliasForUnion(ensureNoDraftsSuffix(req.target.name), req.query.SELECT.from.as, subSelect)
853
+ ])
767
854
 
768
855
  // groupBy, orderBy and limit do not support partial CQNs
769
856
  if (req.query.SELECT.groupBy) {
@@ -802,6 +889,8 @@ const _excludeActiveDraftExists = (req, draftWhere, columns) => {
802
889
  subSelect.where([{ ref: [ensureNoDraftsSuffix(req.target.name), key] }, '=', { ref: [draftName, key] }])
803
890
  }
804
891
 
892
+ draftWhere = _getWhereWithAppendedDraftRestrictions(draftWhere, req)
893
+
805
894
  draftWhere = removeIsActiveEntityRecursively(draftWhere)
806
895
  const cqn = _getUnionCQN(req, draftName, columns, subSelect, draftWhere)
807
896
  cqn.SELECT.from.as = name
@@ -814,7 +903,7 @@ const _excludeActiveDraftExists = (req, draftWhere, columns) => {
814
903
  }
815
904
  }
816
905
 
817
- return { cqn: cqn, scenario: 'UNION' }
906
+ return { cqn: cqn, scenario: SCENARIO.UNION }
818
907
  }
819
908
 
820
909
  const _readDraftParameters = where => {
@@ -858,37 +947,46 @@ const _validatedActive = (req, draftWhere, draftParameters, columns) =>
858
947
  const _validatedDraftOfWhichIAmOwner = (req, draftWhere, draftParameters, columns) =>
859
948
  _isValidDraftOfWhichIAmOwner(draftParameters.isActiveEntity) && _draftOfWhichIAmOwner(req, draftWhere, columns)
860
949
 
861
- const _draftInSubSelect = where => {
950
+ const _draftInSubSelect = (where, req) => {
862
951
  return where.some(({ SELECT }) => {
863
952
  if (SELECT && SELECT.where) {
864
953
  const isActiveEntity = readAndDeleteKeywords(['IsActiveEntity'], SELECT.where, false)
865
954
  if (isActiveEntity) {
866
- return _isFalse(isActiveEntity.value.val)
955
+ const isFalse = _isFalse(isActiveEntity.value.val)
956
+ if (isFalse) SELECT.where = _getWhereWithAppendedDraftRestrictions(SELECT.where, req)
957
+ return isFalse
867
958
  }
868
959
 
869
- return _draftInSubSelect(SELECT.where)
960
+ return _draftInSubSelect(SELECT.where, req)
870
961
  }
871
962
 
872
963
  return false
873
964
  })
874
965
  }
875
966
 
876
- const _generateCQN = (reqOriginal, req, columns, serviceName, model) => {
877
- // REVISIT: get rid of getUrlObject
878
- if (req.getUrlObject && req.getUrlObject().path.endsWith('SiblingEntity')) {
879
- 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
+ }
880
982
  }
881
983
 
882
- if (
883
- req.target.query &&
884
- req.target.query._target &&
885
- req.target.query._target.name === 'DRAFT.DraftAdministrativeData'
886
- ) {
984
+ if (_isDraftAdminScenario(req)) {
887
985
  return _draftAdminTable(req)
888
986
  }
889
987
 
890
988
  if (!req.query.SELECT.where || !_isFiltered(req.query.SELECT.where)) {
891
- return _allActive(req, columns)
989
+ return _allActive(req, columns, model)
892
990
  }
893
991
 
894
992
  if (req.query.SELECT.where.length === 1 && req.query.SELECT.where[0].xpr) {
@@ -908,8 +1006,11 @@ const _generateCQN = (reqOriginal, req, columns, serviceName, model) => {
908
1006
  }
909
1007
 
910
1008
  if (!draftParameters.isActiveEntity) {
911
- if (_draftInSubSelect(req.query.SELECT.where)) {
912
- 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)
913
1014
  }
914
1015
  return _allActive(req, columns)
915
1016
  }
@@ -936,52 +1037,39 @@ const _getColumns = ({ query: { SELECT } }, model) => {
936
1037
  )
937
1038
  : getColumns(model.definitions[ensureNoDraftsSuffix(SELECT.from.ref[0])], {
938
1039
  onlyNames: true,
939
- removeIgnore: true,
940
- filterVirtual: true
1040
+ removeIgnore: true
941
1041
  })
942
1042
  }
943
1043
 
944
- const _isIsActiveEntity = element =>
945
- element.ref &&
946
- (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'
947
1045
 
948
1046
  const _adaptSubSelects = ({ SELECT: { from, where } }, scenario) => {
949
- if (!where) {
950
- return
951
- }
952
-
953
- let indexDel = -1
1047
+ if (!where) return
954
1048
 
955
1049
  if (scenario === 'ALL_INACTIVE') {
956
1050
  replaceRefWithDraft(from.ref)
957
1051
  }
958
- let _removeAnnotationWhere = false
959
1052
 
960
- for (let i = 0, len = where.length; i < len; i++) {
1053
+ for (let i = 0; i < where.length; i++) {
961
1054
  const element = where[i]
962
1055
 
963
- if (_isIsActiveEntity(element) && len > i + 2) {
964
- if (scenario !== 'ALL_INACTIVE' && _isFalse(where[i + 2].val)) {
965
- replaceRefWithDraft(from.ref)
966
- _removeAnnotationWhere = true
967
- // read from draft table because draft has draftUUID
968
- } 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
+ ) {
969
1061
  replaceRefWithDraft(from.ref)
970
- _removeAnnotationWhere = true
971
1062
  }
972
1063
 
973
- indexDel = i
974
- }
975
-
976
- 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) {
977
1070
  _adaptSubSelects(element, scenario)
978
1071
  }
979
1072
  }
980
-
981
- if (indexDel !== -1) {
982
- deleteCondition(indexDel, where)
983
- }
984
- if (_removeAnnotationWhere) removeAnnotationWhere(where)
985
1073
  }
986
1074
 
987
1075
  const _calculateDraftAdminColumns = (result, user) => {
@@ -1006,16 +1094,6 @@ const _adaptDraftColumnsForSiblingEntity = (result, isSiblingActive) => {
1006
1094
  result.HasActiveEntity = !isSiblingActive
1007
1095
  }
1008
1096
 
1009
- const _addAlias = (whereElement, aliases) => {
1010
- if (whereElement.ref) {
1011
- const table = whereElement.ref[0].replace(/\./g, '_')
1012
- const alias = aliases.get(table)
1013
- if (alias) {
1014
- whereElement.ref.splice(0, 1, aliases.get(table))
1015
- }
1016
- }
1017
- }
1018
-
1019
1097
  const _collectAliases = (from, aliases) => {
1020
1098
  if (from) {
1021
1099
  if (from.ref && from.as) {
@@ -1034,39 +1112,13 @@ const _collectAliases = (from, aliases) => {
1034
1112
  }
1035
1113
  }
1036
1114
 
1037
- const _adaptAliasForSubSelect = (subselect, aliases) => {
1038
- if (subselect.where) {
1039
- for (const whereElement of subselect.where) {
1040
- if (whereElement.ref) {
1041
- _addAlias(whereElement, aliases)
1042
- }
1043
-
1044
- if (whereElement.SELECT) {
1045
- _adaptAliasForSubSelect(whereElement.SELECT, aliases)
1046
- }
1047
- }
1048
- }
1049
- }
1050
-
1051
1115
  const _adaptAnnotationAliases = cqn => {
1052
1116
  const aliases = new Map()
1053
1117
  _collectAliases(cqn.SELECT.from, aliases)
1054
-
1055
- if (cqn.SELECT.where) {
1056
- for (const whereElement of cqn.SELECT.where) {
1057
- if (
1058
- whereElement.SELECT &&
1059
- whereElement.SELECT.from.ref &&
1060
- whereElement.SELECT.from.ref[SYMBOL_FROM_ANNOTATION] === true
1061
- ) {
1062
- _adaptAliasForSubSelect(whereElement.SELECT, aliases)
1063
- }
1064
- }
1065
- }
1066
1118
  }
1067
1119
 
1068
1120
  const calculateDraftTimeout = (scenario, result, deleteLastChangeDateTime) => {
1069
- if (scenario === 'DRAFT_ADMIN') {
1121
+ if (scenario === SCENARIO.DRAFT_ADMIN) {
1070
1122
  if (!draftIsLocked(result[0].LastChangeDateTime)) {
1071
1123
  result[0].InProcessByUser = ''
1072
1124
  }
@@ -1090,12 +1142,12 @@ const calculateDraftTimeout = (scenario, result, deleteLastChangeDateTime) => {
1090
1142
  }
1091
1143
 
1092
1144
  const enhanceQueryForTimeoutIfNeeded = (scenario, columns = []) => {
1093
- if (scenario !== 'DRAFT_ADMIN') {
1145
+ if (scenario !== SCENARIO.DRAFT_ADMIN) {
1094
1146
  const draftAdmin = columns.find(col => col.ref && col.ref[col.ref.length - 1] === 'DraftAdministrativeData')
1095
1147
  columns = (draftAdmin && draftAdmin.expand) || []
1096
1148
  }
1097
- const inProcessByUser = columns.find(col => col.ref[col.ref.length - 1] === 'InProcessByUser')
1098
- 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')
1099
1151
  if (inProcessByUser && !lastChangeDateTime) {
1100
1152
  columns.push({ ref: [...inProcessByUser.ref.slice(0, inProcessByUser.ref.length - 1), 'LastChangeDateTime'] })
1101
1153
  return true
@@ -1107,7 +1159,11 @@ const enhanceQueryForTimeoutIfNeeded = (scenario, columns = []) => {
1107
1159
  const _getLocalizedEntity = (model, target, user) => {
1108
1160
  const prefix = 'localized'
1109
1161
  let localizedEntity
1110
- 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)) {
1111
1167
  localizedEntity = model.definitions[`${prefix}.${user.locale}.${target.name}`]
1112
1168
  }
1113
1169
  return localizedEntity || model.definitions[`${prefix}.${target.name}`]
@@ -1118,12 +1174,10 @@ const _getLocalizedEntity = (model, target, user) => {
1118
1174
  *
1119
1175
  * @param req
1120
1176
  */
1177
+ // eslint-disable-next-line complexity
1121
1178
  const _handler = async function (req) {
1122
1179
  // handle localized here as it was previously handled for req.target
1123
1180
  req.target = _getLocalizedEntity(this.model, req.target, req.user) || req.target
1124
- if (req.query.SELECT.limit && req.query.SELECT.limit.rows && req.query.SELECT.limit.rows.val === 0) {
1125
- return Promise.resolve([])
1126
- }
1127
1181
 
1128
1182
  // REVISIT
1129
1183
  delete req.query._validationQuery
@@ -1134,18 +1188,22 @@ const _handler = async function (req) {
1134
1188
  // do not clone with Object.assign as that would skip all non-enumerable properties
1135
1189
  const reqClone = { __proto__: req, query: _copyCQNPartial(sqlQuery) }
1136
1190
 
1191
+ // ensure draft restrictions are copied to new query
1192
+ reqClone.query._draftRestrictions = req.query._draftRestrictions
1193
+
1137
1194
  if (req.query._streaming) {
1138
1195
  adaptStreamCQN(reqClone.query)
1139
1196
  reqClone.query._streaming = true
1140
1197
  return cds.tx(req).run(reqClone.query)
1141
1198
  }
1142
- const virtuals = removeVirtuals(reqClone, this.model)
1143
- 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)
1144
1201
 
1145
1202
  if (!cqnScenario) {
1146
1203
  req.reject(400)
1147
1204
  return
1148
1205
  }
1206
+
1149
1207
  const enhacnedWithLastChangeDateTime = enhanceQueryForTimeoutIfNeeded(
1150
1208
  cqnScenario.scenario,
1151
1209
  cqnScenario.cqn.SELECT.columns
@@ -1157,13 +1215,14 @@ const _handler = async function (req) {
1157
1215
 
1158
1216
  // unlocalize for db and after handlers as it was before
1159
1217
  req.target = this.model.definitions[ensureUnlocalized(req.target.name)]
1160
- const result = await cds.tx(req).run(cqnScenario.cqn)
1218
+
1219
+ const result = await cds.tx(req).send({ query: cqnScenario.cqn, target: req.target })
1161
1220
 
1162
1221
  const resultAsArray = Array.isArray(result) ? result : result ? [result] : []
1163
1222
 
1164
1223
  removeDraftUUIDIfNecessary(resultAsArray, req)
1165
1224
 
1166
- if (cqnScenario.scenario === 'DRAFT_ADMIN') {
1225
+ if (cqnScenario.scenario === SCENARIO.DRAFT_ADMIN) {
1167
1226
  if (!result || (Array.isArray(result) && !result.length)) return result
1168
1227
 
1169
1228
  _calculateDraftAdminColumns(resultAsArray[0], req.user.id)
@@ -1171,12 +1230,9 @@ const _handler = async function (req) {
1171
1230
 
1172
1231
  calculateDraftTimeout(cqnScenario.scenario, resultAsArray, enhacnedWithLastChangeDateTime)
1173
1232
 
1174
- if (cqnScenario.scenario === 'SIBLING_ENTITY') {
1233
+ if (cqnScenario.scenario === SCENARIO.SIBLING_ENTITY) {
1175
1234
  if (!result || (Array.isArray(result) && !result.length)) return result
1176
-
1177
- if (resultAsArray[0].draftAdmin_inProcessByUser !== req.user.id) {
1178
- return []
1179
- }
1235
+ if (resultAsArray[0].draftAdmin_inProcessByUser !== req.user.id) return []
1180
1236
 
1181
1237
  delete resultAsArray[0].draftAdmin_inProcessByUser
1182
1238
  _adaptDraftColumnsForSiblingEntity(resultAsArray[0], cqnScenario.isSiblingActive)
@@ -1187,7 +1243,7 @@ const _handler = async function (req) {
1187
1243
  row.DraftAdministrativeData && _calculateDraftAdminColumns(row.DraftAdministrativeData, req.user.id)
1188
1244
  })
1189
1245
  }
1190
- postProcessVirtuals(virtuals, resultAsArray)
1246
+
1191
1247
  return result
1192
1248
  }
1193
1249