@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,13 +1,14 @@
|
|
|
1
1
|
const cds = require('../cds'),
|
|
2
2
|
{ Object_keys } = cds.utils
|
|
3
|
+
|
|
3
4
|
const { getTransition } = require('../common/utils/resolveView')
|
|
4
|
-
const { getKeyData } = require('./utils/where')
|
|
5
5
|
|
|
6
6
|
const { getPageSize, commonGenericPaging } = require('../common/generic/paging')
|
|
7
7
|
const { handler: commonGenericSorting } = require('../common/generic/sorting')
|
|
8
8
|
const { addEtagColumns } = require('../common/utils/etag')
|
|
9
9
|
|
|
10
10
|
const { calculateLocationHeader } = require('../../odata/utils')
|
|
11
|
+
const { handleStreamProperties } = require('../common/utils/streamProp')
|
|
11
12
|
|
|
12
13
|
const LOG = cds.log('fiori|drafts')
|
|
13
14
|
const original = Symbol('original')
|
|
@@ -17,17 +18,22 @@ const AGGREGATION_FUNCTIONS = ['sum', 'min', 'max', 'avg', 'count']
|
|
|
17
18
|
const calcTimeMs = timeout => {
|
|
18
19
|
const match = timeout.match(/^([0-9]+)(w|d|h|hrs|min)$/)
|
|
19
20
|
if (!match) return
|
|
21
|
+
|
|
20
22
|
const [, val, t] = match
|
|
21
23
|
switch (t) {
|
|
22
24
|
case 'w':
|
|
23
25
|
return val * 1000 * 3600 * 24 * 7
|
|
26
|
+
|
|
24
27
|
case 'd':
|
|
25
28
|
return val * 1000 * 3600 * 24
|
|
29
|
+
|
|
26
30
|
case 'h':
|
|
27
31
|
case 'hrs':
|
|
28
32
|
return val * 1000 * 3600
|
|
33
|
+
|
|
29
34
|
case 'min':
|
|
30
35
|
return val * 1000 * 60
|
|
36
|
+
|
|
31
37
|
default:
|
|
32
38
|
return val
|
|
33
39
|
}
|
|
@@ -64,12 +70,6 @@ const LOCK_TIMEOUT = {
|
|
|
64
70
|
get value() {
|
|
65
71
|
let timeout_ms = _config_to_ms('draft_lock_timeout', '15min')
|
|
66
72
|
|
|
67
|
-
const deprecated = cds.env.drafts?.cancellationTimeout // in min
|
|
68
|
-
if (deprecated) {
|
|
69
|
-
// in order to still support legacy use cases for tests, e. g. 0.000001
|
|
70
|
-
timeout_ms = deprecated * 1000 * 60
|
|
71
|
-
}
|
|
72
|
-
|
|
73
73
|
Object.defineProperty(LOCK_TIMEOUT, 'value', { value: timeout_ms })
|
|
74
74
|
return timeout_ms
|
|
75
75
|
}
|
|
@@ -78,7 +78,7 @@ const LOCK_TIMEOUT = {
|
|
|
78
78
|
const reject_bypassed_draft = req => {
|
|
79
79
|
const message =
|
|
80
80
|
!cds.profiles?.includes('production') &&
|
|
81
|
-
'`cds.env.fiori.bypass_draft` must be enabled or the entity must be annotated with `@odata.draft.bypass` to support
|
|
81
|
+
'`cds.env.fiori.bypass_draft` must be enabled or the entity must be annotated with `@odata.draft.bypass` to support direct modifications of active instances.'
|
|
82
82
|
return req.reject({ code: 501, statusCode: 501, message })
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -107,6 +107,34 @@ const DRAFT_ADMIN_ELEMENTS = [
|
|
|
107
107
|
const numericCollator = { numeric: true }
|
|
108
108
|
const emptyObject = {}
|
|
109
109
|
|
|
110
|
+
const _isKeyValue = (i, keys, where) => {
|
|
111
|
+
if (!where[i].ref || !keys.includes(where[i].ref[0])) {
|
|
112
|
+
return false
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return where[i + 1] === '=' && 'val' in where[i + 2]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const _getKeyData = (keys, where) => {
|
|
119
|
+
if (!where) {
|
|
120
|
+
return {}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const data = {}
|
|
124
|
+
let i = 0
|
|
125
|
+
|
|
126
|
+
while (where[i]) {
|
|
127
|
+
if (_isKeyValue(i, keys, where)) {
|
|
128
|
+
data[where[i].ref[0]] = where[i + 2].val
|
|
129
|
+
i = i + 3
|
|
130
|
+
} else {
|
|
131
|
+
i++
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return data
|
|
136
|
+
}
|
|
137
|
+
|
|
110
138
|
const _fillIsActiveEntity = (row, IsActiveEntity, target) => {
|
|
111
139
|
if (target.drafts) row.IsActiveEntity = IsActiveEntity
|
|
112
140
|
for (const key in target.associations) {
|
|
@@ -131,7 +159,7 @@ const _filterResultSet = (resultSet, limit, offset) => {
|
|
|
131
159
|
return pageResultSet
|
|
132
160
|
}
|
|
133
161
|
|
|
134
|
-
|
|
162
|
+
// It's important to wait for the completion of all promises, otherwise a rollback might happen too soon
|
|
135
163
|
const _promiseAll = async array => {
|
|
136
164
|
const results = await Promise.allSettled(array)
|
|
137
165
|
const firstRejected = results.find(response => response.status === 'rejected')
|
|
@@ -178,31 +206,36 @@ const _redirectRefToActives = (ref, model) => {
|
|
|
178
206
|
return [root.id ? { ...root, id: active.name } : active.name, ...tail]
|
|
179
207
|
}
|
|
180
208
|
|
|
181
|
-
|
|
209
|
+
const lastCheckMap = new Map()
|
|
182
210
|
const _cleanUpOldDrafts = async (service, tenant) => {
|
|
183
211
|
if (!DEL_TIMEOUT.value) return
|
|
184
212
|
|
|
185
|
-
const
|
|
213
|
+
const expiryDate = new Date(Date.now() - DEL_TIMEOUT.value).toISOString()
|
|
186
214
|
const interval = DEL_TIMEOUT.value / 2
|
|
187
215
|
const lastCheck = lastCheckMap.get(tenant)
|
|
188
216
|
|
|
189
217
|
if (lastCheck && Date.now() - lastCheck < Number(interval)) return
|
|
190
218
|
|
|
191
219
|
cds.spawn({ tenant, user: cds.User.privileged }, async () => {
|
|
192
|
-
|
|
220
|
+
const expiredDrafts = await SELECT.from('DRAFT.DraftAdministrativeData', ['DraftUUID']).where(
|
|
193
221
|
`LastChangeDateTime <`,
|
|
194
|
-
|
|
222
|
+
expiryDate
|
|
195
223
|
)
|
|
196
|
-
|
|
197
|
-
|
|
224
|
+
|
|
225
|
+
if (!expiredDrafts.length) return
|
|
226
|
+
|
|
227
|
+
const expiredDraftsIds = expiredDrafts.map(el => el.DraftUUID)
|
|
198
228
|
const cqns = []
|
|
229
|
+
|
|
199
230
|
for (let name in service.model.definitions) {
|
|
200
231
|
const target = service.model.definitions[name]
|
|
201
|
-
if (target.drafts && target['@Common.DraftRoot.ActivationAction'])
|
|
202
|
-
cqns.push(DELETE.from(target.drafts).where(`DraftAdministrativeData_DraftUUID IN`,
|
|
232
|
+
if (target.drafts && target['@Common.DraftRoot.ActivationAction']) {
|
|
233
|
+
cqns.push(DELETE.from(target.drafts).where(`DraftAdministrativeData_DraftUUID IN`, expiredDraftsIds))
|
|
234
|
+
}
|
|
203
235
|
}
|
|
204
|
-
|
|
205
|
-
|
|
236
|
+
|
|
237
|
+
cqns.push(DELETE.from('DRAFT.DraftAdministrativeData').where(`DraftUUID IN`, expiredDraftsIds))
|
|
238
|
+
return await _promiseAll(cqns)
|
|
206
239
|
})
|
|
207
240
|
|
|
208
241
|
lastCheckMap.set(tenant, Date.now())
|
|
@@ -210,7 +243,6 @@ const _cleanUpOldDrafts = async (service, tenant) => {
|
|
|
210
243
|
|
|
211
244
|
const h = cds.ApplicationService.prototype.handle
|
|
212
245
|
|
|
213
|
-
/* eslint-disable complexity */
|
|
214
246
|
cds.ApplicationService.prototype.handle = async function (req) {
|
|
215
247
|
const handle = h.bind(this)
|
|
216
248
|
|
|
@@ -327,9 +359,14 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
327
359
|
|
|
328
360
|
if (req.event === 'draftEdit') req.event = 'EDIT'
|
|
329
361
|
|
|
330
|
-
if (
|
|
362
|
+
if (
|
|
363
|
+
req.event === 'NEW' ||
|
|
364
|
+
req.event === 'CANCEL' ||
|
|
365
|
+
req.event === 'draftPrepare' ||
|
|
366
|
+
(req.event === 'CREATE' && req.target._isDraftEnabled)
|
|
367
|
+
) {
|
|
331
368
|
if (req.event === 'draftPrepare' && draftParams.IsActiveEntity) req.reject({ code: 400, statusCode: 400 })
|
|
332
|
-
if (req.event === 'NEW' && req.data?.IsActiveEntity === true) {
|
|
369
|
+
if ((req.event === 'NEW' || req.event === 'CREATE') && req.data?.IsActiveEntity === true) {
|
|
333
370
|
if (!cds.env.fiori.bypass_draft && !req.target['@odata.draft.bypass']) return reject_bypassed_draft(req)
|
|
334
371
|
const containsDraftRoot =
|
|
335
372
|
this.model.definitions[query.INSERT.into?.ref?.[0]?.id || query.INSERT.into?.ref?.[0] || query.INSERT.into][
|
|
@@ -364,17 +401,33 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
364
401
|
|
|
365
402
|
const cqn = INSERT.into(query.INSERT.into).entries(data)
|
|
366
403
|
await run(cqn, { event: 'CREATE' })
|
|
367
|
-
|
|
404
|
+
const result = { ...data, IsActiveEntity: true }
|
|
405
|
+
req.data = result //> make keys available via req.data (as with normal crud)
|
|
406
|
+
return result
|
|
368
407
|
}
|
|
369
408
|
req.target = req.target.drafts
|
|
370
409
|
|
|
371
410
|
if (query.INSERT?.into) {
|
|
372
411
|
if (typeof query.INSERT.into === 'string') query.INSERT.into = req.target.name
|
|
373
412
|
else if (query.INSERT.into.ref) query.INSERT.into.ref = _redirectRefToDrafts(query.INSERT.into.ref, this.model)
|
|
374
|
-
} else if (query.DELETE?.from?.ref)
|
|
375
|
-
|
|
413
|
+
} else if (query.DELETE?.from?.ref) {
|
|
414
|
+
query.DELETE.from.ref = _redirectRefToDrafts(query.DELETE.from.ref, this.model)
|
|
415
|
+
} else if (query.SELECT?.from?.ref) {
|
|
416
|
+
query.SELECT.from.ref = _redirectRefToDrafts(query.SELECT.from.ref, this.model)
|
|
417
|
+
}
|
|
418
|
+
|
|
376
419
|
const _req = _newReq(req, query, draftParams, { event: req.event })
|
|
420
|
+
|
|
421
|
+
if (
|
|
422
|
+
(req.event === 'NEW' || req.event === 'CREATE') &&
|
|
423
|
+
draftParams.IsActiveEntity === false &&
|
|
424
|
+
!_req.target.isDraft
|
|
425
|
+
) {
|
|
426
|
+
req.reject({ code: 403, statusCode: 403, message: 'ACTIVE_MODIFICATION_VIA_DRAFT' })
|
|
427
|
+
}
|
|
428
|
+
|
|
377
429
|
const result = await handle(_req)
|
|
430
|
+
req.data = result //> make keys available via req.data (as with normal crud)
|
|
378
431
|
return result
|
|
379
432
|
}
|
|
380
433
|
|
|
@@ -422,7 +475,7 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
422
475
|
}
|
|
423
476
|
|
|
424
477
|
const targetDraft = req.target.drafts
|
|
425
|
-
const cols = expandStarStar(targetDraft)
|
|
478
|
+
const cols = expandStarStar(targetDraft, true)
|
|
426
479
|
|
|
427
480
|
// Use `run` (since also etags might need to be checked)
|
|
428
481
|
// REVISIT: Find a better approach (`etag` as part of CQN?)
|
|
@@ -447,25 +500,13 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
447
500
|
args: [res.DraftAdministrativeData?.InProcessByUser]
|
|
448
501
|
})
|
|
449
502
|
}
|
|
503
|
+
|
|
450
504
|
const DraftAdministrativeData_DraftUUID = res.DraftAdministrativeData_DraftUUID
|
|
451
505
|
delete res.DraftAdministrativeData_DraftUUID
|
|
452
506
|
delete res.DraftAdministrativeData
|
|
453
507
|
const HasActiveEntity = res.HasActiveEntity
|
|
454
508
|
delete res.HasActiveEntity
|
|
455
509
|
|
|
456
|
-
if (cds.env.features.cds_assert) {
|
|
457
|
-
const assertOptions = { path: [req.target.actions[req.event]['@cds.odata.bindingparameter.name'] || 'in'] }
|
|
458
|
-
const errs = cds.assert(res, req.target, assertOptions)
|
|
459
|
-
if (errs) {
|
|
460
|
-
if (errs.length === 1) throw Object.assign(errs[0], { '@Common.numericSeverity': 4 })
|
|
461
|
-
throw Object.assign(new Error('MULTIPLE_ERRORS'), {
|
|
462
|
-
statusCode: 400,
|
|
463
|
-
details: errs,
|
|
464
|
-
'@Common.numericSeverity': 4 //> TODO: should not be needed here
|
|
465
|
-
})
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
510
|
// First run the handlers as they might need access to DraftAdministrativeData or the draft entities
|
|
470
511
|
const result = await run(
|
|
471
512
|
HasActiveEntity
|
|
@@ -490,7 +531,11 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
490
531
|
}
|
|
491
532
|
|
|
492
533
|
if (cds.env.features.odata_new_adapter) {
|
|
493
|
-
const read_result = await _readAfterDraftAction.bind(this)({
|
|
534
|
+
const read_result = await _readAfterDraftAction.bind(this)({
|
|
535
|
+
req,
|
|
536
|
+
payload: res,
|
|
537
|
+
action: 'draftActivate'
|
|
538
|
+
})
|
|
494
539
|
req.res.set(
|
|
495
540
|
'location',
|
|
496
541
|
'../' + calculateLocationHeader(req.target, this, read_result || { ...res, IsActiveEntity: true })
|
|
@@ -517,11 +562,12 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
517
562
|
return result
|
|
518
563
|
}
|
|
519
564
|
|
|
520
|
-
if (req.event === 'PATCH') {
|
|
565
|
+
if (req.event === 'PATCH' || (req.event === 'UPDATE' && req.target.drafts)) {
|
|
521
566
|
if (!('IsActiveEntity' in draftParams)) req.reject({ code: 501, statusCode: 501 })
|
|
522
567
|
|
|
523
568
|
if (draftParams.IsActiveEntity === false) {
|
|
524
569
|
LOG.debug('patch draft')
|
|
570
|
+
|
|
525
571
|
if (req.target?.name.endsWith('DraftAdministrativeData')) req.reject({ code: 405, statusCode: 405 })
|
|
526
572
|
const draftsRef = _redirectRefToDrafts(query.UPDATE.entity.ref, this.model)
|
|
527
573
|
const res = await SELECT.one.from({ ref: draftsRef }).columns('DraftAdministrativeData_DraftUUID', {
|
|
@@ -570,6 +616,10 @@ cds.ApplicationService.prototype.handle = async function (req) {
|
|
|
570
616
|
return req.data
|
|
571
617
|
}
|
|
572
618
|
|
|
619
|
+
if (req.event === 'CREATE' && draftParams.IsActiveEntity === false && !req.target.isDraft) {
|
|
620
|
+
req.reject({ code: 403, statusCode: 403, message: 'ACTIVE_MODIFICATION_VIA_DRAFT' })
|
|
621
|
+
}
|
|
622
|
+
|
|
573
623
|
req.query = query
|
|
574
624
|
|
|
575
625
|
return handle(req)
|
|
@@ -604,6 +654,7 @@ const _requested = (result, query) => {
|
|
|
604
654
|
const Read = {
|
|
605
655
|
onlyActives: async function (run, query, { ignoreDrafts } = {}) {
|
|
606
656
|
LOG.debug('List Editing Status: Only Active')
|
|
657
|
+
|
|
607
658
|
// DraftAdministrativeData is only accessible via drafts
|
|
608
659
|
if (_isCount(query)) return run(query)
|
|
609
660
|
if (query._target.name.endsWith('.DraftAdministrativeData')) return run(query._drafts)
|
|
@@ -626,7 +677,7 @@ const Read = {
|
|
|
626
677
|
else {
|
|
627
678
|
try {
|
|
628
679
|
drafts = await Read.complementaryDrafts(query, actives)
|
|
629
|
-
} catch
|
|
680
|
+
} catch {
|
|
630
681
|
drafts = []
|
|
631
682
|
}
|
|
632
683
|
}
|
|
@@ -646,6 +697,7 @@ const Read = {
|
|
|
646
697
|
|
|
647
698
|
unchanged: async function (run, query) {
|
|
648
699
|
LOG.debug('List Editing Status: Unchanged')
|
|
700
|
+
|
|
649
701
|
const draftsQuery = query._drafts
|
|
650
702
|
if (!draftsQuery) throw new Error('Invalid draft request')
|
|
651
703
|
draftsQuery.SELECT.count = undefined
|
|
@@ -697,6 +749,7 @@ const Read = {
|
|
|
697
749
|
|
|
698
750
|
all: async function (run, query) {
|
|
699
751
|
LOG.debug('List Editing Status: All')
|
|
752
|
+
|
|
700
753
|
if (!query._drafts) return []
|
|
701
754
|
|
|
702
755
|
query._drafts.SELECT.count = false
|
|
@@ -890,11 +943,13 @@ const Read = {
|
|
|
890
943
|
|
|
891
944
|
unsavedChangesByAnotherUser: async function (run, query) {
|
|
892
945
|
LOG.debug('List Editing Status: Unsaved Changes by Another User')
|
|
946
|
+
|
|
893
947
|
return Read.activesFromDrafts(run, query, { isLocked: false })
|
|
894
948
|
},
|
|
895
949
|
|
|
896
950
|
lockedByAnotherUser: async function (run, query) {
|
|
897
951
|
LOG.debug('List Editing Status: Locked by Another User')
|
|
952
|
+
|
|
898
953
|
return Read.activesFromDrafts(run, query, { isLocked: true })
|
|
899
954
|
},
|
|
900
955
|
|
|
@@ -904,10 +959,19 @@ const Read = {
|
|
|
904
959
|
const keys = entity_keys(target)
|
|
905
960
|
const dataArray = data ? (Array.isArray(data) ? data : [data]) : []
|
|
906
961
|
if (not && !dataArray.length) return []
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
962
|
+
if (keys.length === 1) {
|
|
963
|
+
// For single keys, make it nicer (without unnecessary lists)
|
|
964
|
+
const key = keys[0]
|
|
965
|
+
const left = { ref: [key] }
|
|
966
|
+
const op = not ? ['not', 'in'] : ['in']
|
|
967
|
+
const right = { list: dataArray.map(r => ({ val: r[key] })) }
|
|
968
|
+
return [left, ...op, right]
|
|
969
|
+
} else {
|
|
970
|
+
const left = { list: keys.map(k => ({ ref: [k] })) }
|
|
971
|
+
const op = not ? ['not', 'in'] : ['in']
|
|
972
|
+
const right = { list: dataArray.map(r => ({ list: keys.map(k => ({ val: r[k] })) })) }
|
|
973
|
+
return [left, ...op, right]
|
|
974
|
+
}
|
|
911
975
|
},
|
|
912
976
|
|
|
913
977
|
complementaryDrafts: (query, _actives) => {
|
|
@@ -1008,6 +1072,7 @@ function _cleansed(query, model) {
|
|
|
1008
1072
|
const getDrafts = () => {
|
|
1009
1073
|
const draftsQuery = _cleanseQuery(query, {}, model) // could just clone `q` but the latter is ruined by database layer
|
|
1010
1074
|
draftsQuery._target = undefined
|
|
1075
|
+
if (!draftsQuery.SELECT.from.ref) return // invalid draft request
|
|
1011
1076
|
const [root, ...tail] = draftsQuery.SELECT.from.ref
|
|
1012
1077
|
const draft = model.definitions[root.id || root].drafts
|
|
1013
1078
|
if (!draft) return
|
|
@@ -1187,12 +1252,14 @@ function _cleansed(query, model) {
|
|
|
1187
1252
|
}
|
|
1188
1253
|
|
|
1189
1254
|
// This function is better defined on DB layer
|
|
1190
|
-
function expandStarStar(target, recursion = new Map()) {
|
|
1255
|
+
function expandStarStar(target, draftActivate, recursion = new Map()) {
|
|
1191
1256
|
const MAX_RECURSION_DEPTH = (cds.env.features.recursion_depth && Number(cds.env.features.recursion_depth)) || 4
|
|
1192
1257
|
const columns = []
|
|
1193
1258
|
for (const el in target.elements) {
|
|
1194
1259
|
const element = target.elements[el]
|
|
1195
|
-
|
|
1260
|
+
const skip_managed = draftActivate && (element['@cds.on.insert'] || element['@cds.on.update'])
|
|
1261
|
+
if (!skip_managed && !element.isAssociation && !DRAFT_ELEMENTS.has(el) && !element['@odata.draft.skip'])
|
|
1262
|
+
columns.push({ ref: [el] })
|
|
1196
1263
|
if (!element.isComposition || element._target['@odata.draft.enabled'] === false) continue // happens for texts if not @fiori.draft.enabled
|
|
1197
1264
|
const _key = target.name + ':' + el
|
|
1198
1265
|
let cache = recursion.get(_key)
|
|
@@ -1204,7 +1271,7 @@ function expandStarStar(target, recursion = new Map()) {
|
|
|
1204
1271
|
recursion.set(_key, cache)
|
|
1205
1272
|
}
|
|
1206
1273
|
if (cache >= MAX_RECURSION_DEPTH) return
|
|
1207
|
-
const expand = expandStarStar(element._target, recursion)
|
|
1274
|
+
const expand = expandStarStar(element._target, draftActivate, recursion)
|
|
1208
1275
|
if (expand) columns.push({ ref: [el], expand })
|
|
1209
1276
|
}
|
|
1210
1277
|
return columns
|
|
@@ -1212,6 +1279,7 @@ function expandStarStar(target, recursion = new Map()) {
|
|
|
1212
1279
|
|
|
1213
1280
|
async function onNew(req) {
|
|
1214
1281
|
LOG.debug('new draft')
|
|
1282
|
+
|
|
1215
1283
|
if (req.target.actives['@Capabilities.InsertRestrictions.Insertable'] === false || req.target.actives['@readonly'])
|
|
1216
1284
|
req.reject({ code: 405, statusCode: 405 })
|
|
1217
1285
|
const isDirectAccess = typeof req.query.INSERT.into === 'string' || req.query.INSERT.into.ref?.length === 1
|
|
@@ -1284,7 +1352,11 @@ async function onNew(req) {
|
|
|
1284
1352
|
const draftCQN = INSERT.into(req.target).entries(draftData)
|
|
1285
1353
|
|
|
1286
1354
|
await _promiseAll([adminDataCQN, this.run(draftCQN)])
|
|
1287
|
-
|
|
1355
|
+
|
|
1356
|
+
// flag to trigger read after write in legacy odata adapter
|
|
1357
|
+
if (req.constructor.name in { ODataRequest: 1 }) req._.readAfterWrite = true
|
|
1358
|
+
if (req.protocol?.match(/odata/)) req._.readAfterWrite = true //> REVISIT for noah
|
|
1359
|
+
|
|
1288
1360
|
return { ...draftData, IsActiveEntity: false }
|
|
1289
1361
|
}
|
|
1290
1362
|
|
|
@@ -1354,7 +1426,7 @@ async function onEdit(req) {
|
|
|
1354
1426
|
// concurrently.
|
|
1355
1427
|
if (this._datasource === cds.db) {
|
|
1356
1428
|
const keys = entity_keys(req.target)
|
|
1357
|
-
const keyData =
|
|
1429
|
+
const keyData = _getKeyData(keys, req.query.SELECT.from.ref[0].where)
|
|
1358
1430
|
const rootWhere = keys.reduce((res, key) => {
|
|
1359
1431
|
res[key] = keyData[key]
|
|
1360
1432
|
return res
|
|
@@ -1382,7 +1454,7 @@ async function onEdit(req) {
|
|
|
1382
1454
|
|
|
1383
1455
|
try {
|
|
1384
1456
|
await this.run(activeLockCQN)
|
|
1385
|
-
} catch
|
|
1457
|
+
} catch {
|
|
1386
1458
|
const draft = await this.run(existingDraft)
|
|
1387
1459
|
if (draft) req.reject({ code: 409, statusCode: 409, message: 'DRAFT_ALREADY_EXISTS' })
|
|
1388
1460
|
req.reject({ code: 409, statusCode: 409, message: 'ENTITY_LOCKED' })
|
|
@@ -1457,7 +1529,11 @@ async function onEdit(req) {
|
|
|
1457
1529
|
}
|
|
1458
1530
|
|
|
1459
1531
|
if (cds.env.features.odata_new_adapter) {
|
|
1460
|
-
const read_result = await _readAfterDraftAction.bind(this)({
|
|
1532
|
+
const read_result = await _readAfterDraftAction.bind(this)({
|
|
1533
|
+
req,
|
|
1534
|
+
payload: res,
|
|
1535
|
+
action: 'draftEdit'
|
|
1536
|
+
})
|
|
1461
1537
|
req.res.set(
|
|
1462
1538
|
'location',
|
|
1463
1539
|
'../' + calculateLocationHeader(req.target, this, read_result || { ...res, IsActiveEntity: false })
|
|
@@ -1470,6 +1546,7 @@ async function onEdit(req) {
|
|
|
1470
1546
|
|
|
1471
1547
|
async function onCancel(req) {
|
|
1472
1548
|
LOG.debug('delete draft')
|
|
1549
|
+
|
|
1473
1550
|
const activeRef = _redirectRefToActives(req.query.DELETE.from.ref, this.model)
|
|
1474
1551
|
const draftParams = req.query[DRAFT_PARAMS]
|
|
1475
1552
|
|
|
@@ -1479,7 +1556,7 @@ async function onCancel(req) {
|
|
|
1479
1556
|
'DraftAdministrativeData_DraftUUID',
|
|
1480
1557
|
{ ref: ['DraftAdministrativeData'], expand: [_inProcessByUserXpr(_lock.shiftedNow)] }
|
|
1481
1558
|
])
|
|
1482
|
-
if (req._etagValidationClause) draftQuery.where(req._etagValidationClause)
|
|
1559
|
+
if (req._etagValidationClause && draftParams.IsActiveEntity === false) draftQuery.where(req._etagValidationClause)
|
|
1483
1560
|
// do not add InProcessByUser restriction
|
|
1484
1561
|
const draft = await draftQuery
|
|
1485
1562
|
if (draftParams.IsActiveEntity === false && !draft) req.reject(req._etagValidationType ? 412 : 404)
|
|
@@ -1488,7 +1565,10 @@ async function onCancel(req) {
|
|
|
1488
1565
|
if (processByUser && processByUser !== cds.context.user.id)
|
|
1489
1566
|
req.reject({ code: 403, statusCode: 403, message: 'DRAFT_LOCKED_BY_ANOTHER_USER', args: [processByUser] })
|
|
1490
1567
|
}
|
|
1491
|
-
const
|
|
1568
|
+
const draftDeleteQuery = DELETE.from({ ref: req.query.DELETE.from.ref })
|
|
1569
|
+
const queries = !draft
|
|
1570
|
+
? []
|
|
1571
|
+
: [draftParams.IsActiveEntity === false ? this.run(draftDeleteQuery, req.data) : draftDeleteQuery]
|
|
1492
1572
|
if (draft && req.target['@Common.DraftRoot.ActivationAction'])
|
|
1493
1573
|
// only for draft root
|
|
1494
1574
|
queries.push(
|
|
@@ -1504,13 +1584,14 @@ async function onCancel(req) {
|
|
|
1504
1584
|
})
|
|
1505
1585
|
.where({ DraftUUID: draft.DraftAdministrativeData_DraftUUID })
|
|
1506
1586
|
)
|
|
1507
|
-
if (draftParams.IsActiveEntity) queries.push(this.run(DELETE.from({ ref: activeRef })))
|
|
1587
|
+
if (draftParams.IsActiveEntity !== false) queries.push(this.run(DELETE.from({ ref: activeRef })))
|
|
1508
1588
|
await _promiseAll(queries)
|
|
1509
1589
|
return req.data
|
|
1510
1590
|
}
|
|
1511
1591
|
|
|
1512
1592
|
async function onPrepare(req) {
|
|
1513
1593
|
LOG.debug('prepare draft')
|
|
1594
|
+
|
|
1514
1595
|
const draftParams = req.query[DRAFT_PARAMS]
|
|
1515
1596
|
if (req.query.SELECT.from.ref.length > 1 || draftParams.IsActiveEntity !== false) {
|
|
1516
1597
|
req.reject({
|
|
@@ -1565,6 +1646,7 @@ const _readAfterDraftAction = async function ({ req, payload, action }) {
|
|
|
1565
1646
|
})
|
|
1566
1647
|
// also ensure selection of etag columns
|
|
1567
1648
|
addEtagColumns(read.SELECT.columns, entity)
|
|
1649
|
+
handleStreamProperties(entity, read.SELECT.columns, this.model)
|
|
1568
1650
|
}
|
|
1569
1651
|
|
|
1570
1652
|
try {
|
|
@@ -122,7 +122,6 @@ class HanaDatabase extends DatabaseService {
|
|
|
122
122
|
/*
|
|
123
123
|
* connection
|
|
124
124
|
*/
|
|
125
|
-
// eslint-disable-next-line complexity
|
|
126
125
|
async acquire(arg) {
|
|
127
126
|
const tenant = arg && (typeof arg === 'string' ? arg : arg.tenant)
|
|
128
127
|
const dbc = await pool.acquire(tenant, this)
|
|
@@ -137,7 +137,7 @@ const _getHanaDriver = name => {
|
|
|
137
137
|
let packageJson
|
|
138
138
|
try {
|
|
139
139
|
packageJson = require(cds.root + '/package.json')
|
|
140
|
-
} catch
|
|
140
|
+
} catch {
|
|
141
141
|
LOG._debug && LOG.debug(`Could not find package.json. Trying to lookup hana driver automatically.`)
|
|
142
142
|
name = 'hdb'
|
|
143
143
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
const dynatrace = {}
|
|
2
2
|
try {
|
|
3
|
-
// eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
|
|
4
3
|
dynatrace.sdk = require('@dynatrace/oneagent-sdk')
|
|
5
4
|
dynatrace.api = dynatrace.sdk.createInstance()
|
|
6
|
-
} catch
|
|
5
|
+
} catch {
|
|
7
6
|
// If module was not required, do not do anything
|
|
8
7
|
}
|
|
9
8
|
|
|
@@ -22,7 +22,6 @@ async function credentials4(tenant, db) {
|
|
|
22
22
|
if (!credentials) throw new Error('No database credentials provided')
|
|
23
23
|
|
|
24
24
|
if (cds.requires.multitenancy) {
|
|
25
|
-
// eslint-disable-next-line cds/no-missing-dependencies
|
|
26
25
|
const res = await require('@sap/cds-mtxs/lib').xt.serviceManager.get(tenant, { disableCache })
|
|
27
26
|
return _getMassagedCreds(res.credentials)
|
|
28
27
|
}
|
|
@@ -67,11 +66,6 @@ const _getPoolConfig = function () {
|
|
|
67
66
|
|
|
68
67
|
// defaults
|
|
69
68
|
if (!poolConfig) {
|
|
70
|
-
if (process.env.NODE_ENV === 'production') {
|
|
71
|
-
mergedConfig.acquireTimeoutMillis = 1000
|
|
72
|
-
} else {
|
|
73
|
-
mergedConfig.acquireTimeoutMillis = 10 * 1000
|
|
74
|
-
}
|
|
75
69
|
mergedConfig.softIdleTimeoutMillis = 30 * 1000
|
|
76
70
|
mergedConfig.idleTimeoutMillis = 30 * 1000
|
|
77
71
|
}
|
|
@@ -101,28 +95,24 @@ async function pool4(tenant, db) {
|
|
|
101
95
|
LOG._info && LOG.info('effective pool configuration:', config)
|
|
102
96
|
const p = pool.createPool(factory4(creds, tenant), config)
|
|
103
97
|
const INVALID_CREDENTIALS_WARNING = `Could not establish connection for tenant "${tenant}". Existing pool will be drained.`
|
|
104
|
-
const INVALID_CREDENTIALS_ERROR =
|
|
105
|
-
|
|
106
|
-
|
|
98
|
+
const INVALID_CREDENTIALS_ERROR = `Create is blocked for tenant "${tenant}" due to invalid credentials.`
|
|
99
|
+
const _create = () =>
|
|
100
|
+
new Promise((_, reject) =>
|
|
101
|
+
setTimeout(() => reject(new Error(INVALID_CREDENTIALS_ERROR)), config.acquireTimeoutMillis)
|
|
102
|
+
)
|
|
107
103
|
|
|
108
104
|
// The error listener for `factoryCreateError` is registered to detect failed connection attempts.
|
|
109
|
-
// If it fails due to invalid credentials, we delete the current pool from the pools map and overwrite the
|
|
110
|
-
// pool factory create function.
|
|
111
|
-
// The background is that the generic pool will keep trying to establish a connection by invoking the factory
|
|
112
|
-
// create function until the `acquireTimeoutMillis` is reached.
|
|
105
|
+
// If it fails due to invalid credentials, we delete the current pool from the pools map and overwrite the pool factory create function.
|
|
106
|
+
// The background is that the generic pool will keep trying to establish a connection by invoking the factory create function until the `acquireTimeoutMillis` is reached.
|
|
113
107
|
// This leads to numerous connection attempts for a single request, even when the credentials are invalid.
|
|
114
108
|
// Due to the deletion in the map, subsequent requests will retrieve the credentials again.
|
|
115
|
-
p.on('factoryCreateError',
|
|
109
|
+
p.on('factoryCreateError', function (err) {
|
|
116
110
|
if (err._connectError) {
|
|
117
111
|
LOG._warn && LOG.warn(INVALID_CREDENTIALS_WARNING)
|
|
118
112
|
pools.delete(tenant)
|
|
119
|
-
if (p._factory
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
new Promise((resolve, reject) => setTimeout(() => reject(INVALID_CREDENTIALS_ERROR), 100))
|
|
123
|
-
}
|
|
124
|
-
await p.drain()
|
|
125
|
-
await p.clear()
|
|
113
|
+
if (p._factory?.create) p._factory.create = _create
|
|
114
|
+
// shutdown
|
|
115
|
+
p.drain().then(p.clear.bind(p)) //> no need to await
|
|
126
116
|
}
|
|
127
117
|
})
|
|
128
118
|
|
|
@@ -9,7 +9,6 @@ const STREAM_PLACEHOLDER = '[<stream>]'
|
|
|
9
9
|
const _loadStreamExtensionIfNeeded = () => {
|
|
10
10
|
const hana = require('./driver')
|
|
11
11
|
if (hana.name !== 'hdb') {
|
|
12
|
-
// eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
|
|
13
12
|
const extension = require('@sap/hana-client/extension/Stream.js')
|
|
14
13
|
return isDynatraceEnabled() ? dynatraceStreamingExtension(extension) : extension
|
|
15
14
|
}
|
|
@@ -39,7 +39,7 @@ const authorizedRequest = ({ method, uri, path, oa2, tenant, dataObj, headers, t
|
|
|
39
39
|
let body
|
|
40
40
|
try {
|
|
41
41
|
body = res.headers && res.headers['content-type'] === 'application/json' ? JSON.parse(chunks) : chunks
|
|
42
|
-
} catch
|
|
42
|
+
} catch {
|
|
43
43
|
// There are some cases (e.g. ReadinessCheck) where the content type is 'application/json'
|
|
44
44
|
// but the chunks are not valid JSON.
|
|
45
45
|
body = chunks
|