@sap/cds 7.9.2 → 8.0.3

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 (279) hide show
  1. package/CHANGELOG.md +139 -3656
  2. package/_i18n/i18n_en_US_saptrc.properties +113 -0
  3. package/_i18n/i18n_zh_CN.properties +7 -4
  4. package/app/index.css +129 -0
  5. package/app/index.html +16 -64
  6. package/app/index.js +14 -9
  7. package/bin/args.js +34 -0
  8. package/bin/serve.js +18 -24
  9. package/bin/test.js +97 -0
  10. package/common.cds +5 -12
  11. package/eslint.config.mjs +133 -0
  12. package/lib/auth/basic-auth.js +16 -20
  13. package/lib/auth/dummy-auth.js +1 -1
  14. package/lib/auth/ias-auth.js +12 -30
  15. package/lib/auth/index.js +1 -14
  16. package/lib/auth/jwt-auth.js +14 -30
  17. package/lib/compile/cds-compile.js +1 -2
  18. package/lib/compile/cdsc.js +21 -26
  19. package/lib/compile/etc/_localized.js +1 -6
  20. package/lib/compile/etc/csv.js +1 -1
  21. package/lib/compile/etc/properties.js +1 -1
  22. package/lib/compile/for/java.js +1 -1
  23. package/lib/compile/for/lean_drafts.js +4 -6
  24. package/lib/compile/for/nodejs.js +1 -1
  25. package/lib/compile/parse.js +4 -0
  26. package/lib/compile/resolve.js +4 -4
  27. package/lib/compile/to/edm-files.js +16 -23
  28. package/lib/compile/to/hana.js +27 -0
  29. package/lib/compile/to/json.js +1 -1
  30. package/lib/compile/to/sql.js +5 -1
  31. package/lib/compile/to/srvinfo.js +1 -1
  32. package/lib/compile/to/yaml.js +3 -3
  33. package/lib/dbs/cds-deploy.js +4 -2
  34. package/lib/env/cds-env.js +10 -14
  35. package/lib/env/cds-requires.js +29 -13
  36. package/lib/env/defaults.js +46 -16
  37. package/lib/env/plugins.js +1 -1
  38. package/lib/env/schemas/cds-rc.js +8 -4
  39. package/lib/env/schemas/index.js +7 -7
  40. package/lib/env/serviceBindings.js +1 -1
  41. package/lib/index.js +12 -10
  42. package/lib/lazy.js +1 -1
  43. package/lib/linked/classes.js +36 -8
  44. package/lib/linked/entities.js +2 -10
  45. package/lib/linked/models.js +2 -1
  46. package/lib/linked/validate.js +292 -0
  47. package/lib/log/cds-error.js +0 -6
  48. package/lib/log/cds-log.js +3 -3
  49. package/lib/log/format/json.js +1 -1
  50. package/lib/log/service/index.js +0 -1
  51. package/lib/plugins.js +3 -3
  52. package/lib/ql/Query.js +2 -10
  53. package/lib/ql/SELECT.js +1 -1
  54. package/lib/ql/Whereable.js +3 -2
  55. package/lib/req/cds-context.js +14 -25
  56. package/lib/req/context.js +23 -25
  57. package/lib/req/request.js +1 -34
  58. package/lib/req/user.js +47 -35
  59. package/lib/srv/bindings.js +1 -1
  60. package/lib/srv/cds-connect.js +4 -4
  61. package/lib/srv/cds-serve.js +2 -2
  62. package/lib/srv/factory.js +1 -1
  63. package/lib/srv/middlewares/cds-context.js +11 -22
  64. package/lib/srv/middlewares/ctx-model.js +2 -3
  65. package/lib/srv/middlewares/errors.js +41 -8
  66. package/lib/srv/middlewares/index.js +3 -3
  67. package/lib/srv/middlewares/trace.js +0 -2
  68. package/lib/srv/protocols/hcql.js +15 -10
  69. package/lib/srv/protocols/http.js +44 -49
  70. package/lib/srv/protocols/index.js +1 -23
  71. package/lib/srv/protocols/odata-v4.js +12 -74
  72. package/lib/srv/protocols/rest.js +1 -13
  73. package/lib/srv/srv-api.js +0 -20
  74. package/lib/srv/srv-dispatch.js +3 -2
  75. package/lib/srv/srv-handlers.js +22 -11
  76. package/lib/srv/srv-methods.js +2 -2
  77. package/lib/srv/srv-models.js +3 -36
  78. package/lib/test/expect.js +343 -0
  79. package/lib/test/index.js +2 -0
  80. package/lib/test/reporter.js +176 -0
  81. package/lib/utils/axios.js +10 -9
  82. package/lib/utils/cds-test.js +86 -37
  83. package/lib/utils/cds-utils.js +54 -7
  84. package/lib/utils/check-version.js +0 -4
  85. package/lib/utils/colors.js +49 -0
  86. package/lib/utils/data.js +5 -4
  87. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -7
  88. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +3 -30
  89. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +6 -12
  90. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -3
  91. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +0 -1
  92. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +4 -7
  93. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +12 -6
  94. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +2 -4
  95. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +1 -0
  96. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
  97. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
  98. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +1 -3
  99. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
  100. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +1 -2
  101. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +5 -0
  102. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +1 -1
  103. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +9 -43
  104. package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +0 -1
  105. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +8 -3
  106. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +4 -2
  107. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -3
  108. package/libx/_runtime/cds-services/util/assert.js +1 -1
  109. package/libx/_runtime/cds.js +10 -3
  110. package/libx/_runtime/common/Service.js +12 -32
  111. package/libx/_runtime/common/aspects/any.js +1 -0
  112. package/libx/_runtime/common/code-ext/execute.js +1 -1
  113. package/libx/_runtime/common/code-ext/worker.js +0 -1
  114. package/libx/_runtime/common/composition/data.js +0 -1
  115. package/libx/_runtime/common/composition/delete.js +0 -1
  116. package/libx/_runtime/common/composition/insert.js +2 -2
  117. package/libx/_runtime/common/composition/tree.js +0 -1
  118. package/libx/_runtime/common/composition/update.js +3 -3
  119. package/libx/_runtime/common/error/frontend.js +21 -12
  120. package/libx/_runtime/common/error/log.js +36 -0
  121. package/libx/_runtime/common/error/utils.js +2 -5
  122. package/libx/_runtime/common/generic/auth/autoexpose.js +18 -17
  123. package/libx/_runtime/common/generic/auth/expand.js +1 -1
  124. package/libx/_runtime/common/generic/auth/readOnly.js +1 -2
  125. package/libx/_runtime/common/generic/auth/restrict.js +23 -42
  126. package/libx/_runtime/common/generic/auth/restrictions.js +2 -7
  127. package/libx/_runtime/common/generic/auth/utils.js +91 -88
  128. package/libx/_runtime/common/generic/crud.js +6 -5
  129. package/libx/_runtime/common/generic/etag.js +7 -12
  130. package/libx/_runtime/common/generic/input.js +70 -68
  131. package/libx/_runtime/common/generic/paging.js +1 -0
  132. package/libx/_runtime/common/generic/sorting.js +1 -0
  133. package/libx/_runtime/common/generic/temporal.js +8 -2
  134. package/libx/_runtime/common/i18n/index.js +1 -1
  135. package/libx/_runtime/common/i18n/messages.properties +3 -1
  136. package/libx/_runtime/common/utils/binary.js +8 -2
  137. package/libx/_runtime/common/utils/compareJson.js +5 -1
  138. package/libx/_runtime/common/utils/copy.js +6 -11
  139. package/libx/_runtime/common/utils/cqn2cqn4sql.js +16 -14
  140. package/libx/_runtime/common/utils/differ.js +3 -6
  141. package/libx/_runtime/common/utils/keys.js +77 -18
  142. package/libx/_runtime/common/utils/postProcess.js +12 -15
  143. package/libx/_runtime/common/utils/propagateForeignKeys.js +0 -1
  144. package/libx/_runtime/common/utils/resolveView.js +2 -3
  145. package/libx/_runtime/common/utils/restrictions.js +45 -17
  146. package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -8
  147. package/libx/_runtime/common/utils/stream.js +3 -16
  148. package/libx/_runtime/common/utils/streamProp.js +8 -18
  149. package/libx/_runtime/common/utils/structured.js +1 -1
  150. package/libx/_runtime/common/utils/ucsn.js +0 -2
  151. package/libx/_runtime/db/Service.js +0 -72
  152. package/libx/_runtime/db/data-conversion/post-processing.js +0 -1
  153. package/libx/_runtime/db/expand/expandCQNToJoin.js +9 -9
  154. package/libx/_runtime/db/expand/rawToExpanded.js +0 -8
  155. package/libx/_runtime/db/generic/input.js +3 -8
  156. package/libx/_runtime/db/generic/rewrite.js +27 -4
  157. package/libx/_runtime/db/query/read.js +2 -2
  158. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -1
  159. package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
  160. package/libx/_runtime/db/utils/columns.js +2 -6
  161. package/libx/_runtime/fiori/lean-draft.js +138 -56
  162. package/libx/_runtime/hana/Service.js +0 -1
  163. package/libx/_runtime/hana/driver.js +1 -1
  164. package/libx/_runtime/hana/dynatrace.js +1 -2
  165. package/libx/_runtime/hana/pool.js +11 -21
  166. package/libx/_runtime/hana/streaming.js +0 -1
  167. package/libx/_runtime/messaging/common-utils/AMQPClient.js +0 -1
  168. package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
  169. package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +1 -1
  170. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
  171. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -33
  172. package/libx/_runtime/messaging/event-broker.js +0 -12
  173. package/libx/_runtime/messaging/file-based.js +3 -3
  174. package/libx/_runtime/messaging/http-utils/token.js +1 -1
  175. package/libx/_runtime/messaging/kafka.js +2 -2
  176. package/libx/_runtime/messaging/redis-messaging.js +0 -1
  177. package/libx/_runtime/remote/Service.js +25 -25
  178. package/libx/_runtime/remote/utils/client.js +4 -5
  179. package/libx/_runtime/remote/utils/cloudSdkProvider.js +0 -3
  180. package/libx/_runtime/remote/utils/data.js +0 -1
  181. package/libx/_runtime/sqlite/Service.js +1 -2
  182. package/libx/_runtime/ucl/Service.js +37 -78
  183. package/libx/common/assert/index.js +22 -21
  184. package/libx/common/assert/type-relaxed.js +39 -0
  185. package/libx/common/assert/utils.js +3 -2
  186. package/libx/common/assert/validation.js +3 -8
  187. package/libx/common/utils/index.js +5 -0
  188. package/libx/common/utils/path.js +51 -0
  189. package/libx/odata/ODataAdapter.js +126 -0
  190. package/libx/odata/index.js +15 -2
  191. package/libx/odata/middleware/batch.js +261 -72
  192. package/libx/odata/middleware/body-parser.js +33 -0
  193. package/libx/odata/middleware/create.js +44 -59
  194. package/libx/odata/middleware/delete.js +23 -12
  195. package/libx/odata/middleware/error.js +30 -6
  196. package/libx/odata/middleware/metadata.js +38 -26
  197. package/libx/odata/middleware/operation.js +93 -69
  198. package/libx/odata/middleware/parse.js +6 -8
  199. package/libx/odata/middleware/read.js +117 -93
  200. package/libx/odata/middleware/service-document.js +22 -19
  201. package/libx/odata/middleware/stream.js +54 -56
  202. package/libx/odata/middleware/update.js +79 -87
  203. package/libx/odata/parse/afterburner.js +191 -175
  204. package/libx/odata/parse/cqn2odata.js +8 -8
  205. package/libx/odata/parse/grammar.peggy +27 -20
  206. package/libx/odata/parse/multipartToJson.js +17 -9
  207. package/libx/odata/parse/parser.js +1 -1
  208. package/libx/odata/utils/etag.js +14 -6
  209. package/libx/odata/utils/index.js +84 -12
  210. package/libx/odata/utils/metadata.js +161 -0
  211. package/libx/odata/utils/postProcess.js +89 -0
  212. package/libx/odata/utils/readAfterWrite.js +134 -17
  213. package/libx/odata/utils/result.js +36 -142
  214. package/libx/outbox/index.js +5 -4
  215. package/libx/rest/RestAdapter.js +115 -182
  216. package/libx/rest/middleware/create.js +28 -24
  217. package/libx/rest/middleware/delete.js +7 -10
  218. package/libx/rest/middleware/error.js +19 -16
  219. package/libx/rest/middleware/operation.js +48 -41
  220. package/libx/rest/middleware/parse.js +128 -126
  221. package/libx/rest/middleware/read.js +20 -27
  222. package/libx/rest/middleware/update.js +26 -31
  223. package/package.json +16 -12
  224. package/server.js +4 -2
  225. package/tasks/enterprise-messaging-deploy.js +1 -1
  226. package/apis/cds.d.ts +0 -3
  227. package/apis/core.d.ts +0 -21
  228. package/apis/cqn.d.ts +0 -18
  229. package/apis/csn.d.ts +0 -21
  230. package/apis/events.d.ts +0 -18
  231. package/apis/internal/inference.d.ts +0 -18
  232. package/apis/linked.d.ts +0 -18
  233. package/apis/log.d.ts +0 -20
  234. package/apis/models.d.ts +0 -18
  235. package/apis/ql.d.ts +0 -18
  236. package/apis/reflect.d.ts +0 -32
  237. package/apis/server.d.ts +0 -18
  238. package/apis/services.d.ts +0 -22
  239. package/bin/cds-serve.js +0 -56
  240. package/lib/compile/to/gql.js +0 -15
  241. package/lib/srv/protocols/_legacy.js +0 -44
  242. package/lib/utils/jest.js +0 -43
  243. package/libx/_runtime/auth/index.js +0 -193
  244. package/libx/_runtime/auth/strategies/JWT.js +0 -37
  245. package/libx/_runtime/auth/strategies/basic.js +0 -20
  246. package/libx/_runtime/auth/strategies/dummy.js +0 -14
  247. package/libx/_runtime/auth/strategies/ias-auth.js +0 -1
  248. package/libx/_runtime/auth/strategies/mock.js +0 -77
  249. package/libx/_runtime/auth/strategies/xssecUtils.js +0 -93
  250. package/libx/_runtime/auth/strategies/xsuaa.js +0 -38
  251. package/libx/_runtime/common/perf/index.js +0 -19
  252. package/libx/_runtime/common/utils/ensureIEEE754.js +0 -29
  253. package/libx/_runtime/fiori/draft.js +0 -2
  254. package/libx/_runtime/fiori/generic/activate.js +0 -190
  255. package/libx/_runtime/fiori/generic/before.js +0 -201
  256. package/libx/_runtime/fiori/generic/cancel.js +0 -19
  257. package/libx/_runtime/fiori/generic/delete.js +0 -21
  258. package/libx/_runtime/fiori/generic/edit.js +0 -157
  259. package/libx/_runtime/fiori/generic/index.js +0 -25
  260. package/libx/_runtime/fiori/generic/new.js +0 -82
  261. package/libx/_runtime/fiori/generic/patch.js +0 -101
  262. package/libx/_runtime/fiori/generic/prepare.js +0 -57
  263. package/libx/_runtime/fiori/generic/read.js +0 -1340
  264. package/libx/_runtime/fiori/generic/readOverDraft.js +0 -146
  265. package/libx/_runtime/fiori/utils/csn.js +0 -13
  266. package/libx/_runtime/fiori/utils/delete.js +0 -114
  267. package/libx/_runtime/fiori/utils/handler.js +0 -264
  268. package/libx/_runtime/fiori/utils/lockInfo.js +0 -27
  269. package/libx/_runtime/fiori/utils/req.js +0 -23
  270. package/libx/_runtime/fiori/utils/stream.js +0 -36
  271. package/libx/_runtime/fiori/utils/where.js +0 -254
  272. package/libx/_runtime/index.js +0 -22
  273. package/libx/odata/utils/handler.js +0 -120
  274. package/libx/odata/utils/metaInfo.js +0 -410
  275. package/libx/odata/utils/path.js +0 -75
  276. package/libx/rest/RestRequest.js +0 -32
  277. package/libx/rest/index.js +0 -3
  278. package/libx/rest/readme.md +0 -1
  279. /package/libx/common/assert/{type.js → type-strict.js} +0 -0
