@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,9 +1,7 @@
|
|
|
1
1
|
const cds = require('../../cds.js')
|
|
2
|
-
// eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
|
|
3
2
|
const express = require('express')
|
|
4
3
|
const getTenantInfo = require('./getTenantInfo.js')
|
|
5
4
|
const isSecured = () => cds.requires.auth && (cds.requires.auth.impl || cds.requires.auth.credentials)
|
|
6
|
-
const _require = require('../../common/utils/require')
|
|
7
5
|
const { ODATA_UNAUTHORIZED } = require('../../common/error/constants')
|
|
8
6
|
|
|
9
7
|
const _isAll = a => a && a.includes('all')
|
|
@@ -15,60 +13,48 @@ class EndpointRegistry {
|
|
|
15
13
|
this.deployCallbacks = new Map()
|
|
16
14
|
if (isSecured()) {
|
|
17
15
|
if (cds.requires.auth.impl) {
|
|
18
|
-
|
|
19
|
-
cds.app.use(basePath, cds.middlewares.before) // contains auth, trace, context
|
|
20
|
-
} else {
|
|
21
|
-
const impl = _require(cds.resolve(cds.requires.auth.impl))
|
|
22
|
-
cds.app.use(basePath, impl)
|
|
23
|
-
}
|
|
16
|
+
cds.app.use(basePath, cds.middlewares.before) // contains auth, trace, context
|
|
24
17
|
} else {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
} else {
|
|
29
|
-
const JWTStrategy = require('../../auth/strategies/JWT.js')
|
|
30
|
-
// eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
|
|
31
|
-
const passport = _require('passport')
|
|
32
|
-
// REVISIT: It's unclear if the credentials from cds.requires.auth need to be used here.
|
|
33
|
-
// In principle, user-facing endpoints might differ from messaging ones.
|
|
34
|
-
passport.use(new JWTStrategy(cds.requires.auth.credentials))
|
|
35
|
-
cds.app.use(basePath, passport.initialize())
|
|
36
|
-
cds.app.use(basePath, passport.authenticate('JWT', { session: false }))
|
|
37
|
-
}
|
|
18
|
+
const jwt_auth = require('../../../../lib/auth/jwt-auth.js')
|
|
19
|
+
cds.app.use(basePath, cds.middlewares.context())
|
|
20
|
+
cds.app.use(basePath, jwt_auth(cds.requires.auth))
|
|
38
21
|
}
|
|
39
22
|
// unsuccessful auth doesn't automatically reject!
|
|
40
23
|
cds.app.use(basePath, (req, res, next) => {
|
|
41
24
|
// REVISIT: we should probably pass an error into next so that a (custom) error middleware can handle it
|
|
42
|
-
if (!
|
|
25
|
+
if (!cds.context.user._is_anonymous) res.status(401).json({ error: ODATA_UNAUTHORIZED })
|
|
43
26
|
next()
|
|
44
27
|
})
|
|
45
28
|
} else if (process.env.NODE_ENV === 'production') {
|
|
46
29
|
LOG.warn('Messaging endpoints not secured')
|
|
30
|
+
} else {
|
|
31
|
+
// auth middlewares set cds.context.user
|
|
32
|
+
cds.app.use(basePath, cds.middlewares.context())
|
|
47
33
|
}
|
|
48
34
|
cds.app.use(basePath, express.json({ type: 'application/*+json' }))
|
|
49
|
-
cds.app.use(basePath, express.json())
|
|
35
|
+
cds.app.use(basePath, express.json()) // REVISIT: Do we need both?
|
|
50
36
|
cds.app.use(basePath, express.urlencoded({ extended: true }))
|
|
51
37
|
LOG._debug && LOG.debug('Register inbound endpoint', { basePath, method: 'OPTIONS' })
|
|
52
38
|
|
|
53
39
|
// Clear cds.context as it would interfere with subsequent transactions
|
|
54
|
-
cds.app.use(basePath, (_req, _res, next) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
})
|
|
40
|
+
// cds.app.use(basePath, (_req, _res, next) => {
|
|
41
|
+
// cds.context = undefined // REVISIT: Why is that necessary?
|
|
42
|
+
// next()
|
|
43
|
+
// })
|
|
58
44
|
|
|
59
45
|
cds.app.options(basePath, (req, res) => {
|
|
60
46
|
try {
|
|
61
|
-
if (isSecured() && !
|
|
47
|
+
if (isSecured() && !cds.context.user.is('emcallback')) return res.sendStatus(403)
|
|
62
48
|
res.set('webhook-allowed-origin', req.headers['webhook-request-origin'])
|
|
63
49
|
res.sendStatus(200)
|
|
64
|
-
} catch
|
|
50
|
+
} catch {
|
|
65
51
|
res.sendStatus(500)
|
|
66
52
|
}
|
|
67
53
|
})
|
|
68
54
|
LOG._debug && LOG.debug('Register inbound endpoint', { basePath, method: 'POST' })
|
|
69
55
|
cds.app.post(basePath, (req, res) => {
|
|
70
56
|
try {
|
|
71
|
-
if (isSecured() && !
|
|
57
|
+
if (isSecured() && !cds.context.user.is('emcallback')) return res.sendStatus(403)
|
|
72
58
|
const queueName = req.query.q
|
|
73
59
|
if (!queueName) {
|
|
74
60
|
LOG.error('Query parameter `q` not found.')
|
|
@@ -83,7 +69,7 @@ class EndpointRegistry {
|
|
|
83
69
|
const payload = req.body
|
|
84
70
|
const cb = this.webhookCallbacks.get(queueName)
|
|
85
71
|
if (!cb) return res.sendStatus(200)
|
|
86
|
-
const tenant =
|
|
72
|
+
const { tenant } = cds.context
|
|
87
73
|
const other = tenant
|
|
88
74
|
? {
|
|
89
75
|
_: { req, res }, // For `cds.context.http`
|
|
@@ -106,7 +92,7 @@ class EndpointRegistry {
|
|
|
106
92
|
})
|
|
107
93
|
cds.app.post(deployPath, async (req, res) => {
|
|
108
94
|
try {
|
|
109
|
-
if (isSecured() && !
|
|
95
|
+
if (isSecured() && !cds.context.user.is('emmanagement')) return res.sendStatus(403)
|
|
110
96
|
const tenants = req.body && !_isAll(req.body.tenants) && req.body.tenants
|
|
111
97
|
const queues = req.body && !_isAll(req.body.queues) && req.body.queues
|
|
112
98
|
const options = { wipeData: req.body && req.body.wipeData }
|
|
@@ -123,7 +109,7 @@ class EndpointRegistry {
|
|
|
123
109
|
const hasError = results.some(r => r.failed.length)
|
|
124
110
|
if (hasError) return res.status(500).send(results)
|
|
125
111
|
return res.status(201).send(results)
|
|
126
|
-
} catch
|
|
112
|
+
} catch {
|
|
127
113
|
// REVISIT: Still needed with cds-mtxs?
|
|
128
114
|
// If an unknown tenant id is provided, cds-mtx will crash ("Cannot read property 'hanaClient' of undefined")
|
|
129
115
|
return res.sendStatus(500)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const cds = require('../cds')
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const normalizeIncomingMessage = require('./common-utils/normalizeIncomingMessage')
|
|
4
4
|
const express = require('express')
|
|
5
5
|
const https = require('https')
|
|
6
6
|
const crypto = require('crypto')
|
|
@@ -90,17 +90,6 @@ function _validateCertificate(req, res, next) {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
// TODO: seems unused
|
|
94
|
-
function _checkAppDomains() {
|
|
95
|
-
const pattern = /.*\.cert\.cfapps\..*\.hana\.ondemand\.com/
|
|
96
|
-
const uris = JSON.parse(process.env.VCAP_APPLICATION).application_uris
|
|
97
|
-
const matchFound = uris.some(uri => pattern.test(uri))
|
|
98
|
-
if (matchFound)
|
|
99
|
-
this.LOG.warn(
|
|
100
|
-
`*.cert.cfapps.*.hana.ondemand.com domain is in use, this is not recommended in production! Please use 'mesh.cf.<region>.hana.ondemand.com' instead!`
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
93
|
let instantiated = false
|
|
105
94
|
|
|
106
95
|
class EventBroker extends cds.MessagingService {
|
|
@@ -153,30 +142,57 @@ class EventBroker extends cds.MessagingService {
|
|
|
153
142
|
|
|
154
143
|
// TODO: What if we're in single tenant variant?
|
|
155
144
|
try {
|
|
156
|
-
const ceSource = `${this.options.credentials.ceSource[0]}/${cds.context.tenant}`
|
|
157
145
|
const hostname = this.options.credentials.eventing.http.x509.url.replace(/^https?:\/\//, '')
|
|
158
|
-
|
|
146
|
+
|
|
147
|
+
// take over and cleanse cloudevents headers
|
|
148
|
+
const headers = { ...(msg.headers ?? {}) }
|
|
149
|
+
|
|
150
|
+
const ceId = headers.id
|
|
151
|
+
delete headers.id
|
|
152
|
+
|
|
153
|
+
const ceSource = headers.source
|
|
154
|
+
delete headers.source
|
|
155
|
+
|
|
156
|
+
const ceType = headers.type
|
|
157
|
+
delete headers.type
|
|
158
|
+
|
|
159
|
+
const ceSpecversion = headers.specversion
|
|
160
|
+
delete headers.specversion
|
|
161
|
+
|
|
162
|
+
// const ceDatacontenttype = headers.datacontenttype // not part of the HTTP API
|
|
163
|
+
delete headers.datacontenttype
|
|
164
|
+
|
|
165
|
+
// const ceTime = headers.time // not part of the HTTP API
|
|
166
|
+
delete headers.time
|
|
167
|
+
|
|
159
168
|
const options = {
|
|
160
169
|
hostname: hostname,
|
|
161
170
|
method: 'POST',
|
|
162
171
|
headers: {
|
|
163
|
-
'ce-id':
|
|
172
|
+
'ce-id': ceId,
|
|
164
173
|
'ce-source': ceSource,
|
|
165
|
-
'ce-type':
|
|
166
|
-
'ce-specversion':
|
|
167
|
-
'Content-Type': 'application/json'
|
|
174
|
+
'ce-type': ceType,
|
|
175
|
+
'ce-specversion': ceSpecversion,
|
|
176
|
+
'Content-Type': 'application/json' // because of { data, ...headers } format
|
|
168
177
|
},
|
|
169
178
|
agent: this.agent
|
|
170
179
|
}
|
|
171
180
|
this.LOG.debug('HTTP headers:', JSON.stringify(options.headers))
|
|
172
181
|
this.LOG.debug('HTTP body:', JSON.stringify(msg.data))
|
|
173
|
-
|
|
182
|
+
// what about headers?
|
|
183
|
+
// TODO: Clarify if we should send `{ data, ...headers }` vs. `data` + HTTP headers (`ce-*`)
|
|
184
|
+
await request(options, { data: msg.data, ...headers }) // TODO: fetch does not work with mTLS as of today, requires another module. see https://github.com/nodejs/node/issues/48977
|
|
174
185
|
if (this.LOG._info) this.LOG.info('Emit', { topic: msg.event })
|
|
175
186
|
} catch (e) {
|
|
176
187
|
this.LOG.error('Emit failed:', e.message)
|
|
177
188
|
}
|
|
178
189
|
}
|
|
179
190
|
|
|
191
|
+
prepareHeaders(headers, event) {
|
|
192
|
+
if (!('source' in headers)) headers.source = `${this.options.credentials.ceSource[0]}/${cds.context.tenant}`
|
|
193
|
+
super.prepareHeaders(headers, event)
|
|
194
|
+
}
|
|
195
|
+
|
|
180
196
|
async registerWebhookEndpoints() {
|
|
181
197
|
const webhookBasePath = this.options.webhookPath || '/-/cds/event-broker/webhook'
|
|
182
198
|
cds.app.post(webhookBasePath, _validateCertificate.bind(this))
|
|
@@ -188,15 +204,26 @@ class EventBroker extends cds.MessagingService {
|
|
|
188
204
|
try {
|
|
189
205
|
const event = req.headers['ce-type'] // TG27: type contains namespace, so there's no collision
|
|
190
206
|
const tenant = req.headers['ce-sapconsumertenant']
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
headers: req.headers
|
|
207
|
+
|
|
208
|
+
// take over cloudevents headers (`ce-*`) without the prefix
|
|
209
|
+
const headers = {}
|
|
210
|
+
for (const header in req.headers) {
|
|
211
|
+
if (header.startsWith('ce-')) headers[header.slice(3)] = req.headers[header]
|
|
197
212
|
}
|
|
198
|
-
|
|
199
|
-
|
|
213
|
+
|
|
214
|
+
const msg = normalizeIncomingMessage(req.body)
|
|
215
|
+
msg.event = event
|
|
216
|
+
Object.assign(msg.headers, headers)
|
|
217
|
+
if (tenant) msg.tenant = tenant
|
|
218
|
+
|
|
219
|
+
// for cds.context.http
|
|
220
|
+
msg._ = {}
|
|
221
|
+
msg._.req = req
|
|
222
|
+
msg._.res = res
|
|
223
|
+
|
|
224
|
+
const context = { user: cds.User.privileged, _: msg._ }
|
|
225
|
+
if (msg.tenant) context.tenant = msg.tenant
|
|
226
|
+
|
|
200
227
|
await this.tx(context, tx => tx.emit(msg))
|
|
201
228
|
this.LOG.debug('Event processed successfully.')
|
|
202
229
|
return res.status(200).json({ message: 'OK' })
|
|
@@ -10,7 +10,7 @@ class FileBasedMessaging extends MessagingService {
|
|
|
10
10
|
this.file = resolve(this.options.file || (this.options.credentials && this.options.credentials.file))
|
|
11
11
|
try {
|
|
12
12
|
await fs.lstat(this.file)
|
|
13
|
-
} catch
|
|
13
|
+
} catch {
|
|
14
14
|
await fs.writeFile(this.file, '\n')
|
|
15
15
|
}
|
|
16
16
|
cds.once('listening', () => {
|
|
@@ -62,7 +62,7 @@ class FileBasedMessaging extends MessagingService {
|
|
|
62
62
|
)
|
|
63
63
|
} else other.push(each + '\n')
|
|
64
64
|
}
|
|
65
|
-
} catch
|
|
65
|
+
} catch {
|
|
66
66
|
// ignore invalid messages
|
|
67
67
|
}
|
|
68
68
|
}
|
|
@@ -89,7 +89,7 @@ const lock = async (file, n = 11) => {
|
|
|
89
89
|
try {
|
|
90
90
|
while (n--) await fs.lstat(lock).then(() => n && sleep(150))
|
|
91
91
|
return false
|
|
92
|
-
} catch
|
|
92
|
+
} catch {
|
|
93
93
|
// lock file does not exist -> create it
|
|
94
94
|
await fs.writeFile(lock, 'locked')
|
|
95
95
|
return true
|
|
@@ -151,7 +151,7 @@ const appId = require('./common-utils/appId')
|
|
|
151
151
|
function _JSONorString(string) {
|
|
152
152
|
try {
|
|
153
153
|
return JSON.parse(string)
|
|
154
|
-
} catch
|
|
154
|
+
} catch {
|
|
155
155
|
return string
|
|
156
156
|
}
|
|
157
157
|
}
|
|
@@ -260,7 +260,7 @@ async function _getCaCerts(srv) {
|
|
|
260
260
|
try {
|
|
261
261
|
const certNext = await fetch(srv.options.credentials.urls.cert_next).then(r => r.text())
|
|
262
262
|
return [certCurrent, certNext]
|
|
263
|
-
} catch
|
|
263
|
+
} catch {
|
|
264
264
|
return [certCurrent]
|
|
265
265
|
}
|
|
266
266
|
}
|
|
@@ -7,8 +7,6 @@ const { resolveView, getTransition, findQueryTarget } = require('../common/utils
|
|
|
7
7
|
const postProcess = require('../common/utils/postProcess')
|
|
8
8
|
const { formatVal } = require('../../odata/utils')
|
|
9
9
|
|
|
10
|
-
const _isSimpleCqnQuery = q => typeof q === 'object' && q !== null && !Array.isArray(q) && Object.keys(q).length > 0
|
|
11
|
-
|
|
12
10
|
const _setHeaders = (defaultHeaders, req) => {
|
|
13
11
|
return Object.assign(
|
|
14
12
|
defaultHeaders,
|
|
@@ -34,6 +32,8 @@ const _buildPartialUrlFunctions = (url, data, params, kind = 'odata-v4') => {
|
|
|
34
32
|
|
|
35
33
|
// REVISIT: take params from params after importer fix (the keys should not be part of params)
|
|
36
34
|
for (const param in _extractParamsFromData(data, params)) {
|
|
35
|
+
if (data[param] === undefined) continue
|
|
36
|
+
|
|
37
37
|
if (kind === 'odata-v2') {
|
|
38
38
|
funcParams.push(`${param}=${_setCorrectValue(param, data, params, kind)}`)
|
|
39
39
|
} else {
|
|
@@ -142,9 +142,9 @@ const _addHandlerActionFunction = (srv, def, target) => {
|
|
|
142
142
|
|
|
143
143
|
if (target) {
|
|
144
144
|
srv.on(event, target, async function (req) {
|
|
145
|
-
const shortEntityName = req.target.name.replace(`${this.
|
|
145
|
+
const shortEntityName = req.target.name.replace(`${this.definition.name}.`, '')
|
|
146
146
|
if (this.kind === 'odata-v2') return _handleV2BoundActionFunction(srv, def, req, event, this.kind)
|
|
147
|
-
const url = `/${shortEntityName}(${_buildKeys(req, this.kind).join(',')})/${this.
|
|
147
|
+
const url = `/${shortEntityName}(${_buildKeys(req, this.kind).join(',')})/${this.definition.name}.${event}`
|
|
148
148
|
return _handleBoundActionFunction(srv, def, req, url)
|
|
149
149
|
})
|
|
150
150
|
} else {
|
|
@@ -155,7 +155,8 @@ const _addHandlerActionFunction = (srv, def, target) => {
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
const
|
|
158
|
+
const _isSelectWithAliasedColumns = q =>
|
|
159
|
+
q?.SELECT && !q.SELECT._transitions && q.SELECT.columns?.some(hasAliasedColumns)
|
|
159
160
|
|
|
160
161
|
const resolvedTargetOfQuery = q => {
|
|
161
162
|
const transitions = (typeof q === 'object' && (q.SELECT || q.INSERT || q.UPDATE || q.DELETE)._transitions) || []
|
|
@@ -200,6 +201,12 @@ class RemoteService extends cds.Service {
|
|
|
200
201
|
if (this.options.credentials) {
|
|
201
202
|
this.datasource = this.options.datasource
|
|
202
203
|
this.destinationOptions = this.options.destinationOptions
|
|
204
|
+
// set useCache by default
|
|
205
|
+
if (!this.destinationOptions) {
|
|
206
|
+
this.destinationOptions = { useCache: true }
|
|
207
|
+
} else if (typeof this.destinationOptions.useCache === 'undefined') {
|
|
208
|
+
this.destinationOptions.useCache = true
|
|
209
|
+
}
|
|
203
210
|
_resolveSelectionStrategy(this.destinationOptions)
|
|
204
211
|
this.destination =
|
|
205
212
|
this.options.credentials.destination ??
|
|
@@ -271,29 +278,22 @@ class RemoteService extends cds.Service {
|
|
|
271
278
|
// Overload .handle in order to resolve projections up to a definition that is known by the remote service instance.
|
|
272
279
|
// Result is post processed according to the inverse projection in order to reflect the correct result of the original query.
|
|
273
280
|
async handle(req) {
|
|
274
|
-
|
|
281
|
+
let result
|
|
275
282
|
|
|
276
|
-
if (
|
|
277
|
-
|
|
278
|
-
//
|
|
279
|
-
if (
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const q = resolveView(req.query, this.model, this)
|
|
288
|
-
const t = findQueryTarget(q) || req.target
|
|
289
|
-
|
|
290
|
-
// REVISIT: We need to provide target explicitly because it's cached already within ensure_target
|
|
291
|
-
const _req = new cds.Request({ query: q, target: t, _resolved: true, headers: req.headers, method: req.method })
|
|
292
|
-
const result = await super.dispatch(_req)
|
|
293
|
-
return postProcess(q, result, this, true)
|
|
283
|
+
if (!this._requires_resolving(req)) {
|
|
284
|
+
result = await super.handle(req)
|
|
285
|
+
// we need to post process if alias was explicitly set in query
|
|
286
|
+
if (_isSelectWithAliasedColumns(req.query)) result = postProcess(req.query, result, this, true)
|
|
287
|
+
} else {
|
|
288
|
+
const query = resolveView(req.query, this.model, this)
|
|
289
|
+
const target = findQueryTarget(query) || req.target
|
|
290
|
+
// we need to provide target explicitly because it's cached within ensure_target
|
|
291
|
+
const _req = new cds.Request({ query, target, _resolved: true, headers: req.headers, method: req.method })
|
|
292
|
+
result = await super.dispatch(_req)
|
|
293
|
+
result = postProcess(query, result, this, true)
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
return
|
|
296
|
+
return result
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
299
|
|
|
@@ -33,6 +33,8 @@ const _executeHttpRequest = async ({ requestConfig, destination, destinationOpti
|
|
|
33
33
|
if (jwt) destination.headers.authorization = `Bearer ${jwt}`
|
|
34
34
|
else LOG._warn && LOG.warn('Missing JWT token for forwardAuthToken')
|
|
35
35
|
}
|
|
36
|
+
// Cloud SDK throws error if useCache is activated and jwt is undefined
|
|
37
|
+
if (destination.jwt === undefined) delete destination.useCache
|
|
36
38
|
|
|
37
39
|
if (LOG._debug) {
|
|
38
40
|
const req2log = { headers: _sanitizeHeaders({ ...requestConfig.headers }) }
|
|
@@ -128,7 +130,6 @@ function _normalizeMetadata(prefix, data, results) {
|
|
|
128
130
|
const _getPurgedRespActionFunc = (data, returnType) => {
|
|
129
131
|
// return type is primitive value or inline/complex type
|
|
130
132
|
if (returnType.kind === 'type' && !returnType.items && Object.values(data).length === 1) {
|
|
131
|
-
// eslint-disable-next-line no-unreachable-loop
|
|
132
133
|
for (const key in data) {
|
|
133
134
|
return data[key]
|
|
134
135
|
}
|
|
@@ -207,14 +208,13 @@ const _getSanitizedError = (e, reqOptions, options = { suppressRemoteResponseBod
|
|
|
207
208
|
// AxiosError's toJSON() method doesn't include the request and response objects
|
|
208
209
|
if (e.__proto__.toJSON) {
|
|
209
210
|
e.toJSON = function () {
|
|
210
|
-
return { ...
|
|
211
|
+
return { ...e.__proto__.toJSON(), request: this.request, response: this.response }
|
|
211
212
|
}
|
|
212
213
|
}
|
|
213
214
|
|
|
214
215
|
return e
|
|
215
216
|
}
|
|
216
217
|
|
|
217
|
-
// eslint-disable-next-line complexity
|
|
218
218
|
const run = async (requestConfig, options) => {
|
|
219
219
|
let response
|
|
220
220
|
|
|
@@ -360,14 +360,13 @@ const _hasHeader = (headers, header) =>
|
|
|
360
360
|
.map(k => k.toLowerCase())
|
|
361
361
|
.includes(header)
|
|
362
362
|
|
|
363
|
-
// eslint-disable-next-line complexity
|
|
364
363
|
const getReqOptions = (req, query, service) => {
|
|
365
364
|
const reqOptions =
|
|
366
365
|
typeof query === 'object'
|
|
367
366
|
? _cqnToReqOptions(query, service, req)
|
|
368
367
|
: typeof query === 'string'
|
|
369
368
|
? _stringToReqOptions(query, req.data, req.target)
|
|
370
|
-
: _pathToReqOptions(req.method, req.path, req.data, req.target, service.name)
|
|
369
|
+
: _pathToReqOptions(req.method, req.path, req.data, req.target, service.definition?.name || service.namespace) //> no model, no service.definition
|
|
371
370
|
|
|
372
371
|
if (service.kind === 'odata-v2' && req.event === 'READ' && reqOptions.url?.match(/(\/any\()|(\/all\()/)) {
|
|
373
372
|
req.reject(501, 'Lambda expressions are not supported in OData v2')
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
let _cloudSdkConnectivity
|
|
2
2
|
const getCloudSdkConnectivity = () => {
|
|
3
3
|
if (_cloudSdkConnectivity) return _cloudSdkConnectivity
|
|
4
|
-
// eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
|
|
5
4
|
_cloudSdkConnectivity = require('@sap-cloud-sdk/connectivity')
|
|
6
5
|
return _cloudSdkConnectivity
|
|
7
6
|
}
|
|
@@ -9,7 +8,6 @@ const getCloudSdkConnectivity = () => {
|
|
|
9
8
|
let _cloudSdkResilience
|
|
10
9
|
const getCloudSdkResilience = () => {
|
|
11
10
|
if (_cloudSdkResilience) return _cloudSdkResilience
|
|
12
|
-
// eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
|
|
13
11
|
_cloudSdkResilience = require('@sap-cloud-sdk/resilience')
|
|
14
12
|
return _cloudSdkResilience
|
|
15
13
|
}
|
|
@@ -18,7 +16,6 @@ let _cloudSdk
|
|
|
18
16
|
const getCloudSdk = () => {
|
|
19
17
|
if (_cloudSdk) return _cloudSdk
|
|
20
18
|
|
|
21
|
-
// eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
|
|
22
19
|
_cloudSdk = require('@sap-cloud-sdk/http-client')
|
|
23
20
|
return _cloudSdk
|
|
24
21
|
}
|
|
@@ -64,7 +64,6 @@ const _convertActionFuncResponse = (returnType, convertValueFn) => data => {
|
|
|
64
64
|
return convertValueFn(data, returnType.items || returnType)
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
// eslint-disable-next-line complexity
|
|
68
67
|
const _convertValue = (ieee754Compatible, exponentialDecimals) => (value, element) => {
|
|
69
68
|
if (value == null) return value
|
|
70
69
|
|
|
@@ -18,7 +18,6 @@ const execute = require('./execute')
|
|
|
18
18
|
|
|
19
19
|
const _new = url => {
|
|
20
20
|
if (url && url !== ':memory:') url = cds.utils.path.resolve(cds.root, url)
|
|
21
|
-
// eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
|
|
22
21
|
if (!_sqlite) _sqlite = require('sqlite3')
|
|
23
22
|
return new Promise((resolve, reject) => {
|
|
24
23
|
const dbc = new _sqlite.Database(url, err => {
|
|
@@ -75,7 +74,7 @@ module.exports = class SQLiteDatabase extends DatabaseService {
|
|
|
75
74
|
this.before(['CREATE', 'UPDATE', 'UPSERT'], '*', this._input) // > has to run before rewrite
|
|
76
75
|
this.before(['CREATE', 'READ', 'UPDATE', 'DELETE', 'UPSERT'], '*', this._rewrite)
|
|
77
76
|
|
|
78
|
-
if (
|
|
77
|
+
if (!cds.db?.cqn2sql) this.before('READ', '*', convertDraftAdminPathExpression)
|
|
79
78
|
this.before('READ', '*', convertAssocToOneManaged)
|
|
80
79
|
this.before('READ', '*', localized) // > has to run after rewrite
|
|
81
80
|
this.before('READ', '*', this._virtual)
|