@sap/cds-dk 9.2.0 → 9.3.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 +36 -0
- package/bin/bind.js +7 -8
- package/bin/up.js +13 -9
- package/lib/bind/cf.js +4 -0
- package/lib/bind/k8s.js +21 -0
- package/lib/bind/plugin.js +9 -0
- package/lib/bind/shared.js +4 -2
- package/lib/build/buildTaskProviderFactory.js +8 -14
- package/lib/build/constants.js +1 -0
- package/lib/build/index.js +17 -2
- package/lib/build/plugin.js +5 -7
- package/lib/build/provider/hana/index.js +4 -4
- package/lib/compile/to-xsuaa.js +11 -2
- package/lib/deploy/to-hana/hana.js +1 -1
- package/lib/deploy/to-hana/hdiDeployUtil.js +1 -1
- package/lib/env/schemas/cds-rc.js +2 -2
- package/lib/init/constants.js +3 -2
- package/lib/init/projectReader.js +5 -5
- package/lib/init/template/ams/index.js +1 -2
- package/lib/init/template/approuter/files/containerize.yaml.hbs +1 -1
- package/lib/init/template/approuter/index.js +1 -1
- package/lib/init/template/attachments/index.js +1 -1
- package/lib/init/template/audit-logging/index.js +1 -1
- package/lib/init/template/extension/files/.base/index.cds +6 -6
- package/lib/init/template/extension/index.js +5 -5
- package/lib/init/template/github-actions/files/{cf-github-deploy → github-deployment}/action.yaml +7 -5
- package/lib/init/template/github-actions/files/kyma-info/action.yaml +1 -1
- package/lib/init/template/github-actions/files/workflows/cf.yaml.hbs +14 -7
- package/lib/init/template/github-actions/files/workflows/kyma.yaml.hbs +6 -3
- package/lib/init/template/github-actions/files/workflows/test.yaml.hbs +5 -2
- package/lib/init/template/github-actions/index.js +8 -13
- package/lib/init/template/hana/index.js +2 -1
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +5 -1
- package/lib/init/template/html5-repo/files/package.json.hbs +1 -1
- package/lib/init/template/html5-repo/index.js +24 -19
- package/lib/init/template/ias/files/mta.yaml.hbs +4 -3
- package/lib/init/template/ias/files/values.yaml.hbs +41 -40
- package/lib/init/template/lint/index.js +6 -11
- package/lib/init/template/mta/files/package.json +1 -1
- package/lib/init/template/nodejs/files/.vscode/extensions.json +0 -1
- package/lib/init/template/nodejs/files/.vscode/settings.json +2 -2
- package/lib/init/template/nodejs/files/package.json.hbs +3 -9
- package/lib/init/template/notifications/index.js +1 -1
- package/lib/init/template/portal/files/mta.yaml.hbs +5 -1
- package/lib/init/template/portal/index.js +0 -1
- package/lib/init/template/postgres/index.js +1 -1
- package/lib/init/template/sample/files/app/_i18n/i18n.properties +15 -0
- package/lib/init/template/sample/files/app/_i18n/i18n_de.properties +15 -0
- package/lib/init/template/sample/files/app/admin-books/fiori-service.cds +14 -15
- package/lib/init/template/sample/files/app/browse/fiori-service.cds +44 -44
- package/lib/init/template/sample/files/app/common.cds +99 -84
- package/lib/init/template/sample/files/db/schema.cds +22 -17
- package/lib/init/template/sample/files/package.json.hbs +1 -3
- package/lib/init/template/sample/files/srv/admin-service.cds +4 -3
- package/lib/init/template/sample/files/srv/cat-service.cds +28 -11
- package/lib/init/template/tiny-sample/files/nodejs/db/schema.cds +3 -3
- package/lib/init/template/typescript/files/package.json +1 -1
- package/lib/init/template/ui5/index.js +22 -0
- package/lib/init/template/workzone-standard/index.js +1 -1
- package/lib/lint/eslintHelp.json +5 -3
- package/lib/util/cf.js +7 -7
- package/npm-shrinkwrap.json +17 -17
- package/package.json +1 -1
- package/lib/init/template/sample/files/javascript/admin-service.js.hbs +0 -24
- package/lib/init/template/sample/files/javascript/cat-service.js.hbs +0 -43
- package/lib/init/template/sample/files/typescript/admin-service.ts.hbs +0 -16
- package/lib/init/template/sample/files/typescript/cat-service.ts.hbs +0 -38
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,42 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
8
8
|
|
|
9
|
+
## Version 9.3.0 - 2025-09-01
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `cds bind -a` is now also supported for Kyma, where the app prefix can be passed for `-a`, e.g. `bookshop-srv`.
|
|
14
|
+
- `cds build --for hana` now trims leading and trailing whitespaces in csv-Files if build option `trimCsvWhitespaces` is set.
|
|
15
|
+
- `cds add github-actions` sets required `permissions`.
|
|
16
|
+
- `cds up` supports a deployment layout where approuter or portal service configuration is in a top-level `.deploy` folder.
|
|
17
|
+
- `cds add ias` sets the `access-token-format` to `jwt` by default.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- `cds add sample` provides more i18n translations.
|
|
22
|
+
- `cds add ias` sets the `xsuaa-cross-consumption` field to `true` by default.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- `cds add ias` adds a subdomain-less application URL in `redirect_uris`.
|
|
27
|
+
|
|
28
|
+
## Version 9.2.1 - 2025-08-22
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- `cds add github-actions` adds `if: always()` to the scripts retrieving Cloud Foundry logs.
|
|
33
|
+
- `cds up` uses a default timeout of 10 minutes for Helm upgrades.
|
|
34
|
+
- `cds bind` works out-of-the-box for PostgreSQL databases.
|
|
35
|
+
- `cds bind -a` gives warning if no services are bound to the targeted app.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- `cds add github-actions` won't try to merge a `cf-info` action if there's an existing `mta.yaml`.
|
|
40
|
+
- `cds add workzone` with missing `sap.app` config in `manifest.json` does not throw a `TypeError`.
|
|
41
|
+
- `cds compile --to xsuaa` generates roles for `@requires` in bound actions.
|
|
42
|
+
- `cds add lint` now adds proper configuration to enable linting of JavaScript and TypeScript files in VS Code.
|
|
43
|
+
- `cds add mta` does no longer adds services in `mta.yaml` for plugins coming from `devDependencies`.
|
|
44
|
+
|
|
9
45
|
## Version 9.2.0 - 2025-07-31
|
|
10
46
|
|
|
11
47
|
### Added
|
package/bin/bind.js
CHANGED
|
@@ -46,7 +46,7 @@ module.exports = Object.assign(cds_bind, {
|
|
|
46
46
|
|
|
47
47
|
*-a | --to-app-services* <app>
|
|
48
48
|
|
|
49
|
-
Bind services of a given application
|
|
49
|
+
Bind services of a given application.
|
|
50
50
|
|
|
51
51
|
*--no-create-service-key*
|
|
52
52
|
|
|
@@ -83,7 +83,7 @@ const { bind, storeBindings } = require('../lib/bind/shared');
|
|
|
83
83
|
|
|
84
84
|
|
|
85
85
|
async function cds_bind(args, options) {
|
|
86
|
-
const { on, 'to-app-services': app, to, out, 'output-file': outputFile, exec, credentials } = options
|
|
86
|
+
const { on = 'cf', 'to-app-services': app, to, out, 'output-file': outputFile, exec, credentials } = options
|
|
87
87
|
|
|
88
88
|
if (exec) {
|
|
89
89
|
process.env.CDS_ENV ??= 'hybrid'
|
|
@@ -103,10 +103,6 @@ async function cds_bind(args, options) {
|
|
|
103
103
|
options.out ??= outputFile
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
if (on === 'k8s' && app) {
|
|
107
|
-
throw `Option ${term.bold('--to-app-services')} is only supported for Cloud Foundry.`;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
106
|
if (credentials === true) {
|
|
111
107
|
throw `Option ${term.bold('--credentials')} requires a JSON object or a file path.`;
|
|
112
108
|
}
|
|
@@ -155,9 +151,12 @@ async function cds_bind(args, options) {
|
|
|
155
151
|
options.for ??= 'hybrid';
|
|
156
152
|
|
|
157
153
|
if (app) {
|
|
158
|
-
const cds = require('../lib/cds')
|
|
154
|
+
const cds = require('../lib/cds')
|
|
159
155
|
const resolvedServices = [], unresolvedServices = {}
|
|
160
|
-
const
|
|
156
|
+
const plugin = require(`../lib/bind/${on}`)
|
|
157
|
+
const services = await plugin.services4(app)
|
|
158
|
+
|
|
159
|
+
if (!services) console.warn(`no services bound to app ${app} - nothing to bind`)
|
|
161
160
|
|
|
162
161
|
services.forEach(service => {
|
|
163
162
|
const type = service.label || service.type;
|
package/bin/up.js
CHANGED
|
@@ -30,7 +30,7 @@ module.exports = Object.assign(up, {
|
|
|
30
30
|
const { execSync, spawn } = require('node:child_process')
|
|
31
31
|
const cds = require('../lib/cds')
|
|
32
32
|
const { highlight, bold } = require('../lib/util/term')
|
|
33
|
-
const { exists, fs, path } = cds.utils
|
|
33
|
+
const { exists, fs, path, path: { join } } = cds.utils
|
|
34
34
|
const { readProject } = require('../lib/init/projectReader')
|
|
35
35
|
|
|
36
36
|
const run = cmd => {
|
|
@@ -79,14 +79,18 @@ async function up() {
|
|
|
79
79
|
if (exists('mtx/sidecar') && !exists('mtx/sidecar/package-lock.json')) {
|
|
80
80
|
run('npm i --package-lock-only --prefix mtx/sidecar')
|
|
81
81
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
|
|
83
|
+
const app = cds.env.folders.app
|
|
84
|
+
const legacyToPreferred = {
|
|
85
|
+
[join(app, 'router')]: '.deploy/app-router',
|
|
86
|
+
[join(app, 'portal')]: '.deploy/portal',
|
|
87
|
+
[join(app, 'html5-deployer')]: '.deploy/html5-deployer'
|
|
87
88
|
}
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
for (const [legacy, preferred] of Object.entries(legacyToPreferred)) {
|
|
90
|
+
const root = exists(preferred) ? preferred : legacy
|
|
91
|
+
if (exists(join(root, 'package.json')) && !exists(join(root, 'package-lock.json'))) {
|
|
92
|
+
run(`npm i --package-lock-only --prefix ${root}`)
|
|
93
|
+
}
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
if (cds.cli.options.to === 'k8s') {
|
|
@@ -116,7 +120,7 @@ async function up() {
|
|
|
116
120
|
|
|
117
121
|
run('npm i --package-lock')
|
|
118
122
|
await runDetectingError('ctz containerize.yaml --log --push', /^\[?ERROR\]?.*/m)
|
|
119
|
-
let helmUpgrade = `helm upgrade --install ${appName} ./gen/chart ${nsHelm} --wait --wait-for-jobs`
|
|
123
|
+
let helmUpgrade = `helm upgrade --install ${appName} ./gen/chart ${nsHelm} --wait --wait-for-jobs --timeout=10m`
|
|
120
124
|
if (hasXsuaa) helmUpgrade += ' --set-file xsuaa.jsonParameters=xs-security.json'
|
|
121
125
|
run(helmUpgrade)
|
|
122
126
|
run(`kubectl rollout status deployment ${appName}-srv ${nsKubectl} --timeout=8m`)
|
package/lib/bind/cf.js
CHANGED
|
@@ -33,6 +33,10 @@ module.exports = new class CFBind extends require('./plugin') {
|
|
|
33
33
|
return service
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
async services4(app) {
|
|
37
|
+
return (await cf.services(app)).filter(s => Object.keys(s.credentials).length > 0)
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
async resolve(name, binding) {
|
|
37
41
|
this.target ??= await cf.target()
|
|
38
42
|
this.#checkApiEndpoint(name, binding)
|
package/lib/bind/k8s.js
CHANGED
|
@@ -30,6 +30,27 @@ module.exports = new class K8SBind extends require('./plugin') {
|
|
|
30
30
|
return service
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
services4(app) {
|
|
34
|
+
const { execSync } = require('node:child_process')
|
|
35
|
+
const json = cmd => JSON.parse(execSync(cmd, { encoding: 'utf8' }))
|
|
36
|
+
|
|
37
|
+
const bindings = json('kubectl get servicebindings -o json').items
|
|
38
|
+
.filter(b => b.metadata.name.startsWith(app))
|
|
39
|
+
if (!bindings.length) throw `cannot find services with metadata name starting with '${app}'`
|
|
40
|
+
|
|
41
|
+
const offerings = Object.fromEntries(
|
|
42
|
+
json('kubectl get serviceinstances -o json').items
|
|
43
|
+
.map(i => [i.metadata.name, i.spec.serviceOfferingName])
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
for (const b of bindings) {
|
|
47
|
+
const offering = offerings[b.spec.serviceInstanceName]
|
|
48
|
+
b.label = b.type = offering
|
|
49
|
+
b.name = b.metadata.name
|
|
50
|
+
}
|
|
51
|
+
return bindings
|
|
52
|
+
}
|
|
53
|
+
|
|
33
54
|
async resolve(name, binding) {
|
|
34
55
|
const { context, namespace, cluster } = await this.#context4(binding)
|
|
35
56
|
|
package/lib/bind/plugin.js
CHANGED
|
@@ -36,6 +36,15 @@ module.exports = class BindPlugin {
|
|
|
36
36
|
throw new Error('credentials4 not implemented')
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Returns the services for the given app
|
|
41
|
+
* @param {string} app - The name of the app
|
|
42
|
+
* @returns {Promise<Array>} A promise that resolves to an array of bound service names
|
|
43
|
+
*/
|
|
44
|
+
services4(_app) {
|
|
45
|
+
throw new Error('services4 not implemented')
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
/**
|
|
40
49
|
* Resolves the binding configuration for the given service name
|
|
41
50
|
* @param {string} name - The name of the service
|
package/lib/bind/shared.js
CHANGED
|
@@ -23,7 +23,8 @@ const KindToRequiresNameMap = {
|
|
|
23
23
|
'enterprise-messaging-shared': 'messaging',
|
|
24
24
|
'redis-messaging': 'messaging',
|
|
25
25
|
'multitenancy': 'multitenancy',
|
|
26
|
-
'portal': 'portal'
|
|
26
|
+
'portal': 'portal',
|
|
27
|
+
'postgres': 'db'
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
const PreferredKinds = {
|
|
@@ -77,7 +78,8 @@ async function bind(options = { on: 'cf' }) {
|
|
|
77
78
|
resolved.name = options.serviceArg
|
|
78
79
|
} else if (kind) {
|
|
79
80
|
const requiresName = KindToRequiresNameMap[kind]
|
|
80
|
-
|
|
81
|
+
|
|
82
|
+
if (!requiresName) throw `unknown CDS service name for service ${bold(instance)} of kind ${bold(kind)} – please specify as argument for ${bold('cds bind')}`
|
|
81
83
|
resolved.name = requiresName
|
|
82
84
|
}
|
|
83
85
|
resolved.name ??= `custom-service:${instance || secret}`
|
|
@@ -8,24 +8,13 @@ const BuildTaskProvider = require('./buildTaskProvider')
|
|
|
8
8
|
const { BuildError, normalizePath } = require('./util')
|
|
9
9
|
const DEBUG = cds.debug('cli|build')
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* @typedef {{
|
|
13
|
-
* clean?: boolean,
|
|
14
|
-
* outputMode?: number,
|
|
15
|
-
* for?: string,
|
|
16
|
-
* src?: string,
|
|
17
|
-
* dest?: string,
|
|
18
|
-
* } & {[key:string]:unknown}} BuildTaskProviderFactoryOptions
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
11
|
/**
|
|
22
|
-
*
|
|
23
|
-
* @typedef {object} Task
|
|
12
|
+
* @typedef {import('./plugin').Task} Task
|
|
24
13
|
*/
|
|
25
14
|
|
|
26
15
|
class BuildTaskProviderFactory {
|
|
27
16
|
/**
|
|
28
|
-
* @param {
|
|
17
|
+
* @param {import('./index').BuildOptions} options - build options
|
|
29
18
|
*/
|
|
30
19
|
constructor(options = {}) {
|
|
31
20
|
options.outputMode = options.outputMode || OUTPUT_MODE_FILESYSTEM
|
|
@@ -43,7 +32,7 @@ class BuildTaskProviderFactory {
|
|
|
43
32
|
}
|
|
44
33
|
}
|
|
45
34
|
})
|
|
46
|
-
/** @type {{ options:
|
|
35
|
+
/** @type {{ options: import('./index').BuildOptions, tasks: Task[]}} */
|
|
47
36
|
this._context = { options, tasks: [] } // resolved tasks
|
|
48
37
|
}
|
|
49
38
|
get options() {
|
|
@@ -112,6 +101,7 @@ class BuildTaskProviderFactory {
|
|
|
112
101
|
* 'for' defines an alias for built-in plugins like 'hana', 'java', 'node', 'fiori' or 'mtx'.
|
|
113
102
|
* 'use' defines the fully qualified module name of custom build plugins implementations.
|
|
114
103
|
* @param {Task} task
|
|
104
|
+
* @returns {typeof import('./plugin')}
|
|
115
105
|
*/
|
|
116
106
|
getPlugin(task) {
|
|
117
107
|
const provider = this._getProvider(task)
|
|
@@ -227,6 +217,10 @@ class PluginBuildTaskProvider extends BuildTaskProvider {
|
|
|
227
217
|
})
|
|
228
218
|
}
|
|
229
219
|
|
|
220
|
+
/**
|
|
221
|
+
* @param {{for: string}} key
|
|
222
|
+
* @returns {Task}
|
|
223
|
+
*/
|
|
230
224
|
getTask(key) {
|
|
231
225
|
// plugin entry is already validated, a taskDefaults default member exists
|
|
232
226
|
const task = JSON.parse(JSON.stringify(this.plugins.get(key.for)?.taskDefaults ?? {}))
|
package/lib/build/constants.js
CHANGED
|
@@ -38,6 +38,7 @@ exports.OPTION_WS = "ws" // run a workspa
|
|
|
38
38
|
exports.OPTION_WS_PACK = "ws-pack" // enable tarball based packaging of workspace dependencies
|
|
39
39
|
exports.CSV_FILE_TARGET = "csvFileTarget" // target folder when copying CSV files to the deployment target folder ./db/src/gen/*, default is 'data'
|
|
40
40
|
exports.CSV_FILE_DETECTION = "csvFileDetection" // detect CSV files based on CDS model location - ENABLED by default
|
|
41
|
+
exports.CSV_TRIM_WHITESPACES = "trimCsvWhitespaces" // trim whitespaces from CSV column names and values - DISABLED by default
|
|
41
42
|
exports.EXTENSION_POINT_VALIDATION = "extensionPointValidation" // validate extension point restrictions defined by the extended SaaS app - ENABLED by default
|
|
42
43
|
exports.MTX_SIDECAR_DB_VALIDATION = "mtxSidecarDbValidation" // validate consistency of app and sidecar db configuration - ENABLED by default
|
|
43
44
|
exports.CONTINUE_UNRESOLVED_SCHEMA_CHANGES = "continueUnresolvedSchemaChanges" // build doesn't fail if unresolved HANA schema changes are detected
|
package/lib/build/index.js
CHANGED
|
@@ -6,10 +6,25 @@ const { BuildError } = require('./util')
|
|
|
6
6
|
|
|
7
7
|
module.exports = { build, register, Plugin, BuildError }
|
|
8
8
|
|
|
9
|
+
// reuse schema definition from cds-rc.js used for package.json code completion
|
|
10
|
+
/** @typedef{import('../env/schemas/cds-rc')['$defs']['buildTaskType']['enum'][number]} BuildFor */
|
|
11
|
+
|
|
12
|
+
// using (string & {}) prevents widening of `'x' | string` to just `string`
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {{
|
|
15
|
+
* clean?: boolean,
|
|
16
|
+
* outputMode?: number,
|
|
17
|
+
* for?: BuildFor | (string & {}),
|
|
18
|
+
* src?: string,
|
|
19
|
+
* dest?: string,
|
|
20
|
+
* model?: string[]
|
|
21
|
+
* } & {[key:string]:unknown}} BuildOptions
|
|
22
|
+
*/
|
|
23
|
+
|
|
9
24
|
/**
|
|
10
25
|
* Executes cds build in the directory defined by cds.root.
|
|
11
26
|
*
|
|
12
|
-
* @param {
|
|
27
|
+
* @param {BuildOptions} options - command options as defined by build command.
|
|
13
28
|
*/
|
|
14
29
|
async function build(options = {}) {
|
|
15
30
|
if (!fs.existsSync(cds.root) || !cds.utils.isdir(cds.root)) {
|
|
@@ -31,4 +46,4 @@ async function build(options = {}) {
|
|
|
31
46
|
|
|
32
47
|
function register(id, plugin) {
|
|
33
48
|
return require('./plugins').register(id, plugin)
|
|
34
|
-
}
|
|
49
|
+
}
|
package/lib/build/plugin.js
CHANGED
|
@@ -21,7 +21,7 @@ const DEBUG = cds.debug('cli|build')
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* The build plugin creates the build output for a dedicated build task. It is uniquely identified
|
|
24
|
-
* by the build task's `for<` or `use` property. The `use` property represents
|
|
24
|
+
* by the build task's `for<` or `use` property. The `use` property represents
|
|
25
25
|
* the fully qualified node module path of the build plugin implementation.
|
|
26
26
|
* The build task engine defines the following protocol. The methods are invoked in descending order:
|
|
27
27
|
* - `init()` - optional
|
|
@@ -40,13 +40,11 @@ class Plugin {
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* @type {Task | undefined}
|
|
43
|
-
* @private
|
|
44
43
|
*/
|
|
45
44
|
_task
|
|
46
45
|
|
|
47
46
|
/**
|
|
48
47
|
* @type {Context | undefined}
|
|
49
|
-
* @private
|
|
50
48
|
*/
|
|
51
49
|
_context
|
|
52
50
|
|
|
@@ -69,7 +67,7 @@ class Plugin {
|
|
|
69
67
|
*/
|
|
70
68
|
_messages = []
|
|
71
69
|
/**
|
|
72
|
-
* Returns a list of build and compiler messages created by this build plugin.
|
|
70
|
+
* Returns a list of build and compiler messages created by this build plugin.
|
|
73
71
|
* Supported message severities are 'Info', 'Warning', and 'Error'.
|
|
74
72
|
*/
|
|
75
73
|
get messages() {
|
|
@@ -176,9 +174,9 @@ class Plugin {
|
|
|
176
174
|
|
|
177
175
|
/**
|
|
178
176
|
* Asynchronously copies a single file or the entire directory structure from 'src' to 'dest', including subdirectories and files.
|
|
179
|
-
*
|
|
177
|
+
*
|
|
180
178
|
* Note: The file names are stored in the list of files written by this build plugin.
|
|
181
|
-
*
|
|
179
|
+
*
|
|
182
180
|
* @param {string} src - The absolute or relative source path of the file or directory to copy.
|
|
183
181
|
* Relative paths will be resolved to this task's source path.
|
|
184
182
|
* @returns {FileWriter}
|
|
@@ -216,7 +214,7 @@ class Plugin {
|
|
|
216
214
|
|
|
217
215
|
/**
|
|
218
216
|
* Adds the given user message and severity to the list of messages issued by this build task.
|
|
219
|
-
*
|
|
217
|
+
*
|
|
220
218
|
* User messages will be logged after CDS build has been finished based on the log-level that has been set.
|
|
221
219
|
* By default messages with severity <em>warning</em> and <em>error</em> will be logged.
|
|
222
220
|
* @param {string} message - the message text
|
|
@@ -74,7 +74,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
74
74
|
await this._writePackageJson()
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
// copy native hana artifacts from other workspaces including .hdbmigrationtable files defined for
|
|
77
|
+
// copy native hana artifacts from other workspaces including .hdbmigrationtable files defined for
|
|
78
78
|
// common model artifacts that are not part of the current project
|
|
79
79
|
if (wsPaths.length || cds.cli.options?.[OPTION_WS] || hasOptionValue(this.task.options[OPTION_WS], true)) {
|
|
80
80
|
// first is winning, e.g. a .hdiconfig file might exist in multiple workspaces
|
|
@@ -272,7 +272,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
272
272
|
|
|
273
273
|
for (const file of csvFiles) {
|
|
274
274
|
if (this.isStagingBuild() || file.startsWith(this.task.options.compileDest)) {
|
|
275
|
-
await stripComments(file)
|
|
275
|
+
await stripComments(file, null, this.task.options.trimCsvWhitespaces)
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
}
|
|
@@ -394,7 +394,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
394
394
|
}
|
|
395
395
|
}
|
|
396
396
|
// The last-dev CSN shall only be updated, if all .hdbmigrationtable files could be successfully updated.
|
|
397
|
-
// Of course, .hdbmigrationtable files already written would need to be manually reverted before the
|
|
397
|
+
// Of course, .hdbmigrationtable files already written would need to be manually reverted before the
|
|
398
398
|
// cds build command is again executed.
|
|
399
399
|
await Promise.all(promises)
|
|
400
400
|
|
|
@@ -578,7 +578,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
578
578
|
/**
|
|
579
579
|
* Returns all hana native artifacts located in db/src/** workspace folders.
|
|
580
580
|
* @param {Array} workspacePaths Array of workspace paths, 'cds.root' is excluded from the list
|
|
581
|
-
* @returns {Array} Array of workspace files, each entry contains the db src directory,
|
|
581
|
+
* @returns {Array} Array of workspace files, each entry contains the db src directory,
|
|
582
582
|
* the relative file path and the file name.
|
|
583
583
|
*/
|
|
584
584
|
async _getWorkspaceFiles(workspacePaths) {
|
package/lib/compile/to-xsuaa.js
CHANGED
|
@@ -60,8 +60,10 @@ module.exports = function compile_to_xsuaa (model, options) {
|
|
|
60
60
|
return findUserAttrInExpression(expr)
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
const authAnnotations = def => {
|
|
64
|
+
if (!def) return
|
|
65
|
+
|
|
66
|
+
let reqAnnotations = def['@requires']
|
|
65
67
|
if(!Array.isArray(reqAnnotations)) reqAnnotations=[reqAnnotations]
|
|
66
68
|
|
|
67
69
|
reqAnnotations.forEach(scope => {
|
|
@@ -84,7 +86,14 @@ module.exports = function compile_to_xsuaa (model, options) {
|
|
|
84
86
|
lattributes.forEach(attr => attributes[attr] = true)
|
|
85
87
|
})
|
|
86
88
|
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
cds.reflect(model).foreach(def => {
|
|
92
|
+
authAnnotations(def)
|
|
93
|
+
if (def.actions) Object.values(def.actions).forEach(authAnnotations)
|
|
94
|
+
if (def.functions) Object.values(def.functions).forEach(authAnnotations)
|
|
87
95
|
})
|
|
96
|
+
|
|
88
97
|
const roleTemplates = Object.keys(scopes).map((s) => {
|
|
89
98
|
return {
|
|
90
99
|
name: s,
|
|
@@ -250,7 +250,7 @@ spec:
|
|
|
250
250
|
if (/[^\w-_]+/g.exec(cfServiceInstanceName)) {
|
|
251
251
|
throw `Service name ${cfServiceInstanceName} must only contain alpha-numeric, hyphens, and underscores.`;
|
|
252
252
|
}
|
|
253
|
-
console.log(`
|
|
253
|
+
console.log(`using container ${bold(cfServiceInstanceName)}`);
|
|
254
254
|
|
|
255
255
|
const cfConfig = cfServiceInstanceMta?.parameters?.config;
|
|
256
256
|
const serviceInstance = await cfUtil.getOrCreateService('hana', 'hdi-shared', cfServiceInstanceName, cfConfig);
|
|
@@ -6,7 +6,7 @@ const HDI_DEPLOY_LIB = '@sap/hdi-deploy';
|
|
|
6
6
|
module.exports = new class HdiDeployUtil {
|
|
7
7
|
|
|
8
8
|
async deploy(dbDir, vcapEnv = {}, { autoUndeploy, parameter={} } = {}) {
|
|
9
|
-
console.log(`
|
|
9
|
+
console.log(`deploying to HANA from ${dbDir}`);
|
|
10
10
|
|
|
11
11
|
const { clean_env, deploy } = this._loadHdiDeployLib(dbDir);
|
|
12
12
|
|
|
@@ -278,7 +278,7 @@ module.exports = {
|
|
|
278
278
|
},
|
|
279
279
|
|
|
280
280
|
buildTaskType: {
|
|
281
|
-
enum: [
|
|
281
|
+
enum: /**@type { const } */ ([
|
|
282
282
|
"nodejs",
|
|
283
283
|
"java",
|
|
284
284
|
"hana",
|
|
@@ -288,7 +288,7 @@ module.exports = {
|
|
|
288
288
|
"node-cf",
|
|
289
289
|
"java-cf",
|
|
290
290
|
"helm"
|
|
291
|
-
],
|
|
291
|
+
]),
|
|
292
292
|
enumDescriptions: [
|
|
293
293
|
"Node.js build plugin",
|
|
294
294
|
"Java build plugin",
|
package/lib/init/constants.js
CHANGED
|
@@ -5,7 +5,7 @@ module.exports = {
|
|
|
5
5
|
COMMAND_ADD: 'add',
|
|
6
6
|
|
|
7
7
|
JAVA_LTS_VERSIONS: [17, 21],
|
|
8
|
-
MAVEN_ARCHETYPE_VERSION: '4.
|
|
8
|
+
MAVEN_ARCHETYPE_VERSION: '4.2.0',
|
|
9
9
|
|
|
10
10
|
OPTIONS: Object.freeze({
|
|
11
11
|
COMPLETION: 'completion',
|
|
@@ -60,6 +60,7 @@ module.exports = {
|
|
|
60
60
|
NOTIFICATIONS: 'notifications',
|
|
61
61
|
WORKZONE: 'workzone',
|
|
62
62
|
WORKZONE_STANDARD: 'workzone-standard',
|
|
63
|
+
UI5: 'ui5',
|
|
63
64
|
DATA: 'data',
|
|
64
65
|
HTTP: 'http',
|
|
65
66
|
LINT: 'lint',
|
|
@@ -74,7 +75,7 @@ module.exports = {
|
|
|
74
75
|
|
|
75
76
|
URLS: Object.freeze({
|
|
76
77
|
PIPELINE_HELP: 'https://www.project-piper.io/',
|
|
77
|
-
SAMPLES: 'https://github.com/
|
|
78
|
+
SAMPLES: 'https://github.com/capire/samples',
|
|
78
79
|
CAPIRE: 'https://cap.cloud.sap',
|
|
79
80
|
MAVEN_ARCHETYPE_HELP: `https://cap.cloud.sap/docs/java/developing-applications/building#the-maven-archetype`,
|
|
80
81
|
MAVEN_INSTALL_HELP: `https://maven.apache.org/install.html`,
|
|
@@ -47,12 +47,14 @@ module.exports = new class ProjectReader {
|
|
|
47
47
|
* Returns cds.env using 'production' profile by default as mta deployment is executed with having production profile set.
|
|
48
48
|
*/
|
|
49
49
|
env4(profile = 'production') {
|
|
50
|
+
const nodeEnv = process.env.NODE_ENV
|
|
50
51
|
const cdsEnv = process.env.CDS_ENV
|
|
51
|
-
process.env.CDS_ENV = profile
|
|
52
|
+
process.env.CDS_ENV = process.env.NODE_ENV = profile
|
|
52
53
|
try {
|
|
53
54
|
return cds.env.for('cds')
|
|
54
55
|
} finally {
|
|
55
56
|
cdsEnv ? process.env.CDS_ENV = cdsEnv : delete process.env.CDS_ENV
|
|
57
|
+
nodeEnv ? process.env.NODE_ENV = nodeEnv : delete process.env.NODE_ENV
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
|
|
@@ -122,7 +124,6 @@ module.exports = new class ProjectReader {
|
|
|
122
124
|
const _isTypescript = () => !!pkgJson().dependencies?.typescript || !!pkgJson().devDependencies?.typescript
|
|
123
125
|
const _hasTyper = () => !!pkgJson().dependencies?.['@cap-js/cds-typer'] || !!pkgJson().devDependencies?.['@cap-js/cds-typer']
|
|
124
126
|
const _inProd = plugin => require(`./template/${plugin}`).hasInProduction(env) || cds.cli.argv.includes(plugin) || cds.cli.options.add?.has(plugin)
|
|
125
|
-
const _ui5 = () => pkgJson().sapux
|
|
126
127
|
const _uiEmbedded = () => exists(join(env.folders.app, 'xs-app.json'))
|
|
127
128
|
const _uiModule = () => !_uiEmbedded() && isdir(env.folders.app) && fs.readdirSync(resolve(cds.root, env.folders.app)).length > 0
|
|
128
129
|
const _isJava = () => this.isJava
|
|
@@ -132,6 +133,7 @@ module.exports = new class ProjectReader {
|
|
|
132
133
|
}
|
|
133
134
|
const reserved = {
|
|
134
135
|
dependencies: () => pkgJson().dependencies,
|
|
136
|
+
devDependencies: () => pkgJson().devDependencies,
|
|
135
137
|
has: x => _inProd(x),
|
|
136
138
|
profile: () => cds.cli.options?.for,
|
|
137
139
|
language: () => _isJava() ? 'java' : 'nodejs',
|
|
@@ -181,7 +183,7 @@ module.exports = new class ProjectReader {
|
|
|
181
183
|
appPath: () => env.folders.app,
|
|
182
184
|
appVersion: () => appVersion,
|
|
183
185
|
jdkVersion: () => jdkVersion,
|
|
184
|
-
approuterPath: () => (_uiEmbedded() ? join(env.folders.app) : join(env.folders.app, 'router')).replace(/\\/g, '/'),
|
|
186
|
+
approuterPath: () => (_uiEmbedded() ? join(env.folders.app) : exists('.deploy/app-router') ? join('.deploy', 'app-router') : join(env.folders.app, 'router')).replace(/\\/g, '/'),
|
|
185
187
|
appName: () => appName,
|
|
186
188
|
cleanedAppName: () => appName.replaceAll('_', '-'),
|
|
187
189
|
strippedAppName: () => appName.replace(/-/g, ''),
|
|
@@ -190,8 +192,6 @@ module.exports = new class ProjectReader {
|
|
|
190
192
|
hasUIEmbedded: _uiEmbedded,
|
|
191
193
|
hasUIModule: _uiModule,
|
|
192
194
|
hasUI: () => _uiEmbedded() || _uiModule(),
|
|
193
|
-
isUI5: _ui5,
|
|
194
|
-
hasUI5: _ui5,
|
|
195
195
|
literal: () => () => text => text,
|
|
196
196
|
hasMTXRoute: () => _inProd('extensibility') || (_inProd('multitenancy') && !_isJava()),
|
|
197
197
|
hasMTXRouteJava: () => (_inProd('helm-unified-runtime') || _inProd('helm')) && _isJava() && _inProd('multitenancy'),
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const cds = require('../../../cds')
|
|
2
1
|
const { readProject } = require('../../projectReader')
|
|
3
2
|
const { merge, sort } = require('../../merge')
|
|
4
3
|
const { srv4 } = require('../../registries/mta')
|
|
@@ -15,7 +14,7 @@ module.exports = class AmsTemplate extends require('../../plugin') {
|
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
static hasInProduction(env) {
|
|
18
|
-
return env.requires?.auth?.ams ||
|
|
17
|
+
return env.requires?.auth?.ams || readProject().devDependencies?.['@sap/ams']
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
async run() {
|
|
@@ -19,7 +19,7 @@ module.exports = class ApprouterTemplate extends require('../../plugin') {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
static hasInProduction(env) {
|
|
22
|
-
return exists(join(env.folders.app, 'xs-app.json')) || exists(join(env.folders.app, 'router', 'xs-app.json'))
|
|
22
|
+
return exists(join('.deploy', 'app-router')) || exists(join(env.folders.app, 'xs-app.json')) || exists(join(env.folders.app, 'router', 'xs-app.json'))
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
async run() {
|
|
@@ -13,7 +13,7 @@ module.exports = class AttachmentsTemplate extends require('../../plugin') {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
static hasInProduction(env) {
|
|
16
|
-
return !!env.requires?.attachments
|
|
16
|
+
return !!env.requires?.attachments || readProject().dependencies?.['@cap-js/attachments']
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
async canRun() {
|
|
@@ -12,7 +12,7 @@ module.exports = class AuditLoggingTemplate extends require('../../plugin') {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
static hasInProduction(env) {
|
|
15
|
-
return env.requires?.['audit-log']
|
|
15
|
+
return env.requires?.['audit-log'] || readProject().dependencies?.['@cap-js/audit-logging']
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
options() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
entity my.bookshop.Books {
|
|
2
|
-
key ID
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
key ID : Integer;
|
|
3
|
+
title : String @title: 'Title';
|
|
4
|
+
stock : Integer @title: 'Stock';
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
service CatalogService {
|
|
@@ -9,7 +9,7 @@ service CatalogService {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
annotate CatalogService.Books with @UI.LineItem: [
|
|
12
|
-
{
|
|
13
|
-
{
|
|
14
|
-
{
|
|
12
|
+
{Value: ID},
|
|
13
|
+
{Value: title},
|
|
14
|
+
{Value: stock},
|
|
15
15
|
];
|
|
@@ -28,7 +28,7 @@ module.exports = class ExtensionProjectTemplate extends require('../../plugin')
|
|
|
28
28
|
if (typeof base !== 'string' || !base?.length) base = '@capire/bookshop'
|
|
29
29
|
|
|
30
30
|
const pkgJson = exists('package.json') ? await read('package.json') : { name: 'my-extension-project' }
|
|
31
|
-
if (!pkgJson.extends
|
|
31
|
+
if (force || !pkgJson.extends) {
|
|
32
32
|
pkgJson.extends = base
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ module.exports = class ExtensionProjectTemplate extends require('../../plugin')
|
|
|
38
38
|
}
|
|
39
39
|
await write('package.json', pkgJson, { spaces: 2 })
|
|
40
40
|
|
|
41
|
-
if (!exists('.base/package.json')
|
|
41
|
+
if (force || !exists('.base/package.json')) {
|
|
42
42
|
await write('.base/package.json', {name: base})
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -56,17 +56,17 @@ module.exports = class ExtensionProjectTemplate extends require('../../plugin')
|
|
|
56
56
|
const extensionNamespace = `${namespacify(proj.appName)}.${namespacify(projectSimple)}`
|
|
57
57
|
const { force } = cds.cli.options
|
|
58
58
|
|
|
59
|
-
if (!exists('.base/index.cds')
|
|
59
|
+
if (force || !exists('.base/index.cds')) {
|
|
60
60
|
await copy(join(__dirname, 'files/.base/index.cds'), '.base/index.cds')
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
if (!exists('app/extension.cds')
|
|
63
|
+
if (force || !exists('app/extension.cds')) {
|
|
64
64
|
await renderAndCopy(join(__dirname, 'files/app'), 'app', {
|
|
65
65
|
basePackage: base,
|
|
66
66
|
extensionNamespace
|
|
67
67
|
})
|
|
68
68
|
}
|
|
69
|
-
if (!exists('test/data')
|
|
69
|
+
if (force || !exists('test/data')) {
|
|
70
70
|
await copy(join(__dirname, 'files/test/data/my.bookshop-Books.csv'), 'test/data/my.bookshop-Books.csv')
|
|
71
71
|
await copy(join(__dirname, 'files/test/data/Authors.csv'), `test/data/${extensionNamespace}-x_Authors.csv`)
|
|
72
72
|
}
|