@sap/cds 7.9.3 → 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 (278) hide show
  1. package/CHANGELOG.md +126 -3655
  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 +29 -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/insert.js +2 -2
  116. package/libx/_runtime/common/composition/tree.js +0 -1
  117. package/libx/_runtime/common/composition/update.js +3 -3
  118. package/libx/_runtime/common/error/frontend.js +21 -12
  119. package/libx/_runtime/common/error/log.js +36 -0
  120. package/libx/_runtime/common/error/utils.js +2 -5
  121. package/libx/_runtime/common/generic/auth/autoexpose.js +18 -17
  122. package/libx/_runtime/common/generic/auth/expand.js +1 -1
  123. package/libx/_runtime/common/generic/auth/readOnly.js +1 -2
  124. package/libx/_runtime/common/generic/auth/restrict.js +23 -42
  125. package/libx/_runtime/common/generic/auth/restrictions.js +2 -7
  126. package/libx/_runtime/common/generic/auth/utils.js +91 -88
  127. package/libx/_runtime/common/generic/crud.js +6 -5
  128. package/libx/_runtime/common/generic/etag.js +7 -12
  129. package/libx/_runtime/common/generic/input.js +70 -68
  130. package/libx/_runtime/common/generic/paging.js +1 -0
  131. package/libx/_runtime/common/generic/sorting.js +1 -0
  132. package/libx/_runtime/common/generic/temporal.js +8 -2
  133. package/libx/_runtime/common/i18n/index.js +1 -1
  134. package/libx/_runtime/common/i18n/messages.properties +3 -1
  135. package/libx/_runtime/common/utils/binary.js +8 -2
  136. package/libx/_runtime/common/utils/compareJson.js +5 -1
  137. package/libx/_runtime/common/utils/copy.js +6 -11
  138. package/libx/_runtime/common/utils/cqn2cqn4sql.js +16 -14
  139. package/libx/_runtime/common/utils/differ.js +3 -6
  140. package/libx/_runtime/common/utils/keys.js +77 -18
  141. package/libx/_runtime/common/utils/postProcess.js +12 -15
  142. package/libx/_runtime/common/utils/propagateForeignKeys.js +0 -1
  143. package/libx/_runtime/common/utils/resolveView.js +2 -3
  144. package/libx/_runtime/common/utils/restrictions.js +45 -17
  145. package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -8
  146. package/libx/_runtime/common/utils/stream.js +3 -16
  147. package/libx/_runtime/common/utils/streamProp.js +8 -18
  148. package/libx/_runtime/common/utils/structured.js +1 -1
  149. package/libx/_runtime/common/utils/ucsn.js +0 -2
  150. package/libx/_runtime/db/Service.js +0 -72
  151. package/libx/_runtime/db/data-conversion/post-processing.js +0 -1
  152. package/libx/_runtime/db/expand/expandCQNToJoin.js +9 -9
  153. package/libx/_runtime/db/expand/rawToExpanded.js +0 -8
  154. package/libx/_runtime/db/generic/input.js +3 -8
  155. package/libx/_runtime/db/generic/rewrite.js +1 -0
  156. package/libx/_runtime/db/query/read.js +2 -2
  157. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -1
  158. package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
  159. package/libx/_runtime/db/utils/columns.js +2 -6
  160. package/libx/_runtime/fiori/lean-draft.js +138 -56
  161. package/libx/_runtime/hana/Service.js +0 -1
  162. package/libx/_runtime/hana/driver.js +1 -1
  163. package/libx/_runtime/hana/dynatrace.js +1 -2
  164. package/libx/_runtime/hana/pool.js +11 -21
  165. package/libx/_runtime/hana/streaming.js +0 -1
  166. package/libx/_runtime/messaging/common-utils/AMQPClient.js +0 -1
  167. package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
  168. package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +1 -1
  169. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
  170. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -33
  171. package/libx/_runtime/messaging/event-broker.js +0 -12
  172. package/libx/_runtime/messaging/file-based.js +3 -3
  173. package/libx/_runtime/messaging/http-utils/token.js +1 -1
  174. package/libx/_runtime/messaging/kafka.js +2 -2
  175. package/libx/_runtime/messaging/redis-messaging.js +0 -1
  176. package/libx/_runtime/remote/Service.js +25 -25
  177. package/libx/_runtime/remote/utils/client.js +4 -5
  178. package/libx/_runtime/remote/utils/cloudSdkProvider.js +0 -3
  179. package/libx/_runtime/remote/utils/data.js +0 -1
  180. package/libx/_runtime/sqlite/Service.js +1 -2
  181. package/libx/_runtime/ucl/Service.js +37 -78
  182. package/libx/common/assert/index.js +22 -21
  183. package/libx/common/assert/type-relaxed.js +39 -0
  184. package/libx/common/assert/utils.js +3 -2
  185. package/libx/common/assert/validation.js +3 -8
  186. package/libx/common/utils/index.js +5 -0
  187. package/libx/common/utils/path.js +51 -0
  188. package/libx/odata/ODataAdapter.js +126 -0
  189. package/libx/odata/index.js +15 -2
  190. package/libx/odata/middleware/batch.js +261 -72
  191. package/libx/odata/middleware/body-parser.js +33 -0
  192. package/libx/odata/middleware/create.js +44 -59
  193. package/libx/odata/middleware/delete.js +23 -12
  194. package/libx/odata/middleware/error.js +30 -6
  195. package/libx/odata/middleware/metadata.js +38 -26
  196. package/libx/odata/middleware/operation.js +93 -69
  197. package/libx/odata/middleware/parse.js +6 -8
  198. package/libx/odata/middleware/read.js +117 -93
  199. package/libx/odata/middleware/service-document.js +22 -19
  200. package/libx/odata/middleware/stream.js +54 -56
  201. package/libx/odata/middleware/update.js +79 -87
  202. package/libx/odata/parse/afterburner.js +191 -175
  203. package/libx/odata/parse/cqn2odata.js +8 -8
  204. package/libx/odata/parse/grammar.peggy +27 -20
  205. package/libx/odata/parse/multipartToJson.js +17 -9
  206. package/libx/odata/parse/parser.js +1 -1
  207. package/libx/odata/utils/etag.js +14 -6
  208. package/libx/odata/utils/index.js +84 -12
  209. package/libx/odata/utils/metadata.js +161 -0
  210. package/libx/odata/utils/postProcess.js +89 -0
  211. package/libx/odata/utils/readAfterWrite.js +134 -17
  212. package/libx/odata/utils/result.js +36 -142
  213. package/libx/outbox/index.js +4 -3
  214. package/libx/rest/RestAdapter.js +115 -182
  215. package/libx/rest/middleware/create.js +28 -24
  216. package/libx/rest/middleware/delete.js +7 -10
  217. package/libx/rest/middleware/error.js +19 -16
  218. package/libx/rest/middleware/operation.js +48 -41
  219. package/libx/rest/middleware/parse.js +128 -126
  220. package/libx/rest/middleware/read.js +20 -27
  221. package/libx/rest/middleware/update.js +26 -31
  222. package/package.json +17 -8
  223. package/server.js +4 -2
  224. package/tasks/enterprise-messaging-deploy.js +1 -1
  225. package/apis/cds.d.ts +0 -3
  226. package/apis/core.d.ts +0 -21
  227. package/apis/cqn.d.ts +0 -18
  228. package/apis/csn.d.ts +0 -21
  229. package/apis/events.d.ts +0 -18
  230. package/apis/internal/inference.d.ts +0 -18
  231. package/apis/linked.d.ts +0 -18
  232. package/apis/log.d.ts +0 -20
  233. package/apis/models.d.ts +0 -18
  234. package/apis/ql.d.ts +0 -18
  235. package/apis/reflect.d.ts +0 -32
  236. package/apis/server.d.ts +0 -18
  237. package/apis/services.d.ts +0 -22
  238. package/bin/cds-serve.js +0 -56
  239. package/lib/compile/to/gql.js +0 -15
  240. package/lib/srv/protocols/_legacy.js +0 -44
  241. package/lib/utils/jest.js +0 -43
  242. package/libx/_runtime/auth/index.js +0 -193
  243. package/libx/_runtime/auth/strategies/JWT.js +0 -37
  244. package/libx/_runtime/auth/strategies/basic.js +0 -20
  245. package/libx/_runtime/auth/strategies/dummy.js +0 -14
  246. package/libx/_runtime/auth/strategies/ias-auth.js +0 -1
  247. package/libx/_runtime/auth/strategies/mock.js +0 -77
  248. package/libx/_runtime/auth/strategies/xssecUtils.js +0 -93
  249. package/libx/_runtime/auth/strategies/xsuaa.js +0 -38
  250. package/libx/_runtime/common/perf/index.js +0 -19
  251. package/libx/_runtime/common/utils/ensureIEEE754.js +0 -29
  252. package/libx/_runtime/fiori/draft.js +0 -2
  253. package/libx/_runtime/fiori/generic/activate.js +0 -190
  254. package/libx/_runtime/fiori/generic/before.js +0 -201
  255. package/libx/_runtime/fiori/generic/cancel.js +0 -19
  256. package/libx/_runtime/fiori/generic/delete.js +0 -21
  257. package/libx/_runtime/fiori/generic/edit.js +0 -157
  258. package/libx/_runtime/fiori/generic/index.js +0 -25
  259. package/libx/_runtime/fiori/generic/new.js +0 -82
  260. package/libx/_runtime/fiori/generic/patch.js +0 -101
  261. package/libx/_runtime/fiori/generic/prepare.js +0 -57
  262. package/libx/_runtime/fiori/generic/read.js +0 -1340
  263. package/libx/_runtime/fiori/generic/readOverDraft.js +0 -146
  264. package/libx/_runtime/fiori/utils/csn.js +0 -13
  265. package/libx/_runtime/fiori/utils/delete.js +0 -114
  266. package/libx/_runtime/fiori/utils/handler.js +0 -264
  267. package/libx/_runtime/fiori/utils/lockInfo.js +0 -27
  268. package/libx/_runtime/fiori/utils/req.js +0 -23
  269. package/libx/_runtime/fiori/utils/stream.js +0 -36
  270. package/libx/_runtime/fiori/utils/where.js +0 -254
  271. package/libx/_runtime/index.js +0 -22
  272. package/libx/odata/utils/handler.js +0 -120
  273. package/libx/odata/utils/metaInfo.js +0 -410
  274. package/libx/odata/utils/path.js +0 -75
  275. package/libx/rest/RestRequest.js +0 -32
  276. package/libx/rest/index.js +0 -3
  277. package/libx/rest/readme.md +0 -1
  278. /package/libx/common/assert/{type.js → type-strict.js} +0 -0
