@sap/cds-dk 10.0.7 → 10.1.0
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 +94 -0
- package/bin/add.js +7 -23
- package/bin/bind.js +10 -0
- package/bin/build.js +20 -34
- package/bin/cds.js +1 -1
- package/bin/compile/index.js +15 -41
- package/bin/completion/completionFs.js +11 -8
- package/bin/completion/index.js +592 -80
- package/bin/completion/scripts/cds.fish +568 -0
- package/bin/completion/scripts/cds.ps1 +329 -0
- package/bin/completion/scripts/cds.sh +261 -0
- package/bin/completion/scripts/cds.zsh +230 -0
- package/bin/completion/templates/cds-tmpl.fish +65 -0
- package/bin/completion/templates/cds-tmpl.ps1 +208 -0
- package/bin/completion/templates/cds-tmpl.sh +93 -0
- package/bin/completion/templates/cds-tmpl.zsh +74 -0
- package/bin/deploy/index.js +7 -1
- package/bin/deploy/options.js +25 -0
- package/{lib → bin}/deploy/to-hana/hana.js +8 -8
- package/{lib → bin}/deploy/to-hana/hdiDeployUtil.js +1 -1
- package/bin/deploy/{to-hana.js → to-hana/index.js} +1 -1
- package/{lib → bin}/deploy/to-hana/mtaUtil.js +1 -1
- package/bin/deploy/to-sql.js +0 -2
- package/bin/export.js +35 -5
- package/bin/help/export.md +3 -0
- package/bin/help.js +1 -5
- package/bin/import.js +6 -0
- package/bin/init.js +5 -24
- package/bin/inspect.js +6 -0
- package/bin/login.js +26 -20
- package/bin/pull.js +19 -6
- package/bin/push.js +19 -6
- package/bin/up.js +14 -11
- package/{lib → bin}/upgrade/constants.js +1 -1
- package/bin/{upgrade.js → upgrade/index.js} +2 -2
- package/{lib → bin}/upgrade/preflight/consistency.js +1 -1
- package/{lib → bin}/upgrade/preflight/index.js +1 -1
- package/{lib → bin}/upgrade/steps/analyze/index.js +1 -1
- package/{lib → bin}/upgrade/steps/install.js +1 -1
- package/{lib → bin}/upgrade/steps/openrewrite.js +1 -1
- package/{lib → bin}/upgrade/steps/removals.js +1 -1
- package/{lib → bin}/upgrade/steps/rewrite.js +1 -1
- package/{lib → bin}/upgrade/steps/verify.js +1 -1
- package/{lib/upgrade/index.js → bin/upgrade/upgrade.js} +2 -2
- package/{lib → bin}/upgrade/util/pom.js +2 -2
- package/{lib → bin}/upgrade/util/prompt.js +1 -1
- package/bin/util/containerize.js +8 -4
- package/bin/version.js +10 -1
- package/{lib → bin}/watch/hot-reload.js +1 -1
- package/bin/{watch.js → watch/index.js} +17 -9
- package/{lib → bin}/watch/livereload-connect.js +1 -1
- package/{lib → bin}/watch/vite-watch.js +1 -1
- package/{lib → bin}/watch/watched.js +2 -2
- package/lib/build/constants.js +1 -1
- package/lib/build/plugins/fiori/index.js +31 -2
- package/lib/build/plugins/internal.js +4 -2
- package/lib/build/plugins/mtx-sidecar/index.js +7 -8
- package/lib/build/plugins/nodejs/index.js +64 -7
- package/lib/compile/to_asyncapi.js +1 -1
- package/lib/env/schema.js +16 -0
- package/lib/env/schemas/cds-rc.js +1 -76
- package/lib/import/importapi.js +2 -3
- package/lib/import/message.js +0 -2
- package/lib/init/constants.js +2 -1
- package/lib/init/index.js +2 -2
- package/lib/init/merge.js +123 -280
- package/lib/init/plugin.js +3 -1
- package/lib/init/projectReader.js +22 -6
- package/lib/init/registries/xs-app.js +3 -5
- package/lib/init/template/ai/files/mta.yaml.hbs +1 -1
- package/lib/init/template/ai/index.js +14 -2
- package/lib/init/template/ams/index.js +2 -2
- package/lib/init/template/app-frontend/files/containerize.yaml.hbs +2 -2
- package/lib/init/template/app-frontend/index.js +4 -4
- package/lib/init/template/application-logging/index.js +2 -2
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/approuter/index.js +8 -3
- package/lib/init/template/attachments/files/package.json.hbs +1 -1
- package/lib/init/template/attachments/index.js +2 -2
- package/lib/init/template/audit-logging/index.js +2 -2
- package/lib/init/template/cloud-logging/files/package.json.hbs +4 -4
- package/lib/init/template/cloud-logging/index.js +2 -2
- package/lib/init/template/completion/completionSetup.js +36 -39
- package/lib/init/template/connectivity/index.js +2 -2
- package/lib/init/template/data/as-csv.js +15 -5
- package/lib/init/template/data/as-json.js +114 -42
- package/lib/init/template/data/index.js +16 -2
- package/lib/init/template/destination/index.js +2 -2
- package/lib/init/template/dynatrace/files/package.json.hbs +3 -3
- package/lib/init/template/dynatrace/index.js +2 -2
- package/lib/init/template/enterprise-messaging/index.js +2 -2
- package/lib/init/template/enterprise-messaging-shared/index.js +2 -2
- package/lib/init/template/extensibility/files/mta.yaml.hbs +11 -0
- package/lib/init/template/extensibility/files/xs-security.json.hbs +1 -1
- package/lib/init/template/extensibility/index.js +3 -3
- package/lib/init/template/github-actions/index.js +2 -2
- package/lib/init/template/hana/index.js +3 -2
- package/lib/init/template/hana-serverless/files/db/package.json +14 -0
- package/lib/init/template/hana-serverless/files/identity.values.yaml.hbs +10 -0
- package/lib/init/template/hana-serverless/files/mta.yaml.hbs +64 -0
- package/lib/init/template/hana-serverless/files/package.json.hbs +35 -0
- package/lib/init/template/hana-serverless/files/src/.hdiconfig +139 -0
- package/lib/init/template/hana-serverless/files/undeploy.json +7 -0
- package/lib/init/template/hana-serverless/files/values.yaml.hbs +18 -0
- package/lib/init/template/hana-serverless/index.js +184 -0
- package/lib/init/template/html5-repo/files/containerize.yaml.hbs +2 -2
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +5 -0
- package/lib/init/template/html5-repo/index.js +18 -14
- package/lib/init/template/http/index.js +123 -26
- package/lib/init/template/ias/index.js +2 -2
- package/lib/init/template/kafka/index.js +2 -2
- package/lib/init/template/kyma/index.js +7 -5
- package/lib/init/template/malware-scanner/index.js +2 -2
- package/lib/init/template/mta/files/mta.yaml.hbs +1 -1
- package/lib/init/template/multitenancy/index.js +18 -3
- package/lib/init/template/notifications/index.js +2 -2
- package/lib/init/template/portal/files/mta.yaml.hbs +4 -0
- package/lib/init/template/portal/index.js +13 -8
- package/lib/init/template/postgres/index.js +2 -2
- package/lib/init/template/redis/index.js +2 -2
- package/lib/init/template/sample/files/db/currencies.cds +2 -0
- package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Authors.csv +1 -1
- package/lib/init/template/tiny-sample/files/nodejs/db/data/CatalogService.Books.csv +2 -2
- package/lib/init/template/workzone-standard/index.js +5 -5
- package/lib/init/template/xsuaa/index.js +20 -2
- package/lib/lint/eslintHelp.json +1 -1
- package/lib/mtx/auth_manager.js +103 -37
- package/lib/mtx/params.js +3 -2
- package/lib/mtx/settings_manager.js +45 -28
- package/lib/mtx/tenant_action.js +0 -1
- package/lib/mtx/util/credentials.js +62 -0
- package/node_modules/@sap/cds/CHANGELOG.md +57 -0
- package/node_modules/@sap/cds/app/index.js +5 -4
- package/node_modules/@sap/cds/eslint.config.mjs +1 -0
- package/node_modules/@sap/cds/lib/compile/minify.js +14 -10
- package/node_modules/@sap/cds/lib/env/cds-env.js +1 -1
- package/node_modules/@sap/cds/lib/env/cds-requires.js +22 -11
- package/node_modules/@sap/cds/lib/env/defaults.js +4 -1
- package/node_modules/@sap/cds/lib/env/schemas/cds-rc.js +5 -2
- package/node_modules/@sap/cds/lib/plugins.js +9 -5
- package/node_modules/@sap/cds/lib/req/spawn.js +1 -1
- package/node_modules/@sap/cds/lib/srv/srv-dispatch.js +1 -1
- package/node_modules/@sap/cds/lib/srv/srv-models.js +1 -1
- package/node_modules/@sap/cds/lib/utils/cds-utils.js +2 -1
- package/node_modules/@sap/cds/libx/_runtime/common/generic/assert.js +2 -3
- package/node_modules/@sap/cds/libx/_runtime/common/generic/auth/restrict.js +8 -1
- package/node_modules/@sap/cds/libx/_runtime/common/generic/crud.js +18 -8
- package/node_modules/@sap/cds/libx/_runtime/fiori/lean-draft.js +25 -17
- package/node_modules/@sap/cds/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +9 -3
- package/node_modules/@sap/cds/libx/_runtime/remote/Service.js +4 -19
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/LruCache.js +42 -0
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/client.js +14 -4
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/cloudSdkProvider.js +2 -9
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/destinationClient.js +150 -0
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/error.js +13 -0
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/fetchClient.js +26 -18
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/query.js +34 -23
- package/node_modules/@sap/cds/libx/_runtime/remote/utils/token.js +82 -0
- package/node_modules/@sap/cds/libx/_runtime/ucl/Service.js +36 -189
- package/node_modules/@sap/cds/libx/common/utils/streaming.js +16 -16
- package/node_modules/@sap/cds/libx/http/location.js +3 -3
- package/node_modules/@sap/cds/libx/odata/middleware/batch.js +16 -10
- package/node_modules/@sap/cds/libx/odata/middleware/metadata.js +9 -6
- package/node_modules/@sap/cds/libx/odata/middleware/service-document.js +1 -1
- package/node_modules/@sap/cds/libx/odata/parse/afterburner.js +19 -3
- package/node_modules/@sap/cds/libx/odata/parse/cqn2odata.js +83 -86
- package/node_modules/@sap/cds/libx/queue/SchedulingService.js +17 -2
- package/node_modules/@sap/cds/libx/queue/processing.js +1 -3
- package/node_modules/@sap/cds/libx/rest/RestAdapter.js +3 -7
- package/node_modules/@sap/cds/libx/rest/middleware/delete.js +3 -3
- package/node_modules/@sap/cds/libx/rest/middleware/parse.js +13 -8
- package/node_modules/@sap/cds/libx/rest/middleware/read.js +3 -3
- package/node_modules/@sap/cds/libx/rest/middleware/upsert.js +2 -2
- package/node_modules/@sap/cds/package.json +1 -1
- package/node_modules/@sap/cds-compiler/CHANGELOG.md +60 -8
- package/node_modules/@sap/cds-compiler/bin/cdsc.js +2 -2
- package/node_modules/@sap/cds-compiler/doc/IncompatibleChanges_v7.md +46 -19
- package/node_modules/@sap/cds-compiler/lib/api/main.js +10 -7
- package/node_modules/@sap/cds-compiler/lib/api/options.js +8 -2
- package/node_modules/@sap/cds-compiler/lib/base/csnRefs.js +5 -2
- package/node_modules/@sap/cds-compiler/lib/base/message-registry.js +4 -0
- package/node_modules/@sap/cds-compiler/lib/base/optionProcessor.js +12 -0
- package/node_modules/@sap/cds-compiler/lib/checks/enricher.js +1 -0
- package/node_modules/@sap/cds-compiler/lib/compiler/assert-consistency.js +8 -7
- package/node_modules/@sap/cds-compiler/lib/compiler/checks.js +3 -3
- package/node_modules/@sap/cds-compiler/lib/compiler/define.js +301 -75
- package/node_modules/@sap/cds-compiler/lib/compiler/dictionaries.js +2 -0
- package/node_modules/@sap/cds-compiler/lib/compiler/extend.js +40 -26
- package/node_modules/@sap/cds-compiler/lib/compiler/populate.js +45 -60
- package/node_modules/@sap/cds-compiler/lib/compiler/propagator.js +16 -7
- package/node_modules/@sap/cds-compiler/lib/compiler/resolve.js +41 -15
- package/node_modules/@sap/cds-compiler/lib/compiler/shared.js +7 -6
- package/node_modules/@sap/cds-compiler/lib/compiler/tweak-assocs.js +62 -50
- package/node_modules/@sap/cds-compiler/lib/compiler/utils.js +29 -14
- package/node_modules/@sap/cds-compiler/lib/edm/edmAnnoPreprocessor.js +6 -5
- package/node_modules/@sap/cds-compiler/lib/gen/CdlGrammar.checksum +1 -1
- package/node_modules/@sap/cds-compiler/lib/gen/CdlParser.js +1119 -1109
- package/node_modules/@sap/cds-compiler/lib/gen/Dictionary.json +19 -3
- package/node_modules/@sap/cds-compiler/lib/json/from-csn.js +10 -16
- package/node_modules/@sap/cds-compiler/lib/json/to-csn.js +58 -39
- package/node_modules/@sap/cds-compiler/lib/main.d.ts +8 -0
- package/node_modules/@sap/cds-compiler/lib/main.js +1 -0
- package/node_modules/@sap/cds-compiler/lib/model/csnUtils.js +2 -0
- package/node_modules/@sap/cds-compiler/lib/parsers/AstBuildingParser.js +2 -1
- package/node_modules/@sap/cds-compiler/lib/parsers/XprTree.js +8 -0
- package/node_modules/@sap/cds-compiler/lib/render/toCdl.js +22 -1
- package/node_modules/@sap/cds-compiler/lib/render/toSql.js +17 -2
- package/node_modules/@sap/cds-compiler/lib/render/utils/common.js +6 -1
- package/node_modules/@sap/cds-compiler/lib/render/utils/operators.js +27 -0
- package/node_modules/@sap/cds-compiler/lib/render/utils/standardDatabaseFunctions.js +125 -3
- package/node_modules/@sap/cds-compiler/lib/tool-lib/revealInternalProperties.js +2 -1
- package/node_modules/@sap/cds-compiler/lib/transform/db/applyTransformations.js +3 -0
- package/node_modules/@sap/cds-compiler/lib/transform/db/associations.js +8 -2
- package/node_modules/@sap/cds-compiler/lib/transform/db/backlinks.js +22 -6
- package/node_modules/@sap/cds-compiler/lib/transform/db/constraints.js +62 -1
- package/node_modules/@sap/cds-compiler/lib/transform/effective/annotations.js +40 -0
- package/node_modules/@sap/cds-compiler/lib/transform/effective/main.js +1 -0
- package/node_modules/@sap/cds-compiler/lib/transform/forRelationalDB.js +6 -1
- package/node_modules/@sap/cds-compiler/lib/transform/translateAssocsToJoins.js +19 -8
- package/node_modules/@sap/cds-compiler/package.json +1 -1
- package/node_modules/@sap/cds-mtxs/CHANGELOG.md +15 -0
- package/node_modules/@sap/cds-mtxs/bin/cds-mtx.js +34 -16
- package/node_modules/@sap/cds-mtxs/lib/utils.js +19 -4
- package/node_modules/@sap/cds-mtxs/package.json +1 -1
- package/node_modules/@sap/cds-mtxs/srv/cf/abstract-provisioning-service.js +1 -1
- package/node_modules/@sap/cds-mtxs/srv/extensibility-service.js +22 -13
- package/node_modules/@sap/cds-mtxs/srv/model-provider.js +17 -2
- package/node_modules/@sap/cds-mtxs/srv/plugins/hana/ctnr-mgr-base.js +1 -1
- package/node_modules/@sap/cds-mtxs/srv/plugins/hana.js +4 -8
- package/node_modules/@sap/xsenv/CHANGELOG.md +7 -0
- package/node_modules/@sap/xsenv/package.json +10 -7
- package/node_modules/body-parser/node_modules/content-type/README.md +2 -0
- package/node_modules/body-parser/node_modules/content-type/dist/index.d.ts +20 -0
- package/node_modules/body-parser/node_modules/content-type/dist/index.js +20 -14
- package/node_modules/body-parser/node_modules/content-type/dist/index.js.map +1 -1
- package/node_modules/body-parser/node_modules/content-type/package.json +1 -1
- package/node_modules/hdb/cfg/VERSION +1 -1
- package/node_modules/hdb/lib/protocol/ClientInfo.js +16 -3
- package/node_modules/hdb/lib/protocol/Connection.js +1 -0
- package/node_modules/hdb/package.json +3 -2
- package/node_modules/negotiator/lib/accept.js +54 -0
- package/node_modules/negotiator/lib/charset.js +10 -34
- package/node_modules/negotiator/lib/encoding.js +9 -30
- package/node_modules/negotiator/lib/language.js +15 -36
- package/node_modules/negotiator/lib/mediaType.js +16 -134
- package/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
- package/node_modules/negotiator/node_modules/content-type/README.md +71 -0
- package/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
- package/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
- package/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
- package/node_modules/negotiator/node_modules/content-type/package.json +52 -0
- package/node_modules/negotiator/package.json +16 -10
- package/node_modules/qs/.github/THREAT_MODEL.md +3 -3
- package/node_modules/qs/CHANGELOG.md +15 -0
- package/node_modules/qs/README.md +19 -1
- package/node_modules/qs/dist/qs.js +21 -21
- package/node_modules/qs/eslint.config.mjs +1 -1
- package/node_modules/qs/lib/parse.js +3 -4
- package/node_modules/qs/lib/stringify.js +23 -8
- package/node_modules/qs/lib/utils.js +10 -3
- package/node_modules/qs/package.json +4 -4
- package/node_modules/qs/test/parse.js +192 -3
- package/node_modules/qs/test/stringify.js +317 -0
- package/node_modules/qs/test/utils.js +12 -0
- package/node_modules/type-is/node_modules/content-type/README.md +2 -0
- package/node_modules/type-is/node_modules/content-type/dist/index.d.ts +20 -0
- package/node_modules/type-is/node_modules/content-type/dist/index.js +20 -14
- package/node_modules/type-is/node_modules/content-type/dist/index.js.map +1 -1
- package/node_modules/type-is/node_modules/content-type/package.json +1 -1
- package/node_modules/ws/lib/permessage-deflate.js +3 -1
- package/node_modules/ws/package.json +1 -1
- package/package.json +3 -3
- package/bin/completion/scripts/cds-bash.sh +0 -20
- package/bin/completion/scripts/cds-fish.fish +0 -13
- package/bin/completion/scripts/cds-ps.ps1 +0 -19
- package/bin/completion/scripts/cds-zsh.sh +0 -13
- package/lib/deploy/index.js +0 -25
- package/lib/import/asyncapi/importAsyncAPI.js +0 -33
- package/lib/import/asyncapi/index.js +0 -18
- package/lib/import/asyncapi/parserContext.js +0 -454
- package/lib/import/asyncapi/utils.js +0 -49
- package/node_modules/@sap/cds/libx/_runtime/ucl/queries.js +0 -61
- package/node_modules/negotiator/HISTORY.md +0 -114
- /package/{lib → bin}/upgrade/context.js +0 -0
- /package/{lib → bin}/upgrade/migrations/cds10/migrations.json +0 -0
- /package/{lib → bin}/upgrade/preflight/cds-version.js +0 -0
- /package/{lib → bin}/upgrade/preflight/project-type.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/classification.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/detector.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/evaluator.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/reporter.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/setup.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/utils/detection.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/utils/files.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/utils/grep.js +0 -0
- /package/{lib → bin}/upgrade/steps/analyze/utils/yaml.js +0 -0
- /package/{lib → bin}/upgrade/util/workspaces.js +0 -0
- /package/{lib → bin}/watch/esm-resolve-registration.js +0 -0
- /package/{lib → bin}/watch/livereload-server.js +0 -0
- /package/{lib → bin}/watch/node-watch.js +0 -0
- /package/{lib → bin}/watch/open.js +0 -0
- /package/{lib → bin}/watch/watcher.js +0 -0
package/lib/mtx/auth_manager.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const cds = require('../cds');
|
|
2
|
-
const { SettingsManager } = require('./settings_manager');
|
|
2
|
+
const { SettingsManager, OAUTH_PATH, OAUTH_PATH_LEGACY_TOKEN } = require('./settings_manager');
|
|
3
3
|
const { ParamCollection } = require('./params');
|
|
4
4
|
const { getMessage } = require('./util/logging');
|
|
5
5
|
const { buildResponseError, buildNetworkError, parseResponseBody } = require('./util/request');
|
|
@@ -39,28 +39,107 @@ function reqParams(method, params) {
|
|
|
39
39
|
const url = params.get('tokenUrl');
|
|
40
40
|
const d = {};
|
|
41
41
|
const ignored = {};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
d.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
const headers = { 'content-type': 'application/x-www-form-urlencoded' };
|
|
43
|
+
const tls = {};
|
|
44
|
+
if (params.get('directUaa')) {
|
|
45
|
+
d.grant_type = 'client_credentials';
|
|
46
|
+
d.client_id = params.get('clientid');
|
|
47
|
+
if (params.has('key')) {
|
|
48
|
+
tls.cert = params.get('certificate');
|
|
49
|
+
tls.key = params.get('key');
|
|
50
|
+
} else {
|
|
51
|
+
const encoded = Buffer.from(`${params.get('clientid')}:${params.get('clientsecret')}`).toString('base64');
|
|
52
|
+
headers.Authorization = `Basic ${encoded}`;
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
if (params.has('subdomain')) {
|
|
56
|
+
d.subdomain = params.get('subdomain');
|
|
57
|
+
}
|
|
58
|
+
if (params.has('refreshToken')) {
|
|
59
|
+
d.refresh_token = params.get('refreshToken');
|
|
60
|
+
const param = ['passcode', 'clientid'].find(param => params.has(param));
|
|
61
|
+
if (param) {
|
|
62
|
+
ignored[param] = 'refresh token is present';
|
|
63
|
+
}
|
|
64
|
+
} else if (params.has('passcode')) {
|
|
65
|
+
d.passcode = params.get('passcode');
|
|
66
|
+
} else if (params.has('clientsecret')) {
|
|
67
|
+
d.clientid = params.get('clientid');
|
|
68
|
+
d.clientsecret = params.get('clientsecret');
|
|
50
69
|
}
|
|
51
|
-
} else if (params.has('passcode')) {
|
|
52
|
-
d.passcode = params.get('passcode');
|
|
53
|
-
} else if (params.has('clientsecret')) {
|
|
54
|
-
d.clientid = params.get('clientid');
|
|
55
|
-
d.clientsecret = params.get('clientsecret');
|
|
56
|
-
} else if (params.has('key')) {
|
|
57
|
-
d.clientid = params.get('clientid');
|
|
58
|
-
d.key = params.get('key');
|
|
59
70
|
}
|
|
60
71
|
const data = new URLSearchParams(d).toString();
|
|
61
|
-
|
|
62
|
-
? { url, data,
|
|
63
|
-
: { url: `${url}?${data}`, data: undefined,
|
|
72
|
+
const req = method === 'post'
|
|
73
|
+
? { url, data, method, headers, ...tls }
|
|
74
|
+
: { url: `${url}?${data}`, data: undefined, method, headers, ...tls };
|
|
75
|
+
return { req, ignored };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function toResponse(res, body) {
|
|
79
|
+
const status = res.statusCode;
|
|
80
|
+
return {
|
|
81
|
+
ok: status >= 200 && status < 300,
|
|
82
|
+
status,
|
|
83
|
+
statusText: res.statusMessage ?? '',
|
|
84
|
+
headers: { get: name => res.headers[name.toLowerCase()] },
|
|
85
|
+
json: async () => JSON.parse(body),
|
|
86
|
+
text: async () => body
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function postTokenViaMtls(url, data, headers, { cert, key }) {
|
|
91
|
+
const https = require('node:https');
|
|
92
|
+
const u = new URL(url);
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
const request = https.request({
|
|
95
|
+
hostname: u.hostname,
|
|
96
|
+
port: u.port || 443,
|
|
97
|
+
path: `${u.pathname}${u.search}`,
|
|
98
|
+
method: 'POST',
|
|
99
|
+
headers,
|
|
100
|
+
cert,
|
|
101
|
+
key
|
|
102
|
+
}, res => {
|
|
103
|
+
let body = '';
|
|
104
|
+
res.setEncoding('utf8');
|
|
105
|
+
res.on('data', chunk => { body += chunk; });
|
|
106
|
+
res.on('end', () => resolve(toResponse(res, body)));
|
|
107
|
+
});
|
|
108
|
+
request.on('error', reject);
|
|
109
|
+
if (data) {
|
|
110
|
+
request.write(data);
|
|
111
|
+
}
|
|
112
|
+
request.end();
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function postTokenTo(req, params) {
|
|
117
|
+
const { url, data, method, headers, cert, key } = req;
|
|
118
|
+
DEBUG?.(`Getting authentication token from ${method.toUpperCase()} ${params.obfuscateQueryParams(url)}`);
|
|
119
|
+
const response = cert
|
|
120
|
+
? await postTokenViaMtls(url, data, headers, { cert, key })
|
|
121
|
+
: await fetch(url, {
|
|
122
|
+
method: method.toUpperCase(),
|
|
123
|
+
body: data,
|
|
124
|
+
headers
|
|
125
|
+
});
|
|
126
|
+
if (!response.ok) {
|
|
127
|
+
throw await buildResponseError(method, url, response);
|
|
128
|
+
}
|
|
129
|
+
return parseResponseBody(response);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function postToken(req, params) {
|
|
133
|
+
try {
|
|
134
|
+
return await postTokenTo(req, params);
|
|
135
|
+
} catch (e) {
|
|
136
|
+
if (e.status !== 404 || !req.url.includes(OAUTH_PATH)) {
|
|
137
|
+
throw e;
|
|
138
|
+
}
|
|
139
|
+
req.url = req.url.replace(OAUTH_PATH, OAUTH_PATH_LEGACY_TOKEN);
|
|
140
|
+
DEBUG?.(`Token endpoint not found, retrying against legacy path ${params.obfuscateQueryParams(req.url)}`);
|
|
141
|
+
return postTokenTo(req, params);
|
|
142
|
+
}
|
|
64
143
|
}
|
|
65
144
|
|
|
66
145
|
async function fetchToken(params) {
|
|
@@ -71,29 +150,16 @@ async function fetchToken(params) {
|
|
|
71
150
|
let responseData, error;
|
|
72
151
|
const method = 'post';
|
|
73
152
|
do {
|
|
74
|
-
const {
|
|
153
|
+
const { req, ignored } = reqParams(method, params);
|
|
75
154
|
if (Object.keys(ignored).length) {
|
|
76
155
|
console.log(`Ignoring parameters for fetching token: ${Object.entries(ignored).map(([param, reason]) => `${param} (${reason})`).join(', ')}.`);
|
|
77
156
|
}
|
|
78
|
-
DEBUG?.(`Getting authentication token from ${method.toUpperCase()} ${params.obfuscateQueryParams(url)}`);
|
|
79
157
|
error = undefined;
|
|
80
158
|
try {
|
|
81
|
-
|
|
82
|
-
method: method.toUpperCase(),
|
|
83
|
-
body: data,
|
|
84
|
-
headers: { 'content-type': 'application/x-www-form-urlencoded' }
|
|
85
|
-
});
|
|
86
|
-
if (!response.ok) {
|
|
87
|
-
throw await buildResponseError(method, url, response);
|
|
88
|
-
}
|
|
89
|
-
responseData = await parseResponseBody(response);
|
|
159
|
+
responseData = await postToken(req, params);
|
|
90
160
|
break;
|
|
91
161
|
} catch (e) {
|
|
92
|
-
|
|
93
|
-
error = e;
|
|
94
|
-
} else {
|
|
95
|
-
error = buildNetworkError(method, url, e);
|
|
96
|
-
}
|
|
162
|
+
error = e.status ? e : buildNetworkError(method, req.url, e);
|
|
97
163
|
DEBUG?.(`HTTP status ${error.status}`);
|
|
98
164
|
if (params.has('refreshToken')) {
|
|
99
165
|
DEBUG?.('Discarding invalid refresh token');
|
|
@@ -122,7 +188,7 @@ async function fetchToken(params) {
|
|
|
122
188
|
message += `. Ensure the client credentials are correct`;
|
|
123
189
|
}
|
|
124
190
|
} else {
|
|
125
|
-
message = '
|
|
191
|
+
message = 'token request failed';
|
|
126
192
|
}
|
|
127
193
|
throw new Error(message + '.', { cause: error });
|
|
128
194
|
}
|
package/lib/mtx/params.js
CHANGED
|
@@ -178,7 +178,8 @@ class BasicParams {
|
|
|
178
178
|
this.password = new Param('password', undefined, { obfuscate: ObfuscationLevel.full });
|
|
179
179
|
this.clientid = new Param('clientid', undefined);
|
|
180
180
|
this.clientsecret = new Param('clientsecret', undefined, { obfuscate: ObfuscationLevel.partial });
|
|
181
|
-
this.
|
|
181
|
+
this.certificate = new Param('certificate', undefined, { obfuscate: ObfuscationLevel.partial }); // client certificate for mTLS
|
|
182
|
+
this.key = new Param('key', undefined, { obfuscate: ObfuscationLevel.partial }); // private key of the client certificate
|
|
182
183
|
this.tokenStorage = new Param('tokenStorage', undefined, { allowedValues: ['plain', 'keyring'] });
|
|
183
184
|
this.clearOtherTokenStorage = new Param('clearOtherTokenStorage', false, { internal: true });
|
|
184
185
|
this.plain = new Param('plain', false, { internal: true }); // for backward compatibility - superseded by tokenStorage
|
|
@@ -195,7 +196,6 @@ class BasicParams {
|
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
|
|
198
|
-
// TODO pass these as extra arguments to lib functions
|
|
199
199
|
/**
|
|
200
200
|
* All known params including those needed for specific commands.
|
|
201
201
|
*/
|
|
@@ -216,6 +216,7 @@ class AllParams extends BasicParams {
|
|
|
216
216
|
|
|
217
217
|
// `login` PARAMS
|
|
218
218
|
this.skipToken = new Param('skipToken', false, { internal: true });
|
|
219
|
+
this.directUaa = new Param('directUaa', false, { internal: true }); // client-credentials login goes directly to the authorization server
|
|
219
220
|
this.saveRefreshToken = new Param('saveRefreshToken', false, { internal: true });
|
|
220
221
|
|
|
221
222
|
// `logout` PARAMS
|
|
@@ -34,9 +34,10 @@ const KEYRING_DESC = {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
const MTX_FULLY_QUALIFIED = 'com.sap.cds.mtx';
|
|
37
|
-
const
|
|
38
|
-
const OAUTH_PATH = '/-/cds/login/
|
|
39
|
-
const OAUTH_META_PATH = '/-/cds/login/
|
|
37
|
+
const OAUTH_PATH_LEGACY_TOKEN = '/-/cds/login/token';
|
|
38
|
+
const OAUTH_PATH = '/-/cds/login/grant';
|
|
39
|
+
const OAUTH_META_PATH = '/-/cds/login/grant-metadata';
|
|
40
|
+
const OAUTH_UAA_PATH = '/oauth/token';
|
|
40
41
|
|
|
41
42
|
const SETTINGS_DIR = path.join(os.homedir(), CONFIG_SUBDIRS[os.platform()] || '', MTX_FULLY_QUALIFIED);
|
|
42
43
|
const SETTINGS_FILE = 'projects.json';
|
|
@@ -102,12 +103,40 @@ async function getTokenUrl(params, renewUrl) {
|
|
|
102
103
|
if (!params.has('appUrl')) {
|
|
103
104
|
throw 'Failed to determine token URL: app URL not given';
|
|
104
105
|
}
|
|
106
|
+
if (params.has('clientid')) {
|
|
107
|
+
return deriveUaaTokenUrl(params);
|
|
108
|
+
}
|
|
105
109
|
return appUrlWith(OAUTH_PATH, params);
|
|
106
110
|
}
|
|
107
111
|
|
|
108
|
-
|
|
112
|
+
function uaaTokenUrl(passcodeUrl, { mtls } = {}) {
|
|
113
|
+
const url = new URL(passcodeUrl);
|
|
114
|
+
url.pathname = url.pathname.replace(/\/passcode\/*$/, '').replace(/\/+$/, '') + OAUTH_UAA_PATH;
|
|
115
|
+
url.search = '';
|
|
116
|
+
if (mtls) {
|
|
117
|
+
url.hostname = url.hostname.replace(/\.authentication\./, '.authentication.cert.');
|
|
118
|
+
}
|
|
119
|
+
return url.toString();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function deriveUaaTokenUrl(params) {
|
|
123
|
+
const mtls = params.has('key');
|
|
124
|
+
const metadata = await fetchAuthMetadata(params);
|
|
125
|
+
const announced = mtls ? metadata?.cert_token_url : metadata?.token_url;
|
|
126
|
+
const passcodeUrl = metadata?.passcode_url;
|
|
127
|
+
const tokenUrl = announced ?? (passcodeUrl && uaaTokenUrl(passcodeUrl, { mtls }));
|
|
128
|
+
if (!tokenUrl) {
|
|
129
|
+
throw 'Cannot determine the authorization server for client-credentials login. ' +
|
|
130
|
+
'Ensure MTX is running with extensibility enabled and up to date.';
|
|
131
|
+
}
|
|
132
|
+
params.set('directUaa', true);
|
|
133
|
+
DEBUG?.(`Derived authorization-server token URL for ${mtls ? 'mTLS' : 'client-credentials'} login: ${tokenUrl}`);
|
|
134
|
+
return tokenUrl;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function fetchAuthMetadata(params) {
|
|
109
138
|
let fetchUrl = appUrlWith(OAUTH_META_PATH, params);
|
|
110
|
-
DEBUG?.(`Trying to get
|
|
139
|
+
DEBUG?.(`Trying to get authorization metadata from GET`, fetchUrl);
|
|
111
140
|
if (params.has('subdomain')) {
|
|
112
141
|
const u = new URL(fetchUrl);
|
|
113
142
|
u.searchParams.set('subdomain', params.get('subdomain'));
|
|
@@ -124,10 +153,10 @@ async function fetchPasscodeUrl(params) {
|
|
|
124
153
|
if (error.status === 404) {
|
|
125
154
|
DEBUG?.(`Request unsupported by server, ignoring (${error.message.replace(/ *Details:.*/s, '')})`);
|
|
126
155
|
} else if (error.status) {
|
|
127
|
-
DEBUG?.(getMessage(`Getting
|
|
156
|
+
DEBUG?.(getMessage(`Getting authorization metadata failed`, { error }));
|
|
128
157
|
} else {
|
|
129
158
|
const netError = buildNetworkError('GET', fetchUrl, error);
|
|
130
|
-
DEBUG?.(getMessage(`Getting
|
|
159
|
+
DEBUG?.(getMessage(`Getting authorization metadata failed`, { error: netError }));
|
|
131
160
|
}
|
|
132
161
|
return undefined;
|
|
133
162
|
}
|
|
@@ -139,7 +168,7 @@ async function fetchPasscodeUrl(params) {
|
|
|
139
168
|
DEBUG?.(`Unsupported signed metadata received. Since these should take precedence, ignoring response.`);
|
|
140
169
|
return undefined;
|
|
141
170
|
}
|
|
142
|
-
return responseData
|
|
171
|
+
return responseData;
|
|
143
172
|
}
|
|
144
173
|
|
|
145
174
|
function getKeyringAccountName(params) {
|
|
@@ -289,8 +318,9 @@ class SettingsManager {
|
|
|
289
318
|
}
|
|
290
319
|
async function updateTokenUrl() {
|
|
291
320
|
const tokenUrl = secure(params.get('tokenUrl'), 'token URL');
|
|
321
|
+
const machineLogin = params.has('clientid');
|
|
292
322
|
const renewUrl = !logout && !params.get('skipToken')
|
|
293
|
-
&& (!tokenUrl || tokenUrl.includes('//-/cds') || tokenUrl.includes(
|
|
323
|
+
&& (machineLogin || !tokenUrl || tokenUrl.includes('//-/cds') || !tokenUrl.includes(OAUTH_PATH)
|
|
294
324
|
|| params.get('appUrl') !== loadedSettings.get('appUrl')
|
|
295
325
|
|| params.get('subdomain') !== loadedSettings.get('subdomain'));
|
|
296
326
|
if (renewUrl) {
|
|
@@ -303,7 +333,6 @@ class SettingsManager {
|
|
|
303
333
|
}
|
|
304
334
|
}
|
|
305
335
|
}
|
|
306
|
-
|
|
307
336
|
await updateAppUrl();
|
|
308
337
|
await updateTokenUrl();
|
|
309
338
|
}
|
|
@@ -353,16 +382,9 @@ class SettingsManager {
|
|
|
353
382
|
throw 'Clientsecret cannot be empty';
|
|
354
383
|
}
|
|
355
384
|
}
|
|
356
|
-
async function addKey() {
|
|
357
|
-
params.set('key', await Question.askQuestion('key: ', undefined, true));
|
|
358
|
-
console.log();
|
|
359
|
-
if (params.get('key') === '') {
|
|
360
|
-
throw 'Key cannot be empty';
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
385
|
async function addPasscode() {
|
|
364
386
|
if (!params.has('passcodeUrl')) {
|
|
365
|
-
const passcodeUrl = await
|
|
387
|
+
const passcodeUrl = (await fetchAuthMetadata(params))?.passcode_url;
|
|
366
388
|
if (passcodeUrl) {
|
|
367
389
|
params.set('passcodeUrl', passcodeUrl);
|
|
368
390
|
}
|
|
@@ -440,15 +462,8 @@ class SettingsManager {
|
|
|
440
462
|
return;
|
|
441
463
|
}
|
|
442
464
|
|
|
443
|
-
if (params.get('clientid') && !logout) {
|
|
444
|
-
|
|
445
|
-
if (params.get('key') === 'ask') {
|
|
446
|
-
params.delete('key');
|
|
447
|
-
await addKey();
|
|
448
|
-
}
|
|
449
|
-
} else if (!params.has('clientsecret')) {
|
|
450
|
-
await addClientSecret();
|
|
451
|
-
}
|
|
465
|
+
if (params.get('clientid') && !logout && !params.has('clientsecret') && !params.has('key')) {
|
|
466
|
+
await addClientSecret();
|
|
452
467
|
}
|
|
453
468
|
|
|
454
469
|
await addAuthSettings();
|
|
@@ -714,5 +729,7 @@ class SettingsManager {
|
|
|
714
729
|
module.exports = {
|
|
715
730
|
SettingsManager,
|
|
716
731
|
other,
|
|
717
|
-
notifyLogin
|
|
732
|
+
notifyLogin,
|
|
733
|
+
OAUTH_PATH,
|
|
734
|
+
OAUTH_PATH_LEGACY_TOKEN
|
|
718
735
|
};
|
package/lib/mtx/tenant_action.js
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const CLIENT_ID_ENV = 'CDS_LOGIN_CLIENT_ID';
|
|
2
|
+
const CLIENT_SECRET_ENV = 'CDS_LOGIN_CLIENT_SECRET';
|
|
3
|
+
const CLIENT_CERT_ENV = 'CDS_LOGIN_CLIENT_CERT';
|
|
4
|
+
const CLIENT_KEY_ENV = 'CDS_LOGIN_CLIENT_KEY';
|
|
5
|
+
|
|
6
|
+
function clientId(inlineId) {
|
|
7
|
+
const clientid = inlineId ?? process.env[CLIENT_ID_ENV];
|
|
8
|
+
if (!clientid) {
|
|
9
|
+
throw `Client ID required: pass it as --client <id> or via the ${CLIENT_ID_ENV} environment variable.`;
|
|
10
|
+
}
|
|
11
|
+
return clientid;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function splitFirst(value, separator) {
|
|
15
|
+
const i = value.indexOf(separator);
|
|
16
|
+
return i === -1 ? [value, undefined] : [value.slice(0, i), value.slice(i + separator.length)];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolve `--client` credentials, each part falling back to the environment
|
|
21
|
+
* (`CDS_LOGIN_CLIENT_ID`/`CDS_LOGIN_CLIENT_SECRET`). Splits on the first colon only;
|
|
22
|
+
* an empty client ID (`:secret`) is rejected.
|
|
23
|
+
* @returns {{ clientid?: string, clientsecret?: string }}
|
|
24
|
+
*/
|
|
25
|
+
function clientCredentials(clientOption) {
|
|
26
|
+
if (!clientOption) {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
const [id, secret] = typeof clientOption === 'string' ? splitFirst(clientOption, ':') : [];
|
|
30
|
+
return { clientid: clientId(id), clientsecret: secret ?? process.env[CLIENT_SECRET_ENV] };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Resolve `--mtls` credentials: client ID inline or from `CDS_LOGIN_CLIENT_ID`,
|
|
35
|
+
* certificate and key only from `CDS_LOGIN_CLIENT_CERT`/`CDS_LOGIN_CLIENT_KEY`.
|
|
36
|
+
* An inline `:key` is rejected.
|
|
37
|
+
* @returns {{ clientid?: string, certificate?: string, key?: string }}
|
|
38
|
+
*/
|
|
39
|
+
function clientCertificate(mtlsOption) {
|
|
40
|
+
if (!mtlsOption) {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
if (typeof mtlsOption === 'string' && mtlsOption.includes(':')) {
|
|
44
|
+
throw `The client key cannot be passed on the command line. Pass only the client ID as --mtls <id>, and provide the certificate and key via the ${CLIENT_CERT_ENV} and ${CLIENT_KEY_ENV} environment variables.`;
|
|
45
|
+
}
|
|
46
|
+
const id = typeof mtlsOption === 'string' ? mtlsOption : undefined;
|
|
47
|
+
const certificate = process.env[CLIENT_CERT_ENV];
|
|
48
|
+
const key = process.env[CLIENT_KEY_ENV];
|
|
49
|
+
if (!certificate || !key) {
|
|
50
|
+
throw `Client certificate and key required: set the ${CLIENT_CERT_ENV} and ${CLIENT_KEY_ENV} environment variables.`;
|
|
51
|
+
}
|
|
52
|
+
return { clientid: clientId(id), certificate, key };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = {
|
|
56
|
+
clientCredentials,
|
|
57
|
+
clientCertificate,
|
|
58
|
+
CLIENT_ID_ENV,
|
|
59
|
+
CLIENT_SECRET_ENV,
|
|
60
|
+
CLIENT_CERT_ENV,
|
|
61
|
+
CLIENT_KEY_ENV
|
|
62
|
+
};
|
|
@@ -4,6 +4,49 @@
|
|
|
4
4
|
- The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
5
5
|
- This project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## Version 10.1.0 - 2026-09-02
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- New `sqlite:memory` preset for `cds.requires.db` runs an in-memory SQLite database, now also in production
|
|
12
|
+
- New `hana-serverless` kind for `cds.requires.db` connects to a HANA Cloud serverless instance (Beta)
|
|
13
|
+
- Node.js-native `fetch` client now resolves named destinations through the SAP BTP Destination service for `NoAuthentication`, `BasicAuthentication`, and `OAuth2ClientCredentials` (proxy type `Internet` only) (beta)
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- `cds.spawn` uses dedicated, short-lived transaction for fetching extended models
|
|
18
|
+
- Messaging webhook endpoints are no longer registered anonymously in production when neither `cds.requires.auth` nor `cds.requires.messaging.xsuaa` is configured.
|
|
19
|
+
- UCL tenant mapping notification handlers will be scheduled as `tenant: receiverTenant` instead of anonymously
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- `@assert` messages using `{i18n>...}` placeholders are no longer mistaken for structured `error(...)` results
|
|
24
|
+
- JSON `$batch` requests now reject invalid `atomicityGroup` identifiers
|
|
25
|
+
- Scheduling a task via `srv.schedule(...).after(<delay>)` or `.every(<interval>)` with a delay above Node's 32-bit `setTimeout` limit (~24.8 days, e.g. `.after('30d')`) no longer emits a `TimeoutOverflowWarning` or fires almost immediately. Such delays are now armed as chained timers, so the task still fires at its exact scheduled time.
|
|
26
|
+
- OData requests with unknown key names in the key predicate (e.g. `Books(ID=1,sad=2)`) are now rejected with `400 Bad Request` instead of causing a `500` error
|
|
27
|
+
- `cds-rc.js` schema now has a `$id` so `jsonschema` 1.5 resolves internal `$ref`s correctly
|
|
28
|
+
- REST `HEAD` responses now report byte-accurate `Content-Length` headers for Unicode JSON payloads
|
|
29
|
+
- Query properties like `limit` and `recurse` are no longer lost when forwarded to remote services
|
|
30
|
+
- `cds.requires.<name>.service = <name>` (a required service naming itself) no longer fails with a spurious `conflicts with` error on server start
|
|
31
|
+
- `UPDATE` and `DELETE` remote-service-queries, that use `.where` instead of a key to specify their subject, will throw
|
|
32
|
+
- Navigating to or creating under a parent protected by an instance-based `@restrict.where` now returns `404` when the parent is not readable
|
|
33
|
+
- A static `@restrict.where` on a collection-bound action is now enforced, rejecting callers who do not satisfy the condition — previously it was silently ignored
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
36
|
+
|
|
37
|
+
- UCL GraphQL interface adapter as dead code after UCL deprecated the GraphQL on-boarding flow
|
|
38
|
+
|
|
39
|
+
## Version 10.0.6 - 2026-08-18
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- `$apply=filter(...)/groupby(...)/aggregate($count)` with non-UTC `DateTimeOffset` filter values now returns correct results on HANA
|
|
44
|
+
- Generated index page now uses relative hrefs, fixing links when served behind an approuter with a path prefix
|
|
45
|
+
- `$batch` sub-requests returned an empty body and headers when `@opentelemetry/instrumentation-express` (or any instrumentation that wraps Express handlers to arity 2) was active
|
|
46
|
+
- `cds.xt.ModelProviderService` can be served in mtx sidecar
|
|
47
|
+
- Modeled service events were not properly ignored in OData adapter
|
|
48
|
+
- Generic handler for draft-specific actions not correctly being bound to the transaction context
|
|
49
|
+
|
|
7
50
|
## Version 10.0.5 - 2026-07-28
|
|
8
51
|
|
|
9
52
|
### Fixed
|
|
@@ -143,6 +186,20 @@
|
|
|
143
186
|
- Internal `_initial` phase
|
|
144
187
|
- Removed support for `async activate()` functions returned from `cds-plugin.js` modules, which was never documented, and deprecated since Oct 2023. If you need to run async code during plugin loading, just return a promise as default export.
|
|
145
188
|
|
|
189
|
+
## Version 9.9.3 - 2026-07-21
|
|
190
|
+
|
|
191
|
+
### Fixed
|
|
192
|
+
|
|
193
|
+
- Fixed context propagation for local app and remote service calls
|
|
194
|
+
|
|
195
|
+
## Version 9.9.2 - 2026-06-29
|
|
196
|
+
|
|
197
|
+
### Fixed
|
|
198
|
+
|
|
199
|
+
- `srv.model` propagation in extensibility scenarios
|
|
200
|
+
- Restored `err.target:'in'` during draft activation with `cds.fiori.draft_messages=false` to avoid the `MULTIPLE_ERRORS` popup
|
|
201
|
+
- `resolveView` prioritizes the original foreign key `parent_ID` if exposed multiple times, e.g., through `*, parent.ID as parentId`
|
|
202
|
+
|
|
146
203
|
## Version 9.9.1 - 2026-04-29
|
|
147
204
|
|
|
148
205
|
### Fixed
|
|
@@ -2,7 +2,8 @@ const cds = require('../lib')
|
|
|
2
2
|
const { find, path, fs } = cds.utils
|
|
3
3
|
|
|
4
4
|
const odata = endpoint => endpoint.kind.startsWith('odata')
|
|
5
|
-
const
|
|
5
|
+
const rel = path => path.replace(/^\//, '')
|
|
6
|
+
const metadata = endpoint => odata(endpoint) ? `<span>/</span> <a href="${rel(endpoint.path)}/$metadata"><span>$metadata</span></a>` : ``
|
|
6
7
|
const asHtmlId = s => s.replace(/[.:/$&?@]/g, '_').toLowerCase()
|
|
7
8
|
|
|
8
9
|
module.exports = { get html(){
|
|
@@ -22,12 +23,12 @@ module.exports = { get html(){
|
|
|
22
23
|
.map (({srv, endpoint}) => `
|
|
23
24
|
<div id="${asHtmlId(srv.name)}-${endpoint.kind}">
|
|
24
25
|
<h3 class="header">
|
|
25
|
-
<a href="${endpoint.path}"><span>${endpoint.path}</span></a>${metadata(endpoint)} ${_moreLinks(srv, endpoint, undefined, false)}
|
|
26
|
+
<a href="${rel(endpoint.path)}"><span>${endpoint.path}</span></a>${metadata(endpoint)} ${_moreLinks(srv, endpoint, undefined, false)}
|
|
26
27
|
</h3>
|
|
27
28
|
<ul>${_entities_in(srv).map (e => `
|
|
28
29
|
<li id="${asHtmlId(srv.name)}-${endpoint.kind}-${asHtmlId(e)}">
|
|
29
30
|
<div>
|
|
30
|
-
<a href="${endpoint.path}/${e.replace(/\./g, '_')}"><span>${e}</span></a>
|
|
31
|
+
<a href="${rel(endpoint.path)}/${e.replace(/\./g, '_')}"><span>${e}</span></a>
|
|
31
32
|
</div>
|
|
32
33
|
${_moreLinks(srv, endpoint, e)}
|
|
33
34
|
</li>`).join('')}
|
|
@@ -35,7 +36,7 @@ module.exports = { get html(){
|
|
|
35
36
|
<ul>${_operations_in(srv).map (e => `
|
|
36
37
|
<li id="${asHtmlId(srv.name)}-${endpoint.kind}-${asHtmlId(e.name)}" class="operation">
|
|
37
38
|
<div>
|
|
38
|
-
<a href="${endpoint.path}/${e.name}" title="${endpoint.path}/${e.name}"><span>${e.name} ${e.params}</span></a>
|
|
39
|
+
<a href="${rel(endpoint.path)}/${e.name}" title="${endpoint.path}/${e.name}"><span>${e.name} ${e.params}</span></a>
|
|
39
40
|
</div>
|
|
40
41
|
</li>`).join('')}
|
|
41
42
|
</ul>
|
|
@@ -17,7 +17,7 @@ class Minifier {
|
|
|
17
17
|
if (skip_unused === 'services') o.services = 'all'
|
|
18
18
|
const all = new Map (Object.entries( this.defs ??= csn.definitions || {} ))
|
|
19
19
|
const children = (n,fn,pre=n+'.') => { for (let [n,d] of all) if (n.startsWith(pre)) fn (n,d) }
|
|
20
|
-
const
|
|
20
|
+
const _keep = (d,kinds) => d.kind in kinds || d['@cds.minify'] === false
|
|
21
21
|
const _own = o.keep ??= { entity:1, type:1, event:1, action:1, function:1 }
|
|
22
22
|
const _ext = { event:1, action:1, function:1 }
|
|
23
23
|
const kept = this.kept = csn.definitions = {}
|
|
@@ -31,16 +31,23 @@ class Minifier {
|
|
|
31
31
|
}
|
|
32
32
|
} else {
|
|
33
33
|
// Otherwise first mark all external services and their children as initially skipped
|
|
34
|
-
for (let [s,d] of all) if (d.kind === 'service' &&
|
|
35
|
-
|
|
34
|
+
for (let [s,d] of all) if (d.kind === 'service' && d['@cds.external'] >= 2) {
|
|
35
|
+
if (d['@cds.minify'] === false) continue
|
|
36
|
+
else skipped[s] = 0
|
|
37
|
+
children (s, (c,d) => _keep(d,_ext) ? this.keep(c,d,s) : skipped[c] = s) // used later on in this.keep()
|
|
36
38
|
}
|
|
37
39
|
// Then keep all own services and their children
|
|
38
|
-
for (let [s,d] of all) if (d.kind === 'service'
|
|
39
|
-
|
|
40
|
+
for (let [s,d] of all) if (d.kind === 'service') {
|
|
41
|
+
if (s in skipped) continue
|
|
42
|
+
else this.keep(s,d)
|
|
43
|
+
children (s, (c,d) => _keep(d,_own) ? this.keep(c,d) : skipped[c] = 0)
|
|
40
44
|
}
|
|
41
45
|
// Also keep remaining non-service entities
|
|
42
|
-
for (let [e,d] of all) if (d.kind === 'entity' ||
|
|
43
|
-
e in
|
|
46
|
+
for (let [e,d] of all) if (d.kind === 'entity' || d['@cds.minify'] === false) {
|
|
47
|
+
if (e in skipped || e in kept) continue //> skip already kept or skipped definitions
|
|
48
|
+
if (d['@cds.persistence.skip'] === 'if-unused') continue //> skip initially, they are kept by reference if used
|
|
49
|
+
if (e.endsWith('.texts')) continue //> skip texts entities, they are kept by reference if used
|
|
50
|
+
else this.keep(e,d)
|
|
44
51
|
}
|
|
45
52
|
}
|
|
46
53
|
|
|
@@ -106,7 +113,4 @@ class Minifier {
|
|
|
106
113
|
|
|
107
114
|
}
|
|
108
115
|
|
|
109
|
-
const _skip_service = (s,d) => (d['@cds.external'] >= 2) && _can_minify(d)
|
|
110
|
-
const _skip_entity = (e,d) => (d['@cds.external'] >= 2 || d['@cds.persistence.skip'] === 'if-unused') && _can_minify(d) || e.endsWith('.texts')
|
|
111
|
-
const _can_minify = d => d['@cds.minify'] !== false
|
|
112
116
|
exports.Minifier = Minifier
|
|
@@ -524,7 +524,7 @@ function _merge (dst, src, _profiles) {
|
|
|
524
524
|
}
|
|
525
525
|
else if (v !== undefined) dst[p] = v
|
|
526
526
|
}
|
|
527
|
-
if (profiled.length >
|
|
527
|
+
if (profiled.length > 1 && !_profiles.has('production')) {
|
|
528
528
|
const profiles = Array.from(_profiles)
|
|
529
529
|
profiled.sort((a,b) => profiles.indexOf(b.profile) - profiles.indexOf(a.profile))
|
|
530
530
|
}
|
|
@@ -24,7 +24,8 @@ exports = module.exports = {
|
|
|
24
24
|
_resolved() {
|
|
25
25
|
const requires = { __proto__: this }
|
|
26
26
|
for (let [name,e] of Object.entries(this)) if (e.service) {
|
|
27
|
-
if (
|
|
27
|
+
if (e.service === name) continue
|
|
28
|
+
if (!(e.service in requires) || e.override) requires[e.service] = { ...e, name }
|
|
28
29
|
else throw new Error (`Config 'cds.requires.${name}.service = ${e.service}' conflicts with 'cds.requires.${e.service}'`)
|
|
29
30
|
}
|
|
30
31
|
return requires
|
|
@@ -127,9 +128,10 @@ const _db_data = {
|
|
|
127
128
|
|
|
128
129
|
const _databases = {
|
|
129
130
|
|
|
130
|
-
"db-defaults": { kind: 'sql', ..._db_data }, //> applied only for cds.requires.db: true
|
|
131
|
-
|
|
131
|
+
"db-defaults": { kind: 'sql', ..._db_data }, //> applied only for cds.requires.db: true
|
|
132
|
+
|
|
132
133
|
"sql": {
|
|
134
|
+
// REVISIT: should be '[development]': { kind: 'sqlite:memory' },
|
|
133
135
|
'[development]': { kind: 'sqlite', credentials: { url: ':memory:' } },
|
|
134
136
|
'[production]': { kind: 'hana' },
|
|
135
137
|
},
|
|
@@ -138,22 +140,33 @@ const _databases = {
|
|
|
138
140
|
impl: '@cap-js/sqlite', credentials: { url: 'db.sqlite' },
|
|
139
141
|
..._db_data,
|
|
140
142
|
},
|
|
143
|
+
"sqlite:memory": {
|
|
144
|
+
kind: 'sqlite', credentials: { url: ':memory:' },
|
|
145
|
+
},
|
|
141
146
|
"hana": {
|
|
142
147
|
impl: '@cap-js/hana',
|
|
143
148
|
..._db_data,
|
|
144
149
|
},
|
|
145
150
|
"hana-cloud": {
|
|
146
|
-
kind: 'hana',
|
|
151
|
+
kind: 'hana',
|
|
152
|
+
"deploy-format": "hdbtable", //> REVISIT: still needed?
|
|
153
|
+
},
|
|
154
|
+
"hana-serverless": {
|
|
155
|
+
kind: 'hana',
|
|
156
|
+
serverless: true,
|
|
157
|
+
vcap: { label: 'hana-cloud', plan: 'serverless' },
|
|
158
|
+
dialect: "hana", //> REVISIT: why needed?
|
|
159
|
+
"deploy-format": "hdbtable", //> REVISIT: still needed?
|
|
147
160
|
},
|
|
161
|
+
|
|
148
162
|
"sql-mt": { // For compatibility only
|
|
149
163
|
'[development]': { kind: 'sqlite' },
|
|
150
164
|
'[production]': { kind: 'hana-mt' },
|
|
151
165
|
},
|
|
152
166
|
"hana-mt": { // For compatibility only
|
|
153
|
-
kind: 'hana',
|
|
154
|
-
"vcap": {
|
|
155
|
-
|
|
156
|
-
}
|
|
167
|
+
kind: 'hana',
|
|
168
|
+
"vcap": { "label": "service-manager" },
|
|
169
|
+
"deploy-format": "hdbtable", //> REVISIT: still needed?
|
|
157
170
|
},
|
|
158
171
|
}
|
|
159
172
|
|
|
@@ -277,12 +290,10 @@ const _messaging = {
|
|
|
277
290
|
const _platform_services = {
|
|
278
291
|
|
|
279
292
|
ucl: {
|
|
280
|
-
model: '@sap/cds/srv/ucl-service.cds'
|
|
293
|
+
model: '@sap/cds/srv/ucl-service.cds'
|
|
281
294
|
// REVISIT: how to configure such that a cds.connect.to('ucl') returns the already initialized UCLService?
|
|
282
295
|
// service: 'UCLService',
|
|
283
296
|
// impl: `${_runtime}/ucl/Service.js`,
|
|
284
|
-
// REVISIT: remove default as only needed for push mode
|
|
285
|
-
vcap: { label: 'xsuaa' }
|
|
286
297
|
},
|
|
287
298
|
|
|
288
299
|
destinations: {
|