@sap/cds 6.1.2 → 6.2.1
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 +92 -8
- package/apis/cds.d.ts +18 -6
- package/apis/connect.d.ts +1 -1
- package/apis/cqn.d.ts +1 -1
- package/apis/log.d.ts +23 -5
- package/apis/ql.d.ts +128 -61
- package/apis/services.d.ts +11 -0
- package/apis/test.d.ts +61 -0
- package/apis/utils.d.ts +15 -0
- package/app/fiori/preview.js +1 -0
- package/bin/build/buildTaskEngine.js +70 -22
- package/bin/build/buildTaskFactory.js +18 -11
- package/bin/build/buildTaskHandler.js +1 -1
- package/bin/build/buildTaskProviderFactory.js +3 -13
- package/bin/build/constants.js +0 -1
- package/bin/build/index.js +14 -6
- package/bin/build/provider/buildTaskHandlerEdmx.js +2 -3
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +2 -2
- package/bin/build/provider/buildTaskHandlerInternal.js +3 -6
- package/bin/build/provider/buildTaskProviderInternal.js +51 -39
- package/bin/build/provider/fiori/index.js +3 -3
- package/bin/build/provider/hana/2migration.js +1 -1
- package/bin/build/provider/hana/index.js +34 -27
- package/bin/build/provider/java/index.js +6 -7
- package/bin/build/provider/mtx/index.js +20 -18
- package/bin/build/provider/mtx/resourcesTarBuilder.js +8 -11
- package/bin/build/provider/mtx-sidecar/index.js +13 -17
- package/bin/build/provider/nodejs/index.js +8 -7
- package/bin/build/util.js +22 -4
- package/bin/cds.js +8 -4
- package/bin/deploy/to-hana/cfUtil.js +53 -18
- package/bin/mtx/in-cds.js +1 -0
- package/bin/serve.js +37 -30
- package/lib/auth/basic-auth.js +33 -0
- package/lib/auth/dummy-auth.js +7 -0
- package/lib/auth/ias-auth.js +2 -0
- package/lib/auth/index.js +31 -0
- package/lib/auth/jwt-auth.js +3 -0
- package/lib/auth/mocked-users.js +72 -0
- package/lib/auth/passport-basic.js +12 -0
- package/lib/auth/passport-digest.js +14 -0
- package/lib/auth/xsuaa-auth.js +3 -0
- package/lib/compile/cds-compile.js +3 -3
- package/lib/compile/to/cdl.js +5 -1
- package/lib/compile/to/edm.js +8 -0
- package/lib/compile/to/gql.js +1 -0
- package/lib/compile/to/json.js +30 -5
- package/lib/compile/to/sql.js +3 -1
- package/lib/core/index.js +5 -1
- package/lib/dbs/cds-deploy.js +36 -6
- package/lib/env/cds-env.js +15 -5
- package/lib/env/cds-requires.js +51 -58
- package/lib/env/defaults.js +1 -0
- package/lib/env/schemas/cds-package.json +4 -0
- package/lib/env/schemas/cds-rc.json +63 -77
- package/lib/i18n/localize.js +16 -5
- package/lib/index.js +9 -4
- package/lib/log/cds-error.js +4 -6
- package/lib/log/cds-log.js +89 -53
- package/lib/log/service/index.js +1 -0
- package/lib/ql/CREATE.js +2 -5
- package/lib/ql/DELETE.js +1 -1
- package/lib/ql/DROP.js +1 -3
- package/lib/ql/INSERT.js +3 -3
- package/lib/ql/Query.js +10 -23
- package/lib/ql/SELECT.js +1 -2
- package/lib/ql/UPDATE.js +2 -2
- package/lib/ql/Whereable.js +7 -15
- package/lib/ql/cds-ql.js +9 -3
- package/lib/req/cds-context.js +11 -3
- package/lib/req/context.js +29 -23
- package/lib/req/locale.js +9 -5
- package/lib/req/request.js +1 -0
- package/lib/req/user.js +2 -1
- package/lib/srv/cds-connect.js +1 -1
- package/lib/srv/cds-serve.js +21 -14
- package/lib/srv/middlewares/cds-context.js +29 -0
- package/lib/srv/middlewares/ctx-model.js +24 -0
- package/lib/srv/middlewares/errors.js +9 -0
- package/lib/srv/middlewares/index.js +22 -0
- package/lib/srv/middlewares/sap-statistics.js +13 -0
- package/lib/srv/middlewares/trace.js +102 -0
- package/lib/srv/protocols/_legacy.js +42 -0
- package/lib/srv/protocols/graphql.js +39 -0
- package/lib/srv/protocols/hcql.js +37 -0
- package/lib/srv/protocols/index.js +86 -0
- package/lib/srv/protocols/odata-v2-proxy.js +3767 -0
- package/lib/srv/protocols/odata-v2.js +26 -0
- package/lib/srv/protocols/odata-v4.js +16 -0
- package/lib/srv/protocols/rest.js +13 -0
- package/lib/srv/srv-api.js +5 -0
- package/lib/srv/srv-models.js +4 -6
- package/lib/utils/axios.js +3 -2
- package/lib/utils/cds-test.js +27 -21
- package/lib/utils/cds-utils.js +19 -20
- package/lib/utils/tar.js +175 -0
- package/libx/_runtime/audit/generic/personal/utils.js +18 -7
- package/libx/_runtime/audit/utils/v2.js +1 -0
- package/libx/_runtime/auth/index.js +4 -0
- package/libx/_runtime/auth/strategies/ias-auth.js +76 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +8 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +15 -4
- 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/orderByToCQN.js +1 -1
- 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/uri/ResourcePathParser.js +9 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriInfo.js +5 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +12 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/BufferedWriter.js +6 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/RequestValidator.js +47 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +0 -2
- package/libx/_runtime/cds-services/services/utils/compareJson.js +3 -1
- package/libx/_runtime/cds-services/util/assert.js +7 -0
- package/libx/_runtime/common/aspects/relation.js +1 -1
- package/libx/_runtime/common/composition/data.js +61 -15
- package/libx/_runtime/common/composition/delete.js +0 -1
- package/libx/_runtime/common/composition/insert.js +0 -1
- package/libx/_runtime/common/composition/tree.js +4 -10
- package/libx/_runtime/common/composition/update.js +44 -21
- package/libx/_runtime/common/generic/auth/capabilities.js +8 -10
- package/libx/_runtime/common/generic/crud.js +1 -2
- package/libx/_runtime/common/generic/etag.js +4 -4
- package/libx/_runtime/common/generic/input.js +21 -6
- package/libx/_runtime/common/generic/paging.js +3 -3
- package/libx/_runtime/common/generic/put.js +7 -4
- package/libx/_runtime/common/generic/sorting.js +4 -4
- package/libx/_runtime/common/generic/temporal.js +3 -6
- package/libx/_runtime/common/i18n/messages.properties +0 -7
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +11 -6
- package/libx/_runtime/common/utils/csn.js +0 -28
- package/libx/_runtime/common/utils/draft.js +8 -1
- package/libx/_runtime/common/utils/path.js +7 -1
- package/libx/_runtime/common/utils/propagateForeignKeys.js +122 -0
- package/libx/_runtime/common/utils/resolveView.js +2 -3
- package/libx/_runtime/common/utils/template.js +2 -3
- package/libx/_runtime/db/data-conversion/post-processing.js +3 -44
- package/libx/_runtime/db/generic/input.js +6 -6
- package/libx/_runtime/db/sql-builder/dataTypes.js +4 -0
- package/libx/_runtime/fiori/generic/activate.js +2 -2
- package/libx/_runtime/fiori/generic/before.js +40 -72
- package/libx/_runtime/fiori/generic/cancel.js +2 -2
- package/libx/_runtime/fiori/generic/delete.js +2 -2
- package/libx/_runtime/fiori/generic/edit.js +2 -2
- package/libx/_runtime/fiori/generic/new.js +3 -5
- package/libx/_runtime/fiori/generic/patch.js +49 -43
- package/libx/_runtime/fiori/generic/prepare.js +2 -2
- package/libx/_runtime/fiori/generic/read.js +27 -37
- package/libx/_runtime/fiori/utils/where.js +4 -2
- package/libx/_runtime/hana/Service.js +1 -3
- package/libx/_runtime/hana/conversion.js +3 -0
- package/libx/_runtime/hana/driver.js +33 -3
- package/libx/_runtime/hana/dynatrace.js +1 -0
- package/libx/_runtime/hana/search2Contains.js +12 -1
- package/libx/_runtime/hana/search2cqn4sql.js +10 -27
- package/libx/_runtime/hana/streaming.js +1 -0
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +4 -2
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +1 -0
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +5 -2
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -0
- package/libx/_runtime/messaging/enterprise-messaging.js +62 -3
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/messaging/redis-messaging.js +1 -0
- package/libx/_runtime/remote/Service.js +2 -2
- package/libx/_runtime/remote/utils/client.js +35 -11
- package/libx/_runtime/remote/utils/data.js +7 -2
- package/libx/_runtime/sqlite/Service.js +18 -7
- package/libx/_runtime/sqlite/conversion.js +3 -0
- package/libx/_runtime/sqlite/convertAssocToOneManaged.js +3 -3
- package/libx/_runtime/sqlite/localized.js +8 -8
- package/libx/odata/afterburner.js +39 -7
- package/libx/odata/cqn2odata.js +6 -3
- package/libx/odata/grammar.pegjs +66 -18
- package/libx/odata/index.js +3 -2
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +2 -0
- package/libx/rest/RestAdapter.js +62 -43
- package/libx/rest/middleware/input.js +2 -3
- package/libx/rest/middleware/parse.js +2 -1
- package/libx/rest/middleware/update.js +1 -1
- package/package.json +2 -2
- package/server.js +5 -4
- package/srv/mtx.cds +1 -1
- package/srv/mtx.js +4 -24
- package/lib/srv/adapters.js +0 -85
- package/lib/utils/resources/index.js +0 -48
- package/lib/utils/resources/tar.js +0 -49
- package/lib/utils/resources/utils.js +0 -11
- package/libx/_runtime/db/utils/propagateForeignKeys.js +0 -93
- package/libx/_runtime/extensibility/activate.js +0 -69
- package/libx/_runtime/extensibility/add.js +0 -50
- package/libx/_runtime/extensibility/addExtension.js +0 -72
- package/libx/_runtime/extensibility/defaults.js +0 -34
- package/libx/_runtime/extensibility/handler/transformREAD.js +0 -121
- package/libx/_runtime/extensibility/handler/transformRESULT.js +0 -51
- package/libx/_runtime/extensibility/handler/transformWRITE.js +0 -64
- package/libx/_runtime/extensibility/linter/allowlist_checker.js +0 -373
- package/libx/_runtime/extensibility/linter/annotations_checker.js +0 -113
- package/libx/_runtime/extensibility/linter/checker_base.js +0 -20
- package/libx/_runtime/extensibility/linter/namespace_checker.js +0 -180
- package/libx/_runtime/extensibility/linter.js +0 -32
- package/libx/_runtime/extensibility/push.js +0 -118
- package/libx/_runtime/extensibility/service.js +0 -38
- package/libx/_runtime/extensibility/token.js +0 -57
- package/libx/_runtime/extensibility/utils.js +0 -131
- package/libx/_runtime/extensibility/validation.js +0 -50
- package/libx/_runtime/extensibility/views.js +0 -12
- package/srv/extensibility-service.cds +0 -59
- package/srv/extensibility-service.js +0 -1
- package/srv/extensions.cds +0 -8
- package/srv/model-provider.cds +0 -61
- package/srv/model-provider.js +0 -143
|
@@ -79,7 +79,7 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
|
-
* Deletes any content that has been created in folder '#this.task.dest/src/gen' by
|
|
82
|
+
* Deletes any content that has been created in folder '#this.task.dest/src/gen' by inplace mode.
|
|
83
83
|
* <br>
|
|
84
84
|
* Note: Content created in staging build will be deleted by the #BuildTaskEngine itself.
|
|
85
85
|
*/
|
|
@@ -91,7 +91,7 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
* Copies all files located at <src> (except HANA
|
|
94
|
+
* Copies all files located at <src> (except HANA artifacts not contained in <db>/src/**) to the folder <dest>.
|
|
95
95
|
* '*.csv' files are read based on the corresponding CDS model file location and copied as flat list into folder '<dest>/src/gen>'.
|
|
96
96
|
*
|
|
97
97
|
* @param {string} src
|
|
@@ -116,22 +116,24 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
116
116
|
})
|
|
117
117
|
await Promise.all(promises)
|
|
118
118
|
|
|
119
|
-
// 2. copy files except *.cds,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
119
|
+
// 2. staging build: copy files except *.cds, .env, default-env.json, ./node_modules/**
|
|
120
|
+
if (this.isStagingBuild()) {
|
|
121
|
+
let blockList = "\\.cds$|\\.csv$|\\.hdbtabledata$"
|
|
122
|
+
blockList += this.hasBuildOption(CONTENT_ENV, false) ? "|\\.env($|\\..*$)" : ""
|
|
123
|
+
blockList += this.hasBuildOption(CONTENT_DEFAULT_ENV_JSON, false) ? "|default-env\\.json$" : ""
|
|
124
|
+
blockList = new RegExp(blockList)
|
|
125
|
+
|
|
126
|
+
await this.copyNativeContent(src, dest, (entry) => {
|
|
127
|
+
if (entry.startsWith(dbSrc)) {
|
|
128
|
+
// entire native content
|
|
129
|
+
return true
|
|
130
|
+
}
|
|
131
|
+
if (fs.statSync(entry).isDirectory()) {
|
|
132
|
+
return !/(\/|\\)node_modules(\/|\\)?$/.test(entry) || this.hasBuildOption(CONTENT_NODE_MODULES, true)
|
|
133
|
+
}
|
|
134
|
+
return !blockList.test(entry)
|
|
135
|
+
})
|
|
136
|
+
}
|
|
135
137
|
}
|
|
136
138
|
|
|
137
139
|
/**
|
|
@@ -145,15 +147,20 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
145
147
|
const dbCsvDir = path.join(src, "csv")
|
|
146
148
|
const dbDataDir = path.join(src, "data")
|
|
147
149
|
const csvDirs = [dbCsvDir, dbDataDir]
|
|
148
|
-
const regex = RegExp('\\.cds$|\\.csv$|\\.hdbtabledata$')
|
|
149
150
|
const regexData = RegExp('\\.csv$|\\.hdbtabledata$')
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
(
|
|
156
|
-
|
|
151
|
+
|
|
152
|
+
if (this.isStagingBuild()) {
|
|
153
|
+
const regex = RegExp('\\.cds$|\\.csv$|\\.hdbtabledata$')
|
|
154
|
+
|
|
155
|
+
await this.copyNativeContent(src, dest, (entry) => {
|
|
156
|
+
if (fs.statSync(entry).isDirectory()) {
|
|
157
|
+
return !/(\/|\\)node_modules(\/|\\)?$/.test(entry)
|
|
158
|
+
}
|
|
159
|
+
return (!regex.test(entry) && entry !== this.env.build.outputfile) ||
|
|
160
|
+
(regexData.test(entry) && !entry.startsWith(dbCsvDir) && !entry.startsWith(dbDataDir))
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
157
164
|
// handle *.csv and *.hdbtabledata located in '<dbSrc>/data' and '<dbSrc>/csv' folder
|
|
158
165
|
const allFiles = csvDirs.reduce((acc, csvDir) => {
|
|
159
166
|
return acc.concat(BuildTaskHandlerInternal._find(csvDir, (entry) => {
|
|
@@ -244,7 +251,7 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
244
251
|
const pluginTypes = new Set([...REQUIRED_PLUGIN_TYPES, ...undeployTypes])
|
|
245
252
|
|
|
246
253
|
// compile to old format (.hdbcds) or new format (.hdbtable / .hdbview)
|
|
247
|
-
const format = this.getBuildOption(DEPLOY_FORMAT) || this.env.
|
|
254
|
+
const format = this.getBuildOption(DEPLOY_FORMAT) || this.env.requires.db?.[DEPLOY_FORMAT] || this.env.hana?.[DEPLOY_FORMAT]
|
|
248
255
|
if (!this.cds.compile.to[format]) {
|
|
249
256
|
return Promise.reject(new Error(`Invalid deploy-format defined: ${format}`))
|
|
250
257
|
}
|
|
@@ -4,7 +4,7 @@ const path = require('path')
|
|
|
4
4
|
const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
|
|
5
5
|
const { isOldJavaStack, BuildError } = require('../../util')
|
|
6
6
|
|
|
7
|
-
const { BUILD_OPTION_OUTPUT_MODE,
|
|
7
|
+
const { BUILD_OPTION_OUTPUT_MODE, ODATA_VERSION_V2, OUTPUT_MODE_RESULT_ONLY, FILE_EXT_CDS, SKIP_ASSERT_COMPILER_V2, CONTENT_LANGUAGE_BUNDLES, CONTENT_DEFAULT_CSN, DEFAULT_CSN_FILE_NAME } = require('../../constants')
|
|
8
8
|
const { INFO } = require('../../buildTaskHandler')
|
|
9
9
|
|
|
10
10
|
const DEFAULT_COMPILE_DEST_FOLDER = path.normalize('src/main/resources/edmx')
|
|
@@ -19,15 +19,15 @@ class JavaModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
19
19
|
const { src, dest } = this.task
|
|
20
20
|
|
|
21
21
|
const odataOptions = {
|
|
22
|
-
version: this.env.
|
|
22
|
+
version: this.env.odata?.version
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
if (await isOldJavaStack([src, this.buildOptions.root])) {
|
|
26
|
-
if (!this._isCompilerV1() && !this.env.
|
|
26
|
+
if (!this._isCompilerV1() && !this.env.build?.[SKIP_ASSERT_COMPILER_V2]) {
|
|
27
27
|
throw new BuildError('CDS compiler version 2 does no longer support the classic CAP Java runtime. It is recommended to migrate to the current CAP Java runtime SDK. See https://cap.cloud.sap/docs/java/migration for more.')
|
|
28
28
|
}
|
|
29
29
|
// default is now v4 and not v2 anymore, so warn and overwrite with v2 if using default
|
|
30
|
-
if (!this.env.for('cds', this.buildOptions.root, false).
|
|
30
|
+
if (!this.env.for('cds', this.buildOptions.root, false).odata?.version) {
|
|
31
31
|
odataOptions.version = ODATA_VERSION_V2
|
|
32
32
|
this.pushMessage('Forcing OData v2 for building though the default is v4. Make sure to define OData v2 in cds configuration.', INFO)
|
|
33
33
|
}
|
|
@@ -53,7 +53,7 @@ class JavaModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
53
53
|
this._result.languageBundles = i18n.bundles
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
if (!this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
|
|
56
|
+
if (this.isStagingBuild() && !this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
|
|
57
57
|
await this._copyNativeContent(src, dest)
|
|
58
58
|
}
|
|
59
59
|
return this._result
|
|
@@ -61,8 +61,7 @@ class JavaModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
61
61
|
|
|
62
62
|
async clean() {
|
|
63
63
|
if (this.isStagingBuild()) {
|
|
64
|
-
|
|
65
|
-
return
|
|
64
|
+
return super.clean()
|
|
66
65
|
}
|
|
67
66
|
this.logger._debug && this.logger.debug(`Deleting build target folder ${this.task.options.compileDest}`)
|
|
68
67
|
await fs.promises.rm(this.task.options.compileDest, { force: true, recursive: true })
|
|
@@ -7,7 +7,7 @@ const { isStreamlinedMtx } = require('../../util')
|
|
|
7
7
|
const ResourcesTarProvider = require('./resourcesTarBuilder')
|
|
8
8
|
|
|
9
9
|
const { BUILD_TASK_HANA, FOLDER_GEN } = require('../../constants')
|
|
10
|
-
const { WARNING } =
|
|
10
|
+
const { WARNING, ERROR } = BuildTaskHandlerEdmx
|
|
11
11
|
const FOLDER_SDC = "sdc"
|
|
12
12
|
const FOLDER_NODE_MODULES = "node_modules"
|
|
13
13
|
const FOLDER_TEMPLATES = "tpl"
|
|
@@ -42,7 +42,7 @@ class StreamlinedMtxBuilder {
|
|
|
42
42
|
async build() {
|
|
43
43
|
// Non-sidecar scenario: model-provider is implemented by the Nodejs app itself (default for Nodejs).
|
|
44
44
|
// Only create resources TAR, compiled CSN(s) are created by the Nodejs app build task.
|
|
45
|
-
const dest = this._handler.env.
|
|
45
|
+
const dest = this._handler.env.build?.target !== "." ? this._handler.task.dest : path.join(this._handler.task.dest, FOLDER_GEN)
|
|
46
46
|
const model = await this._handler.model()
|
|
47
47
|
if (!model) {
|
|
48
48
|
return
|
|
@@ -64,7 +64,7 @@ class ClassicMtxBuilder {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
async build() {
|
|
67
|
-
if (this._handler.env.
|
|
67
|
+
if (this._handler.env.requires.toggles === true) {
|
|
68
68
|
throw new Error("Feature toggles are only supported by Streamlined MTX")
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -78,7 +78,7 @@ class ClassicMtxBuilder {
|
|
|
78
78
|
// custom build tasks for srv and db modules might be defined
|
|
79
79
|
const tenantDbPath = await this._getHanaTenantDbPath()
|
|
80
80
|
|
|
81
|
-
// validate extension
|
|
81
|
+
// validate extension whitelists defined for this SaaS application
|
|
82
82
|
this._validateExtensionAllowLists(model, tenantDbPath)
|
|
83
83
|
|
|
84
84
|
// copy base model sources
|
|
@@ -100,7 +100,7 @@ class ClassicMtxBuilder {
|
|
|
100
100
|
if (i18n) {
|
|
101
101
|
this._handler._result.languageBundles = i18n.bundles
|
|
102
102
|
}
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
// copy native hana content and templates
|
|
105
105
|
await this._copyNativeContent(this._handler.task.src, destSdc, tenantDbPath)
|
|
106
106
|
}
|
|
@@ -114,7 +114,7 @@ class ClassicMtxBuilder {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
async _copyNativeContent(src, dest, tenantDbPath) {
|
|
117
|
-
// copying
|
|
117
|
+
// copying templates
|
|
118
118
|
const tplSrc = path.join(src, FOLDER_TEMPLATES)
|
|
119
119
|
if (fs.existsSync(tplSrc)) {
|
|
120
120
|
const tplDest = path.join(path.dirname(dest), FOLDER_TEMPLATES)
|
|
@@ -122,9 +122,13 @@ class ClassicMtxBuilder {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
if (tenantDbPath) {
|
|
125
|
-
// copy
|
|
125
|
+
// copy native HANA artifacts, e.g. .csv files
|
|
126
126
|
const dbDest = path.resolve(dest, path.relative(this._handler.buildOptions.root, tenantDbPath))
|
|
127
|
-
|
|
127
|
+
if (!src.startsWith(dbDest)) { // ensure valid paths
|
|
128
|
+
await this._copyNativeHanaContent(tenantDbPath, dbDest)
|
|
129
|
+
} else {
|
|
130
|
+
this.pushMessage(`Invalid mtx build task configuration detected, destination folder ${dbDest} must not be nested within 'src' folder`, ERROR)
|
|
131
|
+
}
|
|
128
132
|
}
|
|
129
133
|
}
|
|
130
134
|
|
|
@@ -158,18 +162,16 @@ class ClassicMtxBuilder {
|
|
|
158
162
|
* @param {Object} model - the reflected csn for this SaaS application
|
|
159
163
|
*/
|
|
160
164
|
_validateExtensionAllowLists(model, tenantDbPath) {
|
|
161
|
-
let entityWhitelist = this._handler.env.
|
|
162
|
-
let serviceWhitelist = this._handler.env.
|
|
163
|
-
let extensionAllowlist = this._handler.env.
|
|
165
|
+
let entityWhitelist = this._handler.env.mtx?.["entity-whitelist"]
|
|
166
|
+
let serviceWhitelist = this._handler.env.mtx?.["service-whitelist"]
|
|
167
|
+
let extensionAllowlist = this._handler.env.mtx?.["extension-allowlist"]
|
|
164
168
|
|
|
165
169
|
// for java projects mtx configuration is part of sidecar config
|
|
166
170
|
if (!entityWhitelist && !serviceWhitelist && !extensionAllowlist && tenantDbPath) {
|
|
167
171
|
const dbEnv = this._handler.env.for("cds", tenantDbPath)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
extensionAllowlist = dbEnv.get("mtx.extension-allowlist")
|
|
172
|
-
}
|
|
172
|
+
entityWhitelist = dbEnv.mtx?.["entity-whitelist"]
|
|
173
|
+
serviceWhitelist = dbEnv.mtx?.["service-whitelist"]
|
|
174
|
+
extensionAllowlist = dbEnv.mtx?.["extension-allowlist"]
|
|
173
175
|
}
|
|
174
176
|
|
|
175
177
|
function isValid(e, pattern, nsPattern, kind) {
|
|
@@ -231,14 +233,14 @@ class ClassicMtxBuilder {
|
|
|
231
233
|
// ensure that the hana task is contained even if this mtx task has been executed solely using "cds build --for mtx"
|
|
232
234
|
if (!tasks.find(task => task.for === BUILD_TASK_HANA)) {
|
|
233
235
|
//mtx task might have been executed as separate task
|
|
234
|
-
tasks = this._handler.env.
|
|
236
|
+
tasks = this._handler.env.build?.tasks || []
|
|
235
237
|
if (tasks.length === 0) {
|
|
236
238
|
const provider = new BuildTaskProviderInternal(this._handler.cds, this._handler.logger)
|
|
237
239
|
await provider.lookupTasks(tasks, this._handler.buildOptions)
|
|
238
240
|
}
|
|
239
241
|
}
|
|
240
242
|
// the SaaS app might use a tenant aware db as well as a shared db deployed using static hdi-deployer
|
|
241
|
-
// pick the hana build task
|
|
243
|
+
// pick the hana build task referring to the tenant aware db - the src path has to be contained in this build task's model options
|
|
242
244
|
const hanaDbPaths = tasks.filter(task => task.for === BUILD_TASK_HANA).map(hanaTask => path.resolve(this._handler.buildOptions.root, hanaTask.src || this._handler.env.folders.db))
|
|
243
245
|
let tenantDbPath = hanaDbPaths.find(hanaDbPath => hanaDbPaths.length === 1 || modelPaths.some(modelPath => path.dirname(modelPath) === hanaDbPath))
|
|
244
246
|
|
|
@@ -22,12 +22,9 @@ class ResourcesTarBuilder {
|
|
|
22
22
|
await this.writeTarFile(files, root, path.join(dest, DEFAULT_TAR_NAME))
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
async writeTarFile(files, root, tarFile) {
|
|
26
|
-
const {
|
|
27
|
-
|
|
28
|
-
if (buffer) {
|
|
29
|
-
await this.handler.write(buffer).to(tarFile)
|
|
30
|
-
}
|
|
25
|
+
async writeTarFile(files, root, tarFile) { // REVISIT: eliminate this anti-pattern helper
|
|
26
|
+
const { tar } = require('../../../../lib').utils
|
|
27
|
+
await tar.czfd (tarFile, root, files) // REVISIT: tar.czfd was created for this case only -> it ensures the target's dir exists
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
async _getResources(model) {
|
|
@@ -45,10 +42,10 @@ class ResourcesTarBuilder {
|
|
|
45
42
|
|
|
46
43
|
/**
|
|
47
44
|
* Reads all resources for HANA deployment - 'db/src/**' and 'db/undeploy.json'.
|
|
48
|
-
* - CSV files, native HANA
|
|
45
|
+
* - CSV files, native HANA artifacts, generated HANA artifacts, undeploy.json
|
|
49
46
|
* See '../../../../lib.deploy'
|
|
50
47
|
* Note: the hana build task has already been executed
|
|
51
|
-
* @param {string} root
|
|
48
|
+
* @param {string} root
|
|
52
49
|
* @returns an array containing all resources
|
|
53
50
|
*/
|
|
54
51
|
_getHanaResources(root) {
|
|
@@ -65,9 +62,9 @@ class ResourcesTarBuilder {
|
|
|
65
62
|
|
|
66
63
|
/**
|
|
67
64
|
* Reads all resources for Sqlite deployment - see '../../../../lib.deploy'
|
|
68
|
-
*
|
|
69
|
-
* @param {object} model
|
|
70
|
-
* @returns
|
|
65
|
+
*
|
|
66
|
+
* @param {object} model
|
|
67
|
+
* @returns
|
|
71
68
|
*/
|
|
72
69
|
async _getSqliteResources(model) {
|
|
73
70
|
const { resources } = this.handler.cds.deploy
|
|
@@ -4,7 +4,7 @@ const { FOLDER_GEN, DEFAULT_CSN_FILE_NAME } = require('../../constants')
|
|
|
4
4
|
const NodeCfModuleBuilder = require('../nodejs')
|
|
5
5
|
const ResourcesTarProvider = require('../mtx/resourcesTarBuilder')
|
|
6
6
|
const { INFO, ERROR } = NodeCfModuleBuilder
|
|
7
|
-
const { relativePaths, BuildError } = require('../../util')
|
|
7
|
+
const { relativePaths, BuildError, resolveRequiredSapModels } = require('../../util')
|
|
8
8
|
|
|
9
9
|
const DEFAULT_MAIN_FOLDER = "_main"
|
|
10
10
|
|
|
@@ -47,7 +47,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
47
47
|
const model = this._compileSidecarSync(sidecarEnv)
|
|
48
48
|
await this.compileToJson(model, path.join(destSidecarSrc, DEFAULT_CSN_FILE_NAME))
|
|
49
49
|
await this.collectLanguageBundles(model, destSidecarSrc)
|
|
50
|
-
await this.
|
|
50
|
+
await this.copyProjectRootContent(this.task.src, destSidecar)
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
@@ -56,15 +56,15 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
56
56
|
*/
|
|
57
57
|
async _buildMainApp(sidecarEnv) {
|
|
58
58
|
if (sidecarEnv.requires['cds.xt.ModelProviderService']?.kind !== 'in-sidecar') {
|
|
59
|
-
throw new
|
|
59
|
+
throw new BuildError("CDS build failed", "Invalid MTX sidecar configuration - \"cds.xt.ModelProviderService\": \"in-sidecar\" missing.")
|
|
60
60
|
}
|
|
61
61
|
let main = sidecarEnv.requires['cds.xt.ModelProviderService']?.root
|
|
62
|
-
const profiles = this.env.
|
|
62
|
+
const profiles = this.env.profiles || []
|
|
63
63
|
|
|
64
64
|
if (!profiles.includes("production") && !profiles.includes("prod")) {
|
|
65
65
|
main = DEFAULT_MAIN_FOLDER
|
|
66
66
|
// root should represent the production use case and not development
|
|
67
|
-
this.pushMessage(`
|
|
67
|
+
this.pushMessage(`MTX sidecar build results are created in folder '${main}'. Enable 'production' or 'prod' profile if the folder is configured differently.`, INFO)
|
|
68
68
|
}
|
|
69
69
|
const destRoot = this.task.dest
|
|
70
70
|
const destMain = path.join(destRoot, main)
|
|
@@ -98,21 +98,17 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
98
98
|
const modelFilePaths = this.cds.resolve(modelPaths)
|
|
99
99
|
|
|
100
100
|
if (!modelFilePaths || modelFilePaths.length === 0) {
|
|
101
|
-
throw new BuildError("No
|
|
102
|
-
}
|
|
103
|
-
// candidate for strict mode support
|
|
104
|
-
// entries for @sap/** model paths are missing if the module hasn't been installed, e.g. @sap/cds-mtxs
|
|
105
|
-
const missingModels = modelPaths.filter(p => {
|
|
106
|
-
if (p.startsWith('@sap/')) {
|
|
107
|
-
const files = this.cds.resolve(p)
|
|
108
|
-
return !files || files.length === 0
|
|
109
|
-
}
|
|
110
|
-
})
|
|
111
|
-
if (missingModels.length > 0) {
|
|
112
|
-
this.pushMessage(`Some model paths could not be resolved. Make sure the npm modules have been installed: ${missingModels.join(', ')}`, ERROR)
|
|
101
|
+
throw new BuildError("No CDS service models found in MTX sidecar. Make sure required npm modules are installed.")
|
|
113
102
|
}
|
|
114
103
|
this._logger._debug && this._logger.debug(`sidecar model: ${relativePaths(this.buildOptions.root, modelFilePaths).join(", ")}`)
|
|
115
104
|
|
|
105
|
+
// check whether all models belonging to the @sap namespace can be resolved
|
|
106
|
+
const unresolved = resolveRequiredSapModels(this.cds, modelPaths)
|
|
107
|
+
if (unresolved.length > 0) {
|
|
108
|
+
// log error, but don't fail
|
|
109
|
+
this.pushMessage(`CDS service models [${unresolved.join(', ')}] required by MTX sidecar cannot be resolved. Make sure to install the missing npm modules.`, ERROR)
|
|
110
|
+
}
|
|
111
|
+
|
|
116
112
|
// synchronous compilation
|
|
117
113
|
return this.cds.load(modelFilePaths, { sync: true, ...this.options() })
|
|
118
114
|
} finally {
|
|
@@ -2,7 +2,7 @@ const fs = require('fs')
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
|
|
4
4
|
const { BuildError } = require('../../util')
|
|
5
|
-
const { BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY,
|
|
5
|
+
const { BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY, ODATA_VERSION_V2, FOLDER_GEN, BUILD_NODEJS_EDMX_GENERAION, EDMX_GENERATION,
|
|
6
6
|
SKIP_PACKAGE_JSON_GENERATION, CONTENT_EDMX, CONTENT_PACKAGE_JSON, CONTENT_PACKAGELOCK_JSON, CONTENT_NPMRC, CONTENT_CDSRC_JSON, CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, SEMVER_REGEX } = require('../../constants')
|
|
7
7
|
const { WARNING, ERROR } = BuildTaskHandlerEdmx
|
|
8
8
|
|
|
@@ -29,7 +29,7 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
options() {
|
|
32
|
-
const options = super.options(), {cds} = this
|
|
32
|
+
const options = super.options(), { cds } = this
|
|
33
33
|
if (cds.requires.extensibility || cds.requires.toggles) options.flavor = 'xtended'
|
|
34
34
|
return options
|
|
35
35
|
}
|
|
@@ -38,7 +38,7 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
38
38
|
const destSrv = this.isStagingBuild() ? this.destSrv : path.resolve(this.destSrv, this.env.folders.srv)
|
|
39
39
|
const destRoot = this.isStagingBuild() ? this.task.dest : this.destSrv
|
|
40
40
|
|
|
41
|
-
if (this.env.
|
|
41
|
+
if (this.env.odata?.version === ODATA_VERSION_V2) {
|
|
42
42
|
// log warning as nodejs is only supporting odata version V4
|
|
43
43
|
this.pushMessage("OData v2 is not supported by node runtime. Make sure to define OData v2 in cds configuration.", WARNING)
|
|
44
44
|
}
|
|
@@ -58,7 +58,7 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
58
58
|
await this.compileToEdmx(m, this.destSrv)
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
if (!this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
|
|
61
|
+
if (this.isStagingBuild() && !this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
|
|
62
62
|
const srcSrv = this.task.src === this.buildOptions.root ? path.resolve(this.task.src, this.env.folders.srv) : this.task.src
|
|
63
63
|
await this._copyNativeContent(this.buildOptions.root, srcSrv, destRoot, destSrv)
|
|
64
64
|
}
|
|
@@ -80,11 +80,11 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
80
80
|
const filesFilter = await this._copySrvContent(srcSrv, destRoot, destSrv)
|
|
81
81
|
|
|
82
82
|
// project/* -> 'gen/srv/*'
|
|
83
|
-
await this.
|
|
83
|
+
await this.copyProjectRootContent(srcRoot, destRoot, (entry) => !filesFilter.includes(path.basename(entry)))
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
* Copy files from the given <em>src</em>' folder (e.g. 'project/srv') to either <em>destRoot</em> (e.g. 'project/gen/srv')
|
|
87
|
+
* Copy files for nodejs staging builds from the given <em>src</em>' folder (e.g. 'project/srv') to either <em>destRoot</em> (e.g. 'project/gen/srv')
|
|
88
88
|
* or <em>destSrv</em> (e.g. 'project/gen/srv/srv') folders according to the file semantics.
|
|
89
89
|
* Files with project semantics like 'package.json' or '.npmrc' file are copied to <em>destRoot</em> while others like '.js' service handlers
|
|
90
90
|
* are copied to <em>destSrv</em>.
|
|
@@ -127,6 +127,7 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
127
127
|
return this.copy(path.join(src, fileName)).to(path.join(destRoot, fileName))
|
|
128
128
|
}
|
|
129
129
|
}))
|
|
130
|
+
|
|
130
131
|
return srvRootFileNames
|
|
131
132
|
}
|
|
132
133
|
|
|
@@ -137,7 +138,7 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
137
138
|
* @param {*} dest
|
|
138
139
|
* @param {*} filter - copy file if filter function returns true
|
|
139
140
|
*/
|
|
140
|
-
async
|
|
141
|
+
async copyProjectRootContent(src, dest, filter) {
|
|
141
142
|
let { folders = ['i18n'] } = this.env.i18n
|
|
142
143
|
folders.push('handlers')
|
|
143
144
|
folders = folders.map(folder => path.join(src, folder))
|
package/bin/build/util.js
CHANGED
|
@@ -127,17 +127,34 @@ function isStreamlinedMtx(cds) {
|
|
|
127
127
|
if (hasOptionValue(process.env.OLD_MTX, true)) {
|
|
128
128
|
return false
|
|
129
129
|
}
|
|
130
|
-
return (cds.env.
|
|
130
|
+
return (cds.env.requires.toggles
|
|
131
131
|
|| cds.env.requires['cds.xt.ModelProviderService']
|
|
132
|
-
|| (typeof cds.env.
|
|
132
|
+
|| (typeof cds.env.requires.multitenancy === "object")
|
|
133
133
|
) && !(() => { try { return cds.mtx } catch (e) { /**/ } })()
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Returns a list of fully qualified model names belonging to the '@sap' namespace that cannot be resolved.
|
|
138
|
+
* E.g. the npm module might NOT be installed.
|
|
139
|
+
* @param {*} cds
|
|
140
|
+
* @param {Array} modelPaths
|
|
141
|
+
* @returns {Array}
|
|
142
|
+
*/
|
|
143
|
+
function resolveRequiredSapModels(cds, modelPaths) {
|
|
144
|
+
return modelPaths.filter(p => {
|
|
145
|
+
if (p.startsWith('@sap/')) {
|
|
146
|
+
const files = cds.resolve(p)
|
|
147
|
+
return !files || files.length === 0
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
136
152
|
class BuildMessage extends Error {
|
|
137
|
-
constructor(message, severity) {
|
|
153
|
+
constructor(message, severity = SEVERITY_ERROR) {
|
|
138
154
|
super(message)
|
|
139
155
|
this.name = "BuildMessage"
|
|
140
156
|
this.severity = severity
|
|
157
|
+
this.stack = ""
|
|
141
158
|
}
|
|
142
159
|
toString() {
|
|
143
160
|
return this.severity + ": " + this.message
|
|
@@ -148,7 +165,7 @@ class BuildError extends BuildMessage {
|
|
|
148
165
|
constructor(message, messages = []) {
|
|
149
166
|
super(message, SEVERITY_ERROR)
|
|
150
167
|
this.name = "BuildError"
|
|
151
|
-
this.messages = messages
|
|
168
|
+
this.messages = Array.isArray(messages) ? messages : [messages]
|
|
152
169
|
}
|
|
153
170
|
|
|
154
171
|
// for compatibility reasons
|
|
@@ -170,6 +187,7 @@ module.exports = {
|
|
|
170
187
|
hasOptionValue,
|
|
171
188
|
relativePaths,
|
|
172
189
|
isStreamlinedMtx,
|
|
190
|
+
resolveRequiredSapModels,
|
|
173
191
|
BuildMessage,
|
|
174
192
|
BuildError
|
|
175
193
|
}
|
package/bin/cds.js
CHANGED
|
@@ -8,10 +8,6 @@ const cli = { //NOSONAR
|
|
|
8
8
|
},
|
|
9
9
|
|
|
10
10
|
exec (cmd = process.argv[2], ...argv) {
|
|
11
|
-
if (process[Symbol.for('ts-node.register.instance')]) {
|
|
12
|
-
process.env.CDS_TYPESCRIPT = process.env.CDS_TYPESCRIPT || 'true'
|
|
13
|
-
}
|
|
14
|
-
require('util').inspect.defaultOptions = { colors: !!process.stderr.isTTY, depth:11 }
|
|
15
11
|
if (!argv.length) argv = process.argv.slice(3)
|
|
16
12
|
if (process.env.NODE_ENV !== 'test') this.errorHandlers()
|
|
17
13
|
if (cmd in this.Shortcuts) cmd = process.argv[2] = this.Shortcuts[cmd]
|
|
@@ -102,6 +98,14 @@ const _die_needsDk = (cmd) => {
|
|
|
102
98
|
return console.error (message)
|
|
103
99
|
}
|
|
104
100
|
|
|
101
|
+
require('util').inspect.defaultOptions = {
|
|
102
|
+
colors: !!process.stdout.isTTY && !!process.stderr.isTTY,
|
|
103
|
+
depth:11, breakLength:111
|
|
104
|
+
}
|
|
105
|
+
if (process[Symbol.for('ts-node.register.instance')]) {
|
|
106
|
+
process.env.CDS_TYPESCRIPT = process.env.CDS_TYPESCRIPT || 'true'
|
|
107
|
+
}
|
|
108
|
+
|
|
105
109
|
module.exports = Object.assign ((..._) => cli.exec(..._), cli)
|
|
106
110
|
if (!module.parent) cli.exec()
|
|
107
111
|
/* eslint no-console:off */
|
|
@@ -14,6 +14,11 @@ const CF_COMMAND = 'cf';
|
|
|
14
14
|
const POLL_COUNTER = 40;
|
|
15
15
|
const POLL_DELAY = 2500; //ms
|
|
16
16
|
|
|
17
|
+
const OPERATION_STATE_INITIAL = 'initial';
|
|
18
|
+
const OPERATION_STATE_IN_PROGRESS = 'in progress';
|
|
19
|
+
const OPERATION_STATE_FAILED = 'failed';
|
|
20
|
+
const OPERATION_STATE_SUCCEEDED = 'succeeded';
|
|
21
|
+
|
|
17
22
|
|
|
18
23
|
class CfUtil {
|
|
19
24
|
|
|
@@ -94,7 +99,11 @@ class CfUtil {
|
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
const response = await this._cfRun(...args);
|
|
97
|
-
|
|
102
|
+
if (!response) {
|
|
103
|
+
throw new Error(`The response from the server was empty. Maybe your token is expired. Run the command again and re-log on in case the problem persists.`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const result = JSON.parse(response);
|
|
98
107
|
if (result.errors) {
|
|
99
108
|
const errorMessage = result.errors.map((entry) => `${entry.title}: ${entry.detail} (${entry.code})`).join('\n');
|
|
100
109
|
throw new Error(errorMessage);
|
|
@@ -104,10 +113,10 @@ class CfUtil {
|
|
|
104
113
|
|
|
105
114
|
_extract(string, pattern, errorMsg) {
|
|
106
115
|
const match = string.match(pattern);
|
|
107
|
-
if (
|
|
108
|
-
|
|
116
|
+
if (match?.[1]) {
|
|
117
|
+
return match[1];
|
|
109
118
|
}
|
|
110
|
-
|
|
119
|
+
throw new Error(errorMsg);
|
|
111
120
|
}
|
|
112
121
|
|
|
113
122
|
async getCfTargetFromConfigFile() {
|
|
@@ -138,6 +147,8 @@ class CfUtil {
|
|
|
138
147
|
}
|
|
139
148
|
|
|
140
149
|
async getCfTarget() {
|
|
150
|
+
// check if token is valid or expired / missing
|
|
151
|
+
await this._cfRun('oauth-token');
|
|
141
152
|
return await this.getCfTargetFromConfigFile() || await this.getCfTargetFromCli();
|
|
142
153
|
}
|
|
143
154
|
|
|
@@ -149,13 +160,13 @@ class CfUtil {
|
|
|
149
160
|
|
|
150
161
|
const { org, space } = target;
|
|
151
162
|
const orgs = await this._cfRequest(`/v3/organizations`, { names: org });
|
|
152
|
-
if (!orgs
|
|
163
|
+
if (!orgs?.resources?.length) {
|
|
153
164
|
throw new Error(`CF org ${bold(org)} not found!`);
|
|
154
165
|
}
|
|
155
166
|
|
|
156
167
|
const orgGuid = orgs.resources[0].guid;
|
|
157
168
|
const spaces = await this._cfRequest(`/v3/spaces`, { names: space, organization_guids: orgGuid });
|
|
158
|
-
if (!spaces
|
|
169
|
+
if (!spaces?.resources?.length) {
|
|
159
170
|
throw new Error(`CF space ${bold(space)} not found in org ${bold(org)}!`);
|
|
160
171
|
}
|
|
161
172
|
|
|
@@ -179,16 +190,27 @@ class CfUtil {
|
|
|
179
190
|
space_guids: spaceInfo.spaceGuid,
|
|
180
191
|
organization_guids: spaceInfo.orgGuid
|
|
181
192
|
});
|
|
182
|
-
if (!serviceInstances
|
|
193
|
+
if (!serviceInstances?.resources?.length) {
|
|
183
194
|
return null;
|
|
184
195
|
}
|
|
185
196
|
|
|
186
197
|
const serviceInstance = serviceInstances.resources[0];
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
198
|
+
switch (serviceInstance?.last_operation?.state?.toLowerCase()) {
|
|
199
|
+
case OPERATION_STATE_INITIAL:
|
|
200
|
+
case OPERATION_STATE_IN_PROGRESS:
|
|
201
|
+
await this._sleep(POLL_DELAY);
|
|
202
|
+
break;
|
|
203
|
+
|
|
204
|
+
case OPERATION_STATE_SUCCEEDED:
|
|
205
|
+
return serviceInstance;
|
|
206
|
+
|
|
207
|
+
case OPERATION_STATE_FAILED:
|
|
208
|
+
throw new Error(`The returned service reported state '${OPERATION_STATE_FAILED}'.\n${JSON.stringify(serviceInstance, null, 4)}`);
|
|
190
209
|
|
|
191
|
-
|
|
210
|
+
default:
|
|
211
|
+
LOG.log(`Unsupported server response state '${serviceInstance?.last_operation?.state}'. Waiting for next response.`);
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
192
214
|
}
|
|
193
215
|
|
|
194
216
|
throw new Error(`Timeout occurred while getting service ${bold(serviceName)}`);
|
|
@@ -214,7 +236,7 @@ class CfUtil {
|
|
|
214
236
|
service_offering_names: serviceOfferingName
|
|
215
237
|
});
|
|
216
238
|
|
|
217
|
-
if (!servicePlan
|
|
239
|
+
if (!servicePlan?.resources?.length) {
|
|
218
240
|
throw new Error(`No service plans found`);
|
|
219
241
|
}
|
|
220
242
|
|
|
@@ -261,16 +283,29 @@ class CfUtil {
|
|
|
261
283
|
while (counter > 0) {
|
|
262
284
|
counter--;
|
|
263
285
|
const bindings = await this._cfRequest(`/v3/service_credential_bindings`, { names: serviceKeyName, service_instance_guids: serviceInstance.guid });
|
|
264
|
-
if (!bindings
|
|
286
|
+
if (!bindings?.resources?.length) {
|
|
265
287
|
return null;
|
|
266
288
|
}
|
|
289
|
+
|
|
267
290
|
const binding = bindings.resources[0];
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
291
|
+
switch (binding?.last_operation?.state?.toLowerCase()) {
|
|
292
|
+
case OPERATION_STATE_INITIAL:
|
|
293
|
+
case OPERATION_STATE_IN_PROGRESS:
|
|
294
|
+
await this._sleep(POLL_DELAY);
|
|
295
|
+
break;
|
|
296
|
+
|
|
297
|
+
case OPERATION_STATE_SUCCEEDED: {
|
|
298
|
+
const keyDetails = await this._cfRequest(`/v3/service_credential_bindings/${encodeURIComponent(binding.guid)}/details`);
|
|
299
|
+
return keyDetails.credentials;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
case OPERATION_STATE_FAILED:
|
|
303
|
+
throw new Error(`The returned binding reported state '${OPERATION_STATE_FAILED}'.\n${JSON.stringify(binding, null, 4)}`);
|
|
272
304
|
|
|
273
|
-
|
|
305
|
+
default:
|
|
306
|
+
LOG.log(`Unsupported server response state '${binding?.last_operation?.state}'. Waiting for next response.`);
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
274
309
|
}
|
|
275
310
|
|
|
276
311
|
throw new Error(`Timeout occurred while getting service key ${bold(serviceKeyName)}`);
|