@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 +0,0 @@
|
|
|
1
|
-
module.exports = require('../../../../lib/auth/ias-auth')
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
const CHALLENGE = 'Basic realm="Users"'
|
|
3
|
-
|
|
4
|
-
class MockStrategy {
|
|
5
|
-
constructor({ users, tenants }, name = 'mock') {
|
|
6
|
-
this.name = name
|
|
7
|
-
this.users = _init_users(users || cds.env.requires.auth.users || {}, tenants)
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
authenticate(req) {
|
|
11
|
-
const { authorization } = req.headers
|
|
12
|
-
if (!authorization) return this.fail(CHALLENGE)
|
|
13
|
-
|
|
14
|
-
const [scheme, base64] = authorization.split(' ')
|
|
15
|
-
if (!scheme || scheme.toLowerCase() !== 'basic') return this.fail(CHALLENGE)
|
|
16
|
-
if (!base64) return this.fail(CHALLENGE)
|
|
17
|
-
|
|
18
|
-
const [id, password] = Buffer.from(base64, 'base64').toString().split(':')
|
|
19
|
-
const user = this.users[id] || (this.users['*'] && { id })
|
|
20
|
-
if (!user) return this.fail(CHALLENGE)
|
|
21
|
-
if (user.password && user.password !== password) return this.fail(CHALLENGE)
|
|
22
|
-
|
|
23
|
-
const { features } = req.headers
|
|
24
|
-
this.success(new cds.User(features ? { ...user, features } : user))
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// eslint-disable-next-line complexity
|
|
29
|
-
const _init_users = (users, tenants = {}) => {
|
|
30
|
-
if (!users._initialized) {
|
|
31
|
-
Object.defineProperty(users, '_initialized', { value: true })
|
|
32
|
-
for (let [id, user] of Object.entries(users)) {
|
|
33
|
-
if (id !== '*') user.id = user.ID || user.id || id // fill in user ids
|
|
34
|
-
if (id !== user.id) users[user.id] = user
|
|
35
|
-
if (user.jwt) {
|
|
36
|
-
let scopes = user.jwt.scope || user.jwt.scopes
|
|
37
|
-
if (scopes) {
|
|
38
|
-
const aud = user.jwt.aud
|
|
39
|
-
if (aud)
|
|
40
|
-
scopes = scopes.map(s => {
|
|
41
|
-
for (const each of aud) s = s.replace(`${each}.`, '')
|
|
42
|
-
return s
|
|
43
|
-
})
|
|
44
|
-
Array.isArray(user.roles) ? user.roles.push(...scopes) : (user.roles = scopes)
|
|
45
|
-
}
|
|
46
|
-
if (user.jwt.grant_type === 'client_credentials' || user.jwt.grant_type === 'client_x509') {
|
|
47
|
-
Array.isArray(user.roles) ? user.roles.push('system-user') : (user.roles = ['system-user'])
|
|
48
|
-
}
|
|
49
|
-
if (!user.tenant && user.jwt.zid) user.tenant = user.jwt.zid
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// optimization:
|
|
53
|
-
if (user.roles)
|
|
54
|
-
user.roles = user.roles.reduce((p, n) => {
|
|
55
|
-
p[n] = 1
|
|
56
|
-
return p
|
|
57
|
-
}, {})
|
|
58
|
-
|
|
59
|
-
// apply tenant info if any
|
|
60
|
-
if (!user.features) {
|
|
61
|
-
const features = tenants[user.tenant]?.features
|
|
62
|
-
if (features) user.features = features
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const attr = Object.assign(
|
|
66
|
-
{},
|
|
67
|
-
user.userAttributes,
|
|
68
|
-
user.jwt && user.jwt.userInfo,
|
|
69
|
-
user.jwt && user.jwt.attributes
|
|
70
|
-
)
|
|
71
|
-
if (Object.keys(attr).length > 0) user.attr = attr
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return users
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
module.exports = MockStrategy
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
const CLIENT = { client_credentials: 1, client_x509: 1 }
|
|
2
|
-
|
|
3
|
-
const getUserId = (user, info) => {
|
|
4
|
-
// fallback for grant_type=client_credentials (xssec v3)
|
|
5
|
-
return user.id || (info && info.getClientId && info.getClientId())
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const addRolesFromGrantType = (user, info, credentials) => {
|
|
9
|
-
const grantType = info && (info.grantType || (info.getGrantType && info.getGrantType()))
|
|
10
|
-
if (grantType) {
|
|
11
|
-
// > not "weak"
|
|
12
|
-
user.roles['authenticated-user'] = true
|
|
13
|
-
if (grantType in CLIENT) {
|
|
14
|
-
user.roles['system-user'] = true
|
|
15
|
-
if (info.getClientId() === credentials.clientid) user.roles['internal-user'] = true
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const getRoles = (roles, info) => {
|
|
21
|
-
// convert to object
|
|
22
|
-
roles = Object.assign(...roles.map(ele => ({ [ele]: true })))
|
|
23
|
-
|
|
24
|
-
if (info && info.checkLocalScope && typeof info.checkLocalScope === 'function') {
|
|
25
|
-
// > xssec v3
|
|
26
|
-
roles = new Proxy(roles, {
|
|
27
|
-
get: function (t, role) {
|
|
28
|
-
return role in t ? t[role] : info.checkLocalScope(role)
|
|
29
|
-
}
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return roles
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const getAttrForJWT = info => {
|
|
37
|
-
if (!info) {
|
|
38
|
-
return {}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (info.getAttribute && typeof info.getAttribute === 'function') {
|
|
42
|
-
// > xssec v3
|
|
43
|
-
return new Proxy(
|
|
44
|
-
{},
|
|
45
|
-
{
|
|
46
|
-
get: function (_, attr) {
|
|
47
|
-
return info.getAttribute(attr)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return {}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// xssec v3 only
|
|
57
|
-
const getAttrForXSSEC = info => {
|
|
58
|
-
if (!info) return {}
|
|
59
|
-
|
|
60
|
-
return new Proxy(
|
|
61
|
-
{},
|
|
62
|
-
{
|
|
63
|
-
get: function (_, attr) {
|
|
64
|
-
// try to get saml attribute via API (getEmail, getFamilyName, etc.)
|
|
65
|
-
try {
|
|
66
|
-
const getter = `get${attr[0].toUpperCase()}${attr.slice(1)}`
|
|
67
|
-
if (info[getter] && typeof info[getter] === 'function') {
|
|
68
|
-
return info[getter]()
|
|
69
|
-
}
|
|
70
|
-
} catch (e) {
|
|
71
|
-
// ignore
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// default to getAttribute
|
|
75
|
-
return info.getAttribute(attr)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const getTenant = info => {
|
|
82
|
-
// xssec v3
|
|
83
|
-
return info && info.getZoneId && info.getZoneId()
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
module.exports = {
|
|
87
|
-
getUserId,
|
|
88
|
-
getRoles,
|
|
89
|
-
getAttrForJWT,
|
|
90
|
-
getAttrForXSSEC,
|
|
91
|
-
getTenant,
|
|
92
|
-
addRolesFromGrantType
|
|
93
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
|
|
3
|
-
const _require = require('../../common/utils/require')
|
|
4
|
-
const xssecUtils = require('./xssecUtils')
|
|
5
|
-
|
|
6
|
-
// use _require for a better error message
|
|
7
|
-
const { JWTStrategy: JS } = _require('@sap/xssec')
|
|
8
|
-
|
|
9
|
-
class XSUAAStrategy extends JS {
|
|
10
|
-
constructor(credentials) {
|
|
11
|
-
super(credentials)
|
|
12
|
-
this.credentials = credentials
|
|
13
|
-
this.name = 'xsuaa'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
authenticate(req, options) {
|
|
17
|
-
const credentials = this.credentials
|
|
18
|
-
|
|
19
|
-
// monkey patch success
|
|
20
|
-
const _success = this.success
|
|
21
|
-
this.success = (user, info) => {
|
|
22
|
-
// create cds.User
|
|
23
|
-
user = new cds.User({
|
|
24
|
-
id: xssecUtils.getUserId(user, info),
|
|
25
|
-
roles: xssecUtils.getRoles(['any', 'identified-user'], info, credentials),
|
|
26
|
-
attr: xssecUtils.getAttrForXSSEC(info)
|
|
27
|
-
})
|
|
28
|
-
xssecUtils.addRolesFromGrantType(user, info, credentials)
|
|
29
|
-
const tenant = xssecUtils.getTenant(info)
|
|
30
|
-
if (tenant) user.tenant = tenant
|
|
31
|
-
// call "super.success"
|
|
32
|
-
_success(user, info)
|
|
33
|
-
}
|
|
34
|
-
super.authenticate(req, options)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
module.exports = XSUAAStrategy
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const { performance } = require('perf_hooks')
|
|
2
|
-
|
|
3
|
-
const _statisticsRequested = req =>
|
|
4
|
-
(req.query && req.query['sap-statistics'] === 'true') ||
|
|
5
|
-
(req.headers && req.headers['sap-statistics'] === 'true' && (!req.query || !req.query['sap-statistics']))
|
|
6
|
-
|
|
7
|
-
module.exports = function sap_statistics(req, res, next) {
|
|
8
|
-
if (!_statisticsRequested(req)) return next()
|
|
9
|
-
|
|
10
|
-
const t0 = performance.now()
|
|
11
|
-
const { writeHead } = res
|
|
12
|
-
res.writeHead = function (...args) {
|
|
13
|
-
const total = Number((performance.now() - t0) / 1000).toFixed(2)
|
|
14
|
-
if (res.statusCode < 400) res.setHeader('sap-statistics', `total=${total}`)
|
|
15
|
-
writeHead.call(this, ...args)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
next()
|
|
19
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const getTemplate = require('./template')
|
|
2
|
-
const templateProcessor = require('./templateProcessor')
|
|
3
|
-
|
|
4
|
-
const _pick = element => {
|
|
5
|
-
return element._type in { 'cds.Decimal': 1, 'cds.Integer64': 1 }
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const _processorFn = () => elementInfo => {
|
|
9
|
-
const { row, key } = elementInfo
|
|
10
|
-
if (typeof row?.[key] !== 'number') return
|
|
11
|
-
row[key] = `${row[key]}`
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const ensureIEEE754 = (target, service, result) => {
|
|
15
|
-
const { model } = service
|
|
16
|
-
if (!target || !result || !model || !model.definitions[target.name]) return
|
|
17
|
-
|
|
18
|
-
const template = getTemplate('ensureIEEE754', service, target, { pick: _pick })
|
|
19
|
-
if (template.elements.size === 0) return
|
|
20
|
-
|
|
21
|
-
if (typeof result === 'object' && result != null) {
|
|
22
|
-
const processFn = _processorFn()
|
|
23
|
-
for (const row of Array.isArray(result) ? result : [result]) {
|
|
24
|
-
templateProcessor({ processFn, row, template })
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
module.exports = ensureIEEE754
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
const { INSERT, SELECT, UPDATE, DELETE } = cds.ql
|
|
3
|
-
|
|
4
|
-
const {
|
|
5
|
-
ensureNoDraftsSuffix,
|
|
6
|
-
ensureDraftsSuffix,
|
|
7
|
-
entity_keys,
|
|
8
|
-
getDeleteDraftAdminCqn,
|
|
9
|
-
getCompositionTargets
|
|
10
|
-
} = require('../utils/handler')
|
|
11
|
-
const { readAndDeleteKeywords, isActiveEntityRequested, getKeyData } = require('../utils/where')
|
|
12
|
-
const { isDraftRootEntity } = require('../../fiori/utils/csn')
|
|
13
|
-
const { getColumns } = require('../../common/utils/columns')
|
|
14
|
-
const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
|
|
15
|
-
|
|
16
|
-
const _getRootCQN = (context, requestActiveData) => {
|
|
17
|
-
const keys = entity_keys(context.target)
|
|
18
|
-
const keyData = getKeyData(keys, context.query.SELECT.from.ref[0].where)
|
|
19
|
-
const columns = getColumns(context.target, { onlyNames: true, filterVirtual: true })
|
|
20
|
-
return SELECT.from(
|
|
21
|
-
requestActiveData ? ensureNoDraftsSuffix(context.target.name) : ensureDraftsSuffix(context.target.name),
|
|
22
|
-
columns
|
|
23
|
-
).where(keyData)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const _getExpandSubCqn = (model, parentEntityName, targets, isRoot = true) => {
|
|
27
|
-
const result = []
|
|
28
|
-
const parentEntity = model[parentEntityName]
|
|
29
|
-
|
|
30
|
-
for (const element of Object.values(parentEntity.elements)) {
|
|
31
|
-
const { name, target, cardinality } = element
|
|
32
|
-
if (name in DRAFT_COLUMNS_MAP) {
|
|
33
|
-
continue
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const ref = { ref: [name] }
|
|
37
|
-
if (element.isComposition && cardinality && !targets.includes(target)) {
|
|
38
|
-
if (name === 'texts' && !parentEntity['@fiori.draft.enabled']) {
|
|
39
|
-
continue
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
ref.expand = _getExpandSubCqn(model, target, [...targets, parentEntityName], false)
|
|
43
|
-
result.push(ref)
|
|
44
|
-
} else if (!isRoot && !element.isAssociation) {
|
|
45
|
-
result.push(ref)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return result
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const _getDraftAdminRef = () => {
|
|
53
|
-
return {
|
|
54
|
-
ref: ['DraftAdministrativeData'],
|
|
55
|
-
expand: [{ ref: ['DraftUUID'] }, { ref: ['InProcessByUser'] }]
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const _removeIsActiveEntityRecursively = resultSet => {
|
|
60
|
-
resultSet.forEach(result => {
|
|
61
|
-
delete result.IsActiveEntity
|
|
62
|
-
Object.values(result).forEach(val => {
|
|
63
|
-
if (Array.isArray(val)) {
|
|
64
|
-
_removeIsActiveEntityRecursively(val)
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
})
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const _draftCompositionTree = async (service, req) => {
|
|
71
|
-
let draftData, activeData, adminData
|
|
72
|
-
|
|
73
|
-
const expanded = _getExpandSubCqn(service.model.definitions, ensureNoDraftsSuffix(req.target.name), [])
|
|
74
|
-
|
|
75
|
-
const cqnDraft = _getRootCQN(req, false)
|
|
76
|
-
cqnDraft.SELECT.columns.push(_getDraftAdminRef())
|
|
77
|
-
cqnDraft.SELECT.columns.push(...expanded)
|
|
78
|
-
|
|
79
|
-
const cqnActive = _getRootCQN(req, true)
|
|
80
|
-
cqnActive.SELECT.columns.push(...expanded)
|
|
81
|
-
|
|
82
|
-
const dbtx = cds.tx(req)
|
|
83
|
-
|
|
84
|
-
const results = await Promise.all([dbtx.run(cqnDraft), dbtx.run(cqnActive)])
|
|
85
|
-
|
|
86
|
-
if (results[0].length === 1) {
|
|
87
|
-
_removeIsActiveEntityRecursively(results[0])
|
|
88
|
-
|
|
89
|
-
adminData = results[0][0].DraftAdministrativeData
|
|
90
|
-
delete results[0][0].DraftAdministrativeData
|
|
91
|
-
draftData = results[0][0]
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (results[1].length === 1) {
|
|
95
|
-
_removeIsActiveEntityRecursively(results[1])
|
|
96
|
-
activeData = results[1][0]
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return { draftData, activeData, adminData }
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Generic Handler for draftActivate requests.
|
|
104
|
-
* In case of success it triggers an 'UPDATE' or 'CREATE' event.
|
|
105
|
-
*
|
|
106
|
-
* @param req
|
|
107
|
-
*/
|
|
108
|
-
const fioriGenericActivate = async function (req, next) {
|
|
109
|
-
if (!req.target._isDraftEnabled) return next()
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
isActiveEntityRequested(req.query.SELECT.from.ref[0].where || []) ||
|
|
113
|
-
req.query.SELECT.from.ref.length > 2 ||
|
|
114
|
-
!isDraftRootEntity(this.model.definitions, ensureNoDraftsSuffix(req.target.name))
|
|
115
|
-
) {
|
|
116
|
-
req.reject(400, 'Action "draftActivate" can only be called on the root draft entity')
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
|
|
120
|
-
|
|
121
|
-
const { draftData, activeData, adminData } = await _draftCompositionTree(this, req)
|
|
122
|
-
if (!draftData) req.reject(404)
|
|
123
|
-
|
|
124
|
-
if (adminData.InProcessByUser !== req.user.id) {
|
|
125
|
-
req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER', [adminData.InProcessByUser])
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// create or update
|
|
129
|
-
let query, event
|
|
130
|
-
if (activeData) {
|
|
131
|
-
readAndDeleteKeywords(['IsActiveEntity'], req.query.SELECT.from.ref[0].where)
|
|
132
|
-
event = 'UPDATE'
|
|
133
|
-
// REVISIT: setting data should be part of ql
|
|
134
|
-
query = UPDATE(req.target).where(req.query.SELECT.from.ref[0].where)
|
|
135
|
-
query.UPDATE.data = draftData
|
|
136
|
-
} else {
|
|
137
|
-
event = 'CREATE'
|
|
138
|
-
query = INSERT.into(req.target).entries(draftData)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// REVISIT: _draftMetadata
|
|
142
|
-
const request = new cds.Request({ event, query, data: draftData, _draftMetadata: adminData })
|
|
143
|
-
|
|
144
|
-
// REVISIT: should not be necessary
|
|
145
|
-
request._ = Object.assign(request._, req._)
|
|
146
|
-
request._.params = req.params
|
|
147
|
-
request._.query = req.query
|
|
148
|
-
|
|
149
|
-
// use finally to preserve r.messages in success or error case
|
|
150
|
-
let result
|
|
151
|
-
try {
|
|
152
|
-
result = await this.dispatch(request)
|
|
153
|
-
} finally {
|
|
154
|
-
// REVISIT: should not be necessary
|
|
155
|
-
if (request.messages) for (const m of request.messages) req.info(m)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// delete draft data
|
|
159
|
-
const deleteDraftAdminCqn = getDeleteDraftAdminCqn(adminData.DraftUUID)
|
|
160
|
-
const draftTablesToDeleteFrom = [req.target.name + '_drafts']
|
|
161
|
-
for (const [entity] of getCompositionTargets(req.target, this).entries()) {
|
|
162
|
-
draftTablesToDeleteFrom.push(entity + '_drafts')
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
await cds.db.tx(req).run([
|
|
166
|
-
deleteDraftAdminCqn,
|
|
167
|
-
...draftTablesToDeleteFrom.map(dt => {
|
|
168
|
-
const d = DELETE.from(dt).where({ DraftAdministrativeData_DraftUUID: adminData.DraftUUID })
|
|
169
|
-
d._suppressDeepDelete = true // hidden flag to tell db layer that no deep delete is required
|
|
170
|
-
return d
|
|
171
|
-
})
|
|
172
|
-
])
|
|
173
|
-
|
|
174
|
-
if (event === 'CREATE') {
|
|
175
|
-
// REVISIT: we need to use okra API here because it must be set in the batched request
|
|
176
|
-
// status code must be set in handler to allow overriding for FE V2
|
|
177
|
-
// REVISIT: needs reworking for new adapter, especially re $batch
|
|
178
|
-
if (req._?.odataRes) {
|
|
179
|
-
req._?.odataRes?.setStatusCode(201, { overwrite: true })
|
|
180
|
-
} else if (req.http?.res) {
|
|
181
|
-
req.http.res.status(201)
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return result
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
module.exports = cds.service.impl(function (srv) {
|
|
189
|
-
srv.on('draftActivate', '*', fioriGenericActivate)
|
|
190
|
-
})
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
|
|
3
|
-
const { SELECT } = cds.ql
|
|
4
|
-
|
|
5
|
-
const { isNavigationToMany } = require('../utils/req')
|
|
6
|
-
const { getKeysCondition, removeIsActiveEntityRecursively } = require('../utils/where')
|
|
7
|
-
const { ensureNoDraftsSuffix, ensureDraftsSuffix, draftIsLocked } = require('../utils/handler')
|
|
8
|
-
|
|
9
|
-
const { DRAFT_COLUMNS_ADMIN_MAP } = require('../../common/constants/draft')
|
|
10
|
-
const { deepCopyArray } = require('../../common/utils/copy')
|
|
11
|
-
const DRAFT_COLUMNS_ADMIN = Object.keys(DRAFT_COLUMNS_ADMIN_MAP)
|
|
12
|
-
const PREFIX_DRAFT_COLUMNS = DRAFT_COLUMNS_ADMIN.map(col => ({ ref: ['DRAFT_DraftAdministrativeData', col] }))
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Provide information about the parent entity, i.e. the entity that has the to-many composition element.
|
|
16
|
-
* Limitation: only works for one key (besides IsActiveEntity)
|
|
17
|
-
*
|
|
18
|
-
* @param service
|
|
19
|
-
* @param req
|
|
20
|
-
* @returns {object}
|
|
21
|
-
* @private
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
const _validateDraft = (req, draftResult, isBoundAction) => {
|
|
25
|
-
if (!draftResult || draftResult.length === 0) req.reject(404)
|
|
26
|
-
|
|
27
|
-
const draftAdminData = draftResult[0]
|
|
28
|
-
|
|
29
|
-
// the same user that locked the entity can always delete/update it
|
|
30
|
-
if (draftAdminData.InProcessByUser === req.user.id) return
|
|
31
|
-
|
|
32
|
-
// proceed with the delete/update action only if it was initiated by a different
|
|
33
|
-
// user than the one who locked the entity and the configured drafts cancellation
|
|
34
|
-
// timeout timer has expired
|
|
35
|
-
if (draftIsLocked(draftAdminData.LastChangeDateTime)) {
|
|
36
|
-
req.reject(403, 'DRAFT_LOCKED_BY_ANOTHER_USER', [draftAdminData.CreatedByUser])
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// At this point, the request user ID isn't the owner of the draft.
|
|
40
|
-
if (isBoundAction) req.reject(403)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const _addDraftDataToContext = (req, result) => {
|
|
44
|
-
if (!result || result.length === 0) return
|
|
45
|
-
if (req.rejected) return
|
|
46
|
-
if (!req._draftMetadata) req._draftMetadata = {}
|
|
47
|
-
|
|
48
|
-
DRAFT_COLUMNS_ADMIN.forEach(column => {
|
|
49
|
-
if (column in result[0]) req._draftMetadata[column] = result[0][column]
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
req.data.DraftAdministrativeData_DraftUUID = result[0].DraftUUID
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const _getSelectDraftDataCqn = (entityName, where) => {
|
|
56
|
-
return SELECT.from(ensureDraftsSuffix(entityName), PREFIX_DRAFT_COLUMNS)
|
|
57
|
-
.join('DRAFT.DraftAdministrativeData')
|
|
58
|
-
.on('DraftAdministrativeData_DraftUUID =', { ref: ['DRAFT.DraftAdministrativeData', 'DraftUUID'] })
|
|
59
|
-
.where(where)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const _getRoot = req => {
|
|
63
|
-
if (!req.query) return
|
|
64
|
-
|
|
65
|
-
const refObj = req.query.SELECT?.from || req.query.UPDATE?.entity || req.query.INSERT?.into || req.query.DELETE?.from
|
|
66
|
-
const ref0 = refObj.ref[0]
|
|
67
|
-
|
|
68
|
-
const root = {
|
|
69
|
-
entityName: ensureDraftsSuffix(ref0.id),
|
|
70
|
-
where: removeIsActiveEntityRecursively(deepCopyArray(ref0.where))
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
for (const item of ref0.where) {
|
|
74
|
-
if (item.ref && item.ref[item.ref.length - 1] === 'IsActiveEntity') {
|
|
75
|
-
const index = ref0.where.indexOf(item)
|
|
76
|
-
root.IsActiveEntity = ref0.where[index + 2].val
|
|
77
|
-
break
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return root
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const _getDraftDataFromExistingDraft = async (req, root, isBoundAction) => {
|
|
85
|
-
if (!cds.db) req.reject('NO_DATABASE_CONNECTION')
|
|
86
|
-
if (!root) return []
|
|
87
|
-
if (root?.IsActiveEntity === false) {
|
|
88
|
-
const query = _getSelectDraftDataCqn(root.entityName, root.where)
|
|
89
|
-
const result = await cds.tx(req).run(query)
|
|
90
|
-
return result
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// do not expect validate draft ownership for action call on active instances
|
|
94
|
-
if (isBoundAction) return []
|
|
95
|
-
|
|
96
|
-
const rootWhere = getKeysCondition(req)
|
|
97
|
-
const query = _getSelectDraftDataCqn(ensureNoDraftsSuffix(req.target.name), rootWhere)
|
|
98
|
-
const result = await cds.tx(req).run(query)
|
|
99
|
-
return result
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const _addDraftDataFromExistingDraft = async req => {
|
|
103
|
-
const root = _getRoot(req)
|
|
104
|
-
const result = await _getDraftDataFromExistingDraft(req, root)
|
|
105
|
-
|
|
106
|
-
if (!root) return []
|
|
107
|
-
if (root.IsActiveEntity === false) {
|
|
108
|
-
_validateDraft(req, result)
|
|
109
|
-
_addDraftDataToContext(req, result)
|
|
110
|
-
return result
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (result && result.length > 0) _validateDraft(req, result)
|
|
114
|
-
_addDraftDataToContext(req, result)
|
|
115
|
-
return result
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Generic Handler for before NEW requests.
|
|
120
|
-
*/
|
|
121
|
-
const _new = async function (req) {
|
|
122
|
-
if (!req.target._isDraftEnabled) return
|
|
123
|
-
|
|
124
|
-
if (isNavigationToMany(req, this.model)) {
|
|
125
|
-
const result = await _addDraftDataFromExistingDraft(req)
|
|
126
|
-
|
|
127
|
-
// in order to fix corner case where active subitems are created in draft case
|
|
128
|
-
if (result.length === 0) req.reject(404)
|
|
129
|
-
|
|
130
|
-
return
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
req._draftMetadata = req._draftMetadata || {}
|
|
134
|
-
req.data.DraftAdministrativeData_DraftUUID = cds.utils.uuid()
|
|
135
|
-
req._draftMetadata.DraftUUID = req.data.DraftAdministrativeData_DraftUUID
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Generic Handler for before PATCH and UPDATE requests.
|
|
140
|
-
*/
|
|
141
|
-
const _patch = async function (req) {
|
|
142
|
-
if (!req.target._isDraftEnabled) return
|
|
143
|
-
|
|
144
|
-
const result = await _addDraftDataFromExistingDraft(req)
|
|
145
|
-
|
|
146
|
-
// no result means that the draft does not exist
|
|
147
|
-
if (result.length === 0) req.reject(404)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Generic Handler for before DELETE and CANCEL requests.
|
|
152
|
-
*/
|
|
153
|
-
const _cancel = async function (req) {
|
|
154
|
-
if (!req.target._isDraftEnabled) return
|
|
155
|
-
|
|
156
|
-
await _addDraftDataFromExistingDraft(req)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const _validateDraftBoundAction = async function (req) {
|
|
160
|
-
const isBoundAction = true
|
|
161
|
-
const result = await _getDraftDataFromExistingDraft(req, _getRoot(req), isBoundAction)
|
|
162
|
-
if (result && result.length > 0) _validateDraft(req, result, isBoundAction)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const _allowEntityCollectionOnAction = action => {
|
|
166
|
-
return (
|
|
167
|
-
action['@cds.odata.bindingparameter.collection'] ||
|
|
168
|
-
(action.params && Object.values(action.params).some(e => e?.items?.type === '$self'))
|
|
169
|
-
)
|
|
170
|
-
}
|
|
171
|
-
|
|
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
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
_new._initial = true
|
|
192
|
-
_patch._initial = true
|
|
193
|
-
_cancel._initial = true
|
|
194
|
-
|
|
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)
|
|
201
|
-
})
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const cds = require('../../cds')
|
|
2
|
-
const { deleteDraft } = require('../utils/delete')
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Generic Handler for CANCEL 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 fioriGenericCancel = function (req, next) {
|
|
12
|
-
if (!req.target._isDraftEnabled) return next()
|
|
13
|
-
|
|
14
|
-
return deleteDraft(req, this)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
module.exports = cds.service.impl(function (srv) {
|
|
18
|
-
srv.on('CANCEL', '*', fioriGenericCancel)
|
|
19
|
-
})
|