@sap/cds 7.9.4 → 8.0.4

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 (276) hide show
  1. package/CHANGELOG.md +128 -3659
  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 +9 -41
  15. package/lib/auth/index.js +1 -14
  16. package/lib/auth/jwt-auth.js +10 -40
  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/yaml.js +3 -3
  32. package/lib/dbs/cds-deploy.js +4 -2
  33. package/lib/env/cds-env.js +10 -14
  34. package/lib/env/cds-requires.js +30 -13
  35. package/lib/env/defaults.js +46 -16
  36. package/lib/env/plugins.js +1 -1
  37. package/lib/env/schemas/cds-rc.js +8 -4
  38. package/lib/env/schemas/index.js +7 -7
  39. package/lib/env/serviceBindings.js +1 -1
  40. package/lib/index.js +12 -10
  41. package/lib/lazy.js +1 -1
  42. package/lib/linked/classes.js +36 -8
  43. package/lib/linked/entities.js +2 -10
  44. package/lib/linked/models.js +2 -1
  45. package/lib/linked/validate.js +292 -0
  46. package/lib/log/cds-error.js +0 -6
  47. package/lib/log/cds-log.js +3 -3
  48. package/lib/log/format/json.js +1 -1
  49. package/lib/log/service/index.js +0 -1
  50. package/lib/plugins.js +2 -2
  51. package/lib/ql/Query.js +2 -10
  52. package/lib/ql/SELECT.js +1 -1
  53. package/lib/ql/Whereable.js +3 -2
  54. package/lib/req/cds-context.js +14 -25
  55. package/lib/req/context.js +23 -25
  56. package/lib/req/request.js +1 -34
  57. package/lib/req/user.js +47 -35
  58. package/lib/srv/bindings.js +1 -1
  59. package/lib/srv/cds-connect.js +4 -4
  60. package/lib/srv/cds-serve.js +2 -2
  61. package/lib/srv/factory.js +1 -1
  62. package/lib/srv/middlewares/cds-context.js +11 -22
  63. package/lib/srv/middlewares/ctx-model.js +2 -3
  64. package/lib/srv/middlewares/errors.js +41 -8
  65. package/lib/srv/middlewares/index.js +3 -3
  66. package/lib/srv/middlewares/trace.js +0 -2
  67. package/lib/srv/protocols/hcql.js +15 -10
  68. package/lib/srv/protocols/http.js +44 -49
  69. package/lib/srv/protocols/index.js +1 -23
  70. package/lib/srv/protocols/odata-v4.js +12 -74
  71. package/lib/srv/protocols/rest.js +1 -13
  72. package/lib/srv/srv-api.js +0 -20
  73. package/lib/srv/srv-dispatch.js +3 -2
  74. package/lib/srv/srv-handlers.js +22 -11
  75. package/lib/srv/srv-methods.js +2 -2
  76. package/lib/srv/srv-models.js +3 -36
  77. package/lib/test/expect.js +343 -0
  78. package/lib/test/index.js +2 -0
  79. package/lib/test/reporter.js +176 -0
  80. package/lib/utils/axios.js +10 -9
  81. package/lib/utils/cds-test.js +85 -36
  82. package/lib/utils/cds-utils.js +54 -7
  83. package/lib/utils/check-version.js +0 -4
  84. package/lib/utils/colors.js +49 -0
  85. package/lib/utils/data.js +5 -4
  86. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -7
  87. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +3 -30
  88. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +6 -12
  89. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -3
  90. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +0 -1
  91. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +4 -7
  92. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +12 -6
  93. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +2 -4
  94. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +1 -0
  95. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
  96. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
  97. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +1 -3
  98. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
  99. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +1 -2
  100. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +5 -0
  101. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +1 -1
  102. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +9 -43
  103. package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +0 -1
  104. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +8 -3
  105. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +4 -2
  106. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -3
  107. package/libx/_runtime/cds-services/util/assert.js +1 -1
  108. package/libx/_runtime/cds.js +10 -3
  109. package/libx/_runtime/common/Service.js +12 -32
  110. package/libx/_runtime/common/aspects/any.js +1 -0
  111. package/libx/_runtime/common/code-ext/execute.js +1 -1
  112. package/libx/_runtime/common/code-ext/worker.js +0 -1
  113. package/libx/_runtime/common/composition/data.js +0 -1
  114. package/libx/_runtime/common/composition/delete.js +0 -1
  115. package/libx/_runtime/common/composition/tree.js +0 -1
  116. package/libx/_runtime/common/composition/update.js +3 -3
  117. package/libx/_runtime/common/error/frontend.js +21 -12
  118. package/libx/_runtime/common/error/log.js +36 -0
  119. package/libx/_runtime/common/error/utils.js +2 -5
  120. package/libx/_runtime/common/generic/auth/autoexpose.js +18 -17
  121. package/libx/_runtime/common/generic/auth/expand.js +1 -1
  122. package/libx/_runtime/common/generic/auth/readOnly.js +1 -2
  123. package/libx/_runtime/common/generic/auth/restrict.js +23 -42
  124. package/libx/_runtime/common/generic/auth/restrictions.js +2 -7
  125. package/libx/_runtime/common/generic/auth/utils.js +91 -88
  126. package/libx/_runtime/common/generic/crud.js +6 -5
  127. package/libx/_runtime/common/generic/etag.js +7 -12
  128. package/libx/_runtime/common/generic/input.js +70 -68
  129. package/libx/_runtime/common/generic/paging.js +1 -0
  130. package/libx/_runtime/common/generic/sorting.js +1 -0
  131. package/libx/_runtime/common/generic/temporal.js +8 -2
  132. package/libx/_runtime/common/i18n/index.js +1 -1
  133. package/libx/_runtime/common/i18n/messages.properties +3 -1
  134. package/libx/_runtime/common/utils/binary.js +8 -2
  135. package/libx/_runtime/common/utils/compareJson.js +5 -1
  136. package/libx/_runtime/common/utils/copy.js +6 -11
  137. package/libx/_runtime/common/utils/cqn2cqn4sql.js +16 -14
  138. package/libx/_runtime/common/utils/differ.js +3 -6
  139. package/libx/_runtime/common/utils/keys.js +77 -18
  140. package/libx/_runtime/common/utils/postProcess.js +12 -15
  141. package/libx/_runtime/common/utils/propagateForeignKeys.js +0 -1
  142. package/libx/_runtime/common/utils/resolveView.js +2 -3
  143. package/libx/_runtime/common/utils/restrictions.js +45 -17
  144. package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -8
  145. package/libx/_runtime/common/utils/stream.js +3 -16
  146. package/libx/_runtime/common/utils/streamProp.js +8 -18
  147. package/libx/_runtime/common/utils/structured.js +1 -1
  148. package/libx/_runtime/common/utils/ucsn.js +0 -2
  149. package/libx/_runtime/db/Service.js +0 -72
  150. package/libx/_runtime/db/data-conversion/post-processing.js +0 -1
  151. package/libx/_runtime/db/expand/expandCQNToJoin.js +9 -9
  152. package/libx/_runtime/db/expand/rawToExpanded.js +0 -8
  153. package/libx/_runtime/db/generic/input.js +3 -8
  154. package/libx/_runtime/db/generic/rewrite.js +1 -0
  155. package/libx/_runtime/db/query/read.js +2 -2
  156. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -1
  157. package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
  158. package/libx/_runtime/db/utils/columns.js +2 -6
  159. package/libx/_runtime/fiori/lean-draft.js +138 -56
  160. package/libx/_runtime/hana/Service.js +0 -1
  161. package/libx/_runtime/hana/driver.js +1 -1
  162. package/libx/_runtime/hana/dynatrace.js +1 -2
  163. package/libx/_runtime/hana/pool.js +11 -21
  164. package/libx/_runtime/hana/streaming.js +0 -1
  165. package/libx/_runtime/messaging/common-utils/AMQPClient.js +0 -1
  166. package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
  167. package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +1 -1
  168. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
  169. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -33
  170. package/libx/_runtime/messaging/event-broker.js +54 -27
  171. package/libx/_runtime/messaging/file-based.js +3 -3
  172. package/libx/_runtime/messaging/http-utils/token.js +1 -1
  173. package/libx/_runtime/messaging/kafka.js +2 -2
  174. package/libx/_runtime/messaging/redis-messaging.js +0 -1
  175. package/libx/_runtime/remote/Service.js +25 -25
  176. package/libx/_runtime/remote/utils/client.js +4 -5
  177. package/libx/_runtime/remote/utils/cloudSdkProvider.js +0 -3
  178. package/libx/_runtime/remote/utils/data.js +0 -1
  179. package/libx/_runtime/sqlite/Service.js +1 -2
  180. package/libx/_runtime/ucl/Service.js +37 -78
  181. package/libx/common/assert/index.js +22 -21
  182. package/libx/common/assert/type-relaxed.js +39 -0
  183. package/libx/common/assert/utils.js +3 -2
  184. package/libx/common/assert/validation.js +3 -8
  185. package/libx/common/utils/index.js +5 -0
  186. package/libx/common/utils/path.js +51 -0
  187. package/libx/odata/ODataAdapter.js +126 -0
  188. package/libx/odata/index.js +15 -2
  189. package/libx/odata/middleware/batch.js +320 -84
  190. package/libx/odata/middleware/body-parser.js +33 -0
  191. package/libx/odata/middleware/create.js +44 -59
  192. package/libx/odata/middleware/delete.js +23 -12
  193. package/libx/odata/middleware/error.js +30 -6
  194. package/libx/odata/middleware/metadata.js +38 -26
  195. package/libx/odata/middleware/operation.js +93 -69
  196. package/libx/odata/middleware/parse.js +6 -8
  197. package/libx/odata/middleware/read.js +117 -93
  198. package/libx/odata/middleware/service-document.js +22 -19
  199. package/libx/odata/middleware/stream.js +54 -56
  200. package/libx/odata/middleware/update.js +79 -87
  201. package/libx/odata/parse/afterburner.js +191 -175
  202. package/libx/odata/parse/cqn2odata.js +5 -5
  203. package/libx/odata/parse/grammar.peggy +27 -20
  204. package/libx/odata/parse/multipartToJson.js +17 -9
  205. package/libx/odata/parse/parser.js +1 -1
  206. package/libx/odata/utils/etag.js +14 -6
  207. package/libx/odata/utils/index.js +84 -12
  208. package/libx/odata/utils/metadata.js +161 -0
  209. package/libx/odata/utils/postProcess.js +89 -0
  210. package/libx/odata/utils/readAfterWrite.js +134 -17
  211. package/libx/odata/utils/result.js +36 -142
  212. package/libx/outbox/index.js +4 -3
  213. package/libx/rest/RestAdapter.js +115 -182
  214. package/libx/rest/middleware/create.js +28 -24
  215. package/libx/rest/middleware/delete.js +7 -10
  216. package/libx/rest/middleware/error.js +26 -16
  217. package/libx/rest/middleware/operation.js +48 -41
  218. package/libx/rest/middleware/parse.js +128 -126
  219. package/libx/rest/middleware/read.js +20 -27
  220. package/libx/rest/middleware/update.js +26 -31
  221. package/package.json +17 -8
  222. package/server.js +4 -2
  223. package/apis/cds.d.ts +0 -3
  224. package/apis/core.d.ts +0 -21
  225. package/apis/cqn.d.ts +0 -18
  226. package/apis/csn.d.ts +0 -21
  227. package/apis/events.d.ts +0 -18
  228. package/apis/internal/inference.d.ts +0 -18
  229. package/apis/linked.d.ts +0 -18
  230. package/apis/log.d.ts +0 -20
  231. package/apis/models.d.ts +0 -18
  232. package/apis/ql.d.ts +0 -18
  233. package/apis/reflect.d.ts +0 -32
  234. package/apis/server.d.ts +0 -18
  235. package/apis/services.d.ts +0 -22
  236. package/bin/cds-serve.js +0 -56
  237. package/lib/compile/to/gql.js +0 -15
  238. package/lib/srv/protocols/_legacy.js +0 -44
  239. package/lib/utils/jest.js +0 -43
  240. package/libx/_runtime/auth/index.js +0 -193
  241. package/libx/_runtime/auth/strategies/JWT.js +0 -37
  242. package/libx/_runtime/auth/strategies/basic.js +0 -20
  243. package/libx/_runtime/auth/strategies/dummy.js +0 -14
  244. package/libx/_runtime/auth/strategies/ias-auth.js +0 -1
  245. package/libx/_runtime/auth/strategies/mock.js +0 -77
  246. package/libx/_runtime/auth/strategies/xssecUtils.js +0 -93
  247. package/libx/_runtime/auth/strategies/xsuaa.js +0 -38
  248. package/libx/_runtime/common/perf/index.js +0 -19
  249. package/libx/_runtime/common/utils/ensureIEEE754.js +0 -29
  250. package/libx/_runtime/fiori/draft.js +0 -2
  251. package/libx/_runtime/fiori/generic/activate.js +0 -190
  252. package/libx/_runtime/fiori/generic/before.js +0 -201
  253. package/libx/_runtime/fiori/generic/cancel.js +0 -19
  254. package/libx/_runtime/fiori/generic/delete.js +0 -21
  255. package/libx/_runtime/fiori/generic/edit.js +0 -157
  256. package/libx/_runtime/fiori/generic/index.js +0 -25
  257. package/libx/_runtime/fiori/generic/new.js +0 -82
  258. package/libx/_runtime/fiori/generic/patch.js +0 -101
  259. package/libx/_runtime/fiori/generic/prepare.js +0 -57
  260. package/libx/_runtime/fiori/generic/read.js +0 -1340
  261. package/libx/_runtime/fiori/generic/readOverDraft.js +0 -146
  262. package/libx/_runtime/fiori/utils/csn.js +0 -13
  263. package/libx/_runtime/fiori/utils/delete.js +0 -114
  264. package/libx/_runtime/fiori/utils/handler.js +0 -264
  265. package/libx/_runtime/fiori/utils/lockInfo.js +0 -27
  266. package/libx/_runtime/fiori/utils/req.js +0 -23
  267. package/libx/_runtime/fiori/utils/stream.js +0 -36
  268. package/libx/_runtime/fiori/utils/where.js +0 -254
  269. package/libx/_runtime/index.js +0 -22
  270. package/libx/odata/utils/handler.js +0 -120
  271. package/libx/odata/utils/metaInfo.js +0 -410
  272. package/libx/odata/utils/path.js +0 -75
  273. package/libx/rest/RestRequest.js +0 -32
  274. package/libx/rest/index.js +0 -3
  275. package/libx/rest/readme.md +0 -1
  276. /package/libx/common/assert/{type.js → type-strict.js} +0 -0
