@sap/cds 6.8.4 → 7.0.1
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.
- package/CHANGELOG.md +66 -4
- package/README.md +0 -1
- package/bin/cds-serve.js +50 -3
- package/bin/deploy/to-hana.js +1 -0
- package/bin/serve.js +16 -20
- package/lib/auth/basic-auth.js +6 -4
- package/lib/auth/index.js +4 -3
- package/lib/auth/jwt-auth.js +2 -5
- package/lib/compile/cds-compile.js +34 -89
- package/lib/compile/cdsc.js +11 -0
- package/lib/compile/etc/properties.js +2 -2
- package/lib/compile/for/lean_drafts.js +36 -69
- package/lib/compile/for/nodejs.js +2 -1
- package/lib/compile/load.js +3 -3
- package/lib/compile/minify.js +2 -0
- package/lib/compile/to/csn.js +74 -0
- package/{bin/build/provider/hana/2tabledata.js → lib/compile/to/hdbtabledata.js} +4 -6
- package/lib/compile/to/json.js +1 -1
- package/lib/compile/to/sql.js +8 -6
- package/lib/dbs/cds-deploy.js +174 -114
- package/lib/env/cds-env.js +64 -79
- package/lib/env/cds-requires.js +11 -28
- package/lib/env/defaults.js +13 -3
- package/lib/env/plugins.js +1 -12
- package/lib/env/presets.js +25 -21
- package/lib/index.js +121 -147
- package/lib/{core/reflect.js → linked/models.js} +2 -2
- package/lib/{core/infer.js → linked/queries.js} +2 -0
- package/lib/{core/index.js → linked/types.js} +2 -1
- package/lib/log/cds-error.js +13 -7
- package/lib/log/format/cf.js +1 -1
- package/lib/plugins.js +49 -0
- package/lib/ql/Query.js +0 -9
- package/lib/ql/STREAM.js +0 -1
- package/lib/req/context.js +2 -7
- package/lib/req/request.js +6 -2
- package/lib/req/response.js +23 -10
- package/lib/srv/middlewares/ctx-model.js +2 -2
- package/lib/srv/middlewares/errors.js +1 -1
- package/lib/srv/protocols/_legacy.js +1 -0
- package/lib/srv/protocols/graphql.js +7 -16
- package/lib/srv/protocols/index.js +59 -45
- package/lib/srv/protocols/odata-v2-proxy.js +2 -70
- package/lib/srv/protocols/odata-v4.js +9 -4
- package/lib/srv/srv-api.js +9 -3
- package/lib/srv/srv-dispatch.js +12 -9
- package/lib/srv/srv-models.js +4 -21
- package/lib/srv/srv-tx.js +15 -12
- package/lib/utils/cds-test.js +14 -9
- package/lib/utils/cds-utils.js +2 -12
- package/lib/utils/check-version.js +17 -0
- package/{bin/build → lib/utils}/csv-reader.js +23 -24
- package/libx/_runtime/auth/index.js +27 -23
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +15 -72
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +0 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +33 -63
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +14 -18
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +15 -5
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +5 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +37 -40
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +101 -38
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/errors/AbstractError.js +5 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriTokenizer.js +5 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +9 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +15 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +4 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +5 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +1 -123
- package/libx/_runtime/cds-services/services/Service.js +79 -107
- package/libx/_runtime/cds-services/services/utils/columns.js +23 -19
- package/libx/_runtime/cds-services/services/utils/compareJson.js +11 -1
- package/libx/_runtime/cds-services/services/utils/differ.js +7 -2
- package/libx/_runtime/cds-services/util/assert.js +65 -2
- package/libx/_runtime/common/composition/data.js +1 -0
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/restrict.js +5 -10
- package/libx/_runtime/common/generic/auth/restrictions.js +40 -0
- package/libx/_runtime/common/generic/auth/utils.js +1 -2
- package/libx/_runtime/common/generic/crud.js +32 -16
- package/libx/_runtime/common/generic/etag.js +133 -104
- package/libx/_runtime/common/generic/input.js +6 -21
- package/libx/_runtime/common/generic/put.js +1 -1
- package/libx/_runtime/common/generic/stream.js +52 -0
- package/libx/_runtime/common/generic/temporal.js +25 -8
- package/libx/_runtime/common/i18n/messages.properties +0 -2
- package/libx/_runtime/common/utils/cqn.js +1 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +5 -2
- package/libx/_runtime/common/utils/csn.js +0 -51
- package/libx/_runtime/common/utils/etag.js +30 -0
- package/libx/_runtime/common/utils/keys.js +1 -1
- package/libx/_runtime/common/utils/normalizeTimestamp.js +25 -0
- package/libx/_runtime/common/utils/path.js +1 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -1
- package/libx/_runtime/common/utils/rewriteAsterisks.js +6 -4
- package/libx/_runtime/common/utils/search2cqn4sql.js +12 -16
- package/libx/_runtime/common/utils/stream.js +140 -0
- package/libx/_runtime/common/utils/streamProp.js +29 -12
- package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +0 -2
- package/libx/_runtime/db/generic/index.js +0 -2
- package/libx/_runtime/db/query/delete.js +2 -2
- package/libx/_runtime/db/query/insert.js +2 -2
- package/libx/_runtime/db/query/read.js +2 -2
- package/libx/_runtime/db/query/run.js +2 -2
- package/libx/_runtime/db/query/update.js +2 -2
- package/libx/_runtime/db/sql-builder/BaseBuilder.js +0 -6
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +23 -12
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +18 -6
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -7
- package/libx/_runtime/db/sql-builder/UpsertBuilder.js +1 -0
- package/libx/_runtime/db/utils/normalizeTimeData.js +7 -3
- package/libx/_runtime/fiori/draft.js +2 -0
- package/libx/_runtime/fiori/generic/activate.js +8 -9
- package/libx/_runtime/fiori/generic/before.js +30 -20
- package/libx/_runtime/fiori/generic/cancel.js +5 -3
- package/libx/_runtime/fiori/generic/delete.js +5 -3
- package/libx/_runtime/fiori/generic/edit.js +7 -7
- package/libx/_runtime/fiori/generic/index.js +10 -16
- package/libx/_runtime/fiori/generic/new.js +5 -3
- package/libx/_runtime/fiori/generic/patch.js +11 -8
- package/libx/_runtime/fiori/generic/prepare.js +13 -6
- package/libx/_runtime/fiori/generic/read.js +12 -6
- package/libx/_runtime/fiori/lean-draft.js +207 -152
- package/libx/_runtime/fiori/utils/delete.js +10 -5
- package/libx/_runtime/fiori/utils/req.js +17 -5
- package/libx/_runtime/fiori/utils/stream.js +36 -0
- package/libx/_runtime/hana/Service.js +12 -9
- package/libx/_runtime/hana/conversion.js +10 -15
- package/libx/_runtime/hana/driver.js +2 -0
- package/libx/_runtime/hana/execute.js +28 -6
- package/libx/_runtime/hana/pool.js +36 -122
- package/libx/_runtime/hana/search2cqn4sql.js +34 -36
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +2 -6
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +3 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +10 -58
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/remote/Service.js +20 -1
- package/libx/_runtime/remote/utils/client.js +3 -5
- package/libx/_runtime/sqlite/Service.js +4 -6
- package/libx/_runtime/sqlite/conversion.js +3 -13
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +9 -6
- package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +6 -1
- package/libx/_runtime/sqlite/execute.js +5 -16
- package/libx/odata/afterburner.js +22 -6
- package/libx/odata/grammar.pegjs +6 -1
- package/libx/odata/parser.js +1 -1
- package/libx/rest/RestAdapter.js +16 -9
- package/libx/rest/RestRequest.js +1 -1
- package/libx/rest/middleware/input.js +2 -1
- package/libx/rest/middleware/operation.js +1 -0
- package/libx/rest/middleware/parse.js +3 -2
- package/libx/rest/middleware/payload.js +9 -8
- package/libx/rest/middleware/read.js +1 -0
- package/package.json +9 -16
- package/server.js +1 -1
- package/app/fiori/preview.js +0 -270
- package/app/fiori/routes.js +0 -59
- package/bin/build/buildTaskEngine.js +0 -360
- package/bin/build/buildTaskFactory.js +0 -283
- package/bin/build/buildTaskHandler.js +0 -241
- package/bin/build/buildTaskProvider.js +0 -22
- package/bin/build/buildTaskProviderFactory.js +0 -175
- package/bin/build/cds.js +0 -5
- package/bin/build/constants.js +0 -66
- package/bin/build/index.js +0 -58
- package/bin/build/provider/buildTaskHandlerEdmx.js +0 -82
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +0 -131
- package/bin/build/provider/buildTaskHandlerInternal.js +0 -254
- package/bin/build/provider/buildTaskProviderInternal.js +0 -383
- package/bin/build/provider/fiori/index.js +0 -171
- package/bin/build/provider/hana/2migration.js +0 -179
- package/bin/build/provider/hana/index.js +0 -505
- package/bin/build/provider/hana/migrationtable.js +0 -472
- package/bin/build/provider/hana/template/.hdiconfig-haas +0 -163
- package/bin/build/provider/hana/template/.hdiconfig-hanacloud +0 -137
- package/bin/build/provider/hana/template/.hdinamespace +0 -4
- package/bin/build/provider/hana/template/package.json +0 -12
- package/bin/build/provider/hana/template/undeploy.json +0 -5
- package/bin/build/provider/java/index.js +0 -111
- package/bin/build/provider/java-cf/index.js +0 -1
- package/bin/build/provider/mtx/index.js +0 -268
- package/bin/build/provider/mtx/resourcesTarBuilder.js +0 -95
- package/bin/build/provider/mtx-extension/index.js +0 -131
- package/bin/build/provider/mtx-sidecar/index.js +0 -137
- package/bin/build/provider/node-cf/index.js +0 -1
- package/bin/build/provider/nodejs/index.js +0 -192
- package/bin/build/util.js +0 -299
- package/bin/cds.js +0 -125
- package/bin/deploy/to-hana/cfUtil.js +0 -355
- package/bin/deploy/to-hana/gitUtil.js +0 -57
- package/bin/deploy/to-hana/hana.js +0 -306
- package/bin/deploy/to-hana/hdiDeployUtil.js +0 -153
- package/bin/deploy/to-hana/index.js +0 -16
- package/bin/deploy/to-hana/mtaUtil.js +0 -170
- package/bin/mtx/in-cds.js +0 -17
- package/bin/plugins.js +0 -32
- package/bin/run.js +0 -24
- package/bin/utils/log.js +0 -24
- package/bin/version.js +0 -178
- package/libx/_runtime/audit/Service.js +0 -222
- package/libx/_runtime/audit/generic/personal/access.js +0 -61
- package/libx/_runtime/audit/generic/personal/index.js +0 -56
- package/libx/_runtime/audit/generic/personal/modification.js +0 -132
- package/libx/_runtime/audit/generic/personal/utils.js +0 -186
- package/libx/_runtime/audit/utils/log.js +0 -23
- package/libx/_runtime/audit/utils/v2.js +0 -176
- package/libx/_runtime/db/data-conversion/timestamp.js +0 -9
- package/libx/_runtime/db/generic/integrity.js +0 -455
- package/srv/audit-log.cds +0 -87
- package/srv/mtx.cds +0 -2
- package/srv/mtx.js +0 -8
- /package/lib/{core → linked}/classes.js +0 -0
- /package/lib/{core → linked}/entities.js +0 -0
|
@@ -69,6 +69,11 @@ class FunctionBuilder extends BaseBuilder {
|
|
|
69
69
|
const functionName = this._functionName()
|
|
70
70
|
const args = this._functionArgs()
|
|
71
71
|
|
|
72
|
+
if (this._obj.func === 'indexof') {
|
|
73
|
+
this._handleIndexof(args, functionName)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
72
77
|
if (!args) {
|
|
73
78
|
// > arg-less func such as current_date
|
|
74
79
|
this._outputObj.sql.push(functionName)
|
|
@@ -114,6 +119,17 @@ class FunctionBuilder extends BaseBuilder {
|
|
|
114
119
|
this._handleLikewiseFunc(args)
|
|
115
120
|
}
|
|
116
121
|
|
|
122
|
+
_handleIndexof(args, funcName) {
|
|
123
|
+
this._outputObj.sql.push('(')
|
|
124
|
+
this._outputObj.sql.push(funcName, '(')
|
|
125
|
+
if (typeof args === 'string') this._outputObj.sql.push(args)
|
|
126
|
+
else this._addFunctionArgs(args)
|
|
127
|
+
this._outputObj.sql.push(')')
|
|
128
|
+
this._outputObj.sql.push('-')
|
|
129
|
+
this._outputObj.sql.push('1')
|
|
130
|
+
this._outputObj.sql.push(')')
|
|
131
|
+
}
|
|
132
|
+
|
|
117
133
|
_handleLikewiseFunc(args) {
|
|
118
134
|
const functionName = this._functionName()
|
|
119
135
|
const not = functionName.startsWith('not') ? 'NOT ' : ''
|
|
@@ -193,12 +209,8 @@ class FunctionBuilder extends BaseBuilder {
|
|
|
193
209
|
res.push(sql)
|
|
194
210
|
this._outputObj.values.push(...values)
|
|
195
211
|
} else if (Object.prototype.hasOwnProperty.call(arg, 'val')) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
} else {
|
|
199
|
-
res.push(this._options.placeholder)
|
|
200
|
-
this._outputObj.values.push(arg.val)
|
|
201
|
-
}
|
|
212
|
+
res.push(this._options.placeholder)
|
|
213
|
+
this._outputObj.values.push(arg.val)
|
|
202
214
|
} else if (arg.list) {
|
|
203
215
|
this._addFunctionArgs(arg.list, true)
|
|
204
216
|
// _addFunctionArgs adds the arguments list already to the output object
|
|
@@ -259,6 +259,7 @@ class InsertBuilder extends BaseBuilder {
|
|
|
259
259
|
_getValue(column, { entry, flattenColumn, insertAnnotatedColumns }) {
|
|
260
260
|
let val = entry
|
|
261
261
|
if (!flattenColumn && this.uuidKeys.includes(column)) {
|
|
262
|
+
if (this._UPSERT) return undefined
|
|
262
263
|
val = cds.utils.uuid()
|
|
263
264
|
} else {
|
|
264
265
|
for (const key of flattenColumn) {
|
|
@@ -413,7 +413,7 @@ class SelectBuilder extends BaseBuilder {
|
|
|
413
413
|
_addRows() {
|
|
414
414
|
if (this._obj.SELECT.limit) {
|
|
415
415
|
if (this._obj.SELECT.limit.rows !== undefined) {
|
|
416
|
-
// limit (no placeholder for statement caching)
|
|
416
|
+
// limit (no placeholder for statement caching) - hana does not know how to optimize
|
|
417
417
|
this._outputObj.sql.push('LIMIT', this._obj.SELECT.limit.rows.val)
|
|
418
418
|
} else {
|
|
419
419
|
// rows parameter is mandatory for SQL
|
|
@@ -429,12 +429,8 @@ class SelectBuilder extends BaseBuilder {
|
|
|
429
429
|
_addOffset() {
|
|
430
430
|
// offset
|
|
431
431
|
if (this._obj.SELECT.limit && this._obj.SELECT.limit.offset !== undefined) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
} else {
|
|
435
|
-
this._outputObj.sql.push('OFFSET', '?')
|
|
436
|
-
this._outputObj.values.push(this._obj.SELECT.limit.offset.val)
|
|
437
|
-
}
|
|
432
|
+
this._outputObj.sql.push('OFFSET', '?')
|
|
433
|
+
this._outputObj.values.push(this._obj.SELECT.limit.offset.val)
|
|
438
434
|
}
|
|
439
435
|
}
|
|
440
436
|
|
|
@@ -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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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'
|
|
@@ -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
|
-
|
|
124
|
-
|
|
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
|
|
187
|
-
srv.on('draftActivate',
|
|
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 (
|
|
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 (
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
action
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
|
187
|
-
srv.before('NEW',
|
|
188
|
-
srv.before('PATCH',
|
|
189
|
-
srv.before('CANCEL',
|
|
190
|
-
|
|
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
|
|
16
|
-
srv.on('CANCEL',
|
|
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
|
|
18
|
-
srv.on('DELETE',
|
|
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
|
|
173
|
-
srv.on('EDIT',
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
79
|
-
srv.on('NEW',
|
|
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
|
-
|
|
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
|
|
97
|
-
srv.on('PATCH',
|
|
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
|
-
|
|
41
|
-
|
|
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
|
|
49
|
-
srv.on('draftPrepare',
|
|
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('
|
|
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
|
-
|
|
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
|
|
1334
|
-
srv.on('READ',
|
|
1339
|
+
module.exports = cds.service.impl(function (srv) {
|
|
1340
|
+
srv.on('READ', '*', fioriGenericRead)
|
|
1335
1341
|
})
|