@sap/cds 7.9.4 → 8.0.4
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 +128 -3659
- package/_i18n/i18n_en_US_saptrc.properties +113 -0
- package/_i18n/i18n_zh_CN.properties +7 -4
- package/app/index.css +129 -0
- package/app/index.html +16 -64
- package/app/index.js +14 -9
- package/bin/args.js +34 -0
- package/bin/serve.js +18 -24
- package/bin/test.js +97 -0
- package/common.cds +5 -12
- package/eslint.config.mjs +133 -0
- package/lib/auth/basic-auth.js +16 -20
- package/lib/auth/dummy-auth.js +1 -1
- package/lib/auth/ias-auth.js +9 -41
- package/lib/auth/index.js +1 -14
- package/lib/auth/jwt-auth.js +10 -40
- package/lib/compile/cds-compile.js +1 -2
- package/lib/compile/cdsc.js +21 -26
- package/lib/compile/etc/_localized.js +1 -6
- package/lib/compile/etc/csv.js +1 -1
- package/lib/compile/etc/properties.js +1 -1
- package/lib/compile/for/java.js +1 -1
- package/lib/compile/for/lean_drafts.js +4 -6
- package/lib/compile/for/nodejs.js +1 -1
- package/lib/compile/parse.js +4 -0
- package/lib/compile/resolve.js +4 -4
- package/lib/compile/to/edm-files.js +16 -23
- package/lib/compile/to/hana.js +27 -0
- package/lib/compile/to/json.js +1 -1
- package/lib/compile/to/sql.js +5 -1
- package/lib/compile/to/yaml.js +3 -3
- package/lib/dbs/cds-deploy.js +4 -2
- package/lib/env/cds-env.js +10 -14
- package/lib/env/cds-requires.js +30 -13
- package/lib/env/defaults.js +46 -16
- package/lib/env/plugins.js +1 -1
- package/lib/env/schemas/cds-rc.js +8 -4
- package/lib/env/schemas/index.js +7 -7
- package/lib/env/serviceBindings.js +1 -1
- package/lib/index.js +12 -10
- package/lib/lazy.js +1 -1
- package/lib/linked/classes.js +36 -8
- package/lib/linked/entities.js +2 -10
- package/lib/linked/models.js +2 -1
- package/lib/linked/validate.js +292 -0
- package/lib/log/cds-error.js +0 -6
- package/lib/log/cds-log.js +3 -3
- package/lib/log/format/json.js +1 -1
- package/lib/log/service/index.js +0 -1
- package/lib/plugins.js +2 -2
- package/lib/ql/Query.js +2 -10
- package/lib/ql/SELECT.js +1 -1
- package/lib/ql/Whereable.js +3 -2
- package/lib/req/cds-context.js +14 -25
- package/lib/req/context.js +23 -25
- package/lib/req/request.js +1 -34
- package/lib/req/user.js +47 -35
- package/lib/srv/bindings.js +1 -1
- package/lib/srv/cds-connect.js +4 -4
- package/lib/srv/cds-serve.js +2 -2
- package/lib/srv/factory.js +1 -1
- package/lib/srv/middlewares/cds-context.js +11 -22
- package/lib/srv/middlewares/ctx-model.js +2 -3
- package/lib/srv/middlewares/errors.js +41 -8
- package/lib/srv/middlewares/index.js +3 -3
- package/lib/srv/middlewares/trace.js +0 -2
- package/lib/srv/protocols/hcql.js +15 -10
- package/lib/srv/protocols/http.js +44 -49
- package/lib/srv/protocols/index.js +1 -23
- package/lib/srv/protocols/odata-v4.js +12 -74
- package/lib/srv/protocols/rest.js +1 -13
- package/lib/srv/srv-api.js +0 -20
- package/lib/srv/srv-dispatch.js +3 -2
- package/lib/srv/srv-handlers.js +22 -11
- package/lib/srv/srv-methods.js +2 -2
- package/lib/srv/srv-models.js +3 -36
- package/lib/test/expect.js +343 -0
- package/lib/test/index.js +2 -0
- package/lib/test/reporter.js +176 -0
- package/lib/utils/axios.js +10 -9
- package/lib/utils/cds-test.js +85 -36
- package/lib/utils/cds-utils.js +54 -7
- package/lib/utils/check-version.js +0 -4
- package/lib/utils/colors.js +49 -0
- package/lib/utils/data.js +5 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +3 -30
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +6 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +4 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +12 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +2 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +1 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +1 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/AbstractEdmStructuredType.js +1 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +5 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/ContextURLFactory.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +9 -43
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +8 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +4 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -3
- package/libx/_runtime/cds-services/util/assert.js +1 -1
- package/libx/_runtime/cds.js +10 -3
- package/libx/_runtime/common/Service.js +12 -32
- package/libx/_runtime/common/aspects/any.js +1 -0
- package/libx/_runtime/common/code-ext/execute.js +1 -1
- package/libx/_runtime/common/code-ext/worker.js +0 -1
- package/libx/_runtime/common/composition/data.js +0 -1
- package/libx/_runtime/common/composition/delete.js +0 -1
- package/libx/_runtime/common/composition/tree.js +0 -1
- package/libx/_runtime/common/composition/update.js +3 -3
- package/libx/_runtime/common/error/frontend.js +21 -12
- package/libx/_runtime/common/error/log.js +36 -0
- package/libx/_runtime/common/error/utils.js +2 -5
- package/libx/_runtime/common/generic/auth/autoexpose.js +18 -17
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/readOnly.js +1 -2
- package/libx/_runtime/common/generic/auth/restrict.js +23 -42
- package/libx/_runtime/common/generic/auth/restrictions.js +2 -7
- package/libx/_runtime/common/generic/auth/utils.js +91 -88
- package/libx/_runtime/common/generic/crud.js +6 -5
- package/libx/_runtime/common/generic/etag.js +7 -12
- package/libx/_runtime/common/generic/input.js +70 -68
- package/libx/_runtime/common/generic/paging.js +1 -0
- package/libx/_runtime/common/generic/sorting.js +1 -0
- package/libx/_runtime/common/generic/temporal.js +8 -2
- package/libx/_runtime/common/i18n/index.js +1 -1
- package/libx/_runtime/common/i18n/messages.properties +3 -1
- package/libx/_runtime/common/utils/binary.js +8 -2
- package/libx/_runtime/common/utils/compareJson.js +5 -1
- package/libx/_runtime/common/utils/copy.js +6 -11
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +16 -14
- package/libx/_runtime/common/utils/differ.js +3 -6
- package/libx/_runtime/common/utils/keys.js +77 -18
- package/libx/_runtime/common/utils/postProcess.js +12 -15
- package/libx/_runtime/common/utils/propagateForeignKeys.js +0 -1
- package/libx/_runtime/common/utils/resolveView.js +2 -3
- package/libx/_runtime/common/utils/restrictions.js +45 -17
- package/libx/_runtime/common/utils/rewriteAsterisks.js +1 -8
- package/libx/_runtime/common/utils/stream.js +3 -16
- package/libx/_runtime/common/utils/streamProp.js +8 -18
- package/libx/_runtime/common/utils/structured.js +1 -1
- package/libx/_runtime/common/utils/ucsn.js +0 -2
- package/libx/_runtime/db/Service.js +0 -72
- package/libx/_runtime/db/data-conversion/post-processing.js +0 -1
- package/libx/_runtime/db/expand/expandCQNToJoin.js +9 -9
- package/libx/_runtime/db/expand/rawToExpanded.js +0 -8
- package/libx/_runtime/db/generic/input.js +3 -8
- package/libx/_runtime/db/generic/rewrite.js +1 -0
- package/libx/_runtime/db/query/read.js +2 -2
- package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -1
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
- package/libx/_runtime/db/utils/columns.js +2 -6
- package/libx/_runtime/fiori/lean-draft.js +138 -56
- package/libx/_runtime/hana/Service.js +0 -1
- package/libx/_runtime/hana/driver.js +1 -1
- package/libx/_runtime/hana/dynatrace.js +1 -2
- package/libx/_runtime/hana/pool.js +11 -21
- package/libx/_runtime/hana/streaming.js +0 -1
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +0 -1
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
- package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -33
- package/libx/_runtime/messaging/event-broker.js +54 -27
- package/libx/_runtime/messaging/file-based.js +3 -3
- package/libx/_runtime/messaging/http-utils/token.js +1 -1
- package/libx/_runtime/messaging/kafka.js +2 -2
- package/libx/_runtime/messaging/redis-messaging.js +0 -1
- package/libx/_runtime/remote/Service.js +25 -25
- package/libx/_runtime/remote/utils/client.js +4 -5
- package/libx/_runtime/remote/utils/cloudSdkProvider.js +0 -3
- package/libx/_runtime/remote/utils/data.js +0 -1
- package/libx/_runtime/sqlite/Service.js +1 -2
- package/libx/_runtime/ucl/Service.js +37 -78
- package/libx/common/assert/index.js +22 -21
- package/libx/common/assert/type-relaxed.js +39 -0
- package/libx/common/assert/utils.js +3 -2
- package/libx/common/assert/validation.js +3 -8
- package/libx/common/utils/index.js +5 -0
- package/libx/common/utils/path.js +51 -0
- package/libx/odata/ODataAdapter.js +126 -0
- package/libx/odata/index.js +15 -2
- package/libx/odata/middleware/batch.js +320 -84
- package/libx/odata/middleware/body-parser.js +33 -0
- package/libx/odata/middleware/create.js +44 -59
- package/libx/odata/middleware/delete.js +23 -12
- package/libx/odata/middleware/error.js +30 -6
- package/libx/odata/middleware/metadata.js +38 -26
- package/libx/odata/middleware/operation.js +93 -69
- package/libx/odata/middleware/parse.js +6 -8
- package/libx/odata/middleware/read.js +117 -93
- package/libx/odata/middleware/service-document.js +22 -19
- package/libx/odata/middleware/stream.js +54 -56
- package/libx/odata/middleware/update.js +79 -87
- package/libx/odata/parse/afterburner.js +191 -175
- package/libx/odata/parse/cqn2odata.js +5 -5
- package/libx/odata/parse/grammar.peggy +27 -20
- package/libx/odata/parse/multipartToJson.js +17 -9
- package/libx/odata/parse/parser.js +1 -1
- package/libx/odata/utils/etag.js +14 -6
- package/libx/odata/utils/index.js +84 -12
- package/libx/odata/utils/metadata.js +161 -0
- package/libx/odata/utils/postProcess.js +89 -0
- package/libx/odata/utils/readAfterWrite.js +134 -17
- package/libx/odata/utils/result.js +36 -142
- package/libx/outbox/index.js +4 -3
- package/libx/rest/RestAdapter.js +115 -182
- package/libx/rest/middleware/create.js +28 -24
- package/libx/rest/middleware/delete.js +7 -10
- package/libx/rest/middleware/error.js +26 -16
- package/libx/rest/middleware/operation.js +48 -41
- package/libx/rest/middleware/parse.js +128 -126
- package/libx/rest/middleware/read.js +20 -27
- package/libx/rest/middleware/update.js +26 -31
- package/package.json +17 -8
- package/server.js +4 -2
- package/apis/cds.d.ts +0 -3
- package/apis/core.d.ts +0 -21
- package/apis/cqn.d.ts +0 -18
- package/apis/csn.d.ts +0 -21
- package/apis/events.d.ts +0 -18
- package/apis/internal/inference.d.ts +0 -18
- package/apis/linked.d.ts +0 -18
- package/apis/log.d.ts +0 -20
- package/apis/models.d.ts +0 -18
- package/apis/ql.d.ts +0 -18
- package/apis/reflect.d.ts +0 -32
- package/apis/server.d.ts +0 -18
- package/apis/services.d.ts +0 -22
- package/bin/cds-serve.js +0 -56
- package/lib/compile/to/gql.js +0 -15
- package/lib/srv/protocols/_legacy.js +0 -44
- package/lib/utils/jest.js +0 -43
- package/libx/_runtime/auth/index.js +0 -193
- package/libx/_runtime/auth/strategies/JWT.js +0 -37
- package/libx/_runtime/auth/strategies/basic.js +0 -20
- package/libx/_runtime/auth/strategies/dummy.js +0 -14
- package/libx/_runtime/auth/strategies/ias-auth.js +0 -1
- package/libx/_runtime/auth/strategies/mock.js +0 -77
- package/libx/_runtime/auth/strategies/xssecUtils.js +0 -93
- package/libx/_runtime/auth/strategies/xsuaa.js +0 -38
- package/libx/_runtime/common/perf/index.js +0 -19
- package/libx/_runtime/common/utils/ensureIEEE754.js +0 -29
- package/libx/_runtime/fiori/draft.js +0 -2
- package/libx/_runtime/fiori/generic/activate.js +0 -190
- package/libx/_runtime/fiori/generic/before.js +0 -201
- package/libx/_runtime/fiori/generic/cancel.js +0 -19
- package/libx/_runtime/fiori/generic/delete.js +0 -21
- package/libx/_runtime/fiori/generic/edit.js +0 -157
- package/libx/_runtime/fiori/generic/index.js +0 -25
- package/libx/_runtime/fiori/generic/new.js +0 -82
- package/libx/_runtime/fiori/generic/patch.js +0 -101
- package/libx/_runtime/fiori/generic/prepare.js +0 -57
- package/libx/_runtime/fiori/generic/read.js +0 -1340
- package/libx/_runtime/fiori/generic/readOverDraft.js +0 -146
- package/libx/_runtime/fiori/utils/csn.js +0 -13
- package/libx/_runtime/fiori/utils/delete.js +0 -114
- package/libx/_runtime/fiori/utils/handler.js +0 -264
- package/libx/_runtime/fiori/utils/lockInfo.js +0 -27
- package/libx/_runtime/fiori/utils/req.js +0 -23
- package/libx/_runtime/fiori/utils/stream.js +0 -36
- package/libx/_runtime/fiori/utils/where.js +0 -254
- package/libx/_runtime/index.js +0 -22
- package/libx/odata/utils/handler.js +0 -120
- package/libx/odata/utils/metaInfo.js +0 -410
- package/libx/odata/utils/path.js +0 -75
- package/libx/rest/RestRequest.js +0 -32
- package/libx/rest/index.js +0 -3
- package/libx/rest/readme.md +0 -1
- /package/libx/common/assert/{type.js → type-strict.js} +0 -0
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
const { deleteDraft } = require('../utils/delete')
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Generic Handler for DELETE requests.
|
|
6
|
-
* In case of success it returns an empty object.
|
|
7
|
-
* If the entry to be deleted does not exist, it rejects with error to return a 404.
|
|
8
|
-
*
|
|
9
|
-
* @param req
|
|
10
|
-
*/
|
|
11
|
-
const fioriGenericDelete = function (req, next) {
|
|
12
|
-
if (!req.target._isDraftEnabled) return next()
|
|
13
|
-
|
|
14
|
-
// we should call deleteDraft only for draft tables and call next
|
|
15
|
-
// to pass delete of active tables to our general implementation
|
|
16
|
-
return deleteDraft(req, this, true)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = cds.service.impl(function (srv) {
|
|
20
|
-
srv.on('DELETE', '*', fioriGenericDelete)
|
|
21
|
-
})
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
const getLockInfo = require('../utils/lockInfo')
|
|
3
|
-
const { INSERT, SELECT, DELETE } = cds.ql
|
|
4
|
-
|
|
5
|
-
const { getCompositionTree } = require('../../common/composition')
|
|
6
|
-
const { getColumns } = require('../../common/utils/columns')
|
|
7
|
-
const { draftIsLocked, ensureDraftsSuffix, ensureNoDraftsSuffix, getSubCQNs } = require('../utils/handler')
|
|
8
|
-
const { isActiveEntityRequested } = require('../utils/where')
|
|
9
|
-
|
|
10
|
-
const _getDraftColumns = draftUUID => ({
|
|
11
|
-
IsActiveEntity: false,
|
|
12
|
-
HasDraftEntity: false,
|
|
13
|
-
HasActiveEntity: true,
|
|
14
|
-
DraftAdministrativeData_DraftUUID: draftUUID
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
const _getAdminData = ({ user }, draftUUID, time) => {
|
|
18
|
-
const currentUser = user.id || null
|
|
19
|
-
return {
|
|
20
|
-
DraftUUID: draftUUID,
|
|
21
|
-
CreationDateTime: time,
|
|
22
|
-
CreatedByUser: currentUser,
|
|
23
|
-
LastChangeDateTime: time,
|
|
24
|
-
LastChangedByUser: currentUser,
|
|
25
|
-
DraftIsCreatedByMe: true,
|
|
26
|
-
DraftIsProcessedByMe: true,
|
|
27
|
-
InProcessByUser: currentUser
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const _getInsertAdminDataCQN = ({ user }, draftUUID, time) => {
|
|
32
|
-
return INSERT.into('DRAFT.DraftAdministrativeData').entries(_getAdminData({ user }, draftUUID, time))
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function _lockAndSelectActive(req, lockRecordCQN, selectCQNs, draftExistsCQN) {
|
|
36
|
-
try {
|
|
37
|
-
await this.run(lockRecordCQN)
|
|
38
|
-
} catch (e) {
|
|
39
|
-
const drafts = await this.run(draftExistsCQN)
|
|
40
|
-
if (drafts.length) req.reject(409, 'DRAFT_ALREADY_EXISTS')
|
|
41
|
-
req.reject(409, 'ENTITY_LOCKED')
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const cqns = [this.run(draftExistsCQN), ...selectCQNs.map(cqn => this.run(cqn))]
|
|
45
|
-
const promisesResults = await Promise.allSettled(cqns)
|
|
46
|
-
const firstRejected = promisesResults.find(r => r.status === 'rejected')
|
|
47
|
-
if (firstRejected) req.reject(firstRejected.reason)
|
|
48
|
-
return promisesResults.map(result => result.value)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Generic event handler for draft edit requests.
|
|
53
|
-
*
|
|
54
|
-
* @param req
|
|
55
|
-
*/
|
|
56
|
-
const fioriGenericEdit = async function (req, next) {
|
|
57
|
-
if (!req.target._isDraftEnabled) return next()
|
|
58
|
-
|
|
59
|
-
if (!isActiveEntityRequested(req.query.SELECT.where || [])) {
|
|
60
|
-
req.reject(400, 'Action "draftEdit" can only be called on the active entity')
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
|
|
64
|
-
|
|
65
|
-
const { definitions } = this.model
|
|
66
|
-
const lockInfo = getLockInfo(req)
|
|
67
|
-
const rootWhere = lockInfo.rootWhere
|
|
68
|
-
|
|
69
|
-
// Ensure exclusive access to the root record of the active entity by applying a lock,
|
|
70
|
-
// which effectively prevents the creation or overwriting of duplicate draft entities.
|
|
71
|
-
// This lock mechanism enforces a strict processing order for active entities,
|
|
72
|
-
// allowing only one entity to be worked on at any given time.
|
|
73
|
-
// By using .forUpdate() with a wait value of 0, we immediately lock the record,
|
|
74
|
-
// ensuring there is no waiting time for other users attempting to edit the same record concurrently.
|
|
75
|
-
const activeLockCQN = SELECT.from(lockInfo.target, [1]).where(lockInfo.where).forUpdate({ wait: 0 })
|
|
76
|
-
|
|
77
|
-
const columnNames = getColumns(req.target, { onlyNames: true, filterVirtual: true })
|
|
78
|
-
const rootCQN = SELECT.from(req.target, columnNames).where(rootWhere)
|
|
79
|
-
const subCQNs = getSubCQNs({
|
|
80
|
-
definitions,
|
|
81
|
-
rootCQN,
|
|
82
|
-
compositionTree: getCompositionTree({ definitions, rootEntityName: ensureNoDraftsSuffix(req.target.name) })
|
|
83
|
-
})
|
|
84
|
-
const rootDraftName = ensureDraftsSuffix(req.target.name)
|
|
85
|
-
const draftExistsCQN = SELECT.from(rootDraftName, ['DraftAdministrativeData_DraftUUID as DraftUUID']).where(rootWhere)
|
|
86
|
-
const selectCQNs = [rootCQN, ...subCQNs.map(obj => obj.cqn)]
|
|
87
|
-
|
|
88
|
-
// fetch unlocalized data if not a texts entity
|
|
89
|
-
for (const q of selectCQNs) {
|
|
90
|
-
const entity = definitions[q.SELECT.from.ref[0]]
|
|
91
|
-
if (entity && !entity.name.match(/\.texts$/)) {
|
|
92
|
-
q.SELECT.localized = false
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const dbtx = cds.tx(req)
|
|
97
|
-
// REVISIT: Use service.read with expand **
|
|
98
|
-
const [draftExists, ...results] = await _lockAndSelectActive.call(
|
|
99
|
-
dbtx,
|
|
100
|
-
req,
|
|
101
|
-
activeLockCQN,
|
|
102
|
-
[...selectCQNs],
|
|
103
|
-
draftExistsCQN
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
if (!results[0].length) req.reject(404)
|
|
107
|
-
|
|
108
|
-
if (draftExists.length) {
|
|
109
|
-
const adminData = await dbtx.run(
|
|
110
|
-
SELECT.one('DRAFT.DraftAdministrativeData', ['InProcessByUser', 'LastChangeDateTime']).where(draftExists[0])
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
// temp check if draft admin data in not maintained - raise 500 error
|
|
114
|
-
if (!adminData) req.reject(500, 'Draft administrative data is not maintained')
|
|
115
|
-
|
|
116
|
-
// draft is locked (default cancellation timeout timer has not expired) OR
|
|
117
|
-
// draft is not locked but must be rejected for popup
|
|
118
|
-
if (draftIsLocked(adminData.LastChangeDateTime) || req.data.PreserveChanges) {
|
|
119
|
-
req.reject(409, 'DRAFT_ALREADY_EXISTS')
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
await Promise.all([
|
|
123
|
-
dbtx.run(DELETE.from('DRAFT.DraftAdministrativeData').where(draftExists[0])),
|
|
124
|
-
dbtx.run(DELETE.from(rootDraftName).where(rootWhere))
|
|
125
|
-
])
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const draftUUID = cds.utils.uuid()
|
|
129
|
-
const insertCQNs = [_getInsertAdminDataCQN(req, draftUUID, req.timestamp)]
|
|
130
|
-
|
|
131
|
-
for (const resultIndex in results) {
|
|
132
|
-
if (results[resultIndex].length === 0) continue
|
|
133
|
-
const draftEntity = ensureDraftsSuffix(selectCQNs[resultIndex].SELECT.from.ref[0])
|
|
134
|
-
const entries = results[resultIndex].map(entityResult =>
|
|
135
|
-
Object.assign({}, entityResult, _getDraftColumns(draftUUID))
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
insertCQNs.push(INSERT.into(draftEntity).entries(entries))
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
await Promise.all(insertCQNs.map(CQN => dbtx.run(CQN)))
|
|
142
|
-
|
|
143
|
-
// REVISIT: we need to use okra API here because it must be set in the batched request
|
|
144
|
-
// status code must be set in handler to allow overriding for FE V2
|
|
145
|
-
// REVISIT: needs reworking for new adapter, especially re $batch
|
|
146
|
-
if (req._?.odataRes) {
|
|
147
|
-
req._?.odataRes?.setStatusCode(201, { overwrite: true })
|
|
148
|
-
} else if (req.http?.res) {
|
|
149
|
-
req.http.res.status(201)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return results[0][0]
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
module.exports = cds.service.impl(function (srv) {
|
|
156
|
-
srv.on('EDIT', '*', fioriGenericEdit)
|
|
157
|
-
})
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
|
|
3
|
-
exports.impl = cds.service.impl(function () {
|
|
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
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
const _before = require('../../fiori/generic/before')
|
|
17
|
-
const _new = require('../../fiori/generic/new')
|
|
18
|
-
const _patch = require('../../fiori/generic/patch')
|
|
19
|
-
const _cancel = require('../../fiori/generic/cancel')
|
|
20
|
-
const _edit = require('../../fiori/generic/edit')
|
|
21
|
-
const _prepare = require('../../fiori/generic/prepare')
|
|
22
|
-
const _activate = require('../../fiori/generic/activate')
|
|
23
|
-
const _readOverDraft = require('../../fiori/generic/readOverDraft')
|
|
24
|
-
const _read = require('../../fiori/generic/read')
|
|
25
|
-
const _delete = require('../../fiori/generic/delete')
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
const { INSERT, UPDATE } = cds.ql
|
|
3
|
-
|
|
4
|
-
const onDraftActivate = require('./activate')._handler
|
|
5
|
-
const { isNavigationToMany } = require('../utils/req')
|
|
6
|
-
const { ensureDraftsSuffix } = require('../utils/handler')
|
|
7
|
-
|
|
8
|
-
const _getUpdateDraftAdminCQN = ({ user, timestamp }, draftUUID) => {
|
|
9
|
-
return UPDATE('DRAFT.DraftAdministrativeData')
|
|
10
|
-
.data({
|
|
11
|
-
InProcessByUser: user.id,
|
|
12
|
-
LastChangedByUser: user.id,
|
|
13
|
-
LastChangeDateTime: timestamp
|
|
14
|
-
})
|
|
15
|
-
.where({ DraftUUID: draftUUID })
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const _getInsertDraftAdminCQN = ({ user, timestamp }, uuid) => {
|
|
19
|
-
return INSERT.into('DRAFT.DraftAdministrativeData').entries({
|
|
20
|
-
DraftUUID: uuid,
|
|
21
|
-
CreationDateTime: timestamp,
|
|
22
|
-
CreatedByUser: user.id,
|
|
23
|
-
LastChangeDateTime: timestamp,
|
|
24
|
-
LastChangedByUser: user.id,
|
|
25
|
-
DraftIsCreatedByMe: true,
|
|
26
|
-
DraftIsProcessedByMe: true,
|
|
27
|
-
InProcessByUser: user.id
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const _getInsertDataCQN = (req, draftUUID) => {
|
|
32
|
-
const draftName = ensureDraftsSuffix(req.target.name)
|
|
33
|
-
|
|
34
|
-
const insertData = INSERT.into(draftName).entries(req.query.INSERT.entries[0]) // entries is always set because there are no entities without keys
|
|
35
|
-
|
|
36
|
-
req.data.IsActiveEntity = false
|
|
37
|
-
req.data.HasDraftEntity = false
|
|
38
|
-
req.data.HasActiveEntity = false
|
|
39
|
-
req.data.DraftAdministrativeData_DraftUUID = draftUUID
|
|
40
|
-
|
|
41
|
-
return insertData
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Generic Handler for CREATE requests in the context of draft.
|
|
46
|
-
* In case of success it returns the created entry.
|
|
47
|
-
*
|
|
48
|
-
* @param req
|
|
49
|
-
* @param next
|
|
50
|
-
*/
|
|
51
|
-
const fioriGenericNew = async function (req, next) {
|
|
52
|
-
if (!req.target._isDraftEnabled) return next()
|
|
53
|
-
|
|
54
|
-
if (!req._draftMetadata) {
|
|
55
|
-
// REVISIT: when is this the case?
|
|
56
|
-
return onDraftActivate(req, next)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
|
|
60
|
-
|
|
61
|
-
const isRoot = typeof req.query.INSERT.into === 'string' || req.query.INSERT.into.ref?.length === 1
|
|
62
|
-
// Only allowed for pseudo draft roots (entities with this action)
|
|
63
|
-
if (isRoot && !req.target['@Common.DraftRoot.ActivationAction']) req.reject(403, 'DRAFT_MODIFICATION_ONLY_VIA_ROOT')
|
|
64
|
-
|
|
65
|
-
const navigationToMany = isNavigationToMany(req, this.model)
|
|
66
|
-
|
|
67
|
-
const adminDataCQN = navigationToMany
|
|
68
|
-
? _getUpdateDraftAdminCQN(req, req.data.DraftAdministrativeData_DraftUUID)
|
|
69
|
-
: _getInsertDraftAdminCQN(req, req.data.DraftAdministrativeData_DraftUUID)
|
|
70
|
-
const insertDataCQN = _getInsertDataCQN(req, req.data.DraftAdministrativeData_DraftUUID)
|
|
71
|
-
|
|
72
|
-
const dbtx = cds.tx(req)
|
|
73
|
-
|
|
74
|
-
await Promise.all([dbtx.run(adminDataCQN), dbtx.run(insertDataCQN)])
|
|
75
|
-
|
|
76
|
-
req._.readAfterWrite = true
|
|
77
|
-
return { ...req.data, IsActiveEntity: false }
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
module.exports = cds.service.impl(function (srv) {
|
|
81
|
-
srv.on('NEW', '*', fioriGenericNew)
|
|
82
|
-
})
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
const { UPDATE, SELECT } = cds.ql
|
|
3
|
-
|
|
4
|
-
const { getUpdateDraftAdminCQN, ensureDraftsSuffix } = require('../utils/handler')
|
|
5
|
-
const { removeIsActiveEntityRecursively } = require('../utils/where')
|
|
6
|
-
const { deepCopyArray } = require('../../common/utils/copy')
|
|
7
|
-
const { cqn2cqn4sql } = require('../../common/utils/cqn2cqn4sql')
|
|
8
|
-
|
|
9
|
-
const _getSelectCQN = ({ query }) => {
|
|
10
|
-
const fromRef = deepCopyArray(query.UPDATE.entity.ref)
|
|
11
|
-
for (const item of fromRef) {
|
|
12
|
-
if (item.where) {
|
|
13
|
-
item.where = removeIsActiveEntityRecursively(item.where)
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
fromRef[0].id = ensureDraftsSuffix(fromRef[0].id)
|
|
17
|
-
const from = { ref: fromRef }
|
|
18
|
-
|
|
19
|
-
return SELECT.from(from)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const _getUpdateDraftCQN = ({ query }, where, targetRef) => {
|
|
23
|
-
const set = {}
|
|
24
|
-
|
|
25
|
-
for (const entry in query.UPDATE.data) {
|
|
26
|
-
if (entry === 'DraftAdministrativeData_DraftUUID') {
|
|
27
|
-
continue
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
set[entry] = query.UPDATE.data[entry]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (set.IsActiveEntity) set.IsActiveEntity = false
|
|
34
|
-
|
|
35
|
-
return UPDATE(targetRef).data(set).where(where)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const _joinDraftAdministrativeData = (selectResolved, target) => {
|
|
39
|
-
const columns = []
|
|
40
|
-
columns.push({ ref: ['DRAFT.DraftAdministrativeData', 'inProcessByUser'], as: 'draftAdmin_inProcessByUser' })
|
|
41
|
-
columns.push({ ref: [target, 'DraftAdministrativeData_DraftUUID'] })
|
|
42
|
-
|
|
43
|
-
return selectResolved
|
|
44
|
-
.columns(columns)
|
|
45
|
-
.join('DRAFT.DraftAdministrativeData')
|
|
46
|
-
.on([
|
|
47
|
-
{ ref: [target, 'DraftAdministrativeData_DraftUUID'] },
|
|
48
|
-
'=',
|
|
49
|
-
{ ref: ['DRAFT.DraftAdministrativeData', 'DraftUUID'] }
|
|
50
|
-
])
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Generic Handler for PATCH requests in the context of draft.
|
|
55
|
-
* In case of success it returns the updated entry.
|
|
56
|
-
* If the entry to be updated does not exist, it rejects with error to return a 404.
|
|
57
|
-
* If a draft is already in process of another user it rejects with 403.
|
|
58
|
-
*
|
|
59
|
-
* @param req
|
|
60
|
-
*/
|
|
61
|
-
const fioriGenericPatch = async function (req, next) {
|
|
62
|
-
if (!req.target._isDraftEnabled) return next()
|
|
63
|
-
|
|
64
|
-
if (req.data.IsActiveEntity === true) req.reject(400, 'Patch can only be applied to a draft entity')
|
|
65
|
-
|
|
66
|
-
if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
|
|
67
|
-
|
|
68
|
-
const dbtx = cds.tx(req)
|
|
69
|
-
|
|
70
|
-
const selectResolved = cqn2cqn4sql(_getSelectCQN(req), this.model)
|
|
71
|
-
|
|
72
|
-
const targetName = selectResolved.SELECT.from.ref[selectResolved.SELECT.from.ref.length - 1]
|
|
73
|
-
const alias = selectResolved.SELECT.from.as
|
|
74
|
-
const selectWithAdmin = _joinDraftAdministrativeData(selectResolved, alias || targetName)
|
|
75
|
-
|
|
76
|
-
if (req._etagValidationClause) selectWithAdmin.where(removeIsActiveEntityRecursively(req._etagValidationClause))
|
|
77
|
-
|
|
78
|
-
const results = await dbtx.run(selectWithAdmin)
|
|
79
|
-
|
|
80
|
-
if (results.length === 0) req.reject(req._etagValidationType ? 412 : 404)
|
|
81
|
-
|
|
82
|
-
const result = results[0]
|
|
83
|
-
|
|
84
|
-
// Potential timeout scenario supported
|
|
85
|
-
if (result.draftAdmin_inProcessByUser && result.draftAdmin_inProcessByUser !== req.user.id) req.reject(403)
|
|
86
|
-
|
|
87
|
-
const updateDraftCQN = _getUpdateDraftCQN(req, selectResolved.SELECT.where, {
|
|
88
|
-
ref: [targetName],
|
|
89
|
-
as: alias || targetName
|
|
90
|
-
})
|
|
91
|
-
const updateDraftAdminCQN = getUpdateDraftAdminCQN(req, result.DraftAdministrativeData_DraftUUID)
|
|
92
|
-
|
|
93
|
-
await Promise.all([dbtx.run(updateDraftCQN), dbtx.run(updateDraftAdminCQN)])
|
|
94
|
-
req._.readAfterWrite = true
|
|
95
|
-
|
|
96
|
-
return { ...req.data, IsActiveEntity: false }
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
module.exports = cds.service.impl(function (srv) {
|
|
100
|
-
srv.on('PATCH', '*', fioriGenericPatch)
|
|
101
|
-
})
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
const { SELECT } = cds.ql
|
|
3
|
-
|
|
4
|
-
const { isActiveEntityRequested } = require('../utils/where')
|
|
5
|
-
const { ensureDraftsSuffix, ensureNoDraftsSuffix } = require('../utils/handler')
|
|
6
|
-
const { getColumns } = require('../../common/utils/columns')
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Generic Handler for PreparationAction requests.
|
|
10
|
-
* In case of success it returns the prepared draft entry.
|
|
11
|
-
*
|
|
12
|
-
* @param req
|
|
13
|
-
*/
|
|
14
|
-
const fioriGenericPrepare = async function (req, next) {
|
|
15
|
-
if (!req.target._isDraftEnabled) return next()
|
|
16
|
-
|
|
17
|
-
if (req.query.SELECT.from.ref.length > 1 || isActiveEntityRequested(req.query.SELECT.from.ref[0].where || [])) {
|
|
18
|
-
req.reject(400, 'Action "draftPrepare" can only be called on a draft entity')
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
|
|
22
|
-
|
|
23
|
-
const target = ensureDraftsSuffix(req.target.name)
|
|
24
|
-
|
|
25
|
-
const columns = getColumns(this.model.definitions[ensureNoDraftsSuffix(req.target.name)], {
|
|
26
|
-
keysOnly: true,
|
|
27
|
-
removeIgnore: true,
|
|
28
|
-
filterVirtual: true,
|
|
29
|
-
onlyNames: true
|
|
30
|
-
})
|
|
31
|
-
columns.push({ ref: ['DRAFT.DraftAdministrativeData', 'inProcessByUser'], as: 'draftAdmin_inProcessByUser' })
|
|
32
|
-
|
|
33
|
-
const select = SELECT.one(target)
|
|
34
|
-
.columns(columns)
|
|
35
|
-
.join('DRAFT.DraftAdministrativeData')
|
|
36
|
-
.on([
|
|
37
|
-
{ ref: [target, 'DraftAdministrativeData_DraftUUID'] },
|
|
38
|
-
'=',
|
|
39
|
-
{ ref: ['DRAFT.DraftAdministrativeData', 'DraftUUID'] }
|
|
40
|
-
])
|
|
41
|
-
.where(req.query.SELECT.from.ref[0].where)
|
|
42
|
-
|
|
43
|
-
const result = await cds.tx(req).run(select)
|
|
44
|
-
|
|
45
|
-
if (!result) req.reject(404)
|
|
46
|
-
|
|
47
|
-
if (result.draftAdmin_inProcessByUser !== req.user.id)
|
|
48
|
-
req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER', [result.draftAdmin_inProcessByUser])
|
|
49
|
-
|
|
50
|
-
delete result.draftAdmin_inProcessByUser
|
|
51
|
-
|
|
52
|
-
return result
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
module.exports = cds.service.impl(function (srv) {
|
|
56
|
-
srv.on('draftPrepare', '*', fioriGenericPrepare)
|
|
57
|
-
})
|