@@ -1,410 +0,0 @@
1
- const cds = require('../../_runtime/cds')
2
- const LOG = cds.log('odata')
3
-
4
- const { appURL } = require('../../_runtime/common/utils/vcap')
5
- const { resolveFromSelect, targetFromPath } = require('../../_runtime/common/utils/cqn')
6
- const { setEntityContained } = require('../../_runtime/common/utils/csn')
7
- const { getNavigationIfStruct } = require('../../_runtime/common/utils/structured')
8
- const getTemplate = require('../../_runtime/common/utils/template')
9
- const templateProcessor = require('../../_runtime/common/utils/templateProcessor')
10
-
11
- const _ignoreColumns = columns => {
12
- if (!(Array.isArray(columns) && columns.some(c => c === '*' || c.as || c.ref))) return true
13
- }
14
-
15
- const _getNestedQueryOptions = (ref, expand, expandString) => {
16
- const isNested = expandString.match(new RegExp(`${ref}(?=\\()`))
17
- // if no "ref(" i.e. "ref" without open bracket => no nested options => shift and return
18
- if (!(isNested && Array.isArray(expand) && expand.length)) return { _expand: expandString.replace(ref, '') }
19
- // if "ref(" found, shift to the first position after "("
20
- expandString = expandString.slice(isNested.index + ref.length + 1)
21
- // i.e. we found 1 open bracket already
22
- let openBrackets = 1
23
- let head = ''
24
- // if expandString is '$top=10;$expand=foo($select=*),bar($select=buz));$select=a,b)',
25
- // then outterQueryOptions is '$top=10;$expand=foo,bar;$select=a,b'
26
- let outterQueryOptions = ''
27
- let nestedExpand = ''
28
-
29
- // parse until the last even closing bracket
30
- while (openBrackets) {
31
- const bracketFound = expandString.match(/\(|\)/)
32
- head = expandString.substring(0, bracketFound.index)
33
- expandString = expandString.slice(bracketFound.index + 1)
34
- nestedExpand = `${nestedExpand}${head}${bracketFound[0]}`
35
- // every time we have only 1 opened bracket and find another one,
36
- // everything to the left is related to outter query options
37
- if (openBrackets === 1 && bracketFound[0] === '(') {
38
- outterQueryOptions = `${outterQueryOptions}${head}`
39
- }
40
- openBrackets = bracketFound[0] === '(' ? openBrackets + 1 : openBrackets - 1
41
- }
42
- outterQueryOptions = `${outterQueryOptions}${head}`
43
-
44
- // outterQueryOptions also contain $expand, but without nested options i.e. can safely be split by ";"
45
- const $select = outterQueryOptions.split(';').find(s => s.startsWith('$select'))
46
-
47
- const expandIndex = nestedExpand.indexOf('$expand=')
48
- // last symbol is a pair to open bracket in "ref(" => slice(..., -1)
49
- const $expand = expandIndex === -1 ? '' : nestedExpand.slice(expandIndex + '$expand='.length, -1)
50
-
51
- return {
52
- $select: $select && $select.replace('$select=', ''),
53
- $expand,
54
- _expand: expandString
55
- }
56
- }
57
-
58
- const _columnsFromQuery = (columns, target, options) => {
59
- // must use query.columns as it includes columns from $apply except of $apply=expand()
60
- // must use query options to get nested $selects inside $expand() as they are mixed into query columns
61
- // example: GET /Foo?$select=bar&$expand=bar => @odata.context: $metadata#Foo(bar,bar())
62
- // REVISIT tbd if having expand column in $select could be integrated into query in grammar.peggy
63
- // REVISIT support $apply=expand()
64
- if (_ignoreColumns(columns, options)) return ''
65
- const context = []
66
- const _select = options.$select ? options.$select.split(',') : []
67
- let _expand = options.$expand || ''
68
-
69
- const hasAsterisk = _select.indexOf('*') > -1
70
- if (hasAsterisk) context.push('*')
71
-
72
- for (const c of columns) {
73
- if (!c) continue
74
- const ref = c.ref && c.ref.join('/')
75
- if (!hasAsterisk && !c.expand) {
76
- if (c.as) context.push(c.as)
77
- else if (ref) context.push(ref)
78
- } else if (c.expand) {
79
- if (!hasAsterisk && _select.indexOf(ref) > -1) context.push(ref)
80
-
81
- const nextTarget = getNavigationIfStruct(target, c.ref)
82
- if (nextTarget && nextTarget._target && nextTarget._target.elements) {
83
- const nestedOptions = _getNestedQueryOptions(ref, c.expand, _expand)
84
- _expand = nestedOptions._expand
85
- context.push(`${ref}(${_columnsFromQuery(c.expand, nextTarget._target, nestedOptions)})`)
86
- }
87
- }
88
- }
89
- if (context.length) return context.join(',')
90
- else if (hasAsterisk) return '*'
91
- return ''
92
- }
93
-
94
- const _processFn = columns => {
95
- return ({ row, key, element, pathSegmentsInfo }) => {
96
- if (!(key in row) || row[key] === null) return
97
- let cur = columns
98
- if (element.parent._isStructured) {
99
- const prefix = pathSegmentsInfo.join('/')
100
- key = `${prefix}/${key}`
101
- } else {
102
- for (let p of pathSegmentsInfo) {
103
- if (!cur[p]) cur[p] = {}
104
- cur = cur[p]
105
- }
106
- }
107
- if (!cur[key]) cur[key] = {}
108
- }
109
- }
110
-
111
- const _columnsFromData = (data, definition, service) => {
112
- const columns = {}
113
- const template = getTemplate('odata-context', service, definition, { pick: element => element.isAssociation })
114
- if (!template || !template.elements.size) return ''
115
- const arrayData = Array.isArray(data) ? data : data ? [data] : []
116
- for (const row of arrayData) {
117
- templateProcessor({ processFn: _processFn(columns), row, template, pathOptions: { pathSegmentsInfo: [] } })
118
- }
119
- return _stringifyColumnsFromData(columns)
120
- }
121
-
122
- const _stringifyColumnsFromData = columns =>
123
- Object.keys(columns)
124
- .map(key => `${key}(${_stringifyColumnsFromData(columns[key])})`)
125
- .join(',')
126
-
127
- const _listColumns = ({ columns, data, isUpsert, returnType, event, /* express */ _req, service, propertyName }) => {
128
- if (columns.length === 1 && propertyName) return `/${propertyName}`
129
- // query options ($select, $expand, etc) as strings
130
- const queryOptions = _req.query
131
- let columnsStr
132
- if (!isUpsert && event in { CREATE: 1 }) {
133
- columnsStr = _columnsFromData(data, returnType, service)
134
- } else {
135
- columnsStr = _columnsFromQuery(columns, returnType, queryOptions)
136
- }
137
- return columnsStr && `(${columnsStr})`
138
- }
139
-
140
- const _getContextUrlPrefix = ({ _req, path, target }) => {
141
- if (cds.env.odata.contextAbsoluteUrl) {
142
- try {
143
- if (typeof cds.env.odata.contextAbsoluteUrl === 'string') {
144
- const userDefinedURL = new URL(cds.env.odata.contextAbsoluteUrl, cds.env.odata.contextAbsoluteUrl).toString()
145
- return (!userDefinedURL.endsWith('/') && `${userDefinedURL}/`) || userDefinedURL
146
- }
147
- } catch (e) {
148
- e.message = `cds.odata.contextAbsoluteUrl could not be parsed as URL: ${cds.env.odata.contextAbsoluteUrl}`
149
- LOG._warn && LOG.warn(e)
150
- }
151
- const reqURL = _req && _req.get && _req.get('host') && `${_req.protocol || 'https'}://${_req.get('host')}`
152
- const baseAppURL = appURL || reqURL || ''
153
- const serviceUrl = `${(_req && _req.baseUrl) || ''}/`
154
- return baseAppURL && new URL(serviceUrl, baseAppURL).toString()
155
- }
156
- return target && target.params ? '../'.repeat(path.length) : '../'.repeat(path.length - 1)
157
- }
158
-
159
- const _findEdmNameFor = (definition, namespace, fullyQualified = false) => {
160
- let name
161
- if (!definition) return ''
162
- if (definition._isStructured) {
163
- const structured = [definition.name]
164
- while (definition.parent) {
165
- definition = definition.parent
166
- structured.unshift(definition.name)
167
- }
168
- name = structured.join('_')
169
- } else {
170
- name = definition.name
171
- }
172
- if (!name.startsWith(`${namespace}.`)) return name
173
- return fullyQualified ? name : name.replace(new RegExp(`^${namespace}\\.`), '')
174
- }
175
-
176
- const _opResultName = ({ service, returnType, operation, isServiceEntity }) => {
177
- const { namespace } = service
178
- if (returnType.name) {
179
- const resultName = _findEdmNameFor(returnType, namespace)
180
- if (returnType.name.startsWith(`${namespace}.`)) {
181
- if (isServiceEntity) return resultName.replace(/\./g, '_')
182
- return `${namespace}.${resultName.replace(/\./g, '_')}`
183
- }
184
- return resultName
185
- }
186
- // bound action / function returns inline structure
187
- if (operation.parent) {
188
- const boundEntityName = _findEdmNameFor(operation.parent, namespace, true).replace(/\./g, '_')
189
- // REVISIT exactly this return type name is generated in edm by compiler
190
- return `${namespace}.return_${boundEntityName}_${_findEdmNameFor(operation, namespace)}`
191
- }
192
- // unbound action / function returns inline structure
193
- // REVISIT exactly this return type name is generated in edm by compiler
194
- return `${namespace}.return_${_findEdmNameFor(operation, namespace, true).replace(/\./g, '_')}`
195
- }
196
-
197
- const _isNavToDraftAdmin = path => path.length > 1 && path[path.length - 1] === 'DraftAdministrativeData'
198
-
199
- const _getCanonicalUrl = (path, target, model) => {
200
- const toManySegment =
201
- path.length > 1 && Array.isArray(path[path.length - 1].where) && path[path.length - 1].where.length && path.pop()
202
- if (target.params) path.push('Set')
203
- // construct path with only innermost refs for @odata.context
204
- const _path = []
205
- for (const seg of path) {
206
- if (typeof seg === 'string') _path.push(seg)
207
- else {
208
- const _seg = { ...seg }
209
- if (_seg.where) {
210
- _seg.where = []
211
- for (const ele of seg.where) {
212
- if (ele.ref && ele.ref.length > 1) _seg.where.push({ ref: [ele.ref[ele.ref.length - 1]] })
213
- else _seg.where.push(ele)
214
- }
215
- }
216
- _path.push(_seg)
217
- }
218
- }
219
- const odataUrl = cds.odata.urlify({ SELECT: { from: { ref: _path } } }, { model, kind: 'odata' })
220
- let contextPath = odataUrl.path && odataUrl.path.match(/^([^?]*)\??/)[1]
221
- if (toManySegment) {
222
- contextPath += `/${toManySegment.id}`
223
- path.push(toManySegment)
224
- }
225
- return contextPath
226
- }
227
-
228
- const _getReturnTypeUrl = options => {
229
- const {
230
- service,
231
- isCollection,
232
- returnType,
233
- operation,
234
- path,
235
- target,
236
- propertyName,
237
- isServiceEntity,
238
- isTargetComposition
239
- } = options
240
- const { namespace } = service
241
- if (operation) {
242
- const resultName = _opResultName(options)
243
- if (isServiceEntity) return resultName
244
- return isCollection ? `Collection(${resultName})` : resultName
245
- }
246
- if (isTargetComposition || propertyName || target.params || _isNavToDraftAdmin(path)) {
247
- return _getCanonicalUrl(path, target, service.model)
248
- }
249
- if (isServiceEntity) return _findEdmNameFor(returnType, namespace).replace(/\./g, '_')
250
- return isCollection ? `Collection(${returnType.name})` : returnType.name
251
- }
252
-
253
- const _isSingleEntity = options => {
254
- const { isCollection, propertyName, returnType, isServiceEntity, isTargetComposition } = options
255
- if (isCollection || (returnType && returnType._isSingleton) || propertyName) return false
256
- return isServiceEntity || isTargetComposition
257
- }
258
-
259
- const _isStructuredProperty = ({ returnType }) => {
260
- return returnType.elements && returnType.kind === 'element'
261
- }
262
-
263
- const _getContextUrl = options => {
264
- if (!options.returnType) return ''
265
- const contextUrlPrefix = _getContextUrlPrefix(options)
266
-
267
- if (options.returnType.kind === 'service') {
268
- return `${contextUrlPrefix}$metadata`
269
- }
270
-
271
- const returnTypeUrl = _getReturnTypeUrl(options)
272
- const columnsStringified = options.propertyName || _isStructuredProperty(options) ? '' : _listColumns(options)
273
- const $entity = _isSingleEntity(options) ? '/$entity' : ''
274
- return `${contextUrlPrefix}$metadata#${returnTypeUrl}${columnsStringified}${$entity}`
275
- }
276
-
277
- const _getAdditionalContextUrl = (query, service, data, eventType, _req, isUpsert) => {
278
- if (Array.isArray(query)) {
279
- const additionalContextUrls = []
280
- for (let i = 1; i < query.length; i++) {
281
- additionalContextUrls.push(
282
- _getContextUrl(
283
- Object.assign(_getQueryInfo(query[i], service, data, eventType), { _req, service, data, isUpsert })
284
- )
285
- )
286
- }
287
- return additionalContextUrls
288
- }
289
- return []
290
- }
291
-
292
- const _partialCopyColumn = c => {
293
- if (c.expand) {
294
- const copy = { expand: Array.isArray(c.expand) ? c.expand.map(_partialCopyColumn) : c.expand }
295
- if (c.ref) copy.ref = [...c.ref]
296
- return copy
297
- }
298
- if (c.ref) return { ref: [...c.ref] }
299
- if (c.as) return { as: c.as }
300
- return c
301
- }
302
-
303
- const _partialCopyColumns = query => {
304
- if (query.SELECT) {
305
- // stop digging into subSelects as soon as columns found, as deeper columns will be shadowed by these
306
- if (!query.SELECT.columns && query.SELECT.from.SELECT) return _partialCopyColumns(query.SELECT.from)
307
- if (query.SELECT.columns) return query.SELECT.columns.map(_partialCopyColumn)
308
- }
309
- return []
310
- }
311
-
312
- // eslint-disable-next-line complexity
313
- const _getPathInfo = (query, model) => {
314
- const queryFrom =
315
- (query.SELECT && resolveFromSelect(query)) ||
316
- (query.INSERT && query.INSERT.into) ||
317
- (query.UPDATE && query.UPDATE.entity) ||
318
- (query.DELETE && query.DELETE.from)
319
- const { last, target, path, isTargetComposition } = targetFromPath(queryFrom, model)
320
- const operation = (last.kind === 'action' || last.kind === 'function') && last
321
- let returnType, isCollection, propertyName, unbound
322
- if (operation) {
323
- // last segment is bound action/function => must not be in ref
324
- queryFrom && queryFrom.ref && queryFrom.ref.pop()
325
- unbound = !operation.parent
326
- if (operation.returns) {
327
- returnType = setEntityContained(operation.returns.items || operation.returns, model)
328
- isCollection = !!operation.returns.items
329
- }
330
- // no propertyName as operations do not (yet?) support navigation
331
- } else {
332
- returnType = target
333
- isCollection = Array.isArray(query) || (query.SELECT && !query.SELECT.one)
334
- propertyName = query._propertyAccess && query.SELECT.columns[0].ref[query.SELECT.columns[0].ref.length - 1]
335
- if (propertyName && path.slice(-1)[0] !== propertyName) {
336
- path.push(propertyName)
337
- returnType = query.SELECT.columns[0].ref.reduce((r, c) => r.elements[c], target)
338
- }
339
- }
340
- const isStream = propertyName && target.elements[propertyName]?.['@Core.MediaType']
341
- return {
342
- path,
343
- target,
344
- last,
345
- operation,
346
- returnType,
347
- isCollection,
348
- propertyName,
349
- isStream,
350
- unbound,
351
- isTargetComposition
352
- }
353
- }
354
-
355
- const _getEvent = (eventType, namespace, data, { last, target }) => {
356
- if (last && (last.kind === 'action' || last.kind === 'function')) {
357
- // BOUND
358
- if (last.parent) eventType = last.name
359
- // UNBOUND
360
- eventType = last.name.replace(`${namespace}.`, '')
361
- }
362
- // draft
363
- if (target && target._isDraftEnabled) {
364
- if (eventType === 'CREATE') return 'NEW'
365
- else if (eventType === 'draftEdit') return 'EDIT'
366
- else if (eventType === 'UPDATE') return 'PATCH'
367
- else if (eventType === 'DELETE' && data.IsActiveEntity !== true) return 'CANCEL'
368
- }
369
- return eventType
370
- }
371
-
372
- const _getQueryInfo = (query, service, data, eventType) => {
373
- const { namespace, model } = service
374
- const _pathInfo = _getPathInfo(Array.isArray(query) ? query[0] : query, model)
375
- const { returnType } = _pathInfo
376
-
377
- // store original columns before they are polluted by drafts, db and so on
378
- const columns = _partialCopyColumns(Array.isArray(query) ? query[0] : query)
379
- const isServiceEntity = _findEdmNameFor(returnType, namespace) in service.entities && !returnType._isContained
380
- const event = _getEvent(eventType, namespace, data, _pathInfo)
381
- return Object.assign(_pathInfo, {
382
- columns,
383
- isServiceEntity,
384
- event
385
- })
386
- }
387
-
388
- module.exports = (query, eventType, service, data, /* express req */ _req, isUpsert) => {
389
- const queryInfo = _getQueryInfo(query, service, data, eventType)
390
-
391
- const { isCollection, isStream, propertyName, unbound, event, returnType, isServiceEntity } = queryInfo
392
-
393
- const contextUrl = _getContextUrl(Object.assign(queryInfo, { _req, service, data, isUpsert }))
394
-
395
- const additionalContextUrl = _getAdditionalContextUrl(query, service, data, eventType, _req, isUpsert)
396
-
397
- return {
398
- event,
399
- unbound,
400
- metadata: {
401
- isCollection,
402
- isStream,
403
- propertyName,
404
- contextUrl,
405
- returnType,
406
- isServiceEntity,
407
- additionalContextUrl
408
- }
409
- }
410
- }
@@ -1,75 +0,0 @@
1
- const { where2obj } = require('../../_runtime/common/utils/cqn')
2
-
3
- const _handleXpr = (relation, keys, seg_keys) => {
4
- const join = [...relation]
5
- while (join.length >= 3) {
6
- // eslint-disable-next-line no-unused-vars
7
- const [left, _, right] = join
8
-
9
- if (left.xpr) {
10
- // can be [ref = ref] or [xpr and ref = ref] and [xpr and xpr] so we will always catch xprs as left element, as it follows and/or or is first element
11
- _handleXpr(left.xpr, keys, seg_keys)
12
- join.splice(0, 2)
13
- continue
14
- }
15
-
16
- if (left.ref?.[0] === 'target') {
17
- if (left.ref[1] in keys) break // we already added the foreign key for the last segment
18
- const keyValue = 'val' in right ? right.val : seg_keys[right.ref[1]]
19
- if (keyValue !== undefined) keys[left.ref[1]] = keyValue
20
- join.splice(0, 4)
21
- } else if (right.ref?.[0] === 'target') {
22
- if (right.ref[1] in keys) break // we already added the foreign key for the last segment
23
- const keyValue = 'val' in left ? left.val : seg_keys[left.ref[1]]
24
- if (keyValue !== undefined) keys[right.ref[1]] = keyValue
25
- join.splice(0, 4)
26
- }
27
- }
28
- }
29
-
30
- // REVISIT: do we already have something like this _without using okra api_?
31
- // REVISIT: should we still support process.env.CDS_FEATURES_PARAMS? probably nobody uses it...
32
- const getKeysAndParamsFromPath = (from, srv) => {
33
- if (!from.ref) return {}
34
-
35
- const keys = {}
36
- const params = []
37
-
38
- // last path segment
39
- if (from.ref[from.ref.length - 1].where) {
40
- const seg_keys = where2obj(from.ref[from.ref.length - 1].where)
41
- Object.assign(keys, seg_keys)
42
- params.unshift(seg_keys.ID && Object.keys(seg_keys).length === 1 ? seg_keys.ID : seg_keys)
43
- }
44
-
45
- // previous path segments
46
- if (from.ref.length > 1) {
47
- const entities = []
48
- let cur = srv.model.definitions
49
- for (let i = 0; i < from.ref.length; i++) {
50
- const id = from.ref[i].id || from.ref[i]
51
- const t = cur[id]._target || cur[id]
52
- entities.push(t)
53
- cur = t.elements
54
- }
55
- for (let i = from.ref.length - 2; i >= 0; i--) {
56
- const ref = from.ref[i]
57
- if (ref.where) {
58
- const relation = entities[i]._relations[from.ref[i + 1].id || from.ref[i + 1]].join('target', 'source')
59
- const seg_keys = where2obj(ref.where)
60
- if (relation?.[0].xpr) {
61
- _handleXpr(relation[0].xpr, keys, seg_keys)
62
- } else {
63
- // REVISIT: what to do here?
64
- }
65
- params.unshift(seg_keys.ID && Object.keys(seg_keys).length === 1 ? seg_keys.ID : seg_keys)
66
- }
67
- }
68
- }
69
-
70
- return { keys, params }
71
- }
72
-
73
- module.exports = {
74
- getKeysAndParamsFromPath
75
- }
@@ -1,32 +0,0 @@
1
- const cds = require('../_runtime/cds')
2
-
3
- class RestRequest extends cds.Request {
4
- constructor(args) {
5
- super(args)
6
-
7
- // REVISIT: should not be necessary
8
- /*
9
- * propagate _ (i.e., req._ and, hence, req._.req/res)
10
- * -> in the old adapters this is also set in OdataRequest/RestRequest
11
- */
12
- Object.setPrototypeOf(this._, cds.context._) // REVISIT: We should avoid doing this
13
- if (args._req) this._queryOptions = args._req.query
14
- /*
15
- * new req.res api [work in progress -> not official]:
16
- * - req.res.status(202)
17
- * - req.res.set('location', '/Books/301')
18
- * this way is $batch compatible, i.e., the status and headers can be set on "subresponse"
19
- */
20
- this._status = null
21
- this._headers = {}
22
- Object.defineProperty(this, 'protocol', { value: 'rest' })
23
- const that = this
24
- this.res = {
25
- status: s => (that._status = s),
26
- set: (k, v) => (that._headers[k] = v)
27
- }
28
- // REVISIT: We should avoid all of the above, and hence eliminate this class completely
29
- }
30
- }
31
-
32
- module.exports = RestRequest
@@ -1,3 +0,0 @@
1
- const RestAdapter = require('./RestAdapter')
2
-
3
- module.exports = srv => new RestAdapter(srv)
@@ -1 +0,0 @@
1
- # REST Protocol Adapter
File without changes