@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
@@ -89,7 +89,6 @@ const _preProcessAssertTarget = (assocInfo, assertMap) => {
89
89
  })
90
90
  }
91
91
 
92
- // eslint-disable-next-line complexity
93
92
  const _processCategory = (req, category, value, elementInfo, assertMap) => {
94
93
  const { row, key, element, isRoot } = elementInfo
95
94
  category = _getSimpleCategory(category)
@@ -113,12 +112,8 @@ const _processCategory = (req, category, value, elementInfo, assertMap) => {
113
112
  const managed = `@cds.on.${event === 'CREATE' ? 'insert' : 'update'}`
114
113
  if (cds.env.features.preserve_computed !== false && req._?.event === 'draftActivate' && !element[managed]) return
115
114
 
116
- // FIXME: req.context?.event not available with new odata adapter
117
- // Always take over the values from active entities
118
- if (cds.env.fiori?.lean_draft && req.context?.event === 'EDIT') return
119
-
120
115
  // read-only values are already deleted before `NEW` (and they can be set in a `NEW` handler!)
121
- if (cds.env.fiori?.lean_draft && event === 'CREATE' && req.target.isDraft) return
116
+ if (event === 'CREATE' && req.target.isDraft) return
122
117
 
123
118
  delete row[key]
124
119
  value.val = undefined
@@ -126,11 +121,9 @@ const _processCategory = (req, category, value, elementInfo, assertMap) => {
126
121
  }
127
122
 
128
123
  // remove immutable (can also be complex, so do first)
129
- if (category === 'immutable' && event === 'UPDATE') {
130
- // FIXME: req.context?.event not available with new odata adapter
131
- // Always take over the values from active entities
132
- if (cds.env.fiori?.lean_draft && req.context?.event === 'EDIT') return
133
-
124
+ // for new db drivers (cds.db.cqn2sql is defined), deep immutable values are handled in differ
125
+ // otherwise they're not supported and always filtered out here.
126
+ if (category === 'immutable' && event === 'UPDATE' && (isRoot || !cds.db.cqn2sql)) {
134
127
  delete row[key]
135
128
  value.val = undefined
136
129
  return
@@ -181,28 +174,25 @@ const _pick = element => {
181
174
  categories.push({ category: 'propagateForeignKeys' })
182
175
  }
183
176
 
184
- if (!cds.env.features.cds_assert) {
185
- if (
186
- element['@assert.range'] ||
187
- element['@assert.enum'] ||
188
- element['@assert.format'] ||
189
- element.type === 'cds.Decimal'
190
- ) {
177
+ // some checks are not needed if cds_validate is active (the default in cds^8)
178
+ if (!cds.env.features.cds_validate) {
179
+ if (element['@assert.range'] || element['@assert.format'] || element.type === 'cds.Decimal') {
191
180
  categories.push('assert')
192
181
  }
193
182
 
194
183
  if (element._isMandatory) {
195
184
  categories.push('mandatory')
196
185
  }
197
- }
198
-
199
- // TODO: can we move more checks to cds.assert()?
200
186
 
201
- if (element._isReadOnly) {
202
- // > _isReadOnly includes @cds.on.insert and @cds.on.update
203
- categories.push('readonly')
187
+ if (element._isReadOnly) {
188
+ // > _isReadOnly includes @cds.on.insert and @cds.on.update
189
+ categories.push('readonly')
190
+ }
204
191
  }
205
192
 
193
+ // REVISIT: cleanse @Core.Immutable
194
+ // should be a db feature, as we cannot handle completely on service level (cf. deep update)
195
+ // -> add to attic env behavior once new dbs handle this
206
196
  if (element['@Core.Immutable']) {
207
197
  categories.push('immutable')
208
198
  }
@@ -211,7 +201,9 @@ const _pick = element => {
211
201
  categories.push('uuid')
212
202
  }
213
203
 
214
- if (element['@Core.IsMediaType']) categories.push('stream')
204
+ if (element['@Core.IsMediaType']) {
205
+ categories.push('stream')
206
+ }
215
207
 
216
208
  if (
217
209
  element._isAssociationStrict &&
@@ -225,12 +217,7 @@ const _pick = element => {
225
217
  if (categories.length) return { categories }
226
218
  }
227
219
 
228
- const _callError = (req, errors) => {
229
- if (errors.length === 0) return
230
- for (const error of errors) req.error(error)
231
- }
232
-
233
- // FIXME: req.context?.event not available with new odata adapter
220
+ // REVISIT: needed only for draftActivate with old adapter -> remove with okra
234
221
  const _getBoundAction = req => req.target.actions?.[req._?.event || req.context?.event]
235
222
  const _getBoundActionBindingParameter = action => action['@cds.odata.bindingparameter.name'] || 'in'
236
223
 
@@ -238,6 +225,25 @@ async function commonGenericInput(req) {
238
225
  if (!req.query) return // FIXME: the code below expects req.query to be defined
239
226
  if (!req.target) return
240
227
 
228
+ // validate data
229
+ if (cds.env.features.cds_validate) {
230
+ const assertOptions = { mandatories: req.event === 'CREATE' || req.req?.method === 'PUT' }
231
+
232
+ const _is_activate = req._?.event === 'draftActivate' && cds.env.features.preserve_computed !== false
233
+ const _is_create_after_new = req.target.isDraft && req.event === 'CREATE'
234
+ if (_is_activate || _is_create_after_new) assertOptions.cleanse = false
235
+
236
+ // REVISIT: initialize path if necessary (currently only done in lean-draft -> correct?)
237
+ const bound = req.target.actions?.[req.event] || req.target.actions?.[req._.event]
238
+ if (bound) assertOptions.path = [bound['@cds.odata.bindingparameter.name'] || 'in']
239
+
240
+ const errs = cds.validate(req.data, req.target, assertOptions)
241
+ if (errs) {
242
+ if (errs.length === 1) throw errs[0]
243
+ throw Object.assign(new Error('MULTIPLE_ERRORS'), { statusCode: 400, details: errs })
244
+ }
245
+ }
246
+
241
247
  const template = getTemplate('app-input', this, req.target, {
242
248
  pick: _pick,
243
249
  ignore: element => element._isAssociationStrict
@@ -256,6 +262,7 @@ async function commonGenericInput(req) {
256
262
  pathSegmentsInfo: []
257
263
  }
258
264
 
265
+ // REVISIT: needed only for correct error target in case of draftActivate with old adapter -> remove with okra
259
266
  const boundAction = _getBoundAction(req)
260
267
 
261
268
  if (boundAction) {
@@ -280,7 +287,8 @@ async function commonGenericInput(req) {
280
287
  }
281
288
 
282
289
  setDataFromCQN(req) // REVISIT: req.data should point into req.query
283
- _callError(req, errors)
290
+
291
+ if (errors.length) for (const error of errors) req.error(error)
284
292
  }
285
293
 
286
294
  const _getProcessorFnForActionsFunctions =
@@ -325,63 +333,54 @@ const _processActionFunction = (row, eventParams, errors, event, service) => {
325
333
  }
326
334
  }
327
335
 
328
- const _getEventParameters = (req, service) => {
336
+ const _getOperation = (req, service) => {
329
337
  // in bound case
330
338
  if (req.target) {
331
339
  if (req.target.actions && req.target.actions[req.event]) {
332
- return req.target.actions[req.event].params
340
+ return req.target.actions[req.event]
333
341
  }
334
342
 
335
- return req.target.functions[req.event].params
343
+ return req.target.functions[req.event]
336
344
  }
337
345
 
338
346
  // in unbound case
339
- return service.model.definitions[`${service.name}.${req.event}`].params
347
+ return service.model.definitions[`${service.definition.name}.${req.event}`]
340
348
  }
341
349
 
342
350
  function _actionFunctionHandler(req) {
343
- const eventParams = _getEventParameters(req, this)
344
- if (!eventParams) return
345
-
346
- // REVISIT: find better solution -> this obviously was because params were not linked in the past
347
- // attach aspects, if not yet done
348
- // for (const param of Object.values(eventParams)) {
349
- // if ('_isMandatory' in param) continue
350
- // param._isMandatory = isMandatory(param)
351
- // param._isReadOnly = isReadOnly(param)
352
- // }
353
-
354
- // REVISIT: find better solution, maybe compiler? -> this obviously was because params were not linked in the past
355
- // resolve enums like format, range, etc.
356
- // for (const param of Object.values(eventParams)) {
357
- // // .type of action/function behaves different to .type of other csn elements
358
- // const _type = param.type && this.model && this.model.definitions[param.type]
359
- //
360
- // if (_type) {
361
- // param.enum = _type.enum
362
- // }
363
- // }
351
+ const operation = _getOperation(req, this)
352
+ if (!operation || !operation.params) return
364
353
 
365
- const errors = []
366
- const data = req.data
367
- const arrayData = Array.isArray(data) ? data : [data]
354
+ const data = req.data || {}
368
355
 
369
- for (const row of arrayData) {
370
- _processActionFunction(row, eventParams, errors, req.event, this)
356
+ // REVISIT: skip for mtxs as their models contain invalidities (e.g., properties modeled as strings but provided as objects)
357
+ const is_mtxs = operation.name.match(/^cds\.xt\./)
358
+
359
+ // validate data
360
+ if (cds.env.features.cds_validate && !is_mtxs) {
361
+ const assertOptions = { mandatories: true }
362
+ let errs = cds.validate(data, operation, assertOptions)
363
+ if (errs) {
364
+ if (errs.length === 1) throw errs[0]
365
+ throw Object.assign(new Error('MULTIPLE_ERRORS'), { statusCode: 400, details: errs })
366
+ }
371
367
  }
372
368
 
373
- _callError(req, errors)
369
+ // REVISIT: the below is still needed if !cds.env.features.cds_validate because cds.validate doesn't throw missing mandatory struct.
370
+ // look for comment "skip struct-likes as we check flat payloads above, and deep payloads via struct.validate()".
371
+ // structured params are _not_ flattened and, hence, the assumption in the comment is incorrect (or the flattening must be done).
372
+ const errors = []
373
+ const arrayData = Array.isArray(data) ? data : [data]
374
+ for (const row of arrayData) _processActionFunction(row, operation.params, errors, req.event, this)
375
+ if (errors.length) for (const error of errors) req.error(error)
374
376
  }
375
377
 
376
378
  commonGenericInput._initial = true
377
379
  _actionFunctionHandler._initial = true
378
380
 
379
381
  module.exports = cds.service.impl(function () {
380
- if (cds.env.fiori.lean_draft) {
381
- this.before(['CREATE', 'UPDATE'], '*', commonGenericInput)
382
- } else {
383
- this.before(['CREATE', 'UPDATE', 'NEW', 'PATCH'], '*', commonGenericInput)
384
- }
382
+ this.before(['CREATE', 'UPDATE'], '*', commonGenericInput)
383
+
385
384
  const operationNames = []
386
385
 
387
386
  for (const operation of this.operations) {
@@ -408,3 +407,6 @@ module.exports = cds.service.impl(function () {
408
407
  }
409
408
  }
410
409
  })
410
+
411
+ // needed for testing
412
+ module.exports.commonGenericInput = commonGenericInput
@@ -45,5 +45,6 @@ const _addPaging = function ({ SELECT }, target) {
45
45
  if (SELECT.from.SELECT?.limit) _addPaging(SELECT.from, target)
46
46
  }
47
47
 
48
+ // needed in lean draft
48
49
  exports.getPageSize = getPageSize
49
50
  exports.commonGenericPaging = commonGenericPaging
@@ -92,4 +92,5 @@ module.exports = cds.service.impl(function () {
92
92
  this.before('READ', '*', commonGenericSorting)
93
93
  })
94
94
 
95
+ // needed in lean draft
95
96
  module.exports.handler = commonGenericSorting
@@ -40,8 +40,8 @@ const _getTimeDelta = (target, queryOption) => {
40
40
  * @param req
41
41
  */
42
42
  const commonGenericTemporal = function (req) {
43
- // REVISIT: public API for query options
44
- const { _queryOptions } = req
43
+ // REVISIT: remove req._queryOptions with okra
44
+ const _queryOptions = req.constructor.name === 'ODataRequest' ? req._queryOptions : req.req?.query
45
45
 
46
46
  // REVISIT: stable access
47
47
  const _ = (req.context && req.context._) || req._
@@ -78,6 +78,12 @@ const commonGenericTemporal = function (req) {
78
78
  _getDateFromQueryOptions(_queryOptions['sap-valid-to'] ?? normalizeTimestamp('9999-12-31T23:59:59.9999999Z'))
79
79
  )
80
80
  }
81
+
82
+ // REVISIT: needed without okra
83
+ if (req.constructor.name !== 'ODataRequest') {
84
+ req._['VALID-FROM'] = _['VALID-FROM']
85
+ req._['VALID-TO'] = _['VALID-TO']
86
+ }
81
87
  }
82
88
 
83
89
  /**
@@ -78,7 +78,7 @@ module.exports = (key, locale = '', args = {}) => {
78
78
  const argtext = i18ns[locale][arg] || i18ns[''][arg] || i18ns.default[arg]
79
79
  text = text.replace(match, argtext || (arg != null ? arg : 'NULL'))
80
80
  }
81
- } catch (e) {
81
+ } catch {
82
82
  // nothing to do
83
83
  }
84
84
 
@@ -43,7 +43,8 @@ ACTION=action
43
43
  ASSERT_VALID_ELEMENT=Element is not valid
44
44
  ASSERT_RANGE=Value {0} is not in specified range [{1}, {2}]
45
45
  ASSERT_FORMAT=Value "{0}" is not in specified format "{1}"
46
- ASSERT_DATA_TYPE=Value {0} is invalid according to type definition "{1}"
46
+ ASSERT_DATA_TYPE=Value {0} is not a valid {1}
47
+ ASSERT_ARRAY=Value must be an array
47
48
  ASSERT_ENUM=Value {0} is invalid according to enum declaration {{1}}
48
49
  ASSERT_NOT_NULL=Value is required
49
50
  ASSERT_TARGET="Value doesn't exist"
@@ -91,6 +92,7 @@ DRAFT_ALREADY_EXISTS=A draft for this entity already exists
91
92
  DRAFT_NOT_EXISTING=No draft for this entity exists
92
93
  DRAFT_LOCKED_BY_ANOTHER_USER=The entity is locked by user "{0}"
93
94
  DRAFT_MODIFICATION_ONLY_VIA_ROOT=A draft can only be modified via its root entity
95
+ ACTIVE_MODIFICATION_VIA_DRAFT=Active entities cannot be modified via draft request
94
96
  DRAFT_ACTIVE_DELETE_FORBIDDEN_DRAFT_EXISTS=Entity cannot be deleted because a draft exists
95
97
 
96
98
  # singleton
@@ -2,8 +2,12 @@ const getTemplate = require('./template')
2
2
  const templateProcessor = require('./templateProcessor')
3
3
 
4
4
  // convert the standard base64 encoding to the URL-safe variant
5
- const toBase64url = value =>
6
- (Buffer.isBuffer(value) ? value.toString('base64') : value).replace(/\//g, '_').replace(/\+/g, '-')
5
+ const toBase64url = value => {
6
+ const buffer = Buffer.isBuffer(value) ? value : Buffer.from(value, 'base64')
7
+ const base64url = buffer.toString('base64url')
8
+ // Buffer base64url encoding does not have padding by default -> add it
9
+ return base64url.padEnd(Math.ceil(base64url.length / 4) * 4, '=')
10
+ }
7
11
 
8
12
  const normalizeBase64string = value => {
9
13
  if (typeof value !== 'string') return value
@@ -11,6 +15,8 @@ const normalizeBase64string = value => {
11
15
  return Buffer.from(value, 'base64').toString('base64')
12
16
  }
13
17
 
18
+ // REVISIT: The function is only used in okra.
19
+ // To be removed with the function normalizeBase64string above
14
20
  const isInvalidBase64string = value => {
15
21
  if (Buffer.isBuffer(value)) return // ok
16
22
 
@@ -169,7 +169,11 @@ const _iteratePropsInNewEntry = (newEntry, keys, result, oldEntry, entity, opts)
169
169
  }
170
170
 
171
171
  // if value did not change --> ignored
172
- if (newEntry[prop] === (oldEntry && oldEntry[prop]) || (opts.ignoreDraftColumns && prop in DRAFT_COLUMNS_MAP)) {
172
+ if (
173
+ newEntry[prop] === (oldEntry && oldEntry[prop]) ||
174
+ (oldEntry && entity.elements[prop]?.['@Core.Immutable']) ||
175
+ (opts.ignoreDraftColumns && prop in DRAFT_COLUMNS_MAP)
176
+ ) {
173
177
  continue
174
178
  }
175
179
 
@@ -5,18 +5,18 @@ const _deepCopy = arg => {
5
5
  return Buffer.from(arg)
6
6
  }
7
7
  if (Array.isArray(arg)) {
8
- return deepCopyArray(arg)
8
+ return _deepCopyArray(arg)
9
9
  }
10
10
  if (arg instanceof Readable) {
11
11
  return arg
12
12
  }
13
13
  if (typeof arg === 'object') {
14
- return deepCopyObject(arg)
14
+ return _deepCopyObject(arg)
15
15
  }
16
16
  return arg
17
17
  }
18
18
 
19
- const deepCopyArray = arr => {
19
+ const _deepCopyArray = arr => {
20
20
  if (!arr) return arr
21
21
  const clone = []
22
22
  for (const item of arr) {
@@ -25,7 +25,7 @@ const deepCopyArray = arr => {
25
25
  return clone
26
26
  }
27
27
 
28
- const deepCopyObject = obj => {
28
+ const _deepCopyObject = obj => {
29
29
  if (!obj) return obj
30
30
  const clone = {}
31
31
  for (const key in obj) {
@@ -35,15 +35,10 @@ const deepCopyObject = obj => {
35
35
  }
36
36
 
37
37
  const deepCopy = data => {
38
- if (Array.isArray(data)) {
39
- return deepCopyArray(data)
40
- }
41
-
42
- return deepCopyObject(data)
38
+ if (Array.isArray(data)) return _deepCopyArray(data)
39
+ return _deepCopyObject(data)
43
40
  }
44
41
 
45
42
  module.exports = {
46
- deepCopyObject,
47
- deepCopyArray,
48
43
  deepCopy
49
44
  }
@@ -1,5 +1,3 @@
1
- /* eslint-disable complexity */
2
-
3
1
  const cds = require('../../cds')
4
2
  const { SELECT, INSERT, DELETE, UPDATE } = cds.ql
5
3
  const Query = require('../../../../lib/ql/Query')
@@ -11,8 +9,7 @@ const { getEntityNameFromCQN } = require('./entityFromCqn')
11
9
  const getError = require('../../common/error')
12
10
  const { rewriteAsterisks } = require('./rewriteAsterisks')
13
11
  const { getEntityFromPath } = require('../../common/utils/path')
14
- const { removeIsActiveEntityRecursively } = require('../../fiori/utils/where')
15
- const { addRefToWhereIfNecessary } = require('../../../odata/parse/afterburner')
12
+ const { addRefToWhereIfNecessary } = require('../../../odata/utils')
16
13
  const { addAliasToExpression, PARENT_ALIAS, FOREIGN_ALIAS } = require('../../db/utils/generateAliases')
17
14
  const { getColumns } = require('./columns')
18
15
 
@@ -429,7 +426,6 @@ const _convertWhereExistsColumn = (column, model, options, queryTarget) => {
429
426
  }
430
427
  }
431
428
 
432
- // eslint-disable-next-line complexity
433
429
  const convertWhereExists = (query, model, options, currentTarget) => {
434
430
  const { where, columns, expand } = query
435
431
  let innerAlias, outerAlias, queryTarget
@@ -672,13 +668,7 @@ const _convertPathExpression = (query, model, options = {}) => {
672
668
  }
673
669
 
674
670
  // TODO: REVISIT: We need to add alias to subselect in .where, .columns, .from, ... etc
675
- if (where) {
676
- if (options._4fiori) {
677
- query.where(where)
678
- } else {
679
- query.where({ xpr: removeIsActiveEntityRecursively(where) })
680
- }
681
- }
671
+ if (where) query.where(where)
682
672
  }
683
673
 
684
674
  const _convertToOneEqNullInFilter = (query, target) => {
@@ -713,7 +703,6 @@ const _convertToOneEqNullInFilter = (query, target) => {
713
703
  }
714
704
  }
715
705
 
716
- // eslint-disable-next-line complexity
717
706
  const _convertSelect = (query, model, _options) => {
718
707
  const _4db = _options.service?.isDatabaseService
719
708
  const options = Object.assign(
@@ -765,6 +754,19 @@ const _convertSelect = (query, model, _options) => {
765
754
  _convertOrderByOrWhereIfSkip(query, entityName, model)
766
755
 
767
756
  if (query.SELECT.search && !options.suppressSearch) {
757
+ // COMPAT: new protocol adapter captures input as single "val"
758
+ // old db expects it as cqn xpr
759
+ if (query.SELECT.search.length === 1) {
760
+ query.SELECT.search = query.SELECT.search[0].val
761
+ .replace(/"/g, '')
762
+ .split(' ')
763
+ .reduce((arr, val, i) => {
764
+ if (i > 0) arr.push('and')
765
+ arr.push({ val })
766
+ return arr
767
+ }, [])
768
+ }
769
+
768
770
  search2cqn4sql(query, model, { ...query._searchOptions, ...{ entityName, alias } })
769
771
  }
770
772
 
@@ -800,7 +802,7 @@ const _convertSelect = (query, model, _options) => {
800
802
  if (options._4db && !query.SELECT.columns) {
801
803
  let target = query._target
802
804
  if (target && target._unresolved && typeof target.name === 'string') {
803
- target = model.definitions[cds.env.fiori.lean_draft ? target.name : ensureNoDraftsSuffix(target.name)] || target
805
+ target = model.definitions[target.name] || target
804
806
  }
805
807
 
806
808
  if (target && !Object.prototype.hasOwnProperty.call(target, '_unresolved')) {
@@ -3,12 +3,11 @@ const { SELECT } = cds.ql
3
3
 
4
4
  const { compareJson } = require('./compareJson')
5
5
  const { selectDeepUpdateData } = require('../composition')
6
- const { ensureDraftsSuffix } = require('../../fiori/utils/handler')
6
+ const { ensureDraftsSuffix } = require('./draft')
7
7
 
8
8
  const { DRAFT_COLUMNS_MAP } = require('../constants/draft')
9
9
  const { cqn2cqn4sql, convertPathExpressionToWhere } = require('./cqn2cqn4sql')
10
10
  const { revertData } = require('./resolveView')
11
- const { removeIsActiveEntityRecursively } = require('../../fiori/utils/where')
12
11
  const { enrichDataWithKeysFromWhere } = require('./keys')
13
12
 
14
13
  module.exports = class Differ {
@@ -38,7 +37,7 @@ module.exports = class Differ {
38
37
  }
39
38
 
40
39
  async _diffDelete(req) {
41
- const { DELETE } = cds.env.fiori.lean_draft ? req.query : (req._ && req._.query) || req.query
40
+ const { DELETE } = req.query
42
41
  const target = DELETE._transitions?.[DELETE._transitions.length - 1]?.target || req.target
43
42
  const query = SELECT.from(DELETE.from).columns(this._createSelectColumnsForDelete(target))
44
43
  if (DELETE.where) query.where(DELETE.where)
@@ -71,9 +70,7 @@ module.exports = class Differ {
71
70
  const draftRef = { ref: [ensureDraftsSuffix(target)], as: alias }
72
71
 
73
72
  // SELECT because req.query in custom handler does not have access to _drafts
74
- req._.partialPersistentState = await cds
75
- .tx(req)
76
- .run(SELECT.from(draftRef).where(removeIsActiveEntityRecursively(where)).limit(1))
73
+ req._.partialPersistentState = await cds.tx(req).run(SELECT.from(draftRef).where(where).limit(1))
77
74
 
78
75
  return compareJson(providedData || req.data, req._.partialPersistentState, req.target, {
79
76
  ignoreDraftColumns: true
@@ -1,5 +1,5 @@
1
1
  const { where2obj } = require('./cqn')
2
- const { deepCopyArray } = require('./copy')
2
+ const { deepCopy } = require('./copy')
3
3
  const { getOnCond } = require('./generateOnCond')
4
4
 
5
5
  function _getOnCondElements(onCond, onCondElements = []) {
@@ -7,19 +7,23 @@ function _getOnCondElements(onCond, onCondElements = []) {
7
7
 
8
8
  const ref0 = onCond[0].xpr ? onCond[0].xpr[0].ref : onCond[0].ref
9
9
  const ref1 = onCond[0].xpr ? onCond[0].xpr[2].ref : onCond[2].ref
10
+ const val0 = onCond[0].xpr ? onCond[0].xpr[0].val : onCond[0].val
11
+ const val1 = onCond[0].xpr ? onCond[0].xpr[2].val : onCond[2].val
10
12
 
11
- let entityRef, targetRef
12
- if (ref0 && ref0[0] === 'target') {
13
+ let entityRef, targetRef, entityVal
14
+ if (ref0?.[0] === 'target') {
13
15
  targetRef = ref0
14
16
  entityRef = ref1
15
- } else if (ref1 && ref1[0] === 'target') {
17
+ entityVal = val1
18
+ } else if (ref1?.[0] === 'target') {
16
19
  targetRef = ref1
17
20
  entityRef = ref0
21
+ entityVal = val0
18
22
  }
19
23
 
20
24
  const entityKey = entityRef && entityRef.slice(1).join('.')
21
25
  const targetKey = targetRef && targetRef.slice(1).join('.')
22
- onCondElements.push({ entityKey, targetKey })
26
+ onCondElements.push({ entityKey, targetKey, entityVal })
23
27
 
24
28
  if (andIndex !== -1) {
25
29
  _getOnCondElements(onCond.slice(andIndex + 1), onCondElements)
@@ -30,7 +34,7 @@ function _getOnCondElements(onCond, onCondElements = []) {
30
34
  function _mergeWhere(base, additional) {
31
35
  if (additional?.length) {
32
36
  // copy where else query will be modified
33
- const whereCopy = deepCopyArray(additional)
37
+ const whereCopy = deepCopy(additional)
34
38
  if (base.length > 0) base.push('and')
35
39
  base.push(...whereCopy)
36
40
  }
@@ -39,12 +43,7 @@ function _mergeWhere(base, additional) {
39
43
 
40
44
  function _modifyWhereWithNavigations(where, newWhere, entityKey, targetKey) {
41
45
  _mergeWhere(newWhere, where)
42
-
43
- newWhere.forEach(element => {
44
- if (element.ref && element.ref[0] === targetKey) {
45
- element.ref = [entityKey]
46
- }
47
- })
46
+ _renameOnUp(newWhere, entityKey, targetKey)
48
47
  }
49
48
 
50
49
  function _buildWhereForNavigations(ref, newWhere, model, target) {
@@ -66,7 +65,7 @@ function _buildWhereForNavigations(ref, newWhere, model, target) {
66
65
  const targetKeyElement = navigationElement._target.elements[key.entityKey]
67
66
 
68
67
  if (targetKeyElement && (targetKeyElement.isAssociation || targetKeyElement._foreignKey4)) {
69
- _modifyWhereWithNavigations(!whereAdded && currentRef.where, newWhere, key.entityKey, key.targetKey, whereAdded)
68
+ _modifyWhereWithNavigations(!whereAdded && currentRef.where, newWhere, key.entityKey, key.targetKey)
70
69
  whereAdded = true
71
70
  }
72
71
  }
@@ -74,7 +73,66 @@ function _buildWhereForNavigations(ref, newWhere, model, target) {
74
73
  }
75
74
  }
76
75
 
77
- function _getWhereFromInsert(query, target, model) {
76
+ function _renameOnUp(newWhere, entityKey, targetKey) {
77
+ let renamed = false
78
+ newWhere.forEach(element => {
79
+ if (element.ref && element.ref[0] === targetKey) {
80
+ element.ref = [entityKey]
81
+ renamed = true
82
+ }
83
+ })
84
+ return renamed
85
+ }
86
+
87
+ function calculateWhereForNavigationsFromRefPath(ref, newWhere, target) {
88
+ const currentRef = ref[0]
89
+ const nextRef = ref[1]
90
+
91
+ if (nextRef) {
92
+ const csnEntity = target
93
+ const navigationElement = csnEntity && csnEntity.elements[nextRef.id || nextRef]
94
+
95
+ if (!navigationElement || !navigationElement.on) return
96
+
97
+ const onCond = target._relations[nextRef.id || nextRef].join('target', 'source')
98
+ const nextKeys = _getOnCondElements(onCond[0].xpr)
99
+
100
+ const seg_keys = where2obj(currentRef.where ?? [])
101
+ for (const key of nextKeys) {
102
+ if (navigationElement.is2one && csnEntity.elements[key.entityKey] && !navigationElement._isSelfManaged) {
103
+ // foreign key in root
104
+ continue
105
+ }
106
+
107
+ const targetKeyElement = navigationElement._target.elements[key.targetKey]
108
+
109
+ if (targetKeyElement && key.targetKey && key.entityKey) {
110
+ if (newWhere.length) {
111
+ if (_renameOnUp(newWhere, key.targetKey, key.entityKey)) {
112
+ continue
113
+ }
114
+ newWhere.push('and')
115
+ }
116
+ newWhere.push({ ref: [key.targetKey] }, '=', { val: seg_keys[key.entityKey] })
117
+ } else if (targetKeyElement && key.targetKey && key.entityVal !== undefined) {
118
+ if (newWhere.length) newWhere.push('and')
119
+ newWhere.push({ ref: [key.targetKey] }, '=', { val: key.entityVal })
120
+ }
121
+ }
122
+ calculateWhereForNavigationsFromRefPath(ref.slice(1), newWhere, navigationElement._target)
123
+ }
124
+ }
125
+
126
+ function getKeysForNavigationFromRefPath(ref, target) {
127
+ const where = []
128
+ calculateWhereForNavigationsFromRefPath(ref, where, target)
129
+ if (where.length) {
130
+ return where2obj(where)
131
+ }
132
+ return {}
133
+ }
134
+
135
+ function _getWhereFromInsert(query, model) {
78
136
  const where = []
79
137
  if (query.INSERT.into.ref && query.INSERT.into.ref.length > 1) {
80
138
  _buildWhereForNavigations(query.INSERT.into.ref, where, model)
@@ -82,7 +140,7 @@ function _getWhereFromInsert(query, target, model) {
82
140
  return where
83
141
  }
84
142
 
85
- function _getWhereFromUpdate(query, target, model) {
143
+ function _getWhereFromUpdate(query, model) {
86
144
  if (query.UPDATE.entity.ref && query.UPDATE.entity.ref.length > 1) {
87
145
  const where = []
88
146
  _buildWhereForNavigations(query.UPDATE.entity.ref, where, model)
@@ -99,12 +157,12 @@ function _getWhereFromUpdate(query, target, model) {
99
157
  // params: data, req, service/tx
100
158
  function enrichDataWithKeysFromWhere(data, { query, target }, { model }) {
101
159
  if (query.INSERT) {
102
- const where = _getWhereFromInsert(query, target, model)
160
+ const where = _getWhereFromInsert(query, model)
103
161
  if (!where || !where.length) return
104
162
  if (!Array.isArray(data)) data = [data]
105
163
  for (const d of data) Object.assign(d, where2obj(where, target))
106
164
  } else if (query.UPDATE) {
107
- const where = _getWhereFromUpdate(query, target, model)
165
+ const where = _getWhereFromUpdate(query, model)
108
166
  if (!where || !where.length) return
109
167
  // REVISIT: We should not expect data to be present always!
110
168
  if (!data) data = query.UPDATE.data = {}
@@ -114,5 +172,6 @@ function enrichDataWithKeysFromWhere(data, { query, target }, { model }) {
114
172
 
115
173
  module.exports = {
116
174
  where2obj,
117
- enrichDataWithKeysFromWhere
175
+ enrichDataWithKeysFromWhere,
176
+ getKeysForNavigationFromRefPath
118
177
  }