@sap/cds-dk 9.3.2 → 9.4.2

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.
Files changed (113) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/bin/bind.js +1 -1
  3. package/bin/cds.js +3 -0
  4. package/bin/compile/index.js +53 -3
  5. package/bin/data-products/get-csn-via-dp-cli.js +41 -0
  6. package/bin/data-products/get-csn.js +76 -0
  7. package/bin/data-products/import.js +114 -0
  8. package/bin/data-products/index.js +30 -0
  9. package/bin/data-products/merge-csns.js +46 -0
  10. package/bin/data-products/prepare-csn.js +282 -0
  11. package/bin/data-products/prepare-files.js +69 -0
  12. package/bin/export.js +240 -0
  13. package/bin/help/export.md +37 -0
  14. package/bin/help/import.md +11 -0
  15. package/bin/help/minify.md +23 -0
  16. package/bin/import.js +13 -3
  17. package/bin/inspect.js +226 -0
  18. package/bin/minify.js +123 -0
  19. package/bin/up.js +2 -2
  20. package/bin/util/cli.js +132 -0
  21. package/bin/util/log.js +2 -0
  22. package/bin/version.js +2 -1
  23. package/bin/watch.js +21 -3
  24. package/lib/bind/shared.js +3 -1
  25. package/lib/build/buildTaskEngine.js +2 -1
  26. package/lib/build/plugins/helm/files/templates/NOTES.txt +1 -1
  27. package/lib/build/plugins/helm/index.js +1 -2
  28. package/lib/build/provider/internalBuildTaskProvider.js +3 -3
  29. package/lib/build/provider/nodejs/index.js +62 -3
  30. package/lib/deploy/index.js +1 -1
  31. package/lib/deploy/to-hana/hana.js +18 -16
  32. package/lib/import/odata/v2parser.js +2 -2
  33. package/lib/import/odata/v4parser.js +21 -2
  34. package/lib/import/odata.package.json +4 -4
  35. package/lib/init/constants.js +4 -1
  36. package/lib/init/index.js +6 -5
  37. package/lib/init/mvn.js +3 -2
  38. package/lib/init/projectReader.js +65 -55
  39. package/lib/init/registries/mta.js +6 -0
  40. package/lib/init/template/ams/files/manifest.yml.hbs +1 -1
  41. package/lib/init/template/ams/files/mta.yaml.hbs +1 -1
  42. package/lib/init/template/ams/index.js +7 -6
  43. package/lib/init/template/app-frontend/files/app-package.json.hbs +20 -0
  44. package/lib/init/template/app-frontend/files/index.html.hbs +34 -0
  45. package/lib/init/template/app-frontend/files/manifest.json +13 -0
  46. package/lib/init/template/app-frontend/files/mta.yaml.hbs +64 -0
  47. package/lib/init/template/app-frontend/files/package.json.hbs +17 -0
  48. package/lib/init/template/app-frontend/files/ui5-deploy.yaml.hbs +25 -0
  49. package/lib/init/template/app-frontend/files/ui5.yaml.hbs +6 -0
  50. package/lib/init/template/app-frontend/files/values.yaml.hbs +20 -0
  51. package/lib/init/template/app-frontend/files/xs-app.json.hbs +17 -0
  52. package/lib/init/template/app-frontend/index.js +98 -0
  53. package/lib/init/template/application-logging/index.js +13 -10
  54. package/lib/init/template/approuter/files/mta.yaml.hbs +0 -2
  55. package/lib/init/template/approuter/index.js +15 -9
  56. package/lib/init/template/attachments/index.js +7 -13
  57. package/lib/init/template/audit-logging/files/package.json.hbs +5 -0
  58. package/lib/init/template/audit-logging/index.js +4 -4
  59. package/lib/init/template/cloud-logging/index.js +13 -10
  60. package/lib/init/template/connectivity/index.js +3 -3
  61. package/lib/init/template/destination/index.js +6 -6
  62. package/lib/init/template/dynatrace/index.js +13 -10
  63. package/lib/init/template/enterprise-messaging/index.js +5 -5
  64. package/lib/init/template/enterprise-messaging-shared/index.js +4 -4
  65. package/lib/init/template/extensibility/index.js +2 -2
  66. package/lib/init/template/file-based-messaging/index.js +2 -2
  67. package/lib/init/template/github-actions/index.js +3 -3
  68. package/lib/init/template/hana/index.js +19 -22
  69. package/lib/init/template/handler/index.js +1 -0
  70. package/lib/init/template/helm/index.js +34 -21
  71. package/lib/init/template/html5-repo/files/containerize.yaml.hbs +0 -1
  72. package/lib/init/template/html5-repo/files/mta.yaml.hbs +10 -2
  73. package/lib/init/template/html5-repo/index.js +8 -6
  74. package/lib/init/template/http/index.js +31 -10
  75. package/lib/init/template/ias/files/manifest.yml.hbs +1 -1
  76. package/lib/init/template/ias/files/mta.yaml.hbs +9 -5
  77. package/lib/init/template/ias/files/values.yaml.hbs +14 -2
  78. package/lib/init/template/ias/index.js +8 -8
  79. package/lib/init/template/kafka/index.js +3 -3
  80. package/lib/init/template/local-messaging/index.js +2 -2
  81. package/lib/init/template/malware-scanner/index.js +3 -7
  82. package/lib/init/template/mta/files/mta.yaml.hbs +10 -0
  83. package/lib/init/template/multitenancy/files/mta.yaml.hbs +16 -3
  84. package/lib/init/template/multitenancy/files/package.json.hbs +5 -0
  85. package/lib/init/template/multitenancy/files/values.yaml.hbs +1 -6
  86. package/lib/init/template/multitenancy/index.js +8 -10
  87. package/lib/init/template/notifications/index.js +2 -2
  88. package/lib/init/template/portal/files/mta.yaml.hbs +18 -2
  89. package/lib/init/template/portal/index.js +7 -7
  90. package/lib/init/template/postgres/index.js +4 -4
  91. package/lib/init/template/redis/index.js +2 -2
  92. package/lib/init/template/redis-messaging/index.js +2 -2
  93. package/lib/init/template/sample/files/app/admin-books/fiori-service.cds +2 -2
  94. package/lib/init/template/sample/files/db/schema.cds +5 -8
  95. package/lib/init/template/sample/files/javascript/admin-service.js.hbs +24 -0
  96. package/lib/init/template/sample/files/javascript/cat-service.js.hbs +43 -0
  97. package/lib/init/template/sample/files/typescript/admin-service.ts.hbs +16 -0
  98. package/lib/init/template/sample/files/typescript/cat-service.ts.hbs +31 -0
  99. package/lib/init/template/test/files/test/test.xs.hbs +1 -1
  100. package/lib/init/template/test/index.js +2 -2
  101. package/lib/init/template/tiny-sample/index.js +1 -1
  102. package/lib/init/template/typer/files/xsconfig.json +2 -1
  103. package/lib/init/template/typescript/files/package.json +1 -1
  104. package/lib/init/template/typescript/files/tsconfig.json +4 -1
  105. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +4 -0
  106. package/lib/init/template/workzone-standard/index.js +8 -8
  107. package/lib/init/template/xsuaa/files/mta.yaml.hbs +0 -2
  108. package/lib/init/template/xsuaa/files/package.json.hbs +3 -0
  109. package/lib/init/template/xsuaa/index.js +23 -18
  110. package/lib/lint/eslintHelp.json +1 -1
  111. package/lib/util/term.js +2 -4
  112. package/npm-shrinkwrap.json +28 -28
  113. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,64 @@ 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.2 - 2025-10-23