@@ -1,4 +1,6 @@
1
- const cds = require('../../../')
1
+ const cds = require('../../../lib')
2
+
3
+ const { keysOf, addRefToWhereIfNecessary } = require('../utils')
2
4
 
3
5
  const { where2obj, resolveFromSelect, targetFromPath } = require('../../_runtime/common/utils/cqn')
4
6
  const { findCsnTargetFor } = require('../../_runtime/common/utils/csn')
@@ -6,40 +8,10 @@ const normalizeTimestamp = require('../../_runtime/common/utils/normalizeTimesta
6
8
  const { rewriteExpandAsterisk } = require('../../_runtime/common/utils/rewriteAsterisks')
7
9
  const resolveStructured = require('../../_runtime/common/utils/resolveStructured')
8
10
 
9
- const _addKeysDeep = (keys, keysCollector, ignoreManagedBacklinks) => {
10
- for (const keyName in keys) {
11
- const key = keys[keyName]
12
- const foreignKey = key._foreignKey4
13
- if (key.isAssociation || foreignKey === 'up_' || key['@cds.api.ignore'] === true) continue
14
-
15
- if (ignoreManagedBacklinks && foreignKey) {
16
- const navigationElement = keys[foreignKey]
17
- if (!navigationElement.on && navigationElement._isBacklink) {
18
- // skip navigation elements that are backlinks
19
- continue
20
- }
21
- }
22
-
23
- if ('elements' in key) {
24
- _addKeysDeep(key.elements, keysCollector)
25
- continue
26
- }
27
- keysCollector.push(keyName)
28
- }
29
- }
30
-
31
- function _keysOf(entity, ignoreManagedBacklinks) {
32
- const keysCollector = []
33
- if (!entity || !entity.keys) return keysCollector
34
-
35
- _addKeysDeep(entity.keys, keysCollector, ignoreManagedBacklinks)
36
- return keysCollector
37
- }
38
-
39
11
  function _getDefinition(definition, name, namespace) {
40
12
  return (
41
- (definition.definitions && definition.definitions[name]) ||
42
- (definition.elements && definition.elements[name]) ||
13
+ definition?.definitions?.[name] ||
14
+ definition?.elements?.[name] ||
43
15
  (definition.actions && (definition.actions[name] || definition.actions[name.replace(namespace + '.', '')])) ||
44
16
  definition[name]
45
17
  )
@@ -48,6 +20,7 @@ function _getDefinition(definition, name, namespace) {
48
20
  function _resolveAliasesInRef(ref, target) {
49
21
  if (ref.length === 1) {
50
22
  if (target.keys[ref[0]]) return ref
23
+
51
24
  // resolve multi-part refs for innermost ref in url
52
25
  if (target._flattenedKeys === undefined) {
53
26
  const flattenedKeys = []
@@ -55,11 +28,14 @@ function _resolveAliasesInRef(ref, target) {
55
28
  if (!target.keys[key].elements) continue
56
29
  flattenedKeys.push(...resolveStructured({ element: target.keys[key], structProperties: [] }, false, true))
57
30
  }
31
+
58
32
  target._flattenedKeys = flattenedKeys.length ? flattenedKeys : null
59
33
  }
34
+
60
35
  const fk = target._flattenedKeys?.find(fk => fk.key === ref[0])
61
36
  if (fk) return [...fk.resolved]
62
37
  }
38
+
63
39
  for (const seg of ref) {
64
40
  target = target.elements[seg.id || seg]
65
41
  if (!target) return ref
@@ -68,11 +44,13 @@ function _resolveAliasesInRef(ref, target) {
68
44
  if (seg.where) _resolveAliasesInXpr(seg.where, target)
69
45
  }
70
46
  }
47
+
71
48
  return ref
72
49
  }
73
50
 
74
51
  function _resolveAliasesInXpr(xpr, target) {
75
52
  if (!target || !xpr) return
53
+
76
54
  for (const el of xpr) {
77
55
  if (el.xpr) _resolveAliasesInXpr(el.xpr, target)
78
56
  if (el.args) _resolveAliasesInXpr(el.args, target)
@@ -82,6 +60,7 @@ function _resolveAliasesInXpr(xpr, target) {
82
60
 
83
61
  function _resolveAliasesInNavigation(cqn, target) {
84
62
  if (!target || !cqn) return
63
+
85
64
  if (cqn.SELECT.from.SELECT) _resolveAliasesInNavigation(cqn.SELECT.from, target)
86
65
  if (cqn.SELECT.where) _resolveAliasesInXpr(cqn.SELECT.where, target)
87
66
  if (cqn.SELECT.having) _resolveAliasesInXpr(cqn.SELECT.having, target)
@@ -90,35 +69,25 @@ function _resolveAliasesInNavigation(cqn, target) {
90
69
  function _addDefaultParams(ref, view) {
91
70
  const params = view.params
92
71
  const defaults = params && Object.values(params).filter(p => p.default)
72
+
93
73
  if (defaults && defaults.length > 0) {
94
74
  if (!ref.where) ref.where = []
95
75
  for (const def of defaults) {
96
- if (ref.where.find(e => e.ref && e.ref[0] === def.name)) {
97
- continue
98
- }
76
+ if (ref.where.find(e => e.ref && e.ref[0] === def.name)) continue
99
77
  if (ref.where.length > 0) ref.where.push('and')
100
78
  ref.where.push({ ref: [def.name] }, '=', { val: def.default.val })
101
79
  }
102
80
  }
103
81
  }
104
82
 
105
- // case: single key without name, e.g., Foo(1)
106
- function addRefToWhereIfNecessary(where, entity) {
107
- if (!where || where.length !== 1) return 0
108
-
109
- const isView = !!entity.params
110
-
111
- const keys = isView ? Object.keys(entity.params) : _keysOf(entity)
112
- if (keys.length !== 1) return 0
113
- where.unshift(...[{ ref: [keys[0]] }, '='])
114
- return 1
115
- }
116
-
117
83
  function getResolvedElement(entity, { ref }) {
118
84
  const element = entity.elements[ref[0]]
85
+
119
86
  if (element && element.isAssociation && ref.length > 1) {
120
87
  return getResolvedElement(element._target, { ref: ref.slice(1) })
121
- } else if (element && element._isStructured) {
88
+ }
89
+
90
+ if (element && element._isStructured) {
122
91
  return getResolvedElement(element, { ref: ref.slice(1) })
123
92
  }
124
93
 
@@ -133,18 +102,15 @@ function _processWhere(where, entity) {
133
102
  const operator = where[i + 1]
134
103
  const val = where[i + 2]
135
104
 
136
- if (ref in forbidden || val in forbidden || ref.func) {
137
- continue
138
- }
105
+ if (ref in forbidden || val in forbidden || ref.func) continue
106
+
139
107
  if (ref.xpr) {
140
108
  _processWhere(ref.xpr, entity)
141
109
  continue
142
110
  }
143
111
 
144
- if (operator in forbidden) {
145
- // xpr check needs to be done first, else it could happen, that we ignore xpr OR xpr
146
- continue
147
- }
112
+ // xpr check needs to be done first, else it could happen, that we ignore xpr OR xpr
113
+ if (operator in forbidden) continue
148
114
 
149
115
  let valIndex = -1
150
116
  let refIndex = -1
@@ -152,6 +118,7 @@ function _processWhere(where, entity) {
152
118
  if (val.val !== undefined) valIndex = i + 2
153
119
  if (val.ref != undefined) refIndex = i + 2
154
120
  }
121
+
155
122
  if (typeof ref === 'object') {
156
123
  if (ref.val !== undefined) valIndex = i
157
124
  if (ref.ref != undefined) refIndex = i
@@ -172,6 +139,7 @@ function _processWhere(where, entity) {
172
139
 
173
140
  function _convertVal(value, element) {
174
141
  if (value === null) return value
142
+
175
143
  switch (element._type) {
176
144
  // numbers
177
145
  case 'cds.UInt8':
@@ -182,33 +150,42 @@ function _convertVal(value, element) {
182
150
  const msg = `Element "${element.name}" does not contain a valid Integer`
183
151
  throw Object.assign(new Error(msg), { statusCode: 400 })
184
152
  }
153
+
185
154
  // eslint-disable-next-line no-case-declarations
186
155
  const n = Number(value)
187
156
  if (!Number.isSafeInteger(n)) {
188
157
  const msg = `Element "${element.name}" does not contain a valid Integer`
189
158
  throw Object.assign(new Error(msg), { statusCode: 400 })
190
159
  }
160
+
191
161
  if (element._type === 'cds.UInt8' && n < 0) {
192
162
  const msg = `Element "${element.name}" does not contain a valid positive Integer`
193
163
  throw Object.assign(new Error(msg), { statusCode: 400 })
194
164
  }
165
+
195
166
  return n
167
+
196
168
  case 'cds.Double':
197
169
  return parseFloat(value)
170
+
198
171
  case 'cds.Decimal':
199
172
  case 'cds.DecimalFloat':
200
173
  case 'cds.Int64':
201
174
  case 'cds.Integer64':
202
175
  if (typeof value === 'string') return value
203
176
  return String(value)
177
+
204
178
  // others
205
179
  case 'cds.String':
206
180
  case 'cds.LargeString':
207
181
  return String(value)
182
+
208
183
  case 'cds.Boolean':
209
184
  return typeof value === 'string' ? value === 'true' : value
185
+
210
186
  case 'cds.Timestamp':
211
187
  return normalizeTimestamp(value)
188
+
212
189
  default:
213
190
  return value
214
191
  }
@@ -220,10 +197,12 @@ const getStructRef = (element, ref = []) => {
220
197
  getStructRef(element.parent, ref)
221
198
  ref.push(element.name)
222
199
  }
200
+
223
201
  if (element.parent.kind === 'entity') {
224
202
  ref.push(element.name)
225
203
  }
226
204
  }
205
+
227
206
  return ref
228
207
  }
229
208
 
@@ -232,46 +211,71 @@ const getStructTargetName = element => {
232
211
  if (element.parent.kind === 'element') {
233
212
  return getStructTargetName(element.parent)
234
213
  }
214
+
235
215
  if (element.elements && element.parent.kind === 'entity') {
236
216
  return element.parent.name
237
217
  }
238
218
  }
239
219
  }
240
220
 
241
- function _processSegments(from, model, namespace, cqn, protocol) {
242
- const { ref } = from
221
+ const _getDataFromParams = (params, operation) => {
222
+ try {
223
+ return Object.keys(params).reduce((acc, cur) => {
224
+ acc[cur] =
225
+ typeof params[cur] === 'string' && (operation.params[cur]?.elements || operation.params[cur]?.items)
226
+ ? JSON.parse(params[cur])
227
+ : params[cur]
228
+ return acc
229
+ }, {})
230
+ } catch (e) {
231
+ throw Object.assign(e, { statusCode: 400, internal: e.message, message: 'Malformed parameters' })
232
+ }
233
+ }
243
234
 
244
- let current = model
245
- let path
246
- let keys = null
247
- let keyCount = 0
248
- let incompleteKeys = false
249
- let one
250
- let target
251
-
252
- function _handleCollectionBoundActions(i) {
253
- let action
254
- if (current.actions) {
255
- const nextRef = typeof ref[i + 1] === 'string' && ref[i + 1]
256
- const shortName = nextRef && nextRef.replace(namespace + '.', '')
257
- action = shortName && current.actions[shortName]
258
- }
235
+ function _handleCollectionBoundActions(current, ref, i, namespace, one) {
236
+ let action
259
237
 
260
- incompleteKeys = ref[i].where ? false : i === ref.length - 1 || one ? false : true
238
+ if (current.actions) {
239
+ const nextRef = (typeof ref[i + 1] === 'string' && ref[i + 1]) || ref[i + 1]?.id
240
+ const shortName = nextRef && nextRef.replace(namespace + '.', '')
241
+ action = shortName && current.actions[shortName]
242
+ }
261
243
 
262
- if (incompleteKeys && action) {
263
- if (
264
- action['@cds.odata.bindingparameter.collection'] ||
265
- (action.params && Object.values(action.params).some(e => e?.items?.type === '$self'))
266
- ) {
267
- incompleteKeys = false
268
- } else {
269
- const msg = `${action.kind[0].toUpperCase() + action.kind.slice(1)} "${action.name}" must be called on a single instance of ${current.name}`
270
- throw Object.assign(new Error(msg), { statusCode: 400 })
271
- }
244
+ let incompleteKeys = ref[i].where ? false : i === ref.length - 1 || one ? false : true
245
+ if (!action) return incompleteKeys
246
+
247
+ const onCollection = !!(
248
+ action['@cds.odata.bindingparameter.collection'] || action?.params?.some(p => p?.items?.type === '$self')
249
+ )
250
+
251
+ if (onCollection && one) {
252
+ const msg = `${action.kind.at(0).toUpperCase() + action.kind.slice(1)} "${action.name}" must be called on a collection of ${current.name}`
253
+ throw Object.assign(new Error(msg), { statusCode: 400 })
254
+ }
255
+
256
+ if (incompleteKeys) {
257
+ if (!onCollection) {
258
+ const msg = `${action.kind.at(0).toUpperCase() + action.kind.slice(1)} "${action.name}" must be called on a single instance of ${current.name}`
259
+ throw Object.assign(new Error(msg), { statusCode: 400 })
272
260
  }
261
+
262
+ incompleteKeys = false
273
263
  }
274
264
 
265
+ return incompleteKeys
266
+ }
267
+
268
+ function _processSegments(from, model, namespace, cqn, protocol) {
269
+ const { ref } = from
270
+
271
+ let current = model,
272
+ path,
273
+ keys = null,
274
+ keyCount = 0,
275
+ incompleteKeys = false,
276
+ one,
277
+ target
278
+
275
279
  for (let i = 0; i < ref.length; i++) {
276
280
  const seg = ref[i].id || ref[i]
277
281
  const whereRef = ref[i].where
@@ -279,7 +283,7 @@ function _processSegments(from, model, namespace, cqn, protocol) {
279
283
 
280
284
  if (incompleteKeys) {
281
285
  // > key
282
- keys = keys || _keysOf(current, protocol !== 'rest') // if odata, skip backlinks as key as they are used from structure
286
+ keys = keys || keysOf(current, protocol !== 'rest') // if odata, skip backlinks as key as they are used from structure
283
287
  let key = keys[keyCount++]
284
288
  one = true
285
289
  const element = current.elements[key]
@@ -297,6 +301,7 @@ function _processSegments(from, model, namespace, cqn, protocol) {
297
301
  const val = _convertVal(seg, element)
298
302
  base.where.push({ ref: [key] }, '=', { val })
299
303
  }
304
+
300
305
  ref[i] = null
301
306
  ref[i - keyCount] = base
302
307
  incompleteKeys = keyCount < keys.length
@@ -307,14 +312,17 @@ function _processSegments(from, model, namespace, cqn, protocol) {
307
312
  one = false
308
313
 
309
314
  path = path ? path + `${path.match(/:/) ? '.' : ':'}${seg}` : seg
315
+
310
316
  // REVISIT: replace use case: <namespace>.<entity>_history is at <namespace>.<entity>.history
311
317
  current = _getDefinition(current, seg, namespace) || _getDefinition(current, seg.replace(/_/g, '.'), namespace)
318
+
312
319
  // REVISIT: 404 or 400?
313
320
  if (!current) cds.error(`Invalid resource path "${path}"`, { code: '404', statusCode: 404 })
314
321
 
315
322
  if (current.params && current.kind === 'entity') {
316
323
  // > View with params
317
324
  target = current
325
+
318
326
  if (whereRef) {
319
327
  keyCount += addRefToWhereIfNecessary(ref[i].where, current)
320
328
  _resolveAliasesInXpr(ref[i].where, current)
@@ -323,9 +331,7 @@ function _processSegments(from, model, namespace, cqn, protocol) {
323
331
 
324
332
  _addDefaultParams(ref[i], current)
325
333
  if ((!params || !Object.keys(params).length) && ref[i].where) params = where2obj(ref[i].where)
326
-
327
334
  _checkAllKeysProvided(params, current)
328
-
329
335
  ref[i].args = {}
330
336
 
331
337
  const where = ref[i].where
@@ -335,23 +341,27 @@ function _processSegments(from, model, namespace, cqn, protocol) {
335
341
  ref[i].args[whereElement.ref[0]] = where[j + 2]
336
342
  j += 2
337
343
  }
344
+
338
345
  ref[i].where = undefined
346
+
339
347
  if (ref[i + 1] !== 'Set') {
340
348
  // /Set is missing
341
349
  const msg = `Invalid call to "${current.name}". You need to navigate to Set`
342
350
  throw cds.error(msg, { code: '400', statusCode: 400 })
343
351
  }
352
+
344
353
  ref[++i] = null
345
354
  } else if (current.kind === 'entity') {
346
355
  // > entity
347
356
  target = current
348
357
  one = !!(ref[i].where || current._isSingleton)
349
358
 
350
- _handleCollectionBoundActions(i)
359
+ incompleteKeys = _handleCollectionBoundActions(current, ref, i, namespace, one)
351
360
 
352
361
  if (whereRef) {
353
362
  keyCount += addRefToWhereIfNecessary(whereRef, current)
354
363
  _resolveAliasesInXpr(whereRef, current)
364
+
355
365
  // in case of Foo(1), params will be {} (before addRefToWhereIfNecessary was called)
356
366
  if (!Object.keys(params).length) params = where2obj(ref[i].where)
357
367
  _processWhere(ref[i].where, current)
@@ -359,17 +369,18 @@ function _processSegments(from, model, namespace, cqn, protocol) {
359
369
  }
360
370
  } else if ({ action: 1, function: 1 }[current.kind]) {
361
371
  // > action or function
362
- if (current.kind === 'action' && ref && ref[ref.length - 1]?.where?.length === 0) {
372
+ if (current.kind === 'action' && ref && ref.at(-1)?.where?.length === 0) {
363
373
  const msg = `Parentheses are not allowed for action calls.`
364
374
  throw Object.assign(new Error(msg), { statusCode: 400 })
365
375
  }
366
376
 
367
377
  if (i !== ref.length - 1) {
368
- const msg = `${i ? 'Unbound' : 'Bound'} ${current.kind}s are only supported as the last path segment`
369
- throw Object.assign(new Error(msg), { statusCode: 501 })
378
+ const msg = `${i ? 'Bound' : 'Unbound'} ${current.kind}s are only supported as the last path segment`
379
+ throw Object.assign(new Error(msg), { statusCode: 400 })
370
380
  }
381
+
371
382
  ref[i] = { operation: current.name }
372
- if (params) ref[i].args = params
383
+ if (params) ref[i].args = _getDataFromParams(params, current)
373
384
  if (current.returns && current.returns._type) one = true
374
385
  } else if (current.isAssociation) {
375
386
  if (!current._target._service) {
@@ -385,7 +396,7 @@ function _processSegments(from, model, namespace, cqn, protocol) {
385
396
  current = model.definitions[current.target]
386
397
  target = current
387
398
 
388
- _handleCollectionBoundActions(i)
399
+ incompleteKeys = _handleCollectionBoundActions(current, ref, i, namespace, one)
389
400
 
390
401
  if (ref[i].where) {
391
402
  keyCount += addRefToWhereIfNecessary(ref[i].where, current)
@@ -397,34 +408,40 @@ function _processSegments(from, model, namespace, cqn, protocol) {
397
408
  continue
398
409
  } else {
399
410
  // > property
411
+
400
412
  // we do not support navigations from properties yet
401
413
  one = true
414
+
402
415
  // if the last segment is a property, it must be removed and pushed to columns
403
416
  target = target || _getDefinition(model, ref[0].id, namespace)
417
+
404
418
  if (getStructTargetName(current) === target.name) {
405
419
  // TODO add simple isStructured check before
406
420
  if (!cqn.SELECT.columns) cqn.SELECT.columns = []
407
421
  const ref = getStructRef(current)
408
422
  cqn.SELECT.columns.push({ ref }) // store struct as ref
423
+
409
424
  // we need the keys to generate the correct @odata.context
410
425
  for (const key in target.keys || {}) {
411
426
  if (key !== 'IsActiveEntity' && !cqn.SELECT.columns.some(c => c.ref?.[0] === key))
412
427
  cqn.SELECT.columns.push({ ref: [key] })
413
428
  }
429
+
414
430
  Object.defineProperty(cqn, '_propertyAccess', { value: current.name, enumerable: false })
431
+
415
432
  // if we end up with structured, keep path as is, if we end up with property in structured, cut off property
416
- if (!current.elements) {
417
- from.ref.splice(-1)
418
- }
433
+ if (!current.elements) from.ref.splice(-1)
419
434
  break
420
435
  } else if (Object.keys(target.elements).includes(current.name)) {
421
436
  if (!cqn.SELECT.columns) cqn.SELECT.columns = []
422
437
  cqn.SELECT.columns.push({ ref: ref.slice(i) })
438
+
423
439
  // we need the keys to generate the correct @odata.context
424
440
  for (const key in target.keys || {}) {
425
441
  if (key !== 'IsActiveEntity' && !cqn.SELECT.columns.some(c => c.ref?.[0] === key))
426
442
  cqn.SELECT.columns.push({ ref: [key] })
427
443
  }
444
+
428
445
  // REVISIT: remove hacky _propertyAccess
429
446
  Object.defineProperty(cqn, '_propertyAccess', { value: current.name, enumerable: false })
430
447
  from.ref.splice(i)
@@ -436,7 +453,7 @@ function _processSegments(from, model, namespace, cqn, protocol) {
436
453
 
437
454
  if (incompleteKeys) {
438
455
  // > last segment not fully qualified
439
- const msg = `Entity "${current.name}" has ${_keysOf(current).length} keys. Only ${keyCount} ${keyCount === 1 ? 'was' : 'were'} provided.`
456
+ const msg = `Entity "${current.name}" has ${keysOf(current).length} keys. Only ${keyCount} ${keyCount === 1 ? 'was' : 'were'} provided.`
440
457
  throw Object.assign(new Error(msg), { statusCode: 400 })
441
458
  }
442
459
 
@@ -452,6 +469,7 @@ const CSTM_AGGR = '@Aggregation.CustomAggregate'
452
469
  function _addKeys(columns, target) {
453
470
  let hasAggregatedColumn = false,
454
471
  hasStarColumn = false
472
+
455
473
  for (let k = 0; k < columns.length; k++) {
456
474
  if (columns[k] === '*') hasStarColumn = true
457
475
  else if (columns[k].func || columns[k].func === null) hasAggregatedColumn = true
@@ -466,7 +484,7 @@ function _addKeys(columns, target) {
466
484
 
467
485
  if (hasStarColumn) return
468
486
 
469
- const keys = _keysOf(target)
487
+ const keys = keysOf(target)
470
488
 
471
489
  for (const key of keys) {
472
490
  if (!columns.some(c => (typeof c === 'string' ? c === key : c.ref?.[0] === key))) columns.push({ ref: [key] })
@@ -476,6 +494,7 @@ function _addKeys(columns, target) {
476
494
  // remove duplicate * in expand (e.g. expand=*,*)
477
495
  function _removeDuplicateAsterisk(columns) {
478
496
  let hasExpandStar = false
497
+
479
498
  for (let i = columns.length - 1; i > 0; i--) {
480
499
  const column = columns[i]
481
500
  if (!hasExpandStar && !column.ref && column?.expand?.[0] === '*') hasExpandStar = true
@@ -489,6 +508,7 @@ const _structProperty = (ref, target) => {
489
508
  if (target.elements && target.kind === 'element') {
490
509
  return _structProperty(ref.slice(1), target.elements[ref[0]])
491
510
  }
511
+
492
512
  return target
493
513
  }
494
514
 
@@ -504,8 +524,8 @@ function _processColumns(cqn, target, protocol) {
504
524
  if (target.kind === 'entity') entity = target
505
525
  else if (target.kind === 'action' && target.returns?.kind === 'entity') entity = target.returns
506
526
  if (!entity) return
507
- _removeDuplicateAsterisk(columns)
508
527
 
528
+ _removeDuplicateAsterisk(columns)
509
529
  rewriteExpandAsterisk(columns, entity)
510
530
  if (protocol !== 'rest') _addKeys(columns, entity)
511
531
  }
@@ -539,6 +559,7 @@ function _processColumns(cqn, target, protocol) {
539
559
  const _checkAllKeysProvided = (params, entity) => {
540
560
  let keysOfEntity
541
561
  const isView = !!entity.params
562
+
542
563
  if (isView) {
543
564
  // view with params
544
565
  if (params === undefined) {
@@ -549,7 +570,7 @@ const _checkAllKeysProvided = (params, entity) => {
549
570
 
550
571
  keysOfEntity = Object.keys(entity.params)
551
572
  } else {
552
- keysOfEntity = _keysOf(entity)
573
+ keysOfEntity = keysOf(entity)
553
574
  }
554
575
 
555
576
  if (!keysOfEntity) return
@@ -569,8 +590,8 @@ const _checkAllKeysProvided = (params, entity) => {
569
590
 
570
591
  const _doesNotExistError = (isExpand, refName, targetName) => {
571
592
  const msg = isExpand
572
- ? `Navigation property "${refName}" is not defined in ${targetName}`
573
- : `Property "${refName}" does not exist in ${targetName}`
593
+ ? `Navigation property "${refName}" is not defined in "${targetName}"`
594
+ : `Property "${refName}" does not exist in "${targetName}"`
574
595
  throw Object.assign(new Error(msg), { statusCode: 400 })
575
596
  }
576
597
 
@@ -599,6 +620,10 @@ function _validateXpr(xpr, ignoredColumns, target, isOne, model, aliases = []) {
599
620
  _validateXpr(x.ref[0].where, ignoredColumns, element._target ?? element.items, isOne, model)
600
621
  }
601
622
 
623
+ if (!target?.elements) {
624
+ _doesNotExistError(false, refName, target.name)
625
+ }
626
+
602
627
  if (ignoredColumns.includes(refName) || (!target.elements[refName] && !aliases.includes(refName))) {
603
628
  _doesNotExistError(x.expand, refName, target.name)
604
629
  } else if (x.ref.length > 1) {
@@ -630,6 +655,10 @@ function _validateXpr(xpr, ignoredColumns, target, isOne, model, aliases = []) {
630
655
  element = _structProperty(x.ref.slice(1), element)
631
656
  }
632
657
 
658
+ if (!element._target) {
659
+ _doesNotExistError(true, refName, target.name)
660
+ }
661
+
633
662
  const _ignoredColumns = Object.values(element._target.elements ?? {})
634
663
  .filter(element => element['@cds.api.ignore'])
635
664
  .map(element => element.name)
@@ -638,6 +667,7 @@ function _validateXpr(xpr, ignoredColumns, target, isOne, model, aliases = []) {
638
667
  if (x.where) {
639
668
  _validateXpr(x.where, _ignoredColumns, element._target, false, model)
640
669
  }
670
+
641
671
  if (x.orderBy) {
642
672
  _validateXpr(x.orderBy, _ignoredColumns, element._target, false, model)
643
673
  }
@@ -657,6 +687,7 @@ function _validateXpr(xpr, ignoredColumns, target, isOne, model, aliases = []) {
657
687
  _validateQuery(x.SELECT, _ignoredColumns, target, x.SELECT.one, model)
658
688
  }
659
689
  }
690
+
660
691
  return _aliases
661
692
  }
662
693
 
@@ -680,89 +711,74 @@ function _validateQuery(SELECT, ignoredColumns, target, isOne, model) {
680
711
  return aliases
681
712
  }
682
713
 
683
- function _4service(service) {
684
- const { namespace, model } = service
685
- const protocol = service.options?.to
686
- if (!model) return cqn => cqn
687
-
688
- return cqn => {
689
- const from = resolveFromSelect(cqn)
690
- const { ref } = from
691
-
692
- // REVISIT: shouldn't be necessary
693
- // Second findCsnTargetFor is required for concat query, where the root is already identified with the first query
694
- // and subsequent queries already have correct root
695
- /*
696
- * make first path segment fully qualified
697
- */
698
- const root =
699
- findCsnTargetFor(ref[0].id || ref[0], model, namespace) ||
700
- findCsnTargetFor(
701
- ref[0].id?.split('.')[ref[0].id?.split('.').length - 1] || ref[0].split('.')[ref[0].split('.').length - 1],
702
- model,
703
- namespace
704
- )
705
- // REVISIT: 404 or 400?
706
- if (!root) {
707
- cds.error(`Invalid resource path "${namespace}.${ref[0].id || ref[0]}"`, { code: '404', statusCode: 404 })
708
- }
709
- if (ref[0].id) ref[0].id = root.name
710
- else ref[0] = root.name
711
-
712
- /*
713
- * key vs. path segments (/Books/1/author/books/2/...) and more
714
- */
715
- const { one, current, target } = _processSegments(from, model, namespace, cqn, protocol)
716
-
717
- if (cds.env.effective.odata.proxies && cds.env.effective.odata.xrefs && target) {
718
- if (!target._service) {
719
- // proxy navigation, add keys as columns only
720
- const columns = []
721
- for (const key in target.keys) {
722
- if (target.keys[key].isAssociation) continue
723
- columns.push({ ref: [key] })
724
- }
725
- cqn.SELECT.columns = columns
714
+ module.exports = (cqn, model, namespace, protocol) => {
715
+ const from = resolveFromSelect(cqn)
716
+ const { ref } = from
717
+
718
+ // REVISIT: shouldn't be necessary
719
+ // Second findCsnTargetFor is required for concat query, where the root is already identified with the first query
720
+ // and subsequent queries already have correct root
721
+ /*
722
+ * make first path segment fully qualified
723
+ */
724
+ const root =
725
+ findCsnTargetFor(ref[0].id || ref[0], model, namespace) ||
726
+ findCsnTargetFor(
727
+ ref[0].id?.split('.')[ref[0].id?.split('.').length - 1] || ref[0].split('.')[ref[0].split('.').length - 1],
728
+ model,
729
+ namespace
730
+ )
731
+
732
+ // REVISIT: 404 or 400?
733
+ if (!root) {
734
+ cds.error(`Invalid resource path "${namespace}.${ref[0].id || ref[0]}"`, { code: '404', statusCode: 404 })
735
+ }
736
+ if (ref[0].id) ref[0].id = root.name
737
+ else ref[0] = root.name
738
+
739
+ // key vs. path segments (/Books/1/author/books/2/...) and more
740
+ const { one, current, target } = _processSegments(from, model, namespace, cqn, protocol)
741
+
742
+ if (cds.env.effective.odata.proxies && cds.env.effective.odata.xrefs && target) {
743
+ if (!target._service) {
744
+ // proxy navigation, add keys as columns only
745
+ const columns = []
746
+ for (const key in target.keys) {
747
+ if (target.keys[key].isAssociation) continue
748
+ columns.push({ ref: [key] })
726
749
  }
727
- }
728
750
 
729
- if (cqn.SELECT.where) {
730
- _processWhere(cqn.SELECT.where, root)
751
+ cqn.SELECT.columns = columns
731
752
  }
753
+ }
732
754
 
733
- // one?
734
- if (one) cqn.SELECT.one = true
755
+ if (cqn.SELECT.where) {
756
+ _processWhere(cqn.SELECT.where, root)
757
+ }
735
758
 
736
- // REVISIT: better
737
- // set target (csn definition) for later retrieval
738
- cqn.__target = current.parent?.kind === 'entity' ? `${current.parent.name}:$:${current.name}` : current.name
759
+ // one?
760
+ if (one) cqn.SELECT.one = true
739
761
 
740
- // target <=> endpoint entity, all navigation refs must be resolvable accordingly
741
- if (cds.env.effective.odata.structs) _resolveAliasesInNavigation(cqn, target)
762
+ // REVISIT: better
763
+ // set target (csn definition) for later retrieval
764
+ cqn.__target = current.parent?.kind === 'entity' ? `${current.parent.name}:$:${current.name}` : current.name
742
765
 
743
- /*
744
- * add default aggregation function (and alias)
745
- */
746
- _processColumns(cqn, current, protocol)
766
+ // target <=> endpoint entity, all navigation refs must be resolvable accordingly
767
+ if (cds.env.effective.odata.structs) _resolveAliasesInNavigation(cqn, target)
747
768
 
748
- if (target && cds.env.features.odata_new_parser) {
749
- const ignoredColumns = Object.values(target.elements ?? {})
750
- .filter(element => element['@cds.api.ignore'])
751
- .map(element => element.name)
769
+ /*
770
+ * add default aggregation function (and alias)
771
+ */
772
+ _processColumns(cqn, current, protocol)
752
773
 
753
- _validateQuery(cqn.SELECT, ignoredColumns, target, one, model)
754
- }
774
+ if (target) {
775
+ const ignoredColumns = Object.values(target.elements ?? {})
776
+ .filter(element => element['@cds.api.ignore'])
777
+ .map(element => element.name)
755
778
 
756
- return cqn
779
+ // validate whether only known properties are used in query options
780
+ _validateQuery(cqn.SELECT, ignoredColumns, target, one, model)
757
781
  }
758
- }
759
-
760
- const cache = new WeakMap()
761
782
 
762
- module.exports = {
763
- for: service => {
764
- if (!cache.has(service)) cache.set(service, _4service(service))
765
- return cache.get(service)
766
- },
767
- addRefToWhereIfNecessary
783
+ return cqn
768
784
  }