@sap/cds-dk 9.3.1 → 9.4.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 +48 -0
- package/bin/bind.js +1 -1
- package/bin/cds.js +3 -0
- package/bin/compile/index.js +53 -3
- package/bin/data-products/get-csn-via-dp-cli.js +41 -0
- package/bin/data-products/get-csn.js +76 -0
- package/bin/data-products/import.js +114 -0
- package/bin/data-products/index.js +30 -0
- package/bin/data-products/merge-csns.js +46 -0
- package/bin/data-products/prepare-csn.js +282 -0
- package/bin/data-products/prepare-files.js +69 -0
- package/bin/export.js +240 -0
- package/bin/help/export.md +37 -0
- package/bin/help/import.md +11 -0
- package/bin/help/minify.md +23 -0
- package/bin/import.js +13 -3
- package/bin/inspect.js +226 -0
- package/bin/minify.js +123 -0
- package/bin/up.js +3 -24
- package/bin/util/cli.js +132 -0
- package/bin/util/log.js +2 -0
- package/bin/version.js +2 -1
- package/bin/watch.js +21 -3
- package/lib/bind/shared.js +3 -1
- package/lib/build/buildTaskEngine.js +2 -1
- package/lib/build/plugins/helm/files/templates/NOTES.txt +1 -1
- package/lib/build/plugins/helm/index.js +1 -1
- package/lib/build/provider/internalBuildTaskProvider.js +3 -3
- package/lib/build/provider/nodejs/index.js +62 -3
- package/lib/deploy/index.js +1 -1
- package/lib/deploy/to-hana/hana.js +18 -16
- package/lib/import/odata/v2parser.js +6 -2
- package/lib/import/odata/v4parser.js +21 -2
- package/lib/import/odata.package.json +4 -4
- package/lib/init/constants.js +2 -1
- package/lib/init/index.js +2 -0
- package/lib/init/mvn.js +3 -2
- package/lib/init/projectReader.js +1 -0
- package/lib/init/registries/mta.js +6 -0
- package/lib/init/template/ams/files/manifest.yml.hbs +1 -1
- package/lib/init/template/ams/files/mta.yaml.hbs +1 -1
- package/lib/init/template/app-frontend/files/app-package.json.hbs +20 -0
- package/lib/init/template/app-frontend/files/index.html.hbs +34 -0
- package/lib/init/template/app-frontend/files/manifest.json +13 -0
- package/lib/init/template/app-frontend/files/mta.yaml.hbs +71 -0
- package/lib/init/template/app-frontend/files/package.json.hbs +17 -0
- package/lib/init/template/app-frontend/files/ui5-deploy.yaml.hbs +25 -0
- package/lib/init/template/app-frontend/files/ui5.yaml.hbs +6 -0
- package/lib/init/template/app-frontend/files/values.yaml.hbs +20 -0
- package/lib/init/template/app-frontend/files/xs-app.json.hbs +17 -0
- package/lib/init/template/app-frontend/index.js +98 -0
- package/lib/init/template/approuter/files/mta.yaml.hbs +0 -2
- package/lib/init/template/attachments/index.js +4 -10
- package/lib/init/template/audit-logging/files/package.json.hbs +5 -0
- package/lib/init/template/github-actions/index.js +0 -2
- package/lib/init/template/handler/index.js +1 -0
- package/lib/init/template/helm/index.js +5 -1
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +9 -2
- package/lib/init/template/html5-repo/index.js +0 -3
- package/lib/init/template/ias/files/manifest.yml.hbs +1 -1
- package/lib/init/template/ias/files/mta.yaml.hbs +6 -5
- package/lib/init/template/ias/files/values.yaml.hbs +10 -1
- package/lib/init/template/multitenancy/files/mta.yaml.hbs +16 -3
- package/lib/init/template/multitenancy/files/package.json.hbs +5 -0
- package/lib/init/template/multitenancy/files/values.yaml.hbs +1 -6
- package/lib/init/template/portal/files/mta.yaml.hbs +12 -2
- package/lib/init/template/sample/files/db/schema.cds +5 -8
- package/lib/init/template/typer/files/xsconfig.json +2 -1
- package/lib/init/template/typescript/files/package.json +1 -1
- package/lib/init/template/typescript/files/tsconfig.json +4 -1
- package/lib/init/template/workzone-standard/files/mta.yaml.hbs +4 -0
- package/lib/init/template/workzone-standard/index.js +3 -2
- package/lib/init/template/xsuaa/files/package.json.hbs +3 -0
- package/lib/lint/eslintHelp.json +1 -1
- package/lib/util/term.js +2 -4
- package/npm-shrinkwrap.json +29 -29
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,53 @@ 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.4.1 - 2025-10-09
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `cds add app-frontend` is supported as an alias to `cds add app-front`.
|
|
14
|
+
- `cds add attachments` is supported for Java projects.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- `cds import` adds latest version 4 of `@sap-cloud-sdk` packages.
|
|
19
|
+
- `cds add sample` now uses Integer IDs for Books and Authors again, instead of UUIDs.
|
|
20
|
+
|
|
21
|
+
## Version 9.4.0 - 2025-10-02
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- `cds add app-front` adds configuration for the new SAP BTP Application Frontend service.
|
|
26
|
+
- `cds add ias` has improved support for XSUAA hybrid projects.
|
|
27
|
+
- `cds add multitenancy` automatically adds upgrade hooks for Node.js projects.
|
|
28
|
+
- `cds deploy` support the generic `--resolve-bindings` option to resolve all bound services. This is helpful for use cases with multiple `hana`-tagged service bindings.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- `cds add typescript` and `cds add typer` will now add a `path` entry in the project's _tsconfig.json_ or _jsconfig.json_ respectively, which will mitigate resolution problems with `@cap-js/cds-types`.
|
|
33
|
+
- `cds add multitenancy` adds the `with-mtx` profile to Java apps by default, simplifying local development.
|
|
34
|
+
- `cds import` for EDMX files now maps `Edm.String` types to `cds.String` (before: `cds.LargeString`). Background is that some databases don't support the resulting `NCLOB` type in key fields.
|
|
35
|
+
- `cds build` will now remove dev dependencies starting with `workspace:`, `file:`, as well as the entire `workspace:` block therein, and regenerate the _package-lock.json_ if needed.
|
|
36
|
+
- `cds import` for EDMX correctly imports multiline strings in `DefaultValue`.
|
|
37
|
+
- `cds import` now writes annotations from EDMX files in a flat, i.e. non-structured manner, so that they can be processed by the application runtime.
|
|
38
|
+
- `cds bind` does not fail when setting the profile via `CDS_ENV`.
|
|
39
|
+
- `cds version` is more robust with respect to Java versions.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- `cds add html5-repo` avoids some superfluous configuration combination with `cds add portal`.
|
|
44
|
+
- `cds add handler` ignores external services.
|
|
45
|
+
- `cds add audit-logging` correctly adds the dependency in the _Chart.yaml_ for Helm deployments.
|
|
46
|
+
- `cds deploy --to hana` works with user-provided HANA services.
|
|
47
|
+
- `cds bind` doesn't add the `custom-service:` prefix for Node.js any more.
|
|
48
|
+
|
|
49
|
+
## Version 9.3.2 - 2025-09-16
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- `cds add html5-repo` avoids some superfluous configuration combination with `cds add portal`.
|
|
54
|
+
- `cds import` correctly imports OData v2 EDMX files with `Edm.Time` properties having a precision.
|
|
55
|
+
|
|
9
56
|
## Version 9.3.1 - 2025-09-08
|
|
10
57
|
|
|
11
58
|
### Fixed
|
|
@@ -20,6 +67,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
|
20
67
|
- `cds add github-actions` correctly generates a release workflow.
|
|
21
68
|
- `cds add github-actions` uses a simplified and more resilient Kyma setup script.
|
|
22
69
|
- `cds import` now correctly imports EDMX files with empty NavigationPropertyPath tags.
|
|
70
|
+
- `cds import` now correctly imports OData v2 EDMX files with `Edm.Time` properties, which have a precision.
|
|
23
71
|
|
|
24
72
|
## Version 9.3.0 - 2025-09-01
|
|
25
73
|
|
package/bin/bind.js
CHANGED
|
@@ -94,7 +94,7 @@ async function cds_bind(args, options) {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
// Check if command was cds bind. Don't abort for cds deploy.
|
|
97
|
-
if (process.
|
|
97
|
+
if (process.argv.includes('--profile') && process.argv[2] === 'bind') {
|
|
98
98
|
throw `Option ${term.bold('--profile')} is no longer supported. Use ${term.bold('--for')} instead to specify the profile to use when storing connection settings.`;
|
|
99
99
|
}
|
|
100
100
|
|
package/bin/cds.js
CHANGED
|
@@ -51,6 +51,9 @@ const cli = {
|
|
|
51
51
|
|
|
52
52
|
const task = this.load(cmd, /*else*/(f) => argv.unshift(cmd) && (cmd = 'compile') && _isDefaultCompile(argv, f))
|
|
53
53
|
if (task) {
|
|
54
|
+
|
|
55
|
+
if (task._is_new_cmd) return await task.exec (this)
|
|
56
|
+
|
|
54
57
|
// REVISIT bind: '--' handling for cds bind --exec. Should be moved to cds_cli
|
|
55
58
|
const endOfOptions = argv.indexOf('--')
|
|
56
59
|
let appendArgs = []
|
package/bin/compile/index.js
CHANGED
|
@@ -4,13 +4,47 @@ const {colors} = require('../../lib/util/term')
|
|
|
4
4
|
module.exports = Object.assign ( compile, {
|
|
5
5
|
handleCompletion,
|
|
6
6
|
options: [
|
|
7
|
-
'--from',
|
|
7
|
+
'--from',
|
|
8
|
+
'--service',
|
|
9
|
+
'--lang',
|
|
10
|
+
'--for',
|
|
11
|
+
'--to',
|
|
12
|
+
'--dest',
|
|
13
|
+
'--log-level',
|
|
14
|
+
'--flavor',
|
|
15
|
+
'--dialect',
|
|
16
|
+
'--sql-dialect',
|
|
17
|
+
'--openapi:url',
|
|
18
|
+
'--openapi:servers',
|
|
19
|
+
'--openapi:config-file',
|
|
20
|
+
'--odata-version'
|
|
8
21
|
],
|
|
9
22
|
shortcuts: [
|
|
10
|
-
|
|
23
|
+
undefined,
|
|
24
|
+
'-s',
|
|
25
|
+
'-l',
|
|
26
|
+
'-4',
|
|
27
|
+
'-2',
|
|
28
|
+
'-o',
|
|
29
|
+
undefined,
|
|
30
|
+
'-f'
|
|
11
31
|
],
|
|
12
32
|
flags: [
|
|
13
|
-
|
|
33
|
+
'--parse',
|
|
34
|
+
'--plain',
|
|
35
|
+
'--clean',
|
|
36
|
+
'--files',
|
|
37
|
+
'--sources',
|
|
38
|
+
'--resolved',
|
|
39
|
+
'--all',
|
|
40
|
+
'--beta',
|
|
41
|
+
'--conceptual',
|
|
42
|
+
'--sort',
|
|
43
|
+
'--min',
|
|
44
|
+
'--docs',
|
|
45
|
+
'--locations',
|
|
46
|
+
'--openapi:diagram',
|
|
47
|
+
'--asyncapi:merged'
|
|
14
48
|
],
|
|
15
49
|
help: `
|
|
16
50
|
# SYNOPSIS
|
|
@@ -270,6 +304,9 @@ async function compile (models, options={}) {
|
|
|
270
304
|
chain = chain.then (next (processor ('for', each)))
|
|
271
305
|
}
|
|
272
306
|
|
|
307
|
+
// add processor for sort
|
|
308
|
+
if (options.sort) chain = chain.then (sort)
|
|
309
|
+
|
|
273
310
|
// add processors for compiler backends
|
|
274
311
|
if (typeof options.to === 'string') for (let each of options.to.split(',')) {
|
|
275
312
|
chain = chain.then (next (processor ('to', _suffix=each)))
|
|
@@ -382,4 +419,17 @@ function _log (messages, options) {
|
|
|
382
419
|
cds._log (messages, options)
|
|
383
420
|
}
|
|
384
421
|
|
|
422
|
+
function sort (o) {
|
|
423
|
+
const sorted = {}
|
|
424
|
+
for (let k of Object.keys(o).sort()) {
|
|
425
|
+
const val = o[k]
|
|
426
|
+
if (val && typeof val === 'object' && !Array.isArray(val)) {
|
|
427
|
+
sorted[k] = sort(val)
|
|
428
|
+
} else {
|
|
429
|
+
sorted[k] = val
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return sorted
|
|
433
|
+
}
|
|
434
|
+
|
|
385
435
|
/* eslint no-console:0 */
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const child_process = require ('node:child_process')
|
|
2
|
+
const fs = require ('node:fs')
|
|
3
|
+
|
|
4
|
+
exports.getDataProductCsnViaDpCli = function (ordId) {
|
|
5
|
+
if (dpCliIsAvailable()) {
|
|
6
|
+
const [namespace, , name, version] = ordId.split(':')
|
|
7
|
+
const apiHubName = [namespace, name, version].join('-').replaceAll('.', '-');
|
|
8
|
+
const dataProductMetadata = getDataProductMetadata(apiHubName);
|
|
9
|
+
if (dataProductMetadata.OrdId !== ordId) {
|
|
10
|
+
throw new Error(`Data Product with OrdId ${ordId} not found in API Hub!`)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return getDataProductCsn(apiHubName);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function dpCliIsAvailable() {
|
|
18
|
+
try {
|
|
19
|
+
const output = child_process.execSync('which dp-cli').toString();
|
|
20
|
+
return typeof output === 'string' && output.length > 0;
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getDataProductMetadata(apiHubName) {
|
|
27
|
+
const metadataString = child_process.execSync('dp-cli get -m ' + apiHubName).toString();
|
|
28
|
+
return JSON.parse(metadataString);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
function getDataProductCsn(apiHubName) {
|
|
33
|
+
if (fs.existsSync(apiHubName + '.json')) {
|
|
34
|
+
throw new Error(`Temporary file ${apiHubName}.json already exists! Please remove it before running this command again - or import directly from the file.`)
|
|
35
|
+
}
|
|
36
|
+
// TODO: Do not pipe to file but rather save in memory via custom stream?
|
|
37
|
+
child_process.execSync('dp-cli get ' + apiHubName, { stdio: ['pipe', fs.openSync(apiHubName + '.json', 'w'), 'pipe'] });
|
|
38
|
+
const csn = fs.readFileSync(apiHubName + '.json', 'utf-8');
|
|
39
|
+
fs.unlinkSync(apiHubName + '.json');
|
|
40
|
+
return JSON.parse(csn);
|
|
41
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//
|
|
2
|
+
// get all CSNs related to Data Product from local repo (cloned from https://github.tools.sap/bdc-fos/dp-metadata)
|
|
3
|
+
// and combine them into one CSN file
|
|
4
|
+
//
|
|
5
|
+
// Note: in this repo, ...
|
|
6
|
+
// CSN files are named like Customer_1.0.0.json -> 3 digit version
|
|
7
|
+
// Data Product ORD files are named like Customer_1.0.0.json -> 3 digit version
|
|
8
|
+
// Data Set files are named like Customer_1.0.0.json -> 3 digit version
|
|
9
|
+
|
|
10
|
+
// TODO: sap.capdpprod:dataProduct:CarbonJournalEntries:v1 does not have datasets, only direct data sources?
|
|
11
|
+
|
|
12
|
+
'use strict'
|
|
13
|
+
|
|
14
|
+
const fs = require ('node:fs')
|
|
15
|
+
const { mergePartialCsns } = require ('./merge-csns')
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
*
|
|
19
|
+
* @param ordId
|
|
20
|
+
* @param kind
|
|
21
|
+
*/
|
|
22
|
+
function ordIdToPath(ordId, kind) {
|
|
23
|
+
// sap.s4com:dataProduct:Customer:v1
|
|
24
|
+
const regex = new RegExp('([^:]+):'+ kind + ':([^:]+):v([^:]+)')
|
|
25
|
+
const match = ordId.match(regex)
|
|
26
|
+
const namespace = match[1] // "sap.s4com"
|
|
27
|
+
const name = match[2] // "Customer"
|
|
28
|
+
const version = match[3] // "1"
|
|
29
|
+
return `${namespace.replace('.','/')}/${name}_${version}.json` // TODO version handling!
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
* @param pathToRepo
|
|
35
|
+
* @param region
|
|
36
|
+
* @param landscape
|
|
37
|
+
* @param dataProduct_ordId
|
|
38
|
+
*/
|
|
39
|
+
exports.getDataProductCsnFromLocalRepo = function (pathToRepo, region, landscape, dataProduct_ordId) {
|
|
40
|
+
const dataProduct_ordId_long_version = dataProduct_ordId + '.0.0'
|
|
41
|
+
const path_prefix = pathToRepo + `/environments/${region}/${landscape}/defaults`
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const dataSource_ordIds = getDataSources()
|
|
45
|
+
|
|
46
|
+
let partialCsns = []
|
|
47
|
+
for (let dataSource_ordId of dataSource_ordIds) {
|
|
48
|
+
// read csn file
|
|
49
|
+
const csn_path = `${path_prefix}/csn_documents/`+ ordIdToPath(dataSource_ordId, 'dataSource')
|
|
50
|
+
// environments/eu10/stage-canary/defaults/csn_documents/sap/s4com/Customer_1.0.0.json
|
|
51
|
+
const csn = JSON.parse(fs.readFileSync(csn_path, { encoding: 'utf-8' }))
|
|
52
|
+
partialCsns.push(csn)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const mergedCsn = mergePartialCsns(partialCsns)
|
|
56
|
+
return mergedCsn
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
*
|
|
60
|
+
*/
|
|
61
|
+
function getDataSources() {
|
|
62
|
+
const dpDefinitionFile = JSON.parse(fs.readFileSync(`${path_prefix}/dataproducts/` + ordIdToPath(dataProduct_ordId_long_version, 'dataProduct'), { encoding: 'utf-8' }))
|
|
63
|
+
|
|
64
|
+
const dpdV2 = dpDefinitionFile?.dpdVersion === 'v2'
|
|
65
|
+
|
|
66
|
+
if(dpdV2) {
|
|
67
|
+
return dpDefinitionFile.dependsOn.filter(dep => dep.includes(':dataSource:'))
|
|
68
|
+
} else {
|
|
69
|
+
// read dataset file
|
|
70
|
+
const dataset_path = `${path_prefix}/datasets/` + ordIdToPath(dataProduct_ordId_long_version, 'dataProduct')
|
|
71
|
+
// environments/eu10/stage-canary/defaults/datasets/sap/s4com/Customer_1.0.0.json
|
|
72
|
+
const dataset = JSON.parse(fs.readFileSync(dataset_path, { encoding: 'utf-8' }))
|
|
73
|
+
return dataset.dataSources
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const cds = require ('../../lib/cds.js')
|
|
3
|
+
const { write, path } = cds.utils
|
|
4
|
+
const { Export } = require ('../export.js')
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Import extends Export {
|
|
8
|
+
|
|
9
|
+
options = Import.options // for IntelliSense
|
|
10
|
+
static options = { ...super.options,
|
|
11
|
+
to: './apis/imported/*',
|
|
12
|
+
'data-product': false,
|
|
13
|
+
}
|
|
14
|
+
static filename = __filename;
|
|
15
|
+
|
|
16
|
+
usage() {
|
|
17
|
+
const md = path.join(__dirname, '../help/import.md')
|
|
18
|
+
return super.usage = cds.utils.fs.readFileSync (md,'utf8')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async load (sources, o = this.options) {
|
|
22
|
+
if (!o['data-product']) return super.load (sources)
|
|
23
|
+
if (!sources || sources[0] === '*') return this.help()
|
|
24
|
+
const { load_data_product } = require ('./index.js')
|
|
25
|
+
return load_data_product (sources,o)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export_to (o = this.options) {
|
|
29
|
+
if (!o.apiResourceOrdId) return super.export_to (o)
|
|
30
|
+
this.package_name = o.apiResourceOrdId.replace(':apiResource:','-').replaceAll(':','-').replaceAll('.','-').toLowerCase()
|
|
31
|
+
return this.options.to.slice(0,-1) + this.package_name
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
*export_texts() {
|
|
35
|
+
const bundles = this.csn.i18n; if (!bundles) return
|
|
36
|
+
const serialize = texts => Object.entries(texts).map(([k,v]) => k +'='+ v.replace(/'/g, "''")).join('\n')
|
|
37
|
+
const write = texts => ({ to: file => this.write(serialize(texts)).to(file) })
|
|
38
|
+
for (let [locale,texts] of Object.entries(bundles))
|
|
39
|
+
yield write(texts) .to (`_i18n/i18n_${locale}.properties`)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
*export_models (model, o = this.options) {
|
|
43
|
+
|
|
44
|
+
// If no data product ID is specified, fall back to the default export behavior
|
|
45
|
+
if (!o.apiResourceOrdId) return yield* super.export_models(model, o);
|
|
46
|
+
|
|
47
|
+
const normalizeEOL = process.platform === 'win32' ? (str) => str.replace(/\r?\n/g, EOL) : str => str;
|
|
48
|
+
const {EOL} = require ('node:os')
|
|
49
|
+
|
|
50
|
+
const { definitions, extensions } = model;
|
|
51
|
+
yield this.write (
|
|
52
|
+
`// This file acts as a central facade to exported service definitions.`+ EOL +
|
|
53
|
+
`// You can modify it to tweak things, without your changes being overridden.`+ EOL +
|
|
54
|
+
`using from './services';`+
|
|
55
|
+
(extensions ? EOL + `using from './annotations';` : '')
|
|
56
|
+
) .to ('index.cds')
|
|
57
|
+
|
|
58
|
+
const definitionsCdl = cds.compile.to.cdl ({definitions}, {renderCdlCommonNamespace:false})
|
|
59
|
+
yield this.write (normalizeEOL(definitionsCdl)) .to ('services.cds')
|
|
60
|
+
|
|
61
|
+
if (extensions) {
|
|
62
|
+
// shorten names in the "annotate" to match the local name defined in "using"
|
|
63
|
+
const services = Object.keys(definitions).filter (k => definitions[k].kind === 'service')
|
|
64
|
+
if (services.length === 1 && services[0].includes('.')) {
|
|
65
|
+
let prefix = services[0].substring(0, services[0].lastIndexOf('.')) + '.'
|
|
66
|
+
extensions.forEach(o => {o.annotate = o.annotate.replace(prefix, '') })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let annotationsCdl = cds.compile.to.cdl ({extensions: extensions});
|
|
70
|
+
|
|
71
|
+
if (services.length === 1)
|
|
72
|
+
annotationsCdl = `using { ${services[0]} } from './services';` + EOL + EOL + annotationsCdl
|
|
73
|
+
|
|
74
|
+
yield this.write (normalizeEOL(annotationsCdl)) .to ('annotations.cds')
|
|
75
|
+
}
|
|
76
|
+
// TODO: What does this do? Do we need it or also remove it?
|
|
77
|
+
o.data = false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async export_package_json (o = this.options) {
|
|
81
|
+
|
|
82
|
+
// If no data product ID is specified, fall back to the default export behavior
|
|
83
|
+
if (!o.apiResourceOrdId) return super.export_package_json(o)
|
|
84
|
+
|
|
85
|
+
const [,major,minor=0,patch=0] = o.apiResourceOrdId.match(/v(\d+)(?:\.(\d+)(?:\.(\d+))?)?/)
|
|
86
|
+
const srv = o.apiResourceOrdId.replace(':apiResource:','.').replaceAll(':','.')
|
|
87
|
+
|
|
88
|
+
await super.export_package_json ({
|
|
89
|
+
name: this.package_name,
|
|
90
|
+
version: major + '.' + minor + '.' + patch,
|
|
91
|
+
cds: {
|
|
92
|
+
requires: {
|
|
93
|
+
[srv]: {
|
|
94
|
+
kind: 'data.product',
|
|
95
|
+
model: this.package_name,
|
|
96
|
+
ordId: o.apiResourceOrdId
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return this.update_package_json();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async update_package_json(){
|
|
106
|
+
const rootPackageJsonPath = path.join(cds.root + '/package.json');
|
|
107
|
+
const rootPackageJson = require(rootPackageJsonPath);
|
|
108
|
+
rootPackageJson.dependencies = rootPackageJson.dependencies || {};
|
|
109
|
+
rootPackageJson.dependencies[this.package_name] = `file:${this.options.to}`;
|
|
110
|
+
await write(rootPackageJson).to(rootPackageJsonPath);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = Object.assign (Import._for_cds_dk(), { Import })
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const { getDataProductCsnFromLocalRepo } = require ('./get-csn.js')
|
|
2
|
+
const { getDataProductCsnViaDpCli } = require ('./get-csn-via-dp-cli.js')
|
|
3
|
+
const { prepareCsn } = require ('./prepare-csn.js')
|
|
4
|
+
const fs = require ('node:fs/promises')
|
|
5
|
+
const path = require ('node:path')
|
|
6
|
+
|
|
7
|
+
exports.load_data_product = async function (sources, o) {
|
|
8
|
+
let service = /\/([^/]+)\//.exec(o.services)?.[1].replaceAll('\\.','.')
|
|
9
|
+
if (!service.includes(':apiResource:')) {
|
|
10
|
+
const [,namespace,name,version] = /^(.+)-([^-]+)-(v[0-9]+)\.json$/ .exec (path.basename(String(sources)))
|
|
11
|
+
service = namespace.replace(/-/g,'.') + ':apiResource:' + name + ':' + version
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const ordId = o.apiResourceOrdId = service
|
|
15
|
+
delete o.services // otherwise we minify by ordID, which results in empty csn.definitions
|
|
16
|
+
const from = String (sources)
|
|
17
|
+
|
|
18
|
+
const csn = await getCsn(from, ordId);
|
|
19
|
+
const [,namespace, name, version] = /^([^:]+):apiResource:([^:]+):(v[0-9]+)/.exec(ordId)
|
|
20
|
+
return prepareCsn (csn, namespace + '.' + name + '.' + version, ordId)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function getCsn(from, ordId) {
|
|
24
|
+
if (/dp-metadata$/.test(from))
|
|
25
|
+
return getDataProductCsnFromLocalRepo (from, 'eu10', 'stage-canary', ordId)
|
|
26
|
+
else if(from === 'dp-cli')
|
|
27
|
+
return getDataProductCsnViaDpCli(ordId)
|
|
28
|
+
else
|
|
29
|
+
return JSON.parse(await fs.readFile(from, { encoding: 'utf-8' }))
|
|
30
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Merges multiple partial CSNs (Core Schema Notations) into a single CSN.
|
|
5
|
+
* @param {Array<object>} partialCsns - An array of partial CSN objects to be merged.
|
|
6
|
+
* @returns {object} - The merged CSN object.
|
|
7
|
+
* @throws Will throw an error if there are conflicting definitions or translations.
|
|
8
|
+
*/
|
|
9
|
+
exports.mergePartialCsns = function (partialCsns) {
|
|
10
|
+
const parentCsn = { definitions: Object.create(null), i18n: Object.create(null) }
|
|
11
|
+
|
|
12
|
+
for (const partialCsn of partialCsns) {
|
|
13
|
+
for (const definitionName in partialCsn.definitions) {
|
|
14
|
+
const definition = partialCsn.definitions[definitionName]
|
|
15
|
+
if (parentCsn.definitions[definitionName]) {
|
|
16
|
+
const existing = JSON.stringify(parentCsn.definitions[definitionName])
|
|
17
|
+
const current = JSON.stringify(definition)
|
|
18
|
+
if (existing !== current) {
|
|
19
|
+
throw new Error('Error for definition ' + definitionName + ' - it already exists!')
|
|
20
|
+
}
|
|
21
|
+
} else {
|
|
22
|
+
parentCsn.definitions[definitionName] = definition
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
for (const i18nLanguageCode in partialCsn.i18n) {
|
|
27
|
+
const i18n = partialCsn.i18n[i18nLanguageCode]
|
|
28
|
+
if (!parentCsn.i18n[i18nLanguageCode]) {
|
|
29
|
+
parentCsn.i18n[i18nLanguageCode] = Object.create(null)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const translationCode in i18n) {
|
|
33
|
+
if (parentCsn.i18n[i18nLanguageCode][translationCode]) {
|
|
34
|
+
const existing = parentCsn.i18n[i18nLanguageCode][translationCode]
|
|
35
|
+
const current = i18n[translationCode]
|
|
36
|
+
if (existing !== current) {
|
|
37
|
+
throw new Error('Error for i18n:' + i18nLanguageCode + ' with translation ' + translationCode + ' - it already exists!')
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
parentCsn.i18n[i18nLanguageCode][translationCode] = i18n[translationCode]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return parentCsn
|
|
46
|
+
}
|