@sap/cds 6.8.4 → 7.0.0

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 (214) hide show
  1. package/CHANGELOG.md +58 -5
  2. package/README.md +0 -1
  3. package/bin/cds-serve.js +50 -3
  4. package/bin/deploy/to-hana.js +1 -0
  5. package/bin/serve.js +16 -20
  6. package/lib/auth/basic-auth.js +6 -4
  7. package/lib/auth/index.js +4 -3
  8. package/lib/auth/jwt-auth.js +2 -5
  9. package/lib/compile/cds-compile.js +34 -89
  10. package/lib/compile/cdsc.js +11 -0
  11. package/lib/compile/etc/properties.js +2 -2
  12. package/lib/compile/for/lean_drafts.js +36 -69
  13. package/lib/compile/for/nodejs.js +2 -1
  14. package/lib/compile/load.js +1 -1
  15. package/lib/compile/minify.js +2 -0
  16. package/lib/compile/to/csn.js +74 -0
  17. package/{bin/build/provider/hana/2tabledata.js → lib/compile/to/hdbtabledata.js} +4 -6
  18. package/lib/compile/to/json.js +1 -1
  19. package/lib/compile/to/sql.js +8 -6
  20. package/lib/dbs/cds-deploy.js +174 -114
  21. package/lib/env/cds-env.js +64 -79
  22. package/lib/env/cds-requires.js +11 -28
  23. package/lib/env/defaults.js +13 -3
  24. package/lib/env/plugins.js +1 -12
  25. package/lib/env/presets.js +25 -21
  26. package/lib/index.js +121 -147
  27. package/lib/{core/reflect.js → linked/models.js} +2 -2
  28. package/lib/{core/infer.js → linked/queries.js} +2 -0
  29. package/lib/{core/index.js → linked/types.js} +2 -1
  30. package/lib/log/cds-error.js +13 -7
  31. package/lib/log/format/cf.js +1 -1
  32. package/lib/plugins.js +49 -0
  33. package/lib/ql/Query.js +0 -9
  34. package/lib/ql/STREAM.js +0 -1
  35. package/lib/req/context.js +2 -7
  36. package/lib/req/request.js +6 -2
  37. package/lib/req/response.js +23 -10
  38. package/lib/srv/middlewares/ctx-model.js +1 -1
  39. package/lib/srv/middlewares/errors.js +1 -1
  40. package/lib/srv/protocols/_legacy.js +1 -0
  41. package/lib/srv/protocols/graphql.js +7 -16
  42. package/lib/srv/protocols/index.js +59 -45
  43. package/lib/srv/protocols/odata-v2-proxy.js +2 -70
  44. package/lib/srv/srv-api.js +9 -3
  45. package/lib/srv/srv-dispatch.js +12 -9
  46. package/lib/srv/srv-models.js +4 -21
  47. package/lib/srv/srv-tx.js +15 -12
  48. package/lib/utils/cds-test.js +14 -9
  49. package/lib/utils/cds-utils.js +2 -12
  50. package/lib/utils/check-version.js +17 -0
  51. package/{bin/build → lib/utils}/csv-reader.js +23 -24
  52. package/libx/_runtime/auth/index.js +27 -23
  53. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +15 -72
  54. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
  55. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +0 -2
  56. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +33 -63
  57. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +14 -18
  58. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +15 -5
  59. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +5 -4
  60. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +37 -40
  61. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +7 -1
  62. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +101 -38
  63. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/errors/AbstractError.js +5 -1
  64. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +2 -1
  65. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +9 -8
  66. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
  67. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +15 -11
  68. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +4 -0
  69. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +5 -2
  70. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +1 -123
  71. package/libx/_runtime/cds-services/services/Service.js +79 -107
  72. package/libx/_runtime/cds-services/services/utils/columns.js +23 -19
  73. package/libx/_runtime/cds-services/services/utils/compareJson.js +11 -1
  74. package/libx/_runtime/cds-services/services/utils/differ.js +7 -2
  75. package/libx/_runtime/cds-services/util/assert.js +65 -2
  76. package/libx/_runtime/common/composition/data.js +1 -0
  77. package/libx/_runtime/common/generic/auth/expand.js +1 -1
  78. package/libx/_runtime/common/generic/auth/restrict.js +5 -10
  79. package/libx/_runtime/common/generic/auth/restrictions.js +40 -0
  80. package/libx/_runtime/common/generic/auth/utils.js +1 -2
  81. package/libx/_runtime/common/generic/crud.js +32 -16
  82. package/libx/_runtime/common/generic/etag.js +133 -104
  83. package/libx/_runtime/common/generic/input.js +6 -21
  84. package/libx/_runtime/common/generic/put.js +1 -1
  85. package/libx/_runtime/common/generic/stream.js +52 -0
  86. package/libx/_runtime/common/generic/temporal.js +25 -8
  87. package/libx/_runtime/common/i18n/messages.properties +0 -2
  88. package/libx/_runtime/common/utils/cqn.js +1 -1
  89. package/libx/_runtime/common/utils/cqn2cqn4sql.js +5 -2
  90. package/libx/_runtime/common/utils/csn.js +0 -51
  91. package/libx/_runtime/common/utils/etag.js +30 -0
  92. package/libx/_runtime/common/utils/keys.js +1 -1
  93. package/libx/_runtime/common/utils/normalizeTimestamp.js +25 -0
  94. package/libx/_runtime/common/utils/path.js +1 -1
  95. package/libx/_runtime/common/utils/resolveView.js +2 -1
  96. package/libx/_runtime/common/utils/rewriteAsterisks.js +6 -4
  97. package/libx/_runtime/common/utils/search2cqn4sql.js +12 -16
  98. package/libx/_runtime/common/utils/stream.js +140 -0
  99. package/libx/_runtime/common/utils/streamProp.js +29 -12
  100. package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +0 -2
  101. package/libx/_runtime/db/generic/index.js +0 -2
  102. package/libx/_runtime/db/query/delete.js +2 -2
  103. package/libx/_runtime/db/query/insert.js +2 -2
  104. package/libx/_runtime/db/query/read.js +2 -2
  105. package/libx/_runtime/db/query/run.js +2 -2
  106. package/libx/_runtime/db/query/update.js +2 -2
  107. package/libx/_runtime/db/sql-builder/BaseBuilder.js +0 -6
  108. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +23 -12
  109. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +18 -6
  110. package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -0
  111. package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -7
  112. package/libx/_runtime/db/sql-builder/UpsertBuilder.js +1 -0
  113. package/libx/_runtime/db/utils/normalizeTimeData.js +7 -3
  114. package/libx/_runtime/fiori/draft.js +2 -0
  115. package/libx/_runtime/fiori/generic/activate.js +8 -9
  116. package/libx/_runtime/fiori/generic/before.js +30 -20
  117. package/libx/_runtime/fiori/generic/cancel.js +5 -3
  118. package/libx/_runtime/fiori/generic/delete.js +5 -3
  119. package/libx/_runtime/fiori/generic/edit.js +7 -7
  120. package/libx/_runtime/fiori/generic/index.js +10 -16
  121. package/libx/_runtime/fiori/generic/new.js +5 -3
  122. package/libx/_runtime/fiori/generic/patch.js +11 -8
  123. package/libx/_runtime/fiori/generic/prepare.js +13 -6
  124. package/libx/_runtime/fiori/generic/read.js +12 -6
  125. package/libx/_runtime/fiori/lean-draft.js +207 -152
  126. package/libx/_runtime/fiori/utils/delete.js +10 -5
  127. package/libx/_runtime/fiori/utils/req.js +17 -5
  128. package/libx/_runtime/fiori/utils/stream.js +36 -0
  129. package/libx/_runtime/hana/Service.js +12 -9
  130. package/libx/_runtime/hana/conversion.js +10 -15
  131. package/libx/_runtime/hana/driver.js +2 -0
  132. package/libx/_runtime/hana/execute.js +28 -6
  133. package/libx/_runtime/hana/pool.js +36 -122
  134. package/libx/_runtime/hana/search2cqn4sql.js +34 -36
  135. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +2 -6
  136. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +3 -1
  137. package/libx/_runtime/messaging/enterprise-messaging.js +10 -58
  138. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  139. package/libx/_runtime/remote/Service.js +20 -1
  140. package/libx/_runtime/remote/utils/client.js +3 -5
  141. package/libx/_runtime/sqlite/Service.js +4 -6
  142. package/libx/_runtime/sqlite/conversion.js +3 -13
  143. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +9 -6
  144. package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +6 -1
  145. package/libx/_runtime/sqlite/execute.js +5 -16
  146. package/libx/odata/afterburner.js +22 -6
  147. package/libx/odata/grammar.pegjs +6 -1
  148. package/libx/odata/parser.js +1 -1
  149. package/libx/rest/RestAdapter.js +16 -9
  150. package/libx/rest/RestRequest.js +1 -1
  151. package/libx/rest/middleware/input.js +2 -1
  152. package/libx/rest/middleware/operation.js +1 -0
  153. package/libx/rest/middleware/parse.js +3 -2
  154. package/libx/rest/middleware/payload.js +9 -8
  155. package/libx/rest/middleware/read.js +1 -0
  156. package/package.json +9 -16
  157. package/app/fiori/preview.js +0 -270
  158. package/app/fiori/routes.js +0 -59
  159. package/bin/build/buildTaskEngine.js +0 -360
  160. package/bin/build/buildTaskFactory.js +0 -283
  161. package/bin/build/buildTaskHandler.js +0 -241
  162. package/bin/build/buildTaskProvider.js +0 -22
  163. package/bin/build/buildTaskProviderFactory.js +0 -175
  164. package/bin/build/cds.js +0 -5
  165. package/bin/build/constants.js +0 -66
  166. package/bin/build/index.js +0 -58
  167. package/bin/build/provider/buildTaskHandlerEdmx.js +0 -82
  168. package/bin/build/provider/buildTaskHandlerFeatureToggles.js +0 -131
  169. package/bin/build/provider/buildTaskHandlerInternal.js +0 -254
  170. package/bin/build/provider/buildTaskProviderInternal.js +0 -383
  171. package/bin/build/provider/fiori/index.js +0 -171
  172. package/bin/build/provider/hana/2migration.js +0 -179
  173. package/bin/build/provider/hana/index.js +0 -505
  174. package/bin/build/provider/hana/migrationtable.js +0 -472
  175. package/bin/build/provider/hana/template/.hdiconfig-haas +0 -163
  176. package/bin/build/provider/hana/template/.hdiconfig-hanacloud +0 -137
  177. package/bin/build/provider/hana/template/.hdinamespace +0 -4
  178. package/bin/build/provider/hana/template/package.json +0 -12
  179. package/bin/build/provider/hana/template/undeploy.json +0 -5
  180. package/bin/build/provider/java/index.js +0 -111
  181. package/bin/build/provider/java-cf/index.js +0 -1
  182. package/bin/build/provider/mtx/index.js +0 -268
  183. package/bin/build/provider/mtx/resourcesTarBuilder.js +0 -95
  184. package/bin/build/provider/mtx-extension/index.js +0 -131
  185. package/bin/build/provider/mtx-sidecar/index.js +0 -137
  186. package/bin/build/provider/node-cf/index.js +0 -1
  187. package/bin/build/provider/nodejs/index.js +0 -192
  188. package/bin/build/util.js +0 -299
  189. package/bin/cds.js +0 -125
  190. package/bin/deploy/to-hana/cfUtil.js +0 -355
  191. package/bin/deploy/to-hana/gitUtil.js +0 -57
  192. package/bin/deploy/to-hana/hana.js +0 -306
  193. package/bin/deploy/to-hana/hdiDeployUtil.js +0 -153
  194. package/bin/deploy/to-hana/index.js +0 -16
  195. package/bin/deploy/to-hana/mtaUtil.js +0 -170
  196. package/bin/mtx/in-cds.js +0 -17
  197. package/bin/plugins.js +0 -32
  198. package/bin/run.js +0 -24
  199. package/bin/utils/log.js +0 -24
  200. package/bin/version.js +0 -178
  201. package/libx/_runtime/audit/Service.js +0 -222
  202. package/libx/_runtime/audit/generic/personal/access.js +0 -61
  203. package/libx/_runtime/audit/generic/personal/index.js +0 -56
  204. package/libx/_runtime/audit/generic/personal/modification.js +0 -132
  205. package/libx/_runtime/audit/generic/personal/utils.js +0 -186
  206. package/libx/_runtime/audit/utils/log.js +0 -23
  207. package/libx/_runtime/audit/utils/v2.js +0 -176
  208. package/libx/_runtime/db/data-conversion/timestamp.js +0 -9
  209. package/libx/_runtime/db/generic/integrity.js +0 -455
  210. package/srv/audit-log.cds +0 -87
  211. package/srv/mtx.cds +0 -2
  212. package/srv/mtx.js +0 -8
  213. /package/lib/{core → linked}/classes.js +0 -0
  214. /package/lib/{core → linked}/entities.js +0 -0