@@ -1,48 +1,58 @@
1
1
  const cds = require('../../../')
2
2
  const { UPDATE } = cds.ql
3
3
 
4
- const { toODataResult, postProcess } = require('../utils/result')
5
- const { getKeysAndParamsFromPath, handleSapMessages, getPreferReturnHeader } = require('../utils')
6
- const { deepCopy } = require('../../_runtime/common/utils/copy')
7
-
8
- const readAfterWrite = require('../utils/readAfterWrite')
9
- const metaInfo = require('../utils/metaInfo')
4
+ const {
5
+ calculateLocationHeader,
6
+ handleSapMessages,
7
+ getPreferReturnHeader,
8
+ extractIfNoneMatch,
9
+ isStream
10
+ } = require('../utils')
11
+ const getODataMetadata = require('../utils/metadata')
12
+ const postProcess = require('../utils/postProcess')
13
+ const readAfterWrite4 = require('../utils/readAfterWrite')
14
+ const getODataResult = require('../utils/result')
15
+
16
+ const { getKeysAndParamsFromPath } = require('../../common/utils')
10
17
 
11
18
  const _isUpsertAllowed = ({ target, data, event }) => {
12
19
  return (
13
20
  !(cds.env.runtime && cds.env.runtime.allow_upsert === false) &&
14
- !(target && target._isDraftEnabled && (!cds.env.fiori.lean_draft || (!data.IsActiveEntity && event === 'PATCH')))
21
+ !(target && target._isDraftEnabled && !data.IsActiveEntity && event === 'PATCH')
15
22
  )
16
23
  }