10
+
11
+ ### Changed
12
+
13
+ - `cds add containerize` does not add app module installations scripts to its `containerize.yaml`. Use `cds up --to k8s` instead.
14
+ - `cds add` is less eager with respect to changing unaffected configuration.
15
+ - `cds add ias` adds less unnecessary configuration in combination with the HTML5 repository.
16
+ - `cds add test` includes the service name in the test suite description.
17
+
18
+ ### Fixed
19
+
20
+ - `cds add ams` followed by `cds add mta` correctly generates the AMS deployer module.
21
+ - `cds add hana` does not fail if there's no `.cdsrc.json`.
22
+ - `cds up` doesn't try to create a symlink to the monorepo _package-lock.json_ if a submodule-local one already exists.
23
+ - `cds add test` creates correct OData URLs with unqualified entity names in service paths.
24
+ - `cds add http` skips contained composition targets.
25
+ - `cds add sample` no longer comes with an error when creating a Book through Fiori.
26
+
27
+ ## Version 9.4.1 - 2025-10-09
28
+
29
+ ### Added
30
+
31
+ - `cds add app-frontend` is supported as an alias to `cds add app-front`.
32
+ - `cds add attachments` is supported for Java projects.
33
+
34
+ ### Fixed
35
+
36
+ - `cds import` adds latest version 4 of `@sap-cloud-sdk` packages.
37
+ - `cds add sample` now uses Integer IDs for Books and Authors again, instead of UUIDs.
38
+
39
+ ## Version 9.4.0 - 2025-10-02
40
+
41
+ ### Added
42
+
43
+ - `cds add app-front` adds configuration for the new SAP BTP Application Frontend service.
44
+ - `cds add ias` has improved support for XSUAA hybrid projects.
45
+ - `cds add multitenancy` automatically adds upgrade hooks for Node.js projects.
46
+ - `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.
47
+
48
+ ### Changed
49
+
50
+ - `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`.
51
+ - `cds add multitenancy` adds the `with-mtx` profile to Java apps by default, simplifying local development.
52
+ - `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.
53
+ - `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.
54
+ - `cds import` for EDMX correctly imports multiline strings in `DefaultValue`.
55
+ - `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.
56
+ - `cds bind` does not fail when setting the profile via `CDS_ENV`.
57
+ - `cds version` is more robust with respect to Java versions.
58
+
59
+ ### Fixed
60
+
61
+ - `cds add html5-repo` avoids some superfluous configuration combination with `cds add portal`.
62
+ - `cds add handler` ignores external services.
63
+ - `cds add audit-logging` correctly adds the dependency in the _Chart.yaml_ for Helm deployments.
64
+ - `cds deploy --to hana` works with user-provided HANA services.
65
+ - `cds bind` doesn't add the `custom-service:` prefix for Node.js any more.
66
+
9
67
  ## Version 9.3.2 - 2025-09-16
