@sap/cds 6.3.1 → 6.4.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 +87 -0
- package/apis/cds.d.ts +1 -1
- package/apis/core.d.ts +118 -90
- package/apis/cqn.d.ts +11 -2
- package/apis/internal/inference.d.ts +7 -2
- package/apis/ql.d.ts +45 -11
- package/apis/serve.d.ts +8 -1
- package/apis/services.d.ts +303 -305
- package/bin/build/buildTaskEngine.js +28 -36
- package/bin/build/buildTaskFactory.js +32 -81
- package/bin/build/buildTaskHandler.js +3 -2
- package/bin/build/buildTaskProvider.js +2 -2
- package/bin/build/buildTaskProviderFactory.js +5 -14
- package/bin/build/constants.js +0 -1
- package/bin/build/provider/buildTaskHandlerEdmx.js +7 -6
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +6 -5
- package/bin/build/provider/buildTaskHandlerInternal.js +9 -30
- package/bin/build/provider/buildTaskProviderInternal.js +70 -58
- package/bin/build/provider/fiori/index.js +6 -5
- package/bin/build/provider/hana/2migration.js +20 -3
- package/bin/build/provider/hana/2tabledata.js +1 -0
- package/bin/build/provider/hana/index.js +40 -17
- package/bin/build/provider/java/index.js +10 -10
- package/bin/build/provider/mtx/index.js +25 -16
- package/bin/build/provider/mtx/resourcesTarBuilder.js +22 -27
- package/bin/build/provider/mtx-extension/index.js +3 -2
- package/bin/build/provider/mtx-sidecar/index.js +16 -15
- package/bin/build/provider/nodejs/index.js +14 -56
- package/bin/build/util.js +56 -16
- package/bin/deploy/to-hana/cfUtil.js +4 -1
- package/bin/deploy/to-hana/gitUtil.js +1 -1
- package/bin/deploy/to-hana/hana.js +45 -38
- package/bin/deploy/to-hana/hdiDeployUtil.js +8 -9
- package/bin/deploy/to-hana/mtaUtil.js +13 -14
- package/bin/mtx/in-cds.js +3 -1
- package/bin/serve.js +1 -1
- package/bin/version.js +2 -1
- package/lib/compile/cds-compile.js +1 -0
- package/lib/compile/cdsc.js +1 -0
- package/lib/compile/etc/_localized.js +2 -2
- package/lib/compile/for/lean_drafts.js +83 -0
- package/lib/compile/for/nodejs.js +1 -0
- package/lib/compile/minify.js +2 -1
- package/lib/compile/parse.js +2 -1
- package/lib/compile/to/gql.js +1 -1
- package/lib/compile/to/sql.js +11 -1
- package/lib/core/entities.js +1 -1
- package/lib/core/index.js +8 -9
- package/lib/core/infer.js +1 -0
- package/lib/dbs/cds-deploy.js +97 -41
- package/lib/env/cds-env.js +9 -10
- package/lib/env/cds-requires.js +8 -2
- package/lib/env/defaults.js +0 -4
- package/lib/env/schemas/cds-rc.json +38 -0
- package/lib/ql/SELECT.js +10 -4
- package/lib/srv/bindings.js +1 -1
- package/lib/srv/factory.js +1 -1
- package/lib/srv/protocols/index.js +3 -1
- package/lib/srv/srv-methods.js +1 -1
- package/lib/utils/cds-utils.js +11 -0
- package/lib/utils/inflect.js +13 -12
- package/lib/utils/tar.js +53 -10
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +1 -15
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/errors/UriSyntaxError.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +6 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/utils/BufferedWriter.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/ConditionalRequestValidator.js +0 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/oDataConfiguration.js +1 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +4 -0
- package/libx/_runtime/cds-services/services/Service.js +23 -1
- package/libx/_runtime/cds-services/util/assert.js +0 -41
- package/libx/_runtime/common/composition/data.js +5 -1
- package/libx/_runtime/common/generic/auth/utils.js +3 -3
- package/libx/_runtime/common/generic/input.js +4 -24
- package/libx/_runtime/common/generic/paging.js +3 -3
- package/libx/_runtime/common/utils/csn.js +21 -15
- package/libx/_runtime/common/utils/draft.js +2 -1
- package/libx/_runtime/common/utils/resolveView.js +25 -4
- package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -1
- package/libx/_runtime/common/utils/rowUUIDGenerator.js +21 -0
- package/libx/_runtime/common/utils/templateProcessor.js +12 -15
- package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +23 -0
- package/libx/_runtime/db/expand/expandCQNToJoin.js +29 -12
- package/libx/_runtime/db/generic/input.js +7 -13
- package/libx/_runtime/db/sql-builder/UpsertBuilder.js +47 -0
- package/libx/_runtime/db/sql-builder/index.js +2 -0
- package/libx/_runtime/db/sql-builder/sqlFactory.js +9 -0
- package/libx/_runtime/db/utils/columns.js +4 -2
- package/libx/_runtime/fiori/generic/read.js +1 -12
- package/libx/_runtime/fiori/lean-draft.js +657 -0
- package/libx/_runtime/fiori/utils/handler.js +1 -1
- package/libx/_runtime/hana/pool.js +16 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +2 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +2 -3
- package/libx/_runtime/messaging/outbox/utils.js +109 -70
- package/libx/_runtime/messaging/service.js +16 -7
- package/libx/_runtime/remote/Service.js +15 -2
- package/libx/_runtime/remote/utils/client.js +41 -11
- package/libx/_runtime/sqlite/Service.js +3 -0
- package/libx/_runtime/sqlite/convertDraftAdminPathExpression.js +56 -0
- package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +59 -0
- package/libx/_runtime/sqlite/customBuilder/index.js +5 -0
- package/libx/_runtime/sqlite/execute.js +1 -1
- package/libx/_runtime/types/api.js +2 -2
- package/libx/rest/RestAdapter.js +15 -13
- package/package.json +1 -1
- package/server.js +1 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
|
-
|
|
3
|
+
const cds = require('../../cds')
|
|
4
4
|
const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
|
|
5
5
|
const { isOldJavaStack, BuildError } = require('../../util')
|
|
6
6
|
|
|
@@ -19,22 +19,22 @@ class JavaModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
19
19
|
const { src, dest } = this.task
|
|
20
20
|
|
|
21
21
|
const odataOptions = {
|
|
22
|
-
version:
|
|
22
|
+
version: cds.env.odata?.version
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
if (await isOldJavaStack([src, this.buildOptions.root])) {
|
|
26
|
-
if (!this._isCompilerV1() && !
|
|
26
|
+
if (!this._isCompilerV1() && !cds.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 (!
|
|
30
|
+
if (!cds.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
|
}
|
|
34
34
|
|
|
35
|
-
// 'sql_mapping' and 'cds.
|
|
36
|
-
if (
|
|
37
|
-
odataOptions.sql_mapping =
|
|
35
|
+
// 'sql_mapping' and 'cds.persistence.name' annotations are required by old java stack
|
|
36
|
+
if (cds.env.sql.names !== 'plain') {
|
|
37
|
+
odataOptions.sql_mapping = cds.env.sql.names
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -48,7 +48,7 @@ class JavaModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
48
48
|
|
|
49
49
|
if (this.hasBuildOption(CONTENT_LANGUAGE_BUNDLES, true)) {
|
|
50
50
|
// collect and write language bundles into single i18n.json file
|
|
51
|
-
const i18nFolder =
|
|
51
|
+
const i18nFolder = cds.env.i18n.folders?.[0] || 'i18n'
|
|
52
52
|
const i18n = await this.collectLanguageBundles(model, path.join(this.task.dest, i18nFolder))
|
|
53
53
|
if (i18n) {
|
|
54
54
|
this._result.languageBundles = i18n.bundles
|
|
@@ -81,7 +81,7 @@ class JavaModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
81
81
|
|
|
82
82
|
async _compileForOdata(model, csnDest, compileOptions) {
|
|
83
83
|
// csn for service providers
|
|
84
|
-
const m =
|
|
84
|
+
const m = cds.compile.for.java(model, {
|
|
85
85
|
...this._options4odata(),
|
|
86
86
|
...compileOptions
|
|
87
87
|
})
|
|
@@ -103,7 +103,7 @@ class JavaModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
_isCompilerV1() {
|
|
106
|
-
const version =
|
|
106
|
+
const version = cds.compiler.version()
|
|
107
107
|
const match = version.match(/(\d+)\.?(\d*)\.?(\d*)/)
|
|
108
108
|
return match && match[1] === 1
|
|
109
109
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/* eslint-disable no-empty */
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
const path = require('path')
|
|
4
|
+
const cds = require('../../cds')
|
|
4
5
|
const BuildTaskProviderInternal = require("../buildTaskProviderInternal")
|
|
5
6
|
const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
|
|
6
|
-
const { isStreamlinedMtx } = require('../../util')
|
|
7
|
+
const { isStreamlinedMtx, hasJavaNature } = require('../../util')
|
|
7
8
|
const ResourcesTarProvider = require('./resourcesTarBuilder')
|
|
8
9
|
|
|
9
10
|
const { BUILD_TASK_HANA, FOLDER_GEN } = require('../../constants')
|
|
@@ -19,10 +20,18 @@ class MtxModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
19
20
|
}
|
|
20
21
|
init() {
|
|
21
22
|
super.init()
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
|
|
24
|
+
if (hasJavaNature([this.buildOptions.root, this.task.src, cds.env.folders.srv])) {
|
|
25
|
+
// Java projects having this build task configured are using Classic MTX
|
|
26
|
+
// Streamlined MTX based Java projects have to configure the 'mtx-sidecar' build task instead.
|
|
25
27
|
this._delegate = new ClassicMtxBuilder(this)
|
|
28
|
+
} else {
|
|
29
|
+
// Nodejs projects
|
|
30
|
+
if (isStreamlinedMtx()) {
|
|
31
|
+
this._delegate = new StreamlinedMtxBuilder(this)
|
|
32
|
+
} else {
|
|
33
|
+
this._delegate = new ClassicMtxBuilder(this)
|
|
34
|
+
}
|
|
26
35
|
}
|
|
27
36
|
}
|
|
28
37
|
|
|
@@ -42,7 +51,7 @@ class StreamlinedMtxBuilder {
|
|
|
42
51
|
async build() {
|
|
43
52
|
// Non-sidecar scenario: model-provider is implemented by the Nodejs app itself (default for Nodejs).
|
|
44
53
|
// Only create resources TAR, compiled CSN(s) are created by the Nodejs app build task.
|
|
45
|
-
const dest =
|
|
54
|
+
const dest = cds.env.build?.target !== "." ? this._handler.task.dest : path.join(this._handler.task.dest, FOLDER_GEN)
|
|
46
55
|
const model = await this._handler.model()
|
|
47
56
|
if (!model) {
|
|
48
57
|
return
|
|
@@ -64,7 +73,7 @@ class ClassicMtxBuilder {
|
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
async build() {
|
|
67
|
-
if (
|
|
76
|
+
if (cds.env.requires.toggles === true) {
|
|
68
77
|
throw new Error("Feature toggles are only supported by Streamlined MTX")
|
|
69
78
|
}
|
|
70
79
|
|
|
@@ -96,7 +105,7 @@ class ClassicMtxBuilder {
|
|
|
96
105
|
}))
|
|
97
106
|
|
|
98
107
|
// collect and write language bundles into single i18n.json file
|
|
99
|
-
const i18nFolder =
|
|
108
|
+
const i18nFolder = cds.env.i18n.folders?.[0] || 'i18n'
|
|
100
109
|
const i18n = await this._handler.collectLanguageBundles(model, path.join(destSdc, i18nFolder))
|
|
101
110
|
if (i18n) {
|
|
102
111
|
this._handler._result.languageBundles = i18n.bundles
|
|
@@ -163,13 +172,13 @@ class ClassicMtxBuilder {
|
|
|
163
172
|
* @param {Object} model - the reflected csn for this SaaS application
|
|
164
173
|
*/
|
|
165
174
|
_validateExtensionAllowLists(model, tenantDbPath) {
|
|
166
|
-
let entityWhitelist =
|
|
167
|
-
let serviceWhitelist =
|
|
168
|
-
let extensionAllowlist =
|
|
175
|
+
let entityWhitelist = cds.env.mtx?.["entity-whitelist"]
|
|
176
|
+
let serviceWhitelist = cds.env.mtx?.["service-whitelist"]
|
|
177
|
+
let extensionAllowlist = cds.env.mtx?.["extension-allowlist"]
|
|
169
178
|
|
|
170
179
|
// for java projects mtx configuration is part of sidecar config
|
|
171
180
|
if (!entityWhitelist && !serviceWhitelist && !extensionAllowlist && tenantDbPath) {
|
|
172
|
-
const dbEnv =
|
|
181
|
+
const dbEnv = cds.env.for("cds", tenantDbPath)
|
|
173
182
|
entityWhitelist = dbEnv.mtx?.["entity-whitelist"]
|
|
174
183
|
serviceWhitelist = dbEnv.mtx?.["service-whitelist"]
|
|
175
184
|
extensionAllowlist = dbEnv.mtx?.["extension-allowlist"]
|
|
@@ -181,7 +190,7 @@ class ClassicMtxBuilder {
|
|
|
181
190
|
|
|
182
191
|
if (extensionAllowlist || entityWhitelist || serviceWhitelist) {
|
|
183
192
|
const invalidEntries = new Set()
|
|
184
|
-
const reflected =
|
|
193
|
+
const reflected = cds.reflect(model)
|
|
185
194
|
|
|
186
195
|
if (Array.isArray(extensionAllowlist)) {
|
|
187
196
|
extensionAllowlist.forEach(allowListEntry => {
|
|
@@ -234,19 +243,19 @@ class ClassicMtxBuilder {
|
|
|
234
243
|
// ensure that the hana task is contained even if this mtx task has been executed solely using "cds build --for mtx"
|
|
235
244
|
if (!tasks.find(task => task.for === BUILD_TASK_HANA)) {
|
|
236
245
|
//mtx task might have been executed as separate task
|
|
237
|
-
tasks =
|
|
246
|
+
tasks = cds.env.build?.tasks || []
|
|
238
247
|
if (tasks.length === 0) {
|
|
239
|
-
const provider = new BuildTaskProviderInternal(this._handler.
|
|
248
|
+
const provider = new BuildTaskProviderInternal(this._handler.logger)
|
|
240
249
|
await provider.lookupTasks(tasks, this._handler.buildOptions)
|
|
241
250
|
}
|
|
242
251
|
}
|
|
243
252
|
// the SaaS app might use a tenant aware db as well as a shared db deployed using static hdi-deployer
|
|
244
253
|
// 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
|
|
245
|
-
const hanaDbPaths = tasks.filter(task => task.for === BUILD_TASK_HANA).map(hanaTask => path.resolve(this._handler.buildOptions.root, hanaTask.src ||
|
|
254
|
+
const hanaDbPaths = tasks.filter(task => task.for === BUILD_TASK_HANA).map(hanaTask => path.resolve(this._handler.buildOptions.root, hanaTask.src || cds.env.folders.db))
|
|
246
255
|
let tenantDbPath = hanaDbPaths.find(hanaDbPath => hanaDbPaths.length === 1 || modelPaths.some(modelPath => path.dirname(modelPath) === hanaDbPath))
|
|
247
256
|
|
|
248
257
|
if (!tenantDbPath) {
|
|
249
|
-
tenantDbPath = path.join(this._handler.buildOptions.root,
|
|
258
|
+
tenantDbPath = path.join(this._handler.buildOptions.root, cds.env.folders.db)
|
|
250
259
|
if (hanaDbPaths.length === 0) {
|
|
251
260
|
this._handler.pushMessage(`no 'hana' build task found, use default location '${tenantDbPath}'`, WARNING)
|
|
252
261
|
} else {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
|
+
const cds = require('../../cds')
|
|
2
3
|
const BuildTaskHandlerInternal = require('../buildTaskHandlerInternal')
|
|
3
4
|
|
|
4
5
|
const { BUILD_TASK_HANA } = require('../../constants')
|
|
6
|
+
const { fs } = require('../../../../lib/utils/cds-utils')
|
|
5
7
|
const { WARNING } = BuildTaskHandlerInternal
|
|
6
8
|
const DEFAULT_TAR_NAME = "resources.tgz"
|
|
7
9
|
|
|
@@ -13,52 +15,51 @@ class ResourcesTarBuilder {
|
|
|
13
15
|
get handler() { return this._handler }
|
|
14
16
|
|
|
15
17
|
async createTar(dest, model) {
|
|
16
|
-
const { root,
|
|
17
|
-
if (
|
|
18
|
+
const { root, resources } = await this._getResources(model)
|
|
19
|
+
if (resources.length === 0) {
|
|
18
20
|
// packTarArchive fails otherwise
|
|
19
21
|
this.handler.pushMessage("No deployment resources found - skip resources.tgz", WARNING)
|
|
20
22
|
return
|
|
21
23
|
}
|
|
22
|
-
await this.writeTarFile(
|
|
24
|
+
await this.writeTarFile(resources, root, path.join(dest, DEFAULT_TAR_NAME))
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
async writeTarFile(
|
|
27
|
+
async writeTarFile(resources, root, tarFile) {
|
|
26
28
|
const { tar } = require('../../../../lib').utils
|
|
27
|
-
await tar.czfd
|
|
29
|
+
await tar.czfd(tarFile, root, resources) // REVISIT: tar.czfd was created for this case only -> it ensures the target's dir exists
|
|
28
30
|
this.handler.pushFile(tarFile)
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
async _getResources(model) {
|
|
32
34
|
// distinguish HANA and SQLITE deployment
|
|
33
35
|
let root = await this._getHanaTenantDbDest()
|
|
34
|
-
let
|
|
36
|
+
let resources
|
|
35
37
|
if (root) {
|
|
36
|
-
|
|
38
|
+
resources = this._getHanaResources(root)
|
|
37
39
|
} else {
|
|
38
|
-
root = path.join(this.handler.buildOptions.root,
|
|
39
|
-
|
|
40
|
+
root = path.join(this.handler.buildOptions.root, cds.env.folders.db)
|
|
41
|
+
resources = await this._getSqliteResources(model)
|
|
40
42
|
}
|
|
41
|
-
return { root,
|
|
43
|
+
return { root, resources }
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
/**
|
|
45
|
-
* Reads
|
|
46
|
-
*
|
|
47
|
+
* Reads a list of files and folders required for HANA deployment.
|
|
48
|
+
* This is a subset of all files contained in the directory.
|
|
49
|
+
* - 'db/src', 'db/cfg', 'db/undeploy.json'...
|
|
47
50
|
* See '../../../../lib.deploy'
|
|
48
51
|
* Note: the hana build task has already been executed
|
|
49
52
|
* @param {string} root
|
|
50
53
|
* @returns an array containing all resources
|
|
51
54
|
*/
|
|
52
55
|
_getHanaResources(root) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (hanaNativeFolders.some(folder => res.startsWith(folder)) || hanaNativeFiles.some(file => res === file)) {
|
|
58
|
-
return true
|
|
56
|
+
return ['src', 'cfg', 'undeploy.json', '.hdiignore'].reduce((acc, res) => {
|
|
57
|
+
const resPath = path.join(root, res)
|
|
58
|
+
if (fs.existsSync(resPath)) {
|
|
59
|
+
acc.push(resPath)
|
|
59
60
|
}
|
|
60
|
-
return
|
|
61
|
-
})
|
|
61
|
+
return acc
|
|
62
|
+
}, [])
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
/**
|
|
@@ -68,16 +69,10 @@ class ResourcesTarBuilder {
|
|
|
68
69
|
* @returns
|
|
69
70
|
*/
|
|
70
71
|
async _getSqliteResources(model) {
|
|
71
|
-
const { resources } =
|
|
72
|
+
const { resources } = cds.deploy
|
|
72
73
|
return Object.keys(await resources(model))
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
/**
|
|
76
|
-
* Returns the build tasks of this project - either user defined or calculated by BuildTaskFactory.
|
|
77
|
-
* A build task of type 'hana' is enforced in order to copy existing native hana artifacts later on.
|
|
78
|
-
*
|
|
79
|
-
* @returns {string} the src folder of the tenant db module
|
|
80
|
-
*/
|
|
81
76
|
async _getHanaTenantDbDest() {
|
|
82
77
|
const buildOptions = this.handler.buildOptions
|
|
83
78
|
let hanaTask = buildOptions.tasks ? buildOptions.tasks.find(task => task.for === BUILD_TASK_HANA) : undefined
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
|
+
const cds = require('../../cds')
|
|
2
3
|
const BuildTaskHandlerInternal = require('../buildTaskHandlerInternal')
|
|
3
4
|
const { FOLDER_GEN } = require('../../constants')
|
|
4
5
|
const ResourcesTarBuilder = require('../mtx/resourcesTarBuilder')
|
|
@@ -25,7 +26,7 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
25
26
|
|
|
26
27
|
// extension CSN using parsed format
|
|
27
28
|
const options = { ...this.options(), flavor: 'parsed' }
|
|
28
|
-
const extCsn = await
|
|
29
|
+
const extCsn = await cds.load(this.resolveModel(), options)
|
|
29
30
|
if (extCsn.requires) {
|
|
30
31
|
extCsn.requires.length = 0
|
|
31
32
|
}
|
|
@@ -39,7 +40,7 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
39
40
|
allFiles.push(i18n.file)
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
const files = Object.keys(await
|
|
43
|
+
const files = Object.keys(await cds.deploy.resources(model))
|
|
43
44
|
if (files.length > 0) {
|
|
44
45
|
const dataDest = path.join(destExt, 'data')
|
|
45
46
|
await Promise.all(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
|
+
const cds = require('../../cds')
|
|
2
3
|
const fs = require('fs')
|
|
3
4
|
const { FOLDER_GEN, DEFAULT_CSN_FILE_NAME } = require('../../constants')
|
|
4
5
|
const NodeCfModuleBuilder = require('../nodejs')
|
|
@@ -30,7 +31,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
30
31
|
*/
|
|
31
32
|
async build() {
|
|
32
33
|
// nodejs app parts have to be built using sidecar env
|
|
33
|
-
const sidecarEnv =
|
|
34
|
+
const sidecarEnv = cds.env.for("cds", this.task.src)
|
|
34
35
|
// build main application
|
|
35
36
|
await this._buildMainApp(sidecarEnv)
|
|
36
37
|
// build node application
|
|
@@ -43,8 +44,8 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
43
44
|
*/
|
|
44
45
|
async _buildNodeApp(sidecarEnv) {
|
|
45
46
|
const destSidecar = this.task.dest
|
|
46
|
-
const destSidecarSrc = path.join(destSidecar,
|
|
47
|
-
const i18nFolder =
|
|
47
|
+
const destSidecarSrc = path.join(destSidecar, cds.env.folders.srv)
|
|
48
|
+
const i18nFolder = cds.env.i18n.folders?.[0] || 'i18n'
|
|
48
49
|
const model = this._compileSidecarSync(sidecarEnv)
|
|
49
50
|
await this.compileToJson(model, path.join(destSidecarSrc, DEFAULT_CSN_FILE_NAME))
|
|
50
51
|
|
|
@@ -61,7 +62,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
61
62
|
throw new BuildError("CDS build failed", "Invalid MTX sidecar configuration - \"cds.xt.ModelProviderService\": \"in-sidecar\" missing.")
|
|
62
63
|
}
|
|
63
64
|
let main = sidecarEnv.requires['cds.xt.ModelProviderService']?.root
|
|
64
|
-
const profiles =
|
|
65
|
+
const profiles = cds.env.profiles || []
|
|
65
66
|
|
|
66
67
|
if (!profiles.includes("production") && !profiles.includes("prod")) {
|
|
67
68
|
main = DEFAULT_MAIN_FOLDER
|
|
@@ -70,7 +71,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
70
71
|
}
|
|
71
72
|
const destRoot = this.task.dest
|
|
72
73
|
const destMain = path.join(destRoot, main)
|
|
73
|
-
const destMainSrv = path.join(destMain,
|
|
74
|
+
const destMainSrv = path.join(destMain, cds.env.folders.srv)
|
|
74
75
|
const csn = await this.model()
|
|
75
76
|
if (!csn) {
|
|
76
77
|
return
|
|
@@ -83,7 +84,7 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
83
84
|
await new ResourcesTarProvider(this).createTar(destMain, csn)
|
|
84
85
|
|
|
85
86
|
// copy package.json and .cdsrc.json from project root
|
|
86
|
-
await this._copyMainConfigFiles(
|
|
87
|
+
await this._copyMainConfigFiles(cds.root, destMain)
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
/**
|
|
@@ -92,12 +93,12 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
92
93
|
* @returns the compiled mtx sidecar CSN
|
|
93
94
|
*/
|
|
94
95
|
_compileSidecarSync(sidecarEnv) {
|
|
95
|
-
const env =
|
|
96
|
+
const env = cds.env
|
|
96
97
|
try {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const modelPaths =
|
|
100
|
-
const modelFilePaths =
|
|
98
|
+
cds.root = this.task.src
|
|
99
|
+
cds.env = sidecarEnv
|
|
100
|
+
const modelPaths = cds.resolve('*', false)
|
|
101
|
+
const modelFilePaths = cds.resolve(modelPaths)
|
|
101
102
|
|
|
102
103
|
if (!modelFilePaths || modelFilePaths.length === 0) {
|
|
103
104
|
throw new BuildError("No CDS service models found in MTX sidecar. Make sure required npm modules are installed.")
|
|
@@ -105,18 +106,18 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
105
106
|
this._logger._debug && this._logger.debug(`sidecar model: ${relativePaths(this.buildOptions.root, modelFilePaths).join(", ")}`)
|
|
106
107
|
|
|
107
108
|
// check whether all models belonging to the @sap namespace can be resolved
|
|
108
|
-
const unresolved = resolveRequiredSapModels(
|
|
109
|
+
const unresolved = resolveRequiredSapModels(modelPaths)
|
|
109
110
|
if (unresolved.length > 0) {
|
|
110
111
|
// log error, but don't fail
|
|
111
112
|
this.pushMessage(`CDS service models [${unresolved.join(', ')}] required by MTX sidecar cannot be resolved. Make sure to install the missing npm modules.`, ERROR)
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
// synchronous compilation
|
|
115
|
-
return
|
|
116
|
+
return cds.load(modelFilePaths, { sync: true, ...this.options() })
|
|
116
117
|
} finally {
|
|
117
118
|
// restore project scope
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
cds.root = this.buildOptions.root
|
|
120
|
+
cds.env = env
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
123
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
|
+
const cds = require('../../cds')
|
|
3
4
|
const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
|
|
4
5
|
const { BuildError } = require('../../util')
|
|
5
6
|
const { BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY, ODATA_VERSION_V2, FOLDER_GEN, BUILD_NODEJS_EDMX_GENERAION, EDMX_GENERATION,
|
|
6
|
-
SKIP_PACKAGE_JSON_GENERATION, CONTENT_EDMX, CONTENT_PACKAGE_JSON, CONTENT_PACKAGELOCK_JSON, CONTENT_NPMRC, CONTENT_CDSRC_JSON,
|
|
7
|
-
|
|
7
|
+
SKIP_PACKAGE_JSON_GENERATION, CONTENT_EDMX, CONTENT_PACKAGE_JSON, CONTENT_PACKAGELOCK_JSON, CONTENT_NPMRC, CONTENT_CDSRC_JSON,
|
|
8
|
+
CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON } = require('../../constants')
|
|
9
|
+
const { WARNING } = BuildTaskHandlerEdmx
|
|
8
10
|
|
|
9
11
|
class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
10
12
|
init() {
|
|
@@ -25,20 +27,22 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
25
27
|
throw new BuildError("Option not supported - compileDest")
|
|
26
28
|
}
|
|
27
29
|
// fallback if src has been defined as '.'
|
|
28
|
-
this.destSrv = this.isStagingBuild() ? path.resolve(this.task.dest,
|
|
30
|
+
this.destSrv = this.isStagingBuild() ? path.resolve(this.task.dest, cds.env.folders.srv) : path.join(this.task.dest, FOLDER_GEN)
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
options() {
|
|
32
|
-
const options = super.options()
|
|
33
|
-
if (cds.requires.extensibility || cds.requires.toggles)
|
|
34
|
+
const options = super.options()
|
|
35
|
+
if (cds.env.requires.extensibility || cds.env.requires.toggles) {
|
|
36
|
+
options.flavor = 'xtended'
|
|
37
|
+
}
|
|
34
38
|
return options
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
async build() {
|
|
38
|
-
const destSrv = this.isStagingBuild() ? this.destSrv : path.resolve(this.destSrv,
|
|
42
|
+
const destSrv = this.isStagingBuild() ? this.destSrv : path.resolve(this.destSrv, cds.env.folders.srv)
|
|
39
43
|
const destRoot = this.isStagingBuild() ? this.task.dest : this.destSrv
|
|
40
44
|
|
|
41
|
-
if (
|
|
45
|
+
if (cds.env.odata?.version === ODATA_VERSION_V2) {
|
|
42
46
|
// log warning as nodejs is only supporting odata version V4
|
|
43
47
|
this.pushMessage("OData v2 is not supported by node runtime. Make sure to define OData v2 in cds configuration.", WARNING)
|
|
44
48
|
}
|
|
@@ -54,12 +58,12 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
54
58
|
await this.collectAllLanguageBundles(dictionary, sources, destSrv, destRoot)
|
|
55
59
|
|
|
56
60
|
if (this.hasBuildOption(CONTENT_EDMX, true)) {
|
|
57
|
-
const m = await
|
|
61
|
+
const m = await cds.load(sources.base, super.options()) // REVISIT: Quick hack to get inferred model as expected by cds.compile.to.edmx()
|
|
58
62
|
await this.compileToEdmx(m, this.destSrv)
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
if (this.isStagingBuild() && !this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
|
|
62
|
-
const srcSrv = this.task.src === this.buildOptions.root ? path.resolve(this.task.src,
|
|
66
|
+
const srcSrv = this.task.src === this.buildOptions.root ? path.resolve(this.task.src, cds.env.folders.srv) : this.task.src
|
|
63
67
|
await this._copyNativeContent(this.buildOptions.root, srcSrv, destRoot, destSrv)
|
|
64
68
|
}
|
|
65
69
|
return this._result
|
|
@@ -139,7 +143,7 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
139
143
|
* @param {*} filter - copy file if filter function returns true
|
|
140
144
|
*/
|
|
141
145
|
async copyProjectRootContent(src, dest, filter) {
|
|
142
|
-
let { folders = ['i18n'] } =
|
|
146
|
+
let { folders = ['i18n'] } = cds.env.i18n
|
|
143
147
|
folders.push('handlers')
|
|
144
148
|
folders = folders.map(folder => path.join(src, folder))
|
|
145
149
|
let srvAllowList = "package\\.json$" // always copy package.json, modify only if CONTENT_PACKAGE_JSON is true
|
|
@@ -159,52 +163,6 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
159
163
|
}
|
|
160
164
|
return srvAllowList.test(entry) && (!filter || filter.call(this, entry))
|
|
161
165
|
})
|
|
162
|
-
|
|
163
|
-
if (this.hasBuildOption(CONTENT_PACKAGE_JSON, true) && fs.existsSync(path.join(dest, 'package.json'))) {
|
|
164
|
-
await this._modifyPackageJson(path.join(dest, 'package.json'))
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Modify package.json file defined by the given file path.
|
|
170
|
-
* @param {string} file
|
|
171
|
-
*/
|
|
172
|
-
async _modifyPackageJson(file) {
|
|
173
|
-
function _addEnginesField(content) {
|
|
174
|
-
if (!content.engines || !content.engines.node) {
|
|
175
|
-
// use minimum node version of @sap/cds
|
|
176
|
-
const { engines } = require('../../../../package.json')
|
|
177
|
-
if (engines && engines.node) {
|
|
178
|
-
this.logger.log(`${this.task.for}: adding node engines version to package.json ${engines.node}`)
|
|
179
|
-
content.engines = content.engines || {}
|
|
180
|
-
const nodeVersion = NodejsModuleBuilder._convertToClosedVersionRange(engines.node)
|
|
181
|
-
if (nodeVersion) {
|
|
182
|
-
content.engines.node = nodeVersion
|
|
183
|
-
return true
|
|
184
|
-
} else {
|
|
185
|
-
this.pushMessage(`Invalid node version defined in engines definition for @sap/cds ${engines.node} - skip node engines definition`, ERROR)
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return false
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
let content = await fs.promises.readFile(file)
|
|
193
|
-
let changed = false
|
|
194
|
-
if (content) {
|
|
195
|
-
content = JSON.parse(content)
|
|
196
|
-
changed |= _addEnginesField.call(this, content)
|
|
197
|
-
}
|
|
198
|
-
if (changed) {
|
|
199
|
-
await this.write(content).to(file)
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
static _convertToClosedVersionRange(rtVersion) {
|
|
204
|
-
rtVersion = rtVersion.trim()
|
|
205
|
-
if (rtVersion.match(SEMVER_REGEX)) {
|
|
206
|
-
return rtVersion.replace(SEMVER_REGEX, '^$2$3$4')
|
|
207
|
-
}
|
|
208
166
|
}
|
|
209
167
|
}
|
|
210
168
|
|
package/bin/build/util.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
|
-
const
|
|
3
|
+
const cds = require('./cds')
|
|
4
|
+
const { SEVERITY_ERROR, CDS_MODEL_EXCLUDE_LIST, FILE_EXT_CDS } = require('./constants')
|
|
4
5
|
|
|
5
6
|
function getProperty(src, segments) {
|
|
6
7
|
segments = Array.isArray(segments) ? segments : segments.split('.')
|
|
@@ -26,10 +27,12 @@ function setProperty(src, segments, value) {
|
|
|
26
27
|
* @param {Array} dirs - the absolute path names to check.
|
|
27
28
|
*/
|
|
28
29
|
function readPomFilePaths(dirs) {
|
|
29
|
-
return dirs.reduce((acc, dir) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
return dirs.flat().reduce((acc, dir) => {
|
|
31
|
+
if (dir) {
|
|
32
|
+
const file = path.join(dir, 'pom.xml')
|
|
33
|
+
if (fs.existsSync(file)) {
|
|
34
|
+
acc.push(file)
|
|
35
|
+
}
|
|
33
36
|
}
|
|
34
37
|
return acc
|
|
35
38
|
}, [])
|
|
@@ -44,7 +47,7 @@ function hasJavaNature(dirs) {
|
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
/**
|
|
47
|
-
* Determines
|
|
50
|
+
* Determines whether the both values are identical.
|
|
48
51
|
* @param {*} actual
|
|
49
52
|
* @param {*} expected
|
|
50
53
|
*/
|
|
@@ -116,15 +119,10 @@ function _redacted(cred) {
|
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
/**
|
|
119
|
-
* Distinguishes whether the project
|
|
120
|
-
* defined by the new Streamlined MTX (cds >=6) or whether it represents an old MTX project
|
|
121
|
-
* using the cds-mtx library.
|
|
122
|
-
*
|
|
123
|
-
* @param {object} env
|
|
124
|
-
* @returns
|
|
122
|
+
* Distinguishes whether the Nodejs project is a Streamlined MTX (cds >=6) or an old MTX project.
|
|
125
123
|
*/
|
|
126
|
-
function isStreamlinedMtx(
|
|
127
|
-
if (
|
|
124
|
+
function isStreamlinedMtx() {
|
|
125
|
+
if (cds.utils._oldMtx()) {
|
|
128
126
|
return false
|
|
129
127
|
}
|
|
130
128
|
return (cds.env.requires.toggles
|
|
@@ -136,11 +134,10 @@ function isStreamlinedMtx(cds) {
|
|
|
136
134
|
/**
|
|
137
135
|
* Returns a list of fully qualified model names belonging to the '@sap' namespace that cannot be resolved.
|
|
138
136
|
* E.g. the npm module might NOT be installed.
|
|
139
|
-
* @param {*} cds
|
|
140
137
|
* @param {Array} modelPaths
|
|
141
138
|
* @returns {Array}
|
|
142
139
|
*/
|
|
143
|
-
function resolveRequiredSapModels(
|
|
140
|
+
function resolveRequiredSapModels(modelPaths) {
|
|
144
141
|
return modelPaths.filter(p => {
|
|
145
142
|
if (p.startsWith('@sap/') && !CDS_MODEL_EXCLUDE_LIST.includes(p)) {
|
|
146
143
|
const files = cds.resolve(p)
|
|
@@ -149,6 +146,47 @@ function resolveRequiredSapModels(cds, modelPaths) {
|
|
|
149
146
|
})
|
|
150
147
|
}
|
|
151
148
|
|
|
149
|
+
function getDefaultModelOptions(projectPath) {
|
|
150
|
+
const fts = cds.env.features.folders
|
|
151
|
+
const modelPaths = cds.resolve(!fts ? '*' : ['*', fts], false)
|
|
152
|
+
return _pushModelPaths(projectPath, modelPaths)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function _pushModelPaths(projectPath, ...modelPaths) {
|
|
156
|
+
const model = new Set()
|
|
157
|
+
// may contain nested arrays
|
|
158
|
+
modelPaths = flatten(modelPaths)
|
|
159
|
+
const { roots } = cds.env
|
|
160
|
+
modelPaths.forEach(m => {
|
|
161
|
+
if (m && !model.has(m) && !model.has(m + "/")) {
|
|
162
|
+
// filter root model paths that do not exist
|
|
163
|
+
// other entries are added as is, e.g. reuse model entries
|
|
164
|
+
if (roots.includes(m)) {
|
|
165
|
+
const dir = path.resolve(projectPath, m)
|
|
166
|
+
if (fs.existsSync(dir)) {
|
|
167
|
+
model.add(m.replace(/\/$/, ''))
|
|
168
|
+
} else if (fs.existsSync(dir + FILE_EXT_CDS)) { //might be cds file name, compatibility to old build configs
|
|
169
|
+
model.add(m)
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
model.add(m)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
return model
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function flatten(modelPaths) {
|
|
180
|
+
return modelPaths.reduce((acc, m) => {
|
|
181
|
+
if (Array.isArray(m)) {
|
|
182
|
+
acc = acc.concat(flatten(m))
|
|
183
|
+
} else if (m) {
|
|
184
|
+
acc.push(m)
|
|
185
|
+
}
|
|
186
|
+
return acc
|
|
187
|
+
}, [])
|
|
188
|
+
}
|
|
189
|
+
|
|
152
190
|
class BuildMessage extends Error {
|
|
153
191
|
constructor(message, severity = SEVERITY_ERROR) {
|
|
154
192
|
super(message)
|
|
@@ -188,6 +226,8 @@ module.exports = {
|
|
|
188
226
|
relativePaths,
|
|
189
227
|
isStreamlinedMtx,
|
|
190
228
|
resolveRequiredSapModels,
|
|
229
|
+
getDefaultModelOptions,
|
|
230
|
+
flatten,
|
|
191
231
|
BuildMessage,
|
|
192
232
|
BuildError
|
|
193
233
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// eslint-disable-next-line no-console
|
|
2
|
+
|
|
1
3
|
const cp = require('child_process');
|
|
2
4
|
const fsp = require('fs').promises;
|
|
3
5
|
const os = require('os');
|
|
@@ -88,9 +90,10 @@ class CfUtil {
|
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
if (response.errors) {
|
|
91
|
-
const errorMessage =
|
|
93
|
+
const errorMessage = response.errors.map((entry) => `${entry.title || ''}: ${entry.detail || ''} (${entry.code || ''})`).join('\n');
|
|
92
94
|
throw new Error(errorMessage);
|
|
93
95
|
}
|
|
96
|
+
|
|
94
97
|
return response;
|
|
95
98
|
}
|
|
96
99
|
|
|
@@ -34,7 +34,7 @@ class GitUtil {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
console.log(`adding entry '${file}' to ${GIT_IGNORE_FILE}.`);
|
|
38
38
|
let gitignore = await this.readFileSafely(gitIgnorePath);
|
|
39
39
|
gitignore = gitignore + `
|
|
40
40
|
# added by cds deploy
|