17
-
18
- const _isNavigationWithKeyInParent = (keys, data, pathExpression, model) => {
19
- // keys not in data
20
- if (keys && Object.keys(keys).some(key => key in data)) {
21
- return false
22
- }
23
-
24
- const nav = pathExpression.ref && pathExpression.ref.length !== 0 && pathExpression.ref[1]
25
- const parent = pathExpression.ref && pathExpression.ref[0].id
24
+ // Not supported:
25
+ // 1) If some parent entity needs to be updated, reason: we only generate one CQN statement for the target.
26
+ // 2) If the foreign key is not known, i.e. when having no key information of the immediate parent, e.g. /Root(1)/foo/bar
27
+ const upsertSupported = (pathExpression, model) => {
28
+ const pathExpressionRef = pathExpression?.ref
26
29
 
27
30
  // not a navigation
28
- if (!parent || !nav) {
29
- return false
30
- }
31
+ if (pathExpressionRef.length < 2) return true
32
+
33
+ // foreign key is not known
34
+ if (!pathExpressionRef[pathExpressionRef.length - 2].where) return false
31
35
 
32
- const navID = typeof nav === 'string' ? nav : nav.id
33
- const navElement = model.definitions[parent].elements[navID]
36
+ let currentEntity = model.definitions[pathExpressionRef[0].id]
37
+ let navElement
34
38
 
35
- // not a containment
36
- if (!navElement._isContained) {
37
- return false
39
+ for (let i = 1; i < pathExpressionRef.length; i++) {
40
+ const id = typeof pathExpressionRef[i] === 'string' ? pathExpressionRef[i] : pathExpressionRef[i].id
41
+ navElement = currentEntity.elements[id]
42
+ currentEntity = navElement._target
38
43
  }
39
44
 
40
- const where = pathExpression.ref[0].where
41
- return parent && navElement && where
45
+ // disallow processing of requests along associations to one and containments
46
+ if (!navElement.is2one || navElement._isContained) return true
47
+
48
+ return navElement._foreignKeys.every(foreignKey => foreignKey.parentElement.key)
42
49
  }
43
50
 
44
- module.exports = (srv, router) =>
45
- function update(req, res, next) {
51
+ module.exports = adapter => {
52
+ const { service } = adapter
53
+ const _readAfterWrite = readAfterWrite4(adapter, 'update')
54
+
55
+ return function update(req, res, next) {
46
56
  // REVISIT: better solution for _propertyAccess
47
57
  const {
48
58
  SELECT: { one, from },
@@ -60,23 +70,12 @@ module.exports = (srv, router) =>
60
70
  }
61
71
 
62
72
  // payload & params
63
- let data = _propertyAccess ? { [_propertyAccess]: req.body.value } : deepCopy(req.body)
64
- const { keys, params } = getKeysAndParamsFromPath(from, srv)
73
+ const data = _propertyAccess ? { [_propertyAccess]: req.body.value } : req.body
74
+ const { keys, params } = getKeysAndParamsFromPath(from, service)
65
75
  // add keys from url into payload (overwriting if already present)
66
76
  if (!_propertyAccess) Object.assign(data, keys)
67
77
 
68
- // assert payload
69
- if (!_propertyAccess) {
70
- // assert complex
71
- const assertOptions = { filter: true, http: { req }, mandatories: req.method === 'PUT' || undefined }
72
- const errs = cds.assert(data, target, assertOptions)
73
- if (errs) {
74
- if (errs.length === 1) throw errs[0]
75
- throw Object.assign(new Error('MULTIPLE_ERRORS'), { statusCode: 400, details: errs })
76
- }
77
- } else {
78
- // TODO: assert primitive
79
- }
78
+ const _isDraft = target.drafts && data.IsActiveEntity !== true
80
79
 
81
80
  // query
82
81
  let query = UPDATE.entity(from).with(data)
@@ -85,78 +84,71 @@ module.exports = (srv, router) =>
85
84
  const headers = { ...cds.context.http.req.headers, ...req.headers }
86
85
 
87
86
  // we need a cds.Request for multiple reasons, incl. params, headers, sap-messages, read after write, ...
88
- let cdsReq = new cds.Request({ query, params, headers, req, res })
89
- Object.defineProperty(cdsReq, 'protocol', { value: 'odata-v4' })
90
-
91
- // API for subrequests of $batch (or incoming request)
92
- cdsReq.req = req
93
- cdsReq.res = res
94
-
95
- // REVISIT: adjust in getter?
96
- if (req.method === 'PUT') cdsReq.method = 'PUT'
87
+ const cdsReq = adapter.request4({ method: req.method, query, params, headers, req, res })
97
88
 
98
89
  // rewrite event for draft-enabled entities
99
- if (target._isDraftEnabled) cdsReq.event = 'PATCH'
100
-
101
- // REVISIT: only via srv.run in combination with srv.dispatch inside
102
- // we automatically either use a single auto-managed tx for the req (i.e., insert and read after write in same tx)
103
- // or the auto-managed tx opened for the respective atomicity group, if exists
104
- return srv
90
+ if (_isDraft) cdsReq.event = 'PATCH'
91
+
92
+ // NOTES:
93
+ // - only via srv.run in combination with srv.dispatch inside,
94
+ // we automatically either use a single auto-managed tx for the req (i.e., insert and read after write in same tx)
95
+ // or the auto-managed tx opened for the respective atomicity group, if exists
96
+ // - in the then block of .run(), the transaction is committed (i.e., before sending the response) if a single auto-managed tx is used
97
+ return service
105
98
  .run(() => {
106
- return srv.dispatch(cdsReq).then(result => {
107
- handleSapMessages(cdsReq, req, res)
108
-
109
- // TODO: any other checks needed?
110
- if (cdsReq._.readAfterWrite && !(_propertyAccess && !target._etag)) {
111
- return readAfterWrite(cdsReq, srv, SELECT.one(cdsReq.subject))
112
- }
113
-
99
+ return service.dispatch(cdsReq).then(result => {
100
+ // REVISIT: shouldn't read after write be the default behavior (that could be suppressed)?
101
+ // generic handlers indicate that read after write is required
102
+ // REVISIT: what does target._etag have to do with it?
103
+ if (cdsReq._.readAfterWrite && !(_propertyAccess && !target._etag)) return _readAfterWrite(cdsReq)
114
104
  return result
115
105
  })
116
106
  })
117
107
  .then(result => {
118
- // we use an extra then block, after getting the result, so the transaction is commited, before sending the response
108
+ handleSapMessages(cdsReq, req, res)
119
109
 
110
+ // case: read after write returns no results, e.g., due to auth (academic but possible)
120
111
  if (result == null) return res.sendStatus(204)
121
112
 
122
- // REVISIT: metaInfo needs original query in case of property access, but why?
123
- const info = metaInfo(_propertyAccess ? req._query : query, 'UPDATE', srv, result, req)
124
-
125
113
  const isMinimal = getPreferReturnHeader(req) === 'minimal'
114
+ postProcess(cdsReq.target, service, result, isMinimal)
126
115
 
127
- postProcess(cdsReq.target, srv, result, isMinimal)
128
-
129
- if (result['$etag']) res.set('etag', result['$etag'])
116
+ if (isMinimal && !target._isSingleton) {
117
+ // determine calculation based on result with req.data as fallback
118
+ res.set('location', calculateLocationHeader(cdsReq.target, service, result || cdsReq.data))
119
+ }
120
+ if (result?.$etag) res.set('ETag', result.$etag) //> must be done after post processing
130
121
 
131
- if (isMinimal || (query._propertyAccess && result[query._propertyAccess] == null) || info.metadata.isStream) {
122
+ if (isMinimal || (_propertyAccess && result[_propertyAccess] == null) || isStream(req._query)) {
132
123
  return res.sendStatus(204)
133
124
  }
134
125
 
135
- result = toODataResult(result, info)
136
- res.set('content-type', 'application/json;IEEE754Compatible=true')
126
+ const metadata = getODataMetadata(_propertyAccess ? req._query : query, { result })
127
+ result = getODataResult(result, metadata, { property: _propertyAccess })
137
128
  res.send(result)
138
129
  })
139
130
  .catch(e => {
131
+ handleSapMessages(cdsReq, req, res)
132
+
140
133
  // if UPSERT is allowed, redirect to POST
141
134
  const is404 = e.code === 404 || e.status === 404 || e.statusCode === 404
142
135
  const isForcedInsert =
143
- (e.code === 412 || e.status === 412 || e.statusCode === 412) && req.headers['if-none-match'] === '*'
136
+ (e.code === 412 || e.status === 412 || e.statusCode === 412) &&
137
+ extractIfNoneMatch(req.headers?.['if-none-match']) === '*'
144
138
  if (!_propertyAccess && (is404 || isForcedInsert) && _isUpsertAllowed({ target, data, event: req.method })) {
145
139
  // PUT / PATCH with if-match header means "only if already exists" -> no insert if it does not
146
- if (req.headers['if-match']) {
147
- return next(Object.assign(new Error('412'), { statusCode: 412 }))
148
- }
149
- // (check only works with req.body and not with data)
150
- if (_isNavigationWithKeyInParent(target.keys, req.body, from, srv.model)) {
151
- return next(Object.assign(new Error('422'), { statusCode: 422 }))
152
- }
153
- // -> redirect to POST
154
- return router.handle(Object.assign(Object.create(req), { method: 'POST' }), res)
155
- }
140
+ if (req.headers['if-match']) return next(Object.assign(new Error('412'), { statusCode: 412 }))
156
141
 
157
- handleSapMessages(cdsReq, req, res)
142
+ if (!upsertSupported(from, service.model)) return next(Object.assign(new Error('422'), { statusCode: 422 }))
143
+
144
+ // -> forward to POST
145
+ req.method = 'POST'
146
+ req.body = JSON.parse(req._raw) // REVISIT: Why do we need that?
147
+ return next()
148
+ }
158
149
 
159
150
  // continue with caught error
160
151
  next(e)
161
152
  })
162
153
  }
154
+ }