@@ -1,11 +1,15 @@
1
1
  const cds = require('../../cds')
2
2
 
3
3
  const { ensureNoDraftsSuffix } = require('../../common/utils/draft.js')
4
+ const normalizeTimestamp = require('../../common/utils/normalizeTimestamp')
4
5
 
5
6
  const _convertDateTimeElement = (value, element) => {
6
- value = new Date(value).toISOString()
7
- if (element._type === 'cds.DateTime') value = value.replace(/\.\d\d\d/, '')
8
- return value
7
+ switch (element._type) {
8
+ case 'cds.DateTime':
9
+ return new Date(value).toISOString().replace(/\.\d\d\d/, '')
10
+ case 'cds.Timestamp':
11
+ return normalizeTimestamp(value)
12
+ }
9
13
  }
10
14
 
11
15
  const _isToConvert = type => type === 'cds.DateTime' || type === 'cds.Timestamp'
@@ -0,0 +1,2 @@
1
+ const cds = require('../cds')
2
+ module.exports = cds.env.fiori.lean_draft ? require('./lean-draft') : require('./generic')
@@ -106,7 +106,9 @@ const _draftCompositionTree = async (service, req) => {
106
106
  *
107
107
  * @param req
108
108
  */
109
- const fioriGenericActivate = async function (req) {
109
+ const fioriGenericActivate = async function (req, next) {
110
+ if (!req.target._isDraftEnabled) return next()
111
+
110
112
  if (
111
113
  isActiveEntityRequested(req.query.SELECT.from.ref[0].where || []) ||
112
114
  req.query.SELECT.from.ref.length > 2 ||
@@ -120,10 +122,9 @@ const fioriGenericActivate = async function (req) {
120
122
  const { draftData, activeData, adminData } = await _draftCompositionTree(this, req)
121
123
 
122
124
  if (!draftData) req.reject(404)
123
- if (adminData.InProcessByUser !== req.user.id) {
124
- // REVISIT: security log?
125
+
126
+ if (adminData.InProcessByUser !== req.user.id)
125
127
  req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER', [adminData.InProcessByUser])
126
- }
127
128
 
128
129
  /*
129
130
  * create or update
@@ -146,8 +147,6 @@ const fioriGenericActivate = async function (req) {
146
147
 
147
148
  // REVISIT: should not be necessary
148
149
  r._ = Object.assign(r._, req._)
149
- if (req.getUriInfo) r.getUriInfo = () => req.getUriInfo()
150
- if (req.getUrlObject) r.getUrlObject = () => req.getUrlObject()
151
150
  r._.params = req.params
152
151
  r._.query = req.query
153
152
 
@@ -178,11 +177,11 @@ const fioriGenericActivate = async function (req) {
178
177
 
179
178
  // REVISIT: we need to use okra API here because it must be set in the batched request
180
179
  // status code must be set in handler to allow overriding for FE V2
181
- req?._?.odataRes?.setStatusCode(201)
180
+ if (event === 'CREATE') req?._?.odataRes?.setStatusCode(201, { overwrite: true })
182
181
 
183
182
  return result
184
183
  }
185
184
 
186
- module.exports = cds.service.impl(function (srv, entity) {
187
- srv.on('draftActivate', entity, fioriGenericActivate)
185
+ module.exports = cds.service.impl(function (srv) {
186
+ srv.on('draftActivate', '*', fioriGenericActivate)
188
187
  })
@@ -23,6 +23,7 @@ const PREFIX_DRAFT_COLUMNS = DRAFT_COLUMNS_ADMIN.map(col => ({ ref: ['DRAFT_Draf
23
23
 
24
24
  const _validateDraft = (req, draftResult, isBoundAction) => {
25
25
  if (!draftResult || draftResult.length === 0) req.reject(404)
26
+
26
27
  const draftAdminData = draftResult[0]
27
28
 
28
29
  // the same user that locked the entity can always delete/update it
@@ -118,7 +119,9 @@ const _addDraftDataFromExistingDraft = async req => {
118
119
  * Generic Handler for before NEW requests.
119
120
  */
120
121
  const _new = async function (req) {
121
- if (isNavigationToMany(req)) {
122
+ if (!req.target._isDraftEnabled) return
123
+
124
+ if (isNavigationToMany(req, this.model)) {
122
125
  const result = await _addDraftDataFromExistingDraft(req)
123
126
 
124
127
  // in order to fix corner case where active subitems are created in draft case
@@ -136,6 +139,8 @@ const _new = async function (req) {
136
139
  * Generic Handler for before PATCH and UPDATE requests.
137
140
  */
138
141
  const _patch = async function (req) {
142
+ if (!req.target._isDraftEnabled) return
143
+
139
144
  const result = await _addDraftDataFromExistingDraft(req)
140
145
 
141
146
  // no result means that the draft does not exist
@@ -146,6 +151,8 @@ const _patch = async function (req) {
146
151
  * Generic Handler for before DELETE and CANCEL requests.
147
152
  */
148
153
  const _cancel = async function (req) {
154
+ if (!req.target._isDraftEnabled) return
155
+
149
156
  await _addDraftDataFromExistingDraft(req)
150
157
  }
151
158
 
@@ -162,20 +169,22 @@ const _allowEntityCollectionOnAction = action => {
162
169
  )
163
170
  }
164
171
 
165
- const _registerBoundActionHandlers = function (entityName, actions) {
166
- if (!actions) return
167
-
168
- const boundActions = Object.values(actions).filter(
169
- action =>
170
- action.kind === 'action' &&
171
- action.name !== 'draftPrepare' &&
172
- action.name !== 'draftEdit' &&
173
- action.name !== 'draftActivate' &&
174
- !_allowEntityCollectionOnAction(action)
175
- )
176
-
177
- for (const action of boundActions) {
178
- this.before(action.name, entityName, req => _validateDraftBoundAction(req))
172
+ const _registerBoundActionHandlers = function (entities) {
173
+ for (let entity of entities) {
174
+ if (!entity.actions) return
175
+
176
+ const boundActions = Object.values(entity.actions).filter(
177
+ action =>
178
+ action.kind === 'action' &&
179
+ action.name !== 'draftPrepare' &&
180
+ action.name !== 'draftEdit' &&
181
+ action.name !== 'draftActivate' &&
182
+ !_allowEntityCollectionOnAction(action)
183
+ )
184
+
185
+ for (const action of boundActions) {
186
+ this.before(action.name, entity.name, req => _validateDraftBoundAction(req))
187
+ }
179
188
  }
180
189
  }
181
190
 
@@ -183,9 +192,10 @@ _new._initial = true
183
192
  _patch._initial = true
184
193
  _cancel._initial = true
185
194
 
186
- module.exports = cds.service.impl((srv, entity) => {
187
- srv.before('NEW', entity, _new)
188
- srv.before('PATCH', entity, _patch)
189
- srv.before('CANCEL', entity, _cancel)
190
- _registerBoundActionHandlers.call(srv, entity.name, entity.actions)
195
+ module.exports = cds.service.impl(function (srv) {
196
+ srv.before('NEW', '*', _new)
197
+ srv.before('PATCH', '*', _patch)
198
+ srv.before('CANCEL', '*', _cancel)
199
+ const entities = Object.values(srv.entities || {}).filter(entity => entity._isDraftEnabled)
200
+ _registerBoundActionHandlers.call(srv, entities)
191
201
  })
@@ -8,10 +8,12 @@ const { deleteDraft } = require('../utils/delete')
8
8
  *
9
9
  * @param req
10
10
  */
11
- const fioriGenericCancel = function (req) {
11
+ const fioriGenericCancel = function (req, next) {
12
+ if (!req.target._isDraftEnabled) return next()
13
+
12
14
  return deleteDraft(req, this)
13
15
  }
14
16
 
15
- module.exports = cds.service.impl(function (srv, entity) {
16
- srv.on('CANCEL', entity, fioriGenericCancel)
17
+ module.exports = cds.service.impl(function (srv) {
18
+ srv.on('CANCEL', '*', fioriGenericCancel)
17
19
  })
@@ -8,12 +8,14 @@ const { deleteDraft } = require('../utils/delete')
8
8
  *
9
9
  * @param req
10
10
  */
11
- const fioriGenericDelete = function (req) {
11
+ const fioriGenericDelete = function (req, next) {
12
+ if (!req.target._isDraftEnabled) return next()
13
+
12
14
  // we should call deleteDraft only for draft tables and call next
13
15
  // to pass delete of active tables to our general implementation
14
16
  return deleteDraft(req, this, true)
15
17
  }
16
18
 
17
- module.exports = cds.service.impl(function (srv, entity) {
18
- srv.on('DELETE', entity, fioriGenericDelete)
19
+ module.exports = cds.service.impl(function (srv) {
20
+ srv.on('DELETE', '*', fioriGenericDelete)
19
21
  })
@@ -68,7 +68,9 @@ const _select = async (lockRecordCQN, draftExistsCQN, selectCQNs, req, dbtx) =>
68
68
  *
69
69
  * @param req
70
70
  */
71
- const fioriGenericEdit = async function (req) {
71
+ const fioriGenericEdit = async function (req, next) {
72
+ if (!req.target._isDraftEnabled) return next()
73
+
72
74
  if (!isActiveEntityRequested(req.query.SELECT.where || [])) {
73
75
  req.reject(400, 'Action "draftEdit" can only be called on the active entity')
74
76
  }
@@ -123,9 +125,7 @@ const fioriGenericEdit = async function (req) {
123
125
  // REVISIT: Use service.read with expand **
124
126
  const [draftExists, ...results] = await _select(lockRecordCQN, draftExistsCQN, [...selectCQNs], req, dbtx)
125
127
 
126
- if (!results[0].length) {
127
- req.reject(404)
128
- }
128
+ if (!results[0].length) req.reject(404)
129
129
 
130
130
  if (draftExists.length) {
131
131
  const adminData = await dbtx.run(
@@ -164,11 +164,11 @@ const fioriGenericEdit = async function (req) {
164
164
 
165
165
  // REVISIT: we need to use okra API here because it must be set in the batched request
166
166
  // status code must be set in handler to allow overriding for FE V2
167
- req?._?.odataRes?.setStatusCode(201)
167
+ req?._?.odataRes?.setStatusCode(201, { overwrite: true })
168
168
 
169
169
  return results[0][0]
170
170
  }
171
171
 
172
- module.exports = cds.service.impl(function (srv, entity) {
173
- srv.on('EDIT', entity, fioriGenericEdit)
172
+ module.exports = cds.service.impl(function (srv) {
173
+ srv.on('EDIT', '*', fioriGenericEdit)
174
174
  })
@@ -1,22 +1,16 @@
1
1
  const cds = require('../../cds')
2
2
 
3
3
  exports.impl = cds.service.impl(function () {
4
- const entities = Object.values(this.entities).filter(entity => entity._isDraftEnabled)
5
- if (entities.length === 0) return
6
-
7
- for (const each of entities) {
8
- _before(this, each)
9
- _new(this, each)
10
- _patch(this, each)
11
- _cancel(this, each)
12
- _edit(this, each)
13
- _prepare(this, each)
14
- _activate(this, each)
15
- _delete(this, each)
16
- }
17
-
18
- _readOverDraft(this) // registers an on READ * handler
19
- for (const each of entities) _read(this, each) // have to go last
4
+ _before(this)
5
+ _new(this)
6
+ _patch(this)
7
+ _cancel(this)
8
+ _edit(this)
9
+ _prepare(this)
10
+ _activate(this)
11
+ _delete(this)
12
+ _readOverDraft(this)
13
+ _read(this) // have to go last
20
14
  })
21
15
 
22
16
  const _before = require('../../fiori/generic/before')
@@ -49,6 +49,8 @@ const _getInsertDataCQN = (req, draftUUID) => {
49
49
  * @param next
50
50
  */
51
51
  const fioriGenericNew = async function (req, next) {
52
+ if (!req.target._isDraftEnabled) return next()
53
+
52
54
  if (!req._draftMetadata) {
53
55
  // REVISIT: when is this the case?
54
56
  return onDraftActivate(req, next)
@@ -60,7 +62,7 @@ const fioriGenericNew = async function (req, next) {
60
62
  // Only allowed for pseudo draft roots (entities with this action)
61
63
  if (isRoot && !req.target['@Common.DraftRoot.ActivationAction']) req.reject(403, 'DRAFT_MODIFICATION_ONLY_VIA_ROOT')
62
64
 
63
- const navigationToMany = isNavigationToMany(req)
65
+ const navigationToMany = isNavigationToMany(req, this.model)
64
66
 
65
67
  const adminDataCQN = navigationToMany
66
68
  ? _getUpdateDraftAdminCQN(req, req.data.DraftAdministrativeData_DraftUUID)
@@ -75,6 +77,6 @@ const fioriGenericNew = async function (req, next) {
75
77
  return { ...req.data, IsActiveEntity: false }
76
78
  }
77
79
 
78
- module.exports = cds.service.impl(function (srv, entity) {
79
- srv.on('NEW', entity, fioriGenericNew)
80
+ module.exports = cds.service.impl(function (srv) {
81
+ srv.on('NEW', '*', fioriGenericNew)
80
82
  })
@@ -58,7 +58,9 @@ const _joinDraftAdministrativeData = (selectResolved, target) => {
58
58
  *
59
59
  * @param req
60
60
  */
61
- const fioriGenericPatch = async function (req) {
61
+ const fioriGenericPatch = async function (req, next) {
62
+ if (!req.target._isDraftEnabled) return next()
63
+
62
64
  if (req.data.IsActiveEntity === true) req.reject(400, 'Patch can only be applied to a draft entity')
63
65
 
64
66
  if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
@@ -70,17 +72,18 @@ const fioriGenericPatch = async function (req) {
70
72
  const targetName = selectResolved.SELECT.from.ref[selectResolved.SELECT.from.ref.length - 1]
71
73
  const alias = selectResolved.SELECT.from.as
72
74
  const selectWithAdmin = _joinDraftAdministrativeData(selectResolved, alias || targetName)
75
+
76
+ if (req._etagValidationClause) selectWithAdmin.where(removeIsActiveEntityRecursively(req._etagValidationClause))
77
+
73
78
  const results = await dbtx.run(selectWithAdmin)
74
79
 
75
- if (results.length === 0) req.reject(404)
80
+ if (results.length === 0) req.reject(req._etagValidationType ? 412 : 404)
76
81
 
77
82
  const result = results[0]
78
83
 
79
84
  // Potential timeout scenario supported
80
- if (result.draftAdmin_inProcessByUser && result.draftAdmin_inProcessByUser !== req.user.id) {
81
- // REVISIT: security log?
82
- req.reject(403)
83
- }
85
+ if (result.draftAdmin_inProcessByUser && result.draftAdmin_inProcessByUser !== req.user.id) req.reject(403)
86
+
84
87
  const updateDraftCQN = _getUpdateDraftCQN(req, selectResolved.SELECT.where, {
85
88
  ref: [targetName],
86
89
  as: alias || targetName
@@ -93,6 +96,6 @@ const fioriGenericPatch = async function (req) {
93
96
  return { ...req.data, IsActiveEntity: false }
94
97
  }
95
98
 
96
- module.exports = cds.service.impl(function (srv, entity) {
97
- srv.on('PATCH', entity, fioriGenericPatch)
99
+ module.exports = cds.service.impl(function (srv) {
100
+ srv.on('PATCH', '*', fioriGenericPatch)
98
101
  })
@@ -11,7 +11,9 @@ const { getColumns } = require('../../cds-services/services/utils/columns')
11
11
  *
12
12
  * @param req
13
13
  */
14
- const fioriGenericPrepare = async function (req) {
14
+ const fioriGenericPrepare = async function (req, next) {
15
+ if (!req.target._isDraftEnabled) return next()
16
+
15
17
  if (req.query.SELECT.from.ref.length > 1 || isActiveEntityRequested(req.query.SELECT.from.ref[0].where || [])) {
16
18
  req.reject(400, 'Action "draftPrepare" can only be called on a draft entity')
17
19
  }
@@ -19,6 +21,7 @@ const fioriGenericPrepare = async function (req) {
19
21
  if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
20
22
 
21
23
  const target = ensureDraftsSuffix(req.target.name)
24
+
22
25
  const columns = getColumns(this.model.definitions[ensureNoDraftsSuffix(req.target.name)], {
23
26
  keysOnly: true,
24
27
  removeIgnore: true,
@@ -26,6 +29,7 @@ const fioriGenericPrepare = async function (req) {
26
29
  onlyNames: true
27
30
  })
28
31
  columns.push({ ref: ['DRAFT.DraftAdministrativeData', 'inProcessByUser'], as: 'draftAdmin_inProcessByUser' })
32
+
29
33
  const select = SELECT.one(target)
30
34
  .columns(columns)
31
35
  .join('DRAFT.DraftAdministrativeData')
@@ -35,16 +39,19 @@ const fioriGenericPrepare = async function (req) {
35
39
  { ref: ['DRAFT.DraftAdministrativeData', 'DraftUUID'] }
36
40
  ])
37
41
  .where(req.query.SELECT.from.ref[0].where)
42
+
38
43
  const result = await cds.tx(req).run(select)
44
+
39
45
  if (!result) req.reject(404)
40
- if (result.draftAdmin_inProcessByUser !== req.user.id) {
41
- // REVISIT: security log?
46
+
47
+ if (result.draftAdmin_inProcessByUser !== req.user.id)
42
48
  req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER', [result.draftAdmin_inProcessByUser])
43
- }
49
+
44
50
  delete result.draftAdmin_inProcessByUser
51
+
45
52
  return result
46
53
  }
47
54
 
48
- module.exports = cds.service.impl(function (srv, entity) {
49
- srv.on('draftPrepare', entity, fioriGenericPrepare)
55
+ module.exports = cds.service.impl(function (srv) {
56
+ srv.on('draftPrepare', '*', fioriGenericPrepare)
50
57
  })
@@ -17,7 +17,7 @@ const {
17
17
  filterKeys
18
18
  } = require('../utils/handler')
19
19
  const { deleteCondition, readAndDeleteKeywords, removeIsActiveEntityRecursively } = require('../utils/where')
20
- const { adaptStreamCQN } = require('../../cds-services/adapter/odata-v4/utils/stream')
20
+ const { adaptStreamCQN } = require('../utils/stream')
21
21
  const getError = require('../../common/error')
22
22
 
23
23
  const _findSubselect = where => {
@@ -1189,10 +1189,11 @@ const _getOriginalColumns = req => {
1189
1189
  return originalColumns
1190
1190
  }
1191
1191
 
1192
- const _handlerStreaming = (req, query) => {
1192
+ const _handlerStreaming = async (req, query) => {
1193
1193
  adaptStreamCQN(query)
1194
1194
  query._streaming = true
1195
- return cds.tx(req).run(query)
1195
+ const result = await cds.tx(req).run(query)
1196
+ return result
1196
1197
  }
1197
1198
 
1198
1199
  const _postProcess = (result, req, cqnScenario, deleteLastChangeDateTime) => {
@@ -1269,7 +1270,9 @@ const _adaptColumns4readAfterWrite = (req, cqnScenario, query4sql) => {
1269
1270
  *
1270
1271
  * @param req
1271
1272
  */
1272
- const fioriGenericRead = async function (req) {
1273
+ const fioriGenericRead = async function (req, next) {
1274
+ if (!req.target._isDraftEnabled) return next()
1275
+
1273
1276
  if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
1274
1277
 
1275
1278
  const query = req.query
@@ -1327,9 +1330,12 @@ const fioriGenericRead = async function (req) {
1327
1330
  _adaptColumns4readAfterWrite(req, cqnScenario, query4sql)
1328
1331
 
1329
1332
  const result = await cds.tx(req).send({ query: cqnScenario.cqn, target: req.target })
1333
+
1334
+ if (result == null && req._etagValidationType === 'if-match') req.reject(412)
1335
+
1330
1336
  return _postProcess(result, reqClone, cqnScenario, enhancedWithLastChangeDateTime)
1331
1337
  }
1332
1338
 
1333
- module.exports = cds.service.impl(function (srv, entity) {
1334
- srv.on('READ', entity, fioriGenericRead)
1339
+ module.exports = cds.service.impl(function (srv) {
1340
+ srv.on('READ', '*', fioriGenericRead)
1335
1341
  })