10
68
 
11
69
  ### Fixed
@@ -27,6 +85,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
27
85
  - `cds add github-actions` correctly generates a release workflow.
28
86
  - `cds add github-actions` uses a simplified and more resilient Kyma setup script.
29
87
  - `cds import` now correctly imports EDMX files with empty NavigationPropertyPath tags.
88
+ - `cds import` now correctly imports OData v2 EDMX files with `Edm.Time` properties, which have a precision.
30
89
 
31
90
  ## Version 9.3.0 - 2025-09-01
32
91
 
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.env.CDS_ENV && process.argv[2] === 'bind') {
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 = []
@@ -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', '--service', '--lang', '--for', '--to', '--dest', '--log-level', '--flavor', '--dialect', '--sql-dialect', '--openapi:url', '--openapi:servers', '--openapi:config-file', '--odata-version'
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
- undefined, '-s', '-l', '-4', '-2', '-o', undefined, '-f'
23
+ undefined,
24
+ '-s',
25
+ '-l',
26
+ '-4',
27
+ '-2',
28
+ '-o',
29
+ undefined,
30
+ '-f'
11
31
  ],
12
32
  flags: [
13
- '--parse', '--plain', '--clean', '--files', '--sources', '--resolved', '--all', '--beta', '--conceptual', '--min', '--docs', '--locations', '--openapi:diagram', '--asyncapi:merged'
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
+ }