@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,5 @@
1
1
  const cds = require('../../../../cds')
2
+
2
3
  const { isStandardError } = require('../../../../common/error/standardError')
3
4
 
4
5
  const { StatusCodes: HttpStatusCodes } = require('../okra/odata-commons/http/HttpStatusCode')
@@ -21,7 +22,7 @@ const ERROR_TO_HTTP_CODE = {
21
22
  DeserializationError: HttpStatusCodes.BAD_REQUEST
22
23
  }
23
24
 
24
- const { normalizeError } = require('../../../../common/error/frontend')
25
+ const { normalizeError, unwrapMultipleErrors } = require('../../../../common/error/frontend')
25
26
 
26
27
  const _beautifyMessage = msg => (msg.endsWith('.') ? msg : msg + '.')
27
28
 
@@ -83,7 +84,6 @@ const _betterOkraError = err => {
83
84
  * @returns {Function}
84
85
  */
85
86
  const getErrorHandler = (crashOnError = true, srv) => {
86
- // eslint-disable-next-line complexity
87
87
  return async (odataReq, odataRes, next, err) => {
88
88
  // REVISIT: crashOnError
89
89
  if (isStandardError(err) && crashOnError) {
@@ -124,7 +124,7 @@ const getErrorHandler = (crashOnError = true, srv) => {
124
124
  // lost cds.context -> as we fixed that we don't get into this if branch anymore,
125
125
  // but then the ctx in the else branch below isn't the ODataRequest anymore
126
126
  // > error before req was dispatched
127
- const creq = new cds.Request({ req, res: req.res, user: req.user || new cds.User.default() })
127
+ const creq = new cds.Request({ req, res: req.res, user: cds.context.user })
128
128
  for (const each of srv._handlers._error) each.handler.call(srv, err, creq)
129
129
  } else if (ctx._tx?._done !== 'rolled back') {
130
130
  // > error after req was dispatched, e.g., serialization error in okra
@@ -146,18 +146,12 @@ const getErrorHandler = (crashOnError = true, srv) => {
146
146
  }
147
147
 
148
148
  const { error, statusCode } = normalizeError(err, req)
149
+
149
150
  // REVISIT: We should also pass stack traces in development
150
151
  // if (!cds.env.production) error.stack = err.stack
151
152
 
152
- if (cds.env.fiori.wrap_multiple_errors === false) {
153
- // According to the Fiori Elements Failed Message specification, the format must be:
154
- // Root level: First error, Details: Other errors
155
- if (error.details) {
156
- const [firstDetail, ...restDetails] = error.details
157
- Object.assign(error, firstDetail)
158
- if (restDetails.length) error.details = restDetails
159
- else delete error.details
160
- }
153
+ if (error.details && cds.env.fiori.wrap_multiple_errors === false) {
154
+ unwrapMultipleErrors(error)
161
155
  }
162
156
 
163
157
  next(null, Object.assign(error, { statusCode }))
@@ -20,9 +20,7 @@ const mpSupportsEmptyLocale = () => {
20
20
  const metadata = service => {
21
21
  return async (odataReq, odataRes, next) => {
22
22
  const req = odataReq.getIncomingRequest()
23
- const tenant = req.tenant ?? req.user?.tenant
24
- // REVISIT: can we take locale from user, or is there some odata special wrt metadata?
25
- const locale = odataRes.getContract().getLocale()
23
+ const { tenant, locale } = cds.context
26
24
 
27
25
  try {
28
26
  const { 'cds.xt.ModelProviderService': mps } = cds.services
@@ -252,7 +252,6 @@ const _reliablePagingPossible = req => {
252
252
  * @returns {Promise}
253
253
  * @private
254
254
  */
255
- // eslint-disable-next-line complexity
256
255
  const _readCollection = async (tx, req, odataReq) => {
257
256
  const result = (await tx.dispatch(req)) || []
258
257
 
@@ -7,16 +7,13 @@ module.exports = srv => {
7
7
  const containsRestrictions = containsAnyRestrictions(srv)
8
8
  const accessRestrictions = getAccessRestrictions(srv)
9
9
 
10
- // eslint-disable-next-line complexity
11
10
  return function ODataRequestHandler(odataReq, odataRes, next) {
12
11
  const req = odataReq.getBatchApplicationData()
13
12
  ? odataReq.getBatchApplicationData().req
14
13
  : odataReq.getIncomingRequest()
15
14
 
16
- // ensure there always is a user going forward (not always the case with old or custom auth)
17
- if (!req.user) req.user = new cds.User.default()
18
-
19
- const { res, user, path, headers } = req
15
+ const { res, path, headers } = req
16
+ const { user } = cds.context
20
17
 
21
18
  const { protectMetadata } = cds.env.odata
22
19
  if (protectMetadata === false && (path === '/' || path.endsWith('/$metadata'))) {
@@ -25,7 +22,7 @@ module.exports = srv => {
25
22
  }
26
23
 
27
24
  // in case of $batch we need to challenge directly, as the header is not processed if in $batch response body
28
- if (containsRestrictions && path.endsWith('/$batch') && req.user._is_anonymous) {
25
+ if (containsRestrictions && path.endsWith('/$batch') && user._is_anonymous) {
29
26
  // NOTE: "return req._login()" would not invoke custom error handlers
30
27
  if (req._login) res.set('www-authenticate', `Basic realm="Users"`)
31
28
  else if (user._challenges) res.set('www-authenticate', user._challenges.join(';'))
@@ -35,7 +32,7 @@ module.exports = srv => {
35
32
  // check @restrict and @requires as soon as possible (DoS)
36
33
  if (!accessRestrictions.some(r => user.is(r))) {
37
34
  // > unauthorized or forbidden?
38
- if (req.user._is_anonymous) {
35
+ if (user._is_anonymous) {
39
36
  // NOTE: "return req._login()" would not invoke custom error handlers
40
37
  if (req._login) res.set('www-authenticate', `Basic realm="Users"`)
41
38
  else if (user._challenges) res.set('www-authenticate', user._challenges.join(';'))
@@ -14,14 +14,19 @@ const { hasOmitValuesPreference } = require('../utils/omitValues')
14
14
 
15
15
  const { getSapMessages } = require('../../../../common/error/frontend')
16
16
 
17
+ const _extractIfNoneMatch = header => {
18
+ if (header) {
19
+ if (header.startsWith('W/')) header = header.substring(2)
20
+ if (header.startsWith('"') && header.endsWith('"')) header = header.substring(1, header.length - 1)
21
+ }
22
+
23
+ return header
24
+ }
25
+
17
26
  const _isUpsertAllowed = req => {
18
27
  return (
19
28
  !(cds.env.runtime && cds.env.runtime.allow_upsert === false) &&
20
- !(
21
- req.target &&
22
- req.target._isDraftEnabled &&
23
- (!cds.env.fiori.lean_draft || (!req.data.IsActiveEntity && req.event === 'PATCH'))
24
- )
29
+ !(req.target && req.target._isDraftEnabled && !req.data.IsActiveEntity && req.event === 'PATCH')
25
30
  )
26
31
  }
27
32
 
@@ -102,7 +107,8 @@ const _updateThenCreate = async (req, odataReq, odataRes, tx) => {
102
107
  } catch (e) {
103
108
  const is404 = e.code === 404 || e.status === 404 || e.statusCode === 404
104
109
  const isForcedInsert =
105
- (e.code === 412 || e.status === 412 || e.statusCode === 412) && req.headers['if-none-match'] === '*'
110
+ (e.code === 412 || e.status === 412 || e.statusCode === 412) &&
111
+ _extractIfNoneMatch(req.headers?.['if-none-match']) === '*'
106
112
  if ((is404 || isForcedInsert) && _isUpsertAllowed(req)) {
107
113
  // PUT/ PATCH with if-match header means "only if already exists", i.e., no insert if not
108
114
  if (req.headers['if-match']) {
@@ -56,7 +56,7 @@ class ExpressionToCQN {
56
56
  // cut off ms if cds.DateTime
57
57
  return { val: new Date(value).toISOString().replace(/\.\d\d\dZ$/, 'Z') }
58
58
  return { val: normalizeTimestamp(value) }
59
- } catch (e) {
59
+ } catch {
60
60
  throw Object.assign(new Error(`The type Edm.DateTimeOffset is not compatible with "${value}"`), {
61
61
  status: 400
62
62
  })
@@ -189,14 +189,13 @@ class ExpressionToCQN {
189
189
  ele.list = JSON.parse(ele.val).map(ele => ({ val: ele }))
190
190
  delete ele.val
191
191
  }
192
- } catch (e) {
192
+ } catch {
193
193
  // ignore
194
194
  }
195
195
  }
196
196
  return operator ? [operator, { func: `${methodName}`, args }] : { func: `${methodName}`, args }
197
197
  }
198
198
 
199
- /* eslint-disable complexity */
200
199
  /**
201
200
  * Evaluate an method expression, which in SQL would be 'column condition value'.
202
201
  * Can also be nested.
@@ -241,7 +240,6 @@ class ExpressionToCQN {
241
240
  throw getFeatureNotSupportedError(`Method "${method}" in $filter or $orderby query options`)
242
241
  }
243
242
  }
244
- /* eslint-enable complexity */
245
243
 
246
244
  _ensureArr(something) {
247
245
  return Array.isArray(something) ? something : [something]
@@ -230,6 +230,7 @@ const applyToCQN = (transformations, entity, model) => {
230
230
  break
231
231
  default: {
232
232
  const numericKind = transformation.getKind()
233
+ // eslint-disable-next-line no-unused-vars
233
234
  const stringKind = Object.entries(TransformationKind).find(([_, v]) => v === numericKind)?.[0]
234
235
  throw getFeatureNotSupportedError(`Transformation "${stringKind || numericKind}" with query option $apply`)
235
236
  }
@@ -222,7 +222,7 @@ const expandToCQN = (expandItems, type, service, options) => {
222
222
  const allElements = []
223
223
  const isAll = expandItems.some(item => item.isAll())
224
224
 
225
- for (const [name, navigationProperty] of type.getNavigationProperties(isAll)) {
225
+ for (const [name, navigationProperty] of type.getNavigationProperties()) {
226
226
  const expandItem = _getExpandItem(isAll, expandItems, name)
227
227
 
228
228
  if (isAll || expandItem) {
@@ -40,7 +40,6 @@ const parseToCqn = (component, service, target, data, odataReq, upsert) => {
40
40
  return DELETE.from(_target)
41
41
  }
42
42
  case 'UPDATE':
43
- // eslint-disable-next-line no-throw-literal
44
43
  if (!one) throw { statusCode: 400, code: '400', message: `INVALID_${odataReq.getMethod()}` }
45
44
  return UPDATE(_target).data(data)
46
45
  default:
@@ -44,8 +44,7 @@ const { isStreaming } = require('../utils/stream')
44
44
  const { getPageSize } = require('../../../../common/generic/paging')
45
45
  const { isAsteriskColumn } = require('../../../../common/utils/rewriteAsterisks')
46
46
  const { handleStreamProperties } = require('../../../../common/utils/streamProp')
47
-
48
- const { ensureUnlocalized } = require('../../../../fiori/utils/handler')
47
+ const { ensureUnlocalized } = require('../../../../common/utils/draft')
49
48
 
50
49
  const { skipToken: handleSkipToken } = require('../../../../../odata/utils')
51
50
 
@@ -309,7 +308,6 @@ const _skipToken = (token, cqn) => {
309
308
  * @param {object} odataReq - OKRA's req
310
309
  * @private
311
310
  */
312
- // eslint-disable-next-line complexity
313
311
  const readToCQN = (service, target, odataReq) => {
314
312
  const uriInfo = odataReq.getUriInfo()
315
313
  const segments = uriInfo.getPathSegments()
@@ -195,7 +195,7 @@ const getSegmentKeyValue = segmentParam => {
195
195
  try {
196
196
  val = value === 'undefined' ? undefined : JSON.parse(value)
197
197
  return { keyName, val }
198
- } catch (e) {
198
+ } catch {
199
199
  // plain string
200
200
  }
201
201
  val = _parsePrimitiveValue(edmRef, value) //> REVISIT: undefined handling needed here as well?
@@ -190,11 +190,10 @@ class AbstractEdmStructuredType extends EdmType {
190
190
  * Returns a map of navigation properties including those from base type.
191
191
  * @returns {Map.<string, EdmNavigationProperty>} A Map of navigation properties
192
192
  */
193
- getNavigationProperties (ignoreSibling) {
193
+ getNavigationProperties () {
194
194
  if (!this._navigationProperties) {
195
195
  this._navigationProperties = new Map(this.getBaseType() ? this.getBaseType().getNavigationProperties() : [])
196
196
  for (const [name, property] of this.getOwnNavigationProperties()) {
197
- if (ignoreSibling && name === 'SiblingEntity') continue;
198
197
  this._navigationProperties.set(name, property)
199
198
  }
200
199
  }
@@ -214,6 +214,11 @@ class ResourceJsonDeserializer {
214
214
  throw new DeserializationError(`Parameter '${paramName}' is not nullable`)
215
215
  }
216
216
 
217
+ if (edmParam.isCollection() && !paramValue) {
218
+ if (paramValue === undefined) break
219
+ throw new DeserializationError(`'${edmParam.getName()}' must be a collection.`)
220
+ }
221
+
217
222
  if (paramValue === undefined) {
218
223
  // OData JSON Format Version 4.0 Plus Errata 03 - 17 Action Invocation:
219
224
  // "Any parameter values not specified in the JSON object are assumed to have the null value."
@@ -354,7 +354,7 @@ class ContextURLFactory {
354
354
  // Define a local function because navigation properties must be searched recursively.
355
355
  const getExpandPaths = structuredType => {
356
356
  let structureResult = []
357
- for (const name of structuredType.getNavigationProperties(true).keys()) structureResult.push(name)
357
+ for (const name of structuredType.getNavigationProperties().keys()) structureResult.push(name)
358
358
  for (const [name, property] of structuredType.getProperties()) {
359
359
  if (property.getType().getKind() === EdmTypeKind.COMPLEX) {
360
360
  for (const innerPath of getExpandPaths(property.getType())) {
@@ -160,53 +160,35 @@ const _addForeignKeys = (service, odataReq, data) => {
160
160
  }
161
161
  }
162
162
 
163
- const _getFunctionParameters = (lastSegment, keyValues, service, target, odataReq) => {
164
- const { cds_assert: CDS_ASSERT } = cds.env.features
165
-
163
+ const _getFunctionParameters = (lastSegment, keyValues, service, target) => {
166
164
  const functionParameters = lastSegment.getFunctionParameters()
167
165
  const paramValues = _getParamData(functionParameters)
168
166
 
169
167
  // Working assumption for the case of name collisions: take the entity's key
170
168
  for (const key in keyValues) paramValues[key] = keyValues[key]
171
169
 
172
- let assertTarget
173
-
174
170
  const errors = []
175
171
  if (lastSegment.getKind() === 'BOUND.FUNCTION') {
176
172
  const targetFunction = target && target.actions && target.actions[lastSegment.getFunction().getName()]
177
173
  if (!targetFunction.params) return {}
178
- if (!CDS_ASSERT) convertStructured(service, targetFunction, paramValues, { errors })
179
- else assertTarget = targetFunction
174
+ convertStructured(service, targetFunction, paramValues, { errors })
180
175
  } else if (lastSegment.getKind() === 'FUNCTION.IMPORT') {
181
176
  const { namespace, name } = lastSegment.getFunctionImport().getFullQualifiedName()
182
177
  const targetFunction = service.model && service.model.definitions[`${namespace}.${name}`]
183
178
  if (!targetFunction.params) return {}
184
- if (!CDS_ASSERT) convertStructured(service, targetFunction, paramValues, { errors })
185
- else assertTarget = targetFunction
179
+ convertStructured(service, targetFunction, paramValues, { errors })
186
180
  }
187
181
 
188
182
  if (errors.length > 1) throw Object.assign(new Error(MULTIPLE_ERRORS), { details: errors })
189
183
  if (errors.length === 1) throw errors[0]
190
184
 
191
- if (CDS_ASSERT) {
192
- const assertOptions = { filter: true, http: { req: odataReq.getIncomingRequest() } }
193
- const errs = cds.assert(paramValues, assertTarget, assertOptions)
194
- if (errs) {
195
- if (errs.length === 1) throw errs[0]
196
- throw Object.assign(new Error('MULTIPLE_ERRORS'), { statusCode: 400, details: errs })
197
- }
198
- }
199
-
200
185
  return paramValues
201
186
  }
202
187
 
203
188
  const _getCopiedData = (odataReq, segments, service, target) => {
204
- const { cds_assert: CDS_ASSERT } = cds.env.features
205
-
206
189
  const lastSegment = segments[segments.length - 1]
207
190
 
208
191
  let data = odataReq.getBody() || {}
209
- let assertTarget = target
210
192
 
211
193
  if (lastSegment.getKind() === 'PRIMITIVE.PROPERTY') {
212
194
  data = { [lastSegment.getProperty().getName()]: data }
@@ -224,20 +206,18 @@ const _getCopiedData = (odataReq, segments, service, target) => {
224
206
  if (lastSegment.getKind() === 'BOUND.ACTION') {
225
207
  const targetAction = target.actions && target.actions[lastSegment.getAction().getName()]
226
208
  if (!targetAction || !targetAction.params) return { data: {} }
227
- if (!CDS_ASSERT) convertStructured(service, targetAction, data)
228
- else assertTarget = targetAction
209
+ convertStructured(service, targetAction, data)
229
210
  } else if (lastSegment.getKind() === 'ACTION.IMPORT') {
230
211
  const { namespace, name } = lastSegment.getActionImport().getFullQualifiedName()
231
212
  const targetAction = service.model && service.model.definitions[`${namespace}.${name}`]
232
213
  if (!targetAction || !targetAction.params) return { data: {} }
233
- if (!CDS_ASSERT) convertStructured(service, targetAction, data)
234
- else assertTarget = targetAction
214
+ convertStructured(service, targetAction, data)
235
215
  } else {
236
- if (!CDS_ASSERT) convertStructured(service, target, data)
216
+ convertStructured(service, target, data)
237
217
  }
238
218
  }
239
219
 
240
- return { data, assertTarget }
220
+ return data
241
221
  }
242
222
 
243
223
  /**
@@ -263,7 +243,7 @@ const getData = (component, odataReq, service, target) => {
263
243
  const keyValues = _getParamData(keyPredicates)
264
244
 
265
245
  if (component === DATA_READ_HANDLER && _isFunctionInvocation(odataReq)) {
266
- return _getFunctionParameters(lastSegment, keyValues, service, target, odataReq)
246
+ return _getFunctionParameters(lastSegment, keyValues, service, target)
267
247
  }
268
248
 
269
249
  if (component === DATA_DELETE_HANDLER || component === DATA_READ_HANDLER) {
@@ -274,7 +254,7 @@ const getData = (component, odataReq, service, target) => {
274
254
  }
275
255
 
276
256
  // copy so that original payload is preserved
277
- const { data, assertTarget } = _getCopiedData(odataReq, segments, service, target)
257
+ const data = _getCopiedData(odataReq, segments, service, target)
278
258
 
279
259
  // Only to be done for post via navigation
280
260
  if (
@@ -295,20 +275,6 @@ const getData = (component, odataReq, service, target) => {
295
275
  Array.isArray(data) ? Object.assign(data[0], keyValues) : Object.assign(data, keyValues)
296
276
  }
297
277
 
298
- // we need the (foreign) keys from the URL before asserting -> we can't assert in _getCopiedData()
299
- if (cds.env.features.cds_assert && assertTarget) {
300
- const assertOptions = {
301
- filter: true,
302
- http: { req: odataReq.getIncomingRequest() },
303
- mandatories: component === 'CREATE' || undefined
304
- }
305
- const errs = cds.assert(data, assertTarget, assertOptions)
306
- if (errs) {
307
- if (errs.length === 1) throw errs[0]
308
- throw Object.assign(new Error('MULTIPLE_ERRORS'), { statusCode: 400, details: errs })
309
- }
310
- }
311
-
312
278
  return data
313
279
  }
314
280
 
@@ -308,7 +308,6 @@ const _partialCopyColumns = query => {
308
308
  return []
309
309
  }
310
310
 
311
- // eslint-disable-next-line complexity
312
311
  const _getPathInfo = (query, model) => {
313
312
  const queryFrom =
314
313
  (query.SELECT && resolveFromSelect(query)) ||
@@ -1,4 +1,3 @@
1
- const { removeDraftUUIDIfNecessary } = require('../../../../fiori/utils/handler')
2
1
  const cds = require('../../../../cds')
3
2
  const {
4
3
  Request,
@@ -10,13 +9,19 @@ const { hasDeepUpdate } = require('../../../../common/composition/update')
10
9
  const { WRITE_EVENTS, CDS_EVENTS } = require('../../../../common/constants/events')
11
10
  const { addEtagColumns } = require('../../../../common/utils/etag')
12
11
 
12
+ // Only works for root entity, otherwise the relative position needs to be adapted
13
+ const removeDraftUUIDIfNecessary = req =>
14
+ req.http?.req?.headers?.['x-cds-odata-version'] === 'v2'
15
+ ? () => {}
16
+ : result => delete result.DraftAdministrativeData_DraftUUID
17
+
13
18
  const setLocationHeader = (req, { model }) => {
14
19
  const { odataRes } = req._
15
20
  const cqn = getSimpleSelectCQN(req.target, req.data)
16
21
  const { path: location } = cds.odata.urlify(cqn, { kind: 'odata', model, method: 'GET' })
17
22
  // REVISIT: needs reworking for new adapter, especially re $batch
18
23
  if (odataRes) {
19
- odataRes.setHeader('Location', location.replace(/\?.*$/, ''))
24
+ odataRes.setHeader('location', location.replace(/\?.*$/, ''))
20
25
  } else if (req.http?.res) {
21
26
  req.http.res.set('location', location.replace(/\?.*$/, ''))
22
27
  }
@@ -51,7 +56,7 @@ const _ensureKeysAreSelected = query => {
51
56
  }
52
57
  }
53
58
 
54
- const readAfterWrite = async (req, srv, { operation, isBefore, columns } = { isBefore: false }) => {
59
+ const readAfterWrite = async (req, srv, { operation, columns }) => {
55
60
  let query
56
61
 
57
62
  if (_isActionOrFunction(req)) {
@@ -35,7 +35,9 @@ const validateResourcePath = (odataReq, service) => {
35
35
  if (segment.getKind() === ENTITY || segment.getKind() === ENTITY_COLLECTION) {
36
36
  const name = segment.getEntitySet().getName()
37
37
  // REVISIT: This validation is violating all principles of locality
38
- const entity = service.model.definitions[`${service.definition.name}.${name}`]
38
+ const entity =
39
+ service.model.definitions[`${service.definition.name}.${name}`] ||
40
+ service.model.definitions[`${service.definition.name}.${name.replace(/_texts$/, '.texts')}`]
39
41
  if (!entity) return
40
42
 
41
43
  /*
@@ -50,7 +52,7 @@ const validateResourcePath = (odataReq, service) => {
50
52
  return
51
53
  }
52
54
  throw getError(405, entity['@cds.autoexpose'] ? 'ENTITY_IS_AUTOEXPOSE_READONLY' : 'ENTITY_IS_AUTOEXPOSED', [
53
- entity.name
55
+ `${service.definition.name}.${name}`
54
56
  ])
55
57
  }
56
58
  }
@@ -53,7 +53,7 @@ const setStreamingHeaders = (result, req) => {
53
53
  if ('$mediaContentDispositionFilename' in result && req) {
54
54
  const cdt = result.$mediaContentDispositionType || 'attachment'
55
55
  req._.odataRes.setHeader(
56
- 'Content-Disposition',
56
+ 'content-disposition',
57
57
  `${cdt}; filename="${encodeURIComponent(result.$mediaContentDispositionFilename)}"`
58
58
  )
59
59
  }
@@ -88,8 +88,6 @@ const _cleanupMetadata = (odataResult, result, req) => {
88
88
  * @param {*} [req]
89
89
  * @returns {string | object}
90
90
  */
91
- // REVISIT: complexity
92
- // eslint-disable-next-line complexity
93
91
  const toODataResult = (result, req) => {
94
92
  if (result == null) return ''
95
93
 
@@ -5,7 +5,7 @@ const templatePathSerializer = require('../../common/utils/templateProcessorPath
5
5
  // REVISIT: replace with cds.Request
6
6
  const getEntry = require('../../common/error/entry')
7
7
 
8
- const typeCheckers = require('../../../common/assert/type')
8
+ const typeCheckers = require('../../../common/assert/type-strict')
9
9
  const { 'cds.Decimal': checkDecimal } = typeCheckers
10
10
  const { checkMandatory, checkEnum, checkRange, checkFormat } = require('../../../common/assert/validation')
11
11
 
@@ -1,4 +1,3 @@
1
- /** @type {import('../../lib')} */
2
1
  const cds = require('../../lib')
3
2
  module.exports = cds
4
3
 
@@ -11,6 +10,14 @@ cds.extend(Association).with(require('./common/aspects/Association'))
11
10
  cds.extend(entity).with(require('./common/aspects/entity'))
12
11
 
13
12
  /*
14
- * cds.assert(data, definition, options?)
13
+ * Determines whether a request requires resolving of the target entity.
14
+ * Added to cds.Service so it can be reused in cds.ApplicationService and cds.RemoteService.
15
15
  */
16
- Object.defineProperty(cds, 'assert', { get: () => require('../common/assert') })
16
+ cds.Service.prototype._requires_resolving = function (req) {
17
+ const already_resolved = req._resolved
18
+ const belongs_to_me = req.target?.name?.startsWith(this.definition?.name + '.')
19
+ const is_resolvable = this.model && _is_simple_cqn_query(req.query)
20
+ const requires_resolving = !(already_resolved || belongs_to_me) && is_resolvable
21
+ return requires_resolving
22
+ }
23
+ const _is_simple_cqn_query = q => typeof q === 'object' && q !== null && !Array.isArray(q) && Object.keys(q).length > 0
@@ -2,9 +2,6 @@ const cds = require('../cds')
2
2
 
3
3
  const { resolveView, findQueryTarget } = require('./utils/resolveView')
4
4
  const postProcess = require('./utils/postProcess')
5
- const ensureIEEE754 = require('./utils/ensureIEEE754')
6
-
7
- const _isSimpleCqnQuery = q => typeof q === 'object' && q !== null && !Array.isArray(q) && Object.keys(q).length > 0
8
5
 
9
6
  /**
10
7
  * Generic Application Service Provider
@@ -70,7 +67,7 @@ class ApplicationService extends cds.Service {
70
67
  }
71
68
 
72
69
  static handle_fiori() {
73
- require('../fiori/draft').impl.call(this)
70
+ require('../fiori/lean-draft').impl.call(this)
74
71
  }
75
72
 
76
73
  static handle_crud() {
@@ -80,37 +77,20 @@ class ApplicationService extends cds.Service {
80
77
  // Overload .handle in order to resolve projections up to a definition that is known by the remote service instance.
81
78
  // Result is post processed according to the inverse projection in order to reflect the correct result of the original query.
82
79
  async handle(req) {
83
- let result,
84
- target = req.target
80
+ let result
81
+ let target = req.target
85
82
 
86
- if (req._resolved || req.target?.name?.startsWith(this.namespace + '.')) {
83
+ if (!this._requires_resolving(req)) {
87
84
  result = await super.handle(req)
88
- }
89
- // req.query can be:
90
- // - empty object in case of unbound action/function
91
- // - undefined/null in case of plain string queries
92
- else if (this.model && _isSimpleCqnQuery(req.query)) {
93
- const q = resolveView(req.query, this.model, this)
94
- target = findQueryTarget(q) || req.target // REVISIT: why is req.target not correct?
95
-
96
- // REVISIT: get rid of this
97
- if (req.query.SELECT?._4odata) Object.defineProperty(q.SELECT, '_4odata', { value: true })
98
-
99
- // REVISIT: We need to provide target explicitly because it's cached already within ensure_target
100
- const _req = new cds.Request({ query: q, target, _resolved: true })
85
+ } else {
86
+ const query = resolveView(req.query, this.model, this)
87
+ target = findQueryTarget(query) || req.target
88
+ // REVISIT: get rid of _4odata
89
+ if (req.query.SELECT?._4odata) Object.defineProperty(query.SELECT, '_4odata', { value: true })
90
+ // we need to provide target explicitly because it's cached within ensure_target
91
+ const _req = new cds.Request({ query, target, _resolved: true })
101
92
  result = await super.dispatch(_req)
102
- result = postProcess(q, result, this)
103
- }
104
- // default to super.handle()
105
- else {
106
- result = await super.handle(req)
107
- }
108
-
109
- // REVISIT: when do?
110
- // const ieee754 = req.headers.accept?.match(/IEEE754Compatible=(\w+)/i)?.[1]
111
- // if (cds.env.features.ensure_ieee754 || (cds.env.features.odata_new_adapter && ieee754 === 'true')) {
112
- if (cds.env.features.ensure_ieee754) {
113
- ensureIEEE754(target, this, result)
93
+ result = postProcess(query, result, this)
114
94
  }
115
95
 
116
96
  return result
@@ -78,6 +78,7 @@ module.exports = class {
78
78
  get _mandatories() {
79
79
  return this.own(
80
80
  '__mandatories',
81
+ // eslint-disable-next-line no-unused-vars
81
82
  () => this.elements && Object.entries(this.elements).filter(([_, v]) => v._isMandatory)
82
83
  )
83
84
  }
@@ -36,7 +36,7 @@ module.exports = async function executeCode(code, req) {
36
36
  const workerId = worker.id
37
37
  const contextId = cds.utils.uuid()
38
38
  const executePromise = new Promise(function executeCodePromiseExecutor(resolve, reject) {
39
- queueMicrotask(AsyncResource.bind(onStarted))
39
+ queueMicrotask(AsyncResource.bind(onStarted)) // eslint-disable-line no-undef
40
40
  const onMessageReceivedProxy = AsyncResource.bind(onMessageReceived)
41
41
  const onErrorProxy = AsyncResource.bind(onError)
42
42
  const onExitProxy = AsyncResource.bind(onExit)
@@ -10,7 +10,6 @@ parentPort.on('message', function onWorkerMessageReceived(message) {
10
10
  if (kind !== 'start' || workerId !== workerData.id) return
11
11
  if (LOG._debug) LOG.debug(`Post message received on worker thread (worker.js) from main thread`, message)
12
12
 
13
- // eslint-disable-next-line cds/no-missing-dependencies
14
13
  const { VM } = require('vm2')
15
14
  const workerReq = new WorkerReq(contextId, reqData)
16
15
 
@@ -30,7 +30,6 @@ const _isSameEntityInWhere = (where, target, persistentObj) => {
30
30
  const val = where[i + 2].val
31
31
  const sign = where[i + 1]
32
32
 
33
- // eslint-disable-next-line
34
33
  if (target.elements[key].key && key in persistentObj && sign === '=' && val !== persistentObj[key]) {
35
34
  return false
36
35
  }
@@ -225,7 +225,6 @@ const _getDataFromOncond = (onCond, parent) => {
225
225
  }, {})
226
226
  }
227
227
 
228
- // eslint-disable-next-line complexity
229
228
  const getSetNullParentForeignKeyCQNs = async (model, req, dbQuery) => {
230
229
  const cqns = []
231
230
  const query = dbQuery || req.query
@@ -4,7 +4,7 @@ const { getCompositionTree } = require('./tree')
4
4
  const ctUtils = require('./utils')
5
5
 
6
6
  const { ensureNoDraftsSuffix } = require('../utils/draft')
7
- const { deepCopyArray } = require('../utils/copy')
7
+ const { deepCopy } = require('../utils/copy')
8
8
 
9
9
  /*
10
10
  * own utils
@@ -83,7 +83,7 @@ const getDeepInsertCQNs = (model, cqn) => {
83
83
  const into = (cqn.INSERT.into.ref && cqn.INSERT.into.ref[0]) || cqn.INSERT.into.name || cqn.INSERT.into
84
84
  const entityName = ensureNoDraftsSuffix(into)
85
85
  const draft = entityName !== into
86
- const dataEntries = cqn.INSERT.entries ? deepCopyArray(cqn.INSERT.entries) : []
86
+ const dataEntries = cqn.INSERT.entries ? deepCopy(cqn.INSERT.entries) : []
87
87
  const entity = model.definitions[entityName]
88
88
  const compositionTree = getCompositionTree({
89
89
  definitions: model.definitions,