@sap/cds-dk 9.4.2 → 9.5.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.
Files changed (151) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/bin/compile/index.js +1 -1
  3. package/bin/data-products/index.js +10 -2
  4. package/bin/data-products/prepare-csn.js +63 -26
  5. package/bin/export.js +1 -1
  6. package/bin/help.js +2 -3
  7. package/bin/import.js +13 -3
  8. package/bin/repl.js +7 -11
  9. package/bin/up.js +164 -13
  10. package/bin/util/containerize.js +183 -0
  11. package/bin/version.js +4 -4
  12. package/lib/bind/k8s.js +1 -1
  13. package/lib/build/buildTaskEngine.js +5 -2
  14. package/lib/build/plugin.js +1 -1
  15. package/lib/build/plugins/helm/files/templates/_deployment_helpers.tpl +1 -1
  16. package/lib/build/plugins/helm/index.js +7 -7
  17. package/lib/build/provider/hana/2migration.js +16 -24
  18. package/lib/build/provider/hana/index.js +11 -3
  19. package/lib/build/provider/hana/template/.hdiconfig-all +7 -1
  20. package/lib/build/provider/hana/template/package.json +1 -1
  21. package/lib/build/provider/internalBuildPlugin.js +15 -1
  22. package/lib/build/provider/java/index.js +12 -1
  23. package/lib/build/provider/mtx-sidecar/index.js +2 -1
  24. package/lib/build/provider/nodejs/index.js +2 -1
  25. package/lib/build/util.js +3 -3
  26. package/lib/compile/to_openapi.js +1 -5
  27. package/lib/import/common.js +5 -4
  28. package/lib/import/message.js +1 -1
  29. package/lib/import/odata/ParserContext.js +1 -0
  30. package/lib/import/odata/v4parser.js +48 -3
  31. package/lib/import/openapi/importOpenAPI.js +6 -1
  32. package/lib/init/constants.js +1 -1
  33. package/lib/init/index.js +15 -6
  34. package/lib/init/merge.js +16 -0
  35. package/lib/init/projectReader.js +54 -24
  36. package/lib/init/registries/mta.js +5 -2
  37. package/lib/init/registries/xs-app.js +7 -1
  38. package/lib/init/template/ams/index.js +1 -1
  39. package/lib/init/template/app-frontend/files/app-package.json.hbs +1 -7
  40. package/lib/init/template/app-frontend/files/ui5.yaml.hbs +20 -1
  41. package/lib/init/template/app-frontend/index.js +0 -4
  42. package/lib/init/template/application-logging/index.js +9 -14
  43. package/lib/init/template/approuter/files/mta.yaml.hbs +7 -2
  44. package/lib/init/template/approuter/index.js +21 -16
  45. package/lib/init/template/audit-logging/index.js +2 -2
  46. package/lib/init/template/cloud-logging/index.js +2 -3
  47. package/lib/init/template/containerize/files/containerize.yaml.hbs +17 -3
  48. package/lib/init/template/containerize/index.js +0 -4
  49. package/lib/init/template/data/as-json.js +18 -20
  50. package/lib/init/template/destination/index.js +0 -4
  51. package/lib/init/template/dynatrace/index.js +0 -2
  52. package/lib/init/template/github-actions/files/kyma-setup/action.yaml +0 -2
  53. package/lib/init/template/github-actions/files/workflows/cf.yaml.hbs +16 -1
  54. package/lib/init/template/github-actions/files/workflows/test.yaml.hbs +20 -0
  55. package/lib/init/template/github-actions/index.js +5 -8
  56. package/lib/init/template/hana/files/db/package.json +2 -2
  57. package/lib/init/template/hana/files/mta.yaml.hbs +0 -4
  58. package/lib/init/template/hana/index.js +5 -7
  59. package/lib/init/template/helm/index.js +16 -33
  60. package/lib/init/template/helm-unified-runtime/index.js +0 -4
  61. package/lib/init/template/html5-repo/files/app-package.json.hbs +1 -7
  62. package/lib/init/template/html5-repo/files/containerize.yaml.hbs +0 -9
  63. package/lib/init/template/html5-repo/files/mta.yaml.hbs +0 -7
  64. package/lib/init/template/html5-repo/files/ui5.yaml.hbs +20 -1
  65. package/lib/init/template/html5-repo/files/values.yaml.hbs +6 -0
  66. package/lib/init/template/html5-repo/files/xs-app.json.hbs +5 -3
  67. package/lib/init/template/html5-repo/index.js +8 -14
  68. package/lib/init/template/ias/files/values.yaml.hbs +6 -6
  69. package/lib/init/template/ias/index.js +14 -23
  70. package/lib/init/template/kyma/index.js +14 -1
  71. package/lib/init/template/malware-scanner/files/mta.yaml.hbs +3 -1
  72. package/lib/init/template/malware-scanner/files/values.yaml +3 -1
  73. package/lib/init/template/multitenancy/files/mta.yaml.hbs +1 -3
  74. package/lib/init/template/multitenancy/files/values.yaml.hbs +8 -0
  75. package/lib/init/template/multitenancy/index.js +10 -5
  76. package/lib/init/template/nodejs/files/package.json.hbs +1 -1
  77. package/lib/init/template/pipeline/files/app-package.json.hbs +1 -7
  78. package/lib/init/template/pipeline/files/ui5.yaml.hbs +20 -1
  79. package/lib/init/template/pipeline/index.js +6 -3
  80. package/lib/init/template/portal/files/mta.yaml.hbs +5 -3
  81. package/lib/init/template/portal/index.js +3 -6
  82. package/lib/init/template/sample/files/_i18n/i18n_de.properties +12 -0
  83. package/lib/init/template/sample/files/_i18n/i18n_en.properties +21 -0
  84. package/lib/init/template/sample/files/_i18n/i18n_fr.properties +20 -0
  85. package/lib/init/template/sample/files/_i18n/messages_de.properties +34 -0
  86. package/lib/init/template/sample/files/_i18n/messages_en.properties +34 -0
  87. package/lib/init/template/sample/files/_i18n/messages_fr.properties +34 -0
  88. package/lib/init/template/sample/files/app/_i18n/i18n_de.properties +0 -26
  89. package/lib/init/template/sample/files/app/_i18n/i18n_en.properties +8 -0
  90. package/lib/init/template/sample/files/app/admin-authors/fiori-service.cds +52 -0
  91. package/lib/init/template/sample/files/app/admin-authors/webapp/Component.js.hbs +3 -0
  92. package/lib/init/template/sample/files/app/admin-authors/webapp/i18n/i18n.properties +11 -0
  93. package/lib/init/template/sample/files/app/admin-authors/webapp/manifest.json +130 -0
  94. package/lib/init/template/sample/files/app/admin-books/fiori-service.cds +81 -88
  95. package/lib/init/template/sample/files/app/admin-books/webapp/Component.js.hbs +3 -0
  96. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n.properties +10 -2
  97. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +24 -48
  98. package/lib/init/template/sample/files/app/appconfig/fioriSandboxConfig.json +54 -3
  99. package/lib/init/template/sample/files/app/browse/fiori-service.cds +30 -30
  100. package/lib/init/template/sample/files/app/browse/webapp/Component.js.hbs +3 -0
  101. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n.properties +10 -3
  102. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +18 -23
  103. package/lib/init/template/sample/files/app/common.cds +126 -158
  104. package/lib/init/template/sample/files/app/fiori-apps.html +30 -0
  105. package/lib/init/template/sample/files/app/genres/fiori-service.cds +33 -0
  106. package/lib/init/template/sample/files/app/genres/tree-view.cds +42 -0
  107. package/lib/init/template/sample/files/app/genres/value-help.cds +6 -0
  108. package/lib/init/template/sample/files/app/genres/webapp/Component.js.hbs +3 -0
  109. package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n.properties +4 -0
  110. package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n_de.properties +2 -0
  111. package/lib/init/template/sample/files/app/genres/webapp/manifest.json +126 -0
  112. package/lib/init/template/sample/files/app/services.cds +6 -2
  113. package/lib/init/template/sample/files/db/currencies.cds +13 -0
  114. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books.csv +5 -5
  115. package/lib/init/template/sample/files/db/data/{sap.capire.bookshop-Books_texts.csv → sap.capire.bookshop-Books.texts.csv} +4 -4
  116. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.csv +42 -15
  117. package/lib/init/template/sample/files/db/data/sap.common-Currencies.csv +12 -0
  118. package/lib/init/template/sample/files/db/data/sap.common-Currencies.texts.csv +15 -0
  119. package/lib/init/template/sample/files/db/hana/index.cds +10 -0
  120. package/lib/init/template/sample/files/db/schema.cds +35 -22
  121. package/lib/init/template/sample/files/db/sqlite/index.cds +10 -0
  122. package/lib/init/template/sample/files/package.json.hbs +14 -1
  123. package/lib/init/template/sample/files/srv/access-control.cds +3 -0
  124. package/lib/init/template/sample/files/srv/admin-service.cds +3 -2
  125. package/lib/init/template/sample/files/srv/cat-service.cds +7 -11
  126. package/lib/init/template/sample/index.js +26 -17
  127. package/lib/init/template/telemetry/index.js +1 -3
  128. package/lib/init/template/typescript/files/package.json +1 -1
  129. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +0 -5
  130. package/lib/init/template/workzone-standard/index.js +0 -1
  131. package/lib/init/template/xsuaa/files/mta.yaml.hbs +4 -0
  132. package/lib/init/template/xsuaa/index.js +5 -7
  133. package/lib/lint/Linter.js +10 -4
  134. package/lib/lint/eslintHelp.json +1 -1
  135. package/lib/mtx/tenant_action.js +12 -4
  136. package/lib/util/checksum.js +32 -0
  137. package/lib/util/question.js +60 -7
  138. package/npm-shrinkwrap.json +61 -37
  139. package/package.json +2 -2
  140. package/lib/init/template/app-frontend/files/ui5-deploy.yaml.hbs +0 -25
  141. package/lib/init/template/github-actions/files/cf-setup/action.yaml +0 -53
  142. package/lib/init/template/html5-repo/files/ui5-deploy.yaml.hbs +0 -25
  143. package/lib/init/template/pipeline/files/ui5-deploy.yaml.hbs +0 -25
  144. package/lib/init/template/sample/files/app/_i18n/i18n.properties +0 -29
  145. package/lib/init/template/sample/files/app/admin-books/webapp/Component.js +0 -8
  146. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_de.properties +0 -3
  147. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_en.properties +0 -3
  148. package/lib/init/template/sample/files/app/browse/webapp/Component.js +0 -7
  149. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_de.properties +0 -3
  150. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_en.properties +0 -4
  151. package/lib/init/template/sample/files/app/index.html +0 -32
@@ -634,7 +634,7 @@ function _generateStructuredDataTypeCsn(dataType, propertyObject, isPrimaryKey,
634
634
 
635
635
  function _parseAssociationProperty(foreignReference, associationNode, parserContext) {
636
636
  const associationProperty = {};
637
- associationProperty.type = (associationNode.$OnDelete === 'Cascade') ? 'cds.Composition' : 'cds.Association';
637
+ associationProperty.type = (associationNode.$OnDelete === 'Cascade' || associationNode.$ContainsTarget) ? 'cds.Composition' : 'cds.Association';
638
638
 
639
639
  let schemaName = associationNode.$Type.substring(0, associationNode.$Type.lastIndexOf('.'));
640
640
 
@@ -642,6 +642,11 @@ function _parseAssociationProperty(foreignReference, associationNode, parserCont
642
642
  schemaName = parserContext.schemaAliasToNamespace[schemaName];
643
643
  }
644
644
 
645
+ if (associationNode.$ContainsTarget) {
646
+ parserContext.schemaDetails[schemaName] ??= {}
647
+ parserContext.schemaDetails[schemaName].containment = true
648
+ }
649
+
645
650
  let resolvedEntityName;
646
651
  let entityTypeMapping = (
647
652
  parserContext.entityToEntitySetMap?.get(associationNode.$Type) ||
@@ -676,9 +681,18 @@ function _parseAssociationProperty(foreignReference, associationNode, parserCont
676
681
  } else {
677
682
  associationProperty.cardinality.max = 1;
678
683
  }
679
- // Convert managed associations and compositions in unmanaged with empty key to avoid
684
+
685
+ // Generate on condition in case $Partner exists, as that indicates a backlink with which the
686
+ // condition can be generated.
687
+ // If not given convert managed associations and compositions in unmanaged with empty key to avoid
680
688
  // "generation" of keys, that do not exist in the external service.
681
- if (common.checkForEmptyKeys(associationProperty, "V4")) {
689
+ if (associationNode.$Partner && associationProperty.type === 'cds.Composition') {
690
+ associationProperty.on = [
691
+ {ref: [foreignReference, associationNode.$Partner]},
692
+ '=',
693
+ {ref: ['$self']}
694
+ ]
695
+ } else if (common.checkForEmptyKeys(associationProperty, "V4")) {
682
696
  associationProperty.keys = [];
683
697
  }
684
698
  _captureAnnotationWithinElement(associationNode, associationProperty, parserContext);
@@ -1653,12 +1667,43 @@ function _generateCSN(edmxAsJson, ignorePersistenceSkip, parserContext, importer
1653
1667
  csn.definitions[parserContext.primarySchema].kind = 'service';
1654
1668
  csn.definitions[parserContext.primarySchema]['@cds.external'] = true;
1655
1669
 
1670
+ if (parserContext.schemaDetails[parserContext.primarySchema]?.containment) {
1671
+ csn.definitions[parserContext.primarySchema]['@odata.containment'] = true;
1672
+ }
1673
+
1656
1674
  // Step 8: parse entity container contents and their annotations
1657
1675
  let schemaData = schemaDataList[parserContext.schemaToSchemaDataIndex[parserContext.primarySchema]];
1658
1676
  let entityContainerElements = schemaData[parserContext.entityContainerName];
1659
1677
  csn.definitions = Object.assign(_parseEntityContainer(csn.definitions, entityContainerElements, parserContext), csn.definitions);
1660
1678
  }
1661
1679
 
1680
+
1681
+ // Step 9: 2. Step for generating $ContainsTarget & $Partner links
1682
+ // The containing composition is already generated. Now the Keys: [] has to be removed
1683
+ // on the backlink association because per OData spec, OData does not expose the foreign key fields
1684
+ // used by the backlink and thus it is save to assume they must be generated for proper mocking
1685
+ //
1686
+ // This happens after Step 8 because that adjusts the names of entity container entities where
1687
+ // entity type and entity set differ in naming
1688
+ for (const entity in csn.definitions) {
1689
+ if (entity.type !== 'entity') continue;
1690
+
1691
+ const containingCompositions = Object.keys(csn.definitions[entity].elements).filter(e =>
1692
+ csn.definitions[entity].elements[e].type === 'cds.Composition' &&
1693
+ csn.definitions[entity].elements[e].on
1694
+ );
1695
+ for (const comp of containingCompositions) {
1696
+ const compTarget = csn.definitions[csn.definitions[entity].elements[comp].target];
1697
+ const backLink = Object.keys(compTarget.elements).find(e =>
1698
+ compTarget.elements[e].type === 'cds.Association' &&
1699
+ compTarget.elements[e].target === entity
1700
+ )
1701
+ if (compTarget.elements[backLink].keys) {
1702
+ delete compTarget.elements[backLink].keys
1703
+ }
1704
+ }
1705
+ }
1706
+
1662
1707
  return JSON.stringify(csn, null, 4);
1663
1708
  }
1664
1709
 
@@ -4,6 +4,8 @@ const {
4
4
  pathAndMethod,
5
5
  serviceName,
6
6
  } = require("./utilities");
7
+ const cds = require('../../cds');
8
+
7
9
 
8
10
  module.exports = { importOpenAPI };
9
11
 
@@ -32,7 +34,10 @@ function importOpenAPI(input) {
32
34
  };
33
35
 
34
36
  const context = {
35
- serviceName: serviceName(input.info?.title || "TODO.service"),
37
+ serviceName: serviceName(cds.cli.options["into-namespace"]
38
+ ?? input.info?.title
39
+ ?? "TODO.service"
40
+ ),
36
41
  oasVersion: input.openapi || input.swagger,
37
42
  v3: !!input.openapi,
38
43
  schemas: input.openapi ? input.components?.schemas : input.definitions,
@@ -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.4.1',
8
+ MAVEN_ARCHETYPE_VERSION: '4.4.2',
9
9
 
10
10
  OPTIONS: Object.freeze({
11
11
  COMPLETION: 'completion',
package/lib/init/index.js CHANGED
@@ -231,17 +231,26 @@ module.exports = class CDSGenerator {
231
231
  const templates = new Map
232
232
  for (let plugin of plugins) {
233
233
  // Compat
234
- if (plugin === 'appfront') plugin = 'app-front'
235
234
  if (plugin === 'helm' && cds.cli.options['internal-unified-runtime-charts']) {
235
+ console.warn(term.warn('"--internal-unified-runtime-charts" is deprecated, please use "cds add kyma --unified-runtime" instead'))
236
236
  plugin = 'helm-unified-runtime'
237
237
  cds.cli.options.add?.delete('helm')
238
238
  cds.cli.options.add?.add('helm-unified-runtime')
239
239
  }
240
- if (plugin === 'sample-tiny') plugin = 'tiny-sample'
241
- if (plugin in { 'kibana-logging': 1, 'kibana': 1 }) plugin = 'application-logging'
242
- if (plugin === 'postgresql') plugin = 'postgres'
243
- if (plugin === 'gha') plugin = 'github-actions'
244
- if (plugin === 'app-front') plugin = 'app-frontend'
240
+ const _alias = (alias, preferred) => {
241
+ if (plugin === alias) {
242
+ plugin = preferred
243
+ cds.cli.options.add?.delete(alias)
244
+ cds.cli.options.add?.add(preferred)
245
+ cds.cli.argv[0] = cds.cli.argv[0].replace(alias, preferred)
246
+ }
247
+ }
248
+ _alias('appfront', 'app-frontend')
249
+ _alias('app-front', 'app-frontend')
250
+ _alias('gha', 'github-actions')
251
+ _alias('postgresql', 'postgres')
252
+ _alias('kibana', 'application-logging')
253
+ _alias('kibana-logging', 'application-logging')
245
254
 
246
255
  if (!templates.has(plugin)) {
247
256
  const template = await this._createTemplate(plugin)
package/lib/init/merge.js CHANGED
@@ -106,6 +106,9 @@ async function _mergeYAML(into, from, project, semantics = {}) {
106
106
  if (project) await writeYAML(into, source)
107
107
  else await copy(from, into)
108
108
  return source
109
+ } else if (typeof into === 'string') {
110
+ await writeYAML(into, source)
111
+ return source
109
112
  }
110
113
  throw new Error(`Target YAML doesn't exist`)
111
114
  }
@@ -113,6 +116,19 @@ async function _mergeYAML(into, from, project, semantics = {}) {
113
116
  const entryMap = new Map, templateEntryMap = new Map
114
117
 
115
118
  const { additions, overwrites, deletions, relationships } = semantics
119
+ if (relationships && Array.isArray(relationships)) {
120
+ semantics.additions ??= []
121
+ const add = semantics.additions
122
+
123
+ for (const rel of relationships) {
124
+ for (const ins of (Array.isArray(rel.insert) ? rel.insert : [rel.insert])) {
125
+ if (!add.includes(ins)) add.push(ins)
126
+ }
127
+ const base = rel.into?.[0]
128
+ if (base && !add.includes(base)) add.push(base)
129
+ }
130
+ }
131
+
116
132
  for (const entry of additions ?? []) {
117
133
  entryMap.set(entry, undefined)
118
134
  templateEntryMap.set(entry, undefined)
@@ -15,7 +15,7 @@ module.exports = new class ProjectReader {
15
15
 
16
16
  #setPackageInfo() {
17
17
  this.isJava = (exists('pom.xml') || cds.cli.options?.add?.has('java'))
18
- && !cds.env.for('cds').profiles.includes('node') // opt-out for hybrid projects like sflight
18
+ && !cds.env.for('cds').profiles.includes('node') // opt-out for hybrid projects like sflight
19
19
 
20
20
  // 1. Use project name and static default values
21
21
  this.appVersion = this.isJava ? '1.0.0-SNAPSHOT' : '1.0.0'
@@ -32,7 +32,7 @@ module.exports = new class ProjectReader {
32
32
  const v = version?.[0] === '${revision}' ? properties?.[0]?.revision[0] : version?.[0] ?? '1.0.0-SNAPSHOT'
33
33
  if (v && REGEX_JAVA_VERSION.test(v)) this.appVersion = v
34
34
  this.jdkVersion = properties?.[0]?.['jdk.version']?.[0]?._text?.[0] ?? JAVA_LTS_VERSIONS[JAVA_LTS_VERSIONS.length - 1]
35
- if (artifactId?.[0]) this.appName = this.appId = artifactId?.[0]?.split(/-parent/)[0]
35
+ if (artifactId?.[0]) this.appName = this.appId = artifactId?.[0]?.split(/-parent/)[0]
36
36
  if (description?.[0]) this.appDescription = description[0]
37
37
  }
38
38
  else if (exists('package.json')) {
@@ -73,9 +73,9 @@ module.exports = new class ProjectReader {
73
73
  TRACE?.({ env })
74
74
 
75
75
  const pkgJson = () => exists('package.json') ? JSON.parse(fs.readFileSync(join(cds.root, 'package.json'))) : {}
76
- const _isESM = () => pkgJson().type === 'module'
76
+ const _isESM = () => pkgJson().type === 'module'
77
77
  const _isTypescript = () => !!pkgJson().dependencies?.typescript || !!pkgJson().devDependencies?.typescript
78
- const _hasTyper = () => !!pkgJson().dependencies?.['@cap-js/cds-typer'] || !!pkgJson().devDependencies?.['@cap-js/cds-typer']
78
+ const _hasTyper = () => !!pkgJson().dependencies?.['@cap-js/cds-typer'] || !!pkgJson().devDependencies?.['@cap-js/cds-typer']
79
79
  const _inProd = plugin => require(`./template/${plugin}`).hasInProduction(env) || cds.cli.argv[0]?.includes(plugin) || cds.cli.options.add?.has(plugin)
80
80
  const _uiEmbedded = () => exists(join(env.folders.app, 'xs-app.json'))
81
81
  const _uiModule = () => !_uiEmbedded() && isdir(env.folders.app) && fs.readdirSync(resolve(cds.root, env.folders.app)).length > 0
@@ -121,9 +121,22 @@ module.exports = new class ProjectReader {
121
121
  const manifestPath = path.resolve(cds.root, cds.env.folders.app, app, 'webapp/manifest.json')
122
122
  const manifest = exists(manifestPath) ? JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) : {}
123
123
  const inbounds = manifest?.['sap.app']?.crossNavigation?.inbounds
124
- const [firstInbound] = Object.values(inbounds ?? { intent: { semanticObject: 'Books', action: 'display' }})
124
+ const [firstInbound] = Object.values(inbounds ?? { intent: { semanticObject: 'Books', action: 'display' } })
125
125
  const vizId = firstInbound.semanticObject + '-' + firstInbound.action
126
- return { app, strippedApp: app.replace(/-/g, ''), vizId, isNotLast: i < apps.length - 1, manifestAppId: manifest?.['sap.app']?.id ?? app}
126
+ // Try to read archiveName from ui5.yaml if it exists
127
+ let archiveName = ''
128
+ const ui5DeployPath = path.resolve(cds.root, cds.env.folders.app, app, 'ui5.yaml')
129
+ if (exists(ui5DeployPath)) {
130
+ const ui5Deploy = cds.load.yaml(ui5DeployPath)
131
+ const customTasks = ui5Deploy?.builder?.customTasks
132
+ if (Array.isArray(customTasks)) {
133
+ const zipperTask = customTasks.find(t => t.name === 'ui5-task-zipper')
134
+ if (zipperTask?.configuration?.archiveName) {
135
+ archiveName = `${zipperTask.configuration.archiveName}.zip`
136
+ }
137
+ }
138
+ }
139
+ return { app, strippedApp: app.replace(/-/g, ''), vizId, isNotLast: i < apps.length - 1, manifestAppId: manifest?.['sap.app']?.id ?? app, archiveName }
127
140
  })
128
141
  },
129
142
  appUIPaths: () => {
@@ -133,7 +146,7 @@ module.exports = new class ProjectReader {
133
146
  isdir(path.join(cds.root, cds.env.folders.app, e)) && isApp(e)
134
147
  )
135
148
  },
136
- hasIndexHtml: () => {},
149
+ hasIndexHtml: () => { },
137
150
  appPath: () => env.folders.app,
138
151
  appVersion: () => appVersion,
139
152
  jdkVersion: () => jdkVersion,
@@ -146,12 +159,14 @@ module.exports = new class ProjectReader {
146
159
  hasUIEmbedded: _uiEmbedded,
147
160
  hasUIModule: _uiModule,
148
161
  hasUI: () => _uiEmbedded() || _uiModule(),
162
+ hasUI5: () => _uiEmbedded() || _uiModule(),
149
163
  literal: () => () => text => text,
150
164
  hasMTXRoute: () => _inProd('extensibility') || (_inProd('multitenancy') && !_isJava()),
151
165
  hasMTXRouteJava: () => (_inProd('helm-unified-runtime') || _inProd('helm')) && _isJava() && _inProd('multitenancy'),
152
166
  sap: () => cds.cli.options.sap,
153
167
  imageRegistry: () => exists('chart/values.yaml') ? cds.load.yaml(join(cds.root, 'chart/values.yaml')).global?.image?.registry ?? '<your-container-registry-server>' : '<your-container-registry-server>',
154
168
  extendsApp: _extends,
169
+ isBas: () => Object.entries(process.env).some(([key, value]) => key === 'WS_BASE_URL' && value.includes('applicationstudio') || /THEIA_(DEFAULT_)?PLUGINS/.test(key)),
155
170
  npmWorkspaceRoot: () => {
156
171
  if (this.cachedWorkspaceRoot) return this.cachedWorkspaceRoot
157
172
  this.cachedWorkspaceRoot = execSync('npm prefix', { encoding: 'utf8', cwd: cds.root, stdio: 'pipe' }).trim()
@@ -160,8 +175,22 @@ module.exports = new class ProjectReader {
160
175
  isMonorepoMicroservice: function () {
161
176
  return this.npmWorkspaceRoot() !== cds.root
162
177
  },
163
- hasRoles: () => {}, roles: () => {}, // for xs-security generation
164
- hasCustomRoleCollections: () => exists('xs-security.json') && !!JSON.parse(fs.readFileSync(path.join(cds.root, 'xs-security.json')))['role-collections']
178
+ hasRoles: () => { }, roles: () => { }, // for xs-security generation
179
+ hasCustomRoleCollections: () => exists('xs-security.json') && !!JSON.parse(fs.readFileSync(path.join(cds.root, 'xs-security.json')))['role-collections'],
180
+ k8s: () => {
181
+ if (!exists('chart/values.yaml')) return {}
182
+
183
+ const values = cds.load.yaml(join(cds.root, 'chart/values.yaml'))
184
+
185
+ let containerize = {}
186
+ if (exists('containerize.yaml'))
187
+ containerize = cds.load.yaml(join(cds.root, 'containerize.yaml'))
188
+
189
+ return {
190
+ values: values ?? {},
191
+ containerize: containerize ?? {}
192
+ }
193
+ }
165
194
  }
166
195
 
167
196
  // Automatically creates availability checks for `cds add` commands.
@@ -169,7 +198,8 @@ module.exports = new class ProjectReader {
169
198
  // E.g. `cds add hana` can be checked using `hasHana` or `isHana`
170
199
  const project = (() => {
171
200
  const defined = {}
172
- const _get = property => { const p = property
201
+ const _get = property => {
202
+ const p = property
173
203
  if (p in reserved) return reserved[p]()
174
204
  for (const prefix of ['has', 'is', 'add']) if (p.startsWith(prefix) && p.length > prefix.length) {
175
205
  const facet = p.slice(prefix.length).replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
@@ -200,23 +230,23 @@ module.exports = new class ProjectReader {
200
230
  * @param {string[]?} roots array of directories to search for application.yaml, like `srv`
201
231
  * @returns {Promise<object[]>} array of objects with `cds` property
202
232
  */
203
- module.exports._readSpringBootConfig = async function (roots=[cds.env.folders.srv]) {
233
+ module.exports._readSpringBootConfig = async function (roots = [cds.env.folders.srv]) {
204
234
  const configNodes = []
205
235
  const rootDirs = roots.map(d => join(cds.root, d))
206
236
  for (let r of rootDirs) {
207
- const file = isfile (join (r,'./src/main/resources/application.yaml'))
208
- if (file) {
209
- const yaml = cds.load.yaml(file)
210
- for (let yamlDoc of Array.isArray(yaml) ? yaml : [yaml]) {
211
- let cds = yamlDoc?.cds;
212
- if (!cds) continue
213
- cds = _normalizeSpringBootCfg(cds)
214
- configNodes.push({cds})
237
+ const file = isfile(join(r, './src/main/resources/application.yaml'))
238
+ if (file) {
239
+ const yaml = cds.load.yaml(file)
240
+ for (let yamlDoc of Array.isArray(yaml) ? yaml : [yaml]) {
241
+ let cds = yamlDoc?.cds;
242
+ if (!cds) continue
243
+ cds = _normalizeSpringBootCfg(cds)
244
+ configNodes.push({ cds })
245
+ }
215
246
  }
216
- }
217
247
  }
218
248
  return configNodes
219
- }
249
+ }
220
250
 
221
251
  /**
222
252
  * Normalize SpringBoot's dots in keys to express nested objects
@@ -226,7 +256,7 @@ module.exports._readSpringBootConfig = async function (roots=[cds.env.folders.sr
226
256
  * @returns {object}
227
257
  */
228
258
  function _normalizeSpringBootCfg(obj) {
229
- if (typeof obj !== 'object') return obj
259
+ if (typeof obj !== 'object') return obj
230
260
  Object.keys(obj).forEach(k => {
231
261
  const prop = k.split('.')
232
262
  const last = prop.pop()
@@ -239,7 +269,7 @@ function _normalizeSpringBootCfg(obj) {
239
269
  // recursively normalize
240
270
  _normalizeSpringBootCfg(obj[k])
241
271
  // delete the original property from object if it was rewritten
242
- if (prop.length) delete obj[k]
272
+ if (prop.length) delete obj[k]
243
273
  })
244
274
  return obj
245
275
  }
@@ -296,4 +326,4 @@ function _normalizeSpringBootCfg(obj) {
296
326
  * @property {String} githubSlug GitHub org/repo.
297
327
  * @property {boolean} isMonorepoMicroservice Indicates if the project is a monorepo microservice.
298
328
  * @property {String} npmWorkspaceRoot The root path of the monorepo workspace.
299
- */
329
+ */
@@ -15,6 +15,8 @@ const appFront = {
15
15
  where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'app-front' }
16
16
  }
17
17
 
18
+ const ias = { in: 'resources', where: { 'parameters.service': 'identity' } }
19
+
18
20
  const xsuaa = {
19
21
  in: 'resources',
20
22
  where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'xsuaa', 'parameters.service-plan': 'application' }
@@ -150,9 +152,9 @@ const requiredAppApi4 = module => ({
150
152
  where: { name: 'app-api' }
151
153
  })
152
154
 
153
- const requiredMtxSidecarApi4 = srv => ({
155
+ const requiredMtxSidecarApi4 = (srv, name) => ({
154
156
  in: [srv, 'requires'],
155
- where: { 'properties.CDS_MULTITENANCY_SIDECAR_URL': '~{mtx-url}' }
157
+ where: { 'properties.CDS_MULTITENANCY_SIDECAR_URL': name }
156
158
  })
157
159
 
158
160
  const requiredJavaApprouterApi4 = srv => ({
@@ -208,6 +210,7 @@ const auditlog = {
208
210
  module.exports = {
209
211
  srv4,
210
212
  approuter,
213
+ ias,
211
214
  xsuaa,
212
215
  xsuaa4,
213
216
  destination,
@@ -5,6 +5,12 @@ const odataApi = {
5
5
  where: { target: '/odata/$1' }
6
6
  }
7
7
 
8
+ const html5RepoApi = {
9
+ in: 'routes',
10
+ where: { service: 'html5-apps-repo-rt' }
11
+ }
12
+
8
13
  module.exports = {
9
- odataApi
14
+ odataApi,
15
+ html5RepoApi
10
16
  }
@@ -15,7 +15,7 @@ module.exports = class AmsTemplate extends require('../../plugin') {
15
15
 
16
16
  static hasInProduction(env) {
17
17
  const { dependencies, devDependencies } = readProject()
18
- return env.requires?.auth?.ams || devDependencies?.['@sap/ams-dev'] || dependencies?.['@sap/ams']
18
+ return env.requires?.auth?.ams || devDependencies?.['@sap/ams-dev'] || devDependencies?.['@sap/ams'] || dependencies?.['@sap/ams']
19
19
  }
20
20
 
21
21
  async run() {
@@ -3,18 +3,12 @@
3
3
  "version": "1.0.0",
4
4
  "main": "webapp/index.html",
5
5
  "scripts": {
6
- "build": "ui5 build preload --clean-dest --config ui5-deploy.yaml",
7
- "build-local": "ui5 build preload --clean-dest",
6
+ "build": "ui5 build preload --clean-dest",
8
7
  "start": "ui5 serve"
9
8
  },
10
9
  "devDependencies": {
11
10
  "@ui5/cli": "^4",
12
11
  "ui5-task-zipper": "^3"
13
- },
14
- "ui5": {
15
- "dependencies": [
16
- "ui5-task-zipper"
17
- ]
18
12
  }
19
13
  {{/hasUI5}}
20
14
  }
@@ -1,6 +1,25 @@
1
1
  ---
2
2
  # yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json
3
- specVersion: '3.1'
3
+ specVersion: '4.0'
4
4
  metadata:
5
5
  name: {{app}}
6
6
  type: application
7
+ resources:
8
+ configuration:
9
+ propertiesFileSourceEncoding: UTF-8
10
+ builder:
11
+ resources:
12
+ excludes:
13
+ - "/test/**"
14
+ - "/localService/**"
15
+ customTasks:
16
+ - name: ui5-task-zipper
17
+ afterTask: generateVersionInfo
18
+ configuration:
19
+ archiveName: {{app}}
20
+ additionalFiles:
21
+ - xs-app.json
22
+ {{#isTypescript}}
23
+ - name: ui5-tooling-transpile-task
24
+ afterTask: replaceVersion
25
+ {{/isTypescript}}
@@ -26,10 +26,6 @@ module.exports = class extends require('../../plugin') {
26
26
  project.app = app
27
27
  await merge(__dirname, 'files/ui5.yaml.hbs').into(join(appPath, app, 'ui5.yaml'), { project })
28
28
  }
29
- for (const { app } of apps) {
30
- project.app = app
31
- await merge(__dirname, 'files/ui5-deploy.yaml.hbs').into(join(appPath, app, 'ui5-deploy.yaml'), { project })
32
- }
33
29
  }
34
30
 
35
31
  for (const { app } of apps) {
@@ -28,28 +28,23 @@ module.exports = class ApplicationLoggingTemplate extends require('../../plugin'
28
28
 
29
29
  async combine() {
30
30
  const project = readProject()
31
- const { addMta, addApprouter, isJava, addMultitenancy, srvPath } = project
31
+ const { addMta, hasMta, hasApprouter, isJava, addMultitenancy, hasMultitenancy, srvPath } = project
32
32
 
33
- if (addMta || addMultitenancy) {
33
+ if (addMta || (addMultitenancy && hasMta)) {
34
34
  const additions = [], relationships = []
35
- if (addMta) {
36
- const srv = srv4(srvPath)
37
- additions.push(srv, applicationLogging)
38
- relationships.push({
39
- insert: [applicationLogging, 'name'],
40
- into: [srv, 'requires', 'name']
41
- })
42
- }
43
- if (addMultitenancy) {
35
+ const srv = srv4(srvPath)
36
+ relationships.push({
37
+ insert: [applicationLogging, 'name'],
38
+ into: [srv, 'requires', 'name']
39
+ })
40
+ if (hasMultitenancy) {
44
41
  const mtxSidecar = mtxSidecar4(isJava ? 'mtx/sidecar' : 'gen/mtx/sidecar')
45
- additions.push(mtxSidecar)
46
42
  relationships.push({
47
43
  insert: [applicationLogging, 'name'],
48
44
  into: [mtxSidecar, 'requires', 'name']
49
45
  })
50
46
  }
51
- if (addApprouter) {
52
- additions.push(approuter)
47
+ if (hasApprouter) {
53
48
  relationships.push({
54
49
  insert: [applicationLogging, 'name'],
55
50
  into: [approuter, 'requires', 'name']
@@ -57,7 +57,6 @@ modules:
57
57
  url: ${default-url}
58
58
  {{/isJava}}
59
59
 
60
- {{#hasMultitenancy}}
61
60
  - name: {{appName}}-mtx
62
61
  type: nodejs
63
62
  path: {{#isNodejs}}gen/{{/isNodejs}}mtx/sidecar{{#isNodejs}}
@@ -66,7 +65,6 @@ modules:
66
65
  properties:
67
66
  SUBSCRIPTION_URL: ~{app-protocol}://\${tenant_subdomain}-~{app-uri}
68
67
  {{/isNodejs}}
69
- {{/hasMultitenancy}}
70
68
 
71
69
  resources:
72
70
  - name: {{appName}}-auth
@@ -83,3 +81,10 @@ resources:
83
81
  oauth2-configuration:
84
82
  redirect-uris:
85
83
  - https://*~{app-api/app-uri}/**
84
+
85
+ - name: {{appName}}-ias
86
+ type: org.cloudfoundry.managed-service
87
+ requires:
88
+ - name: app-api
89
+ parameters:
90
+ service: identity
@@ -5,7 +5,7 @@ const { readProject } = require('../../projectReader')
5
5
  const { sort, merge } = require('../../merge')
6
6
  const {
7
7
  srv4, mtxSidecar4, approuter, // Modules
8
- xsuaa, // BTP Services
8
+ xsuaa, ias, // BTP Services
9
9
  requiredMtxApi, requiredJavaApprouterApi4, providedJavaApprouterApi, // APIs
10
10
  requiredAppApi4, providedAppApi,
11
11
  approuterMTXRoute,
@@ -34,30 +34,35 @@ module.exports = class ApprouterTemplate extends require('../../plugin') {
34
34
  const project = readProject()
35
35
  const {
36
36
  isNodejs, isJava,
37
- addXsuaa, hasXsuaa, addMultitenancy, hasMTXRoute, hasMTXRouteJava,
38
- addMta, addHelm, addHelmUnifiedRuntime, hasMta, hasHelm, hasHelmUnifiedRuntime,
37
+ addIas, addXsuaa, hasXsuaa, hasIas, addMultitenancy, hasMTXRoute, hasMTXRouteJava,
38
+ addMta, addHelm, addHelmUnifiedRuntime,
39
+ hasMta, hasHelm, hasHelmUnifiedRuntime, hasMultitenancy,
39
40
  addContainerize, srvPath, approuterPath
40
41
  } = project
41
42
 
42
43
 
43
- if (addMta || (addMultitenancy || addXsuaa) && hasMta) {
44
+ if (addMta || (addMultitenancy || addXsuaa || addIas) && hasMta) {
44
45
  const srv = srv4(srvPath)
45
- const apis = isJava && addMultitenancy ? [requiredJavaApprouterApi4(srv), providedJavaApprouterApi] : []
46
+ const apis = isJava && hasMultitenancy ? [requiredJavaApprouterApi4(srv), providedJavaApprouterApi] : []
46
47
  const mtxSidecar = mtxSidecar4(isJava ? 'mtx/sidecar' : 'gen/mtx/sidecar')
47
- if (isNodejs && addMultitenancy) apis.push(requiredMtxApi, requiredAppApi4(mtxSidecar), providedAppApi)
48
- if (isJava && addMultitenancy) apis.push(requiredMtxApi)
48
+ if (isNodejs && hasMultitenancy) apis.push(requiredMtxApi, requiredAppApi4(mtxSidecar), providedAppApi)
49
+ if (isJava && hasMultitenancy) apis.push(requiredMtxApi)
49
50
  const modules = [srv, approuter]
50
- if (addMultitenancy) modules.push(mtxSidecar)
51
+ if (hasMultitenancy) modules.push(mtxSidecar)
51
52
  const additions = [...modules, ...apis]
52
- if (hasXsuaa) additions.push(xsuaa)
53
53
  const relationships = []
54
- if (hasXsuaa) relationships.push({
55
- insert: [xsuaa, 'name'],
56
- into: [srv, 'requires', 'name']
57
- }, {
58
- insert: [xsuaa, 'name'],
59
- into: [approuter, 'requires', 'name']
60
- })
54
+ if (hasXsuaa) {
55
+ relationships.push({
56
+ insert: [xsuaa, 'name'],
57
+ into: [approuter, 'requires', 'name']
58
+ })
59
+ }
60
+ if (hasIas) {
61
+ relationships.push({
62
+ insert: [ias, 'name'],
63
+ into: [approuter, 'requires', 'name']
64
+ })
65
+ }
61
66
  await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', {
62
67
  with: project, additions, relationships,
63
68
  })
@@ -33,7 +33,7 @@ module.exports = class AuditLoggingTemplate extends require('../../plugin') {
33
33
 
34
34
  async combine() {
35
35
  const project = readProject()
36
- const { addMta, addHelm, addMultitenancy, srvPath } = project
36
+ const { addMta, addHelm, addHelmUnifiedRuntime, addMultitenancy, srvPath } = project
37
37
  const { plan = 'premium' } = cds.cli.options
38
38
  project.servicePlan = plan
39
39
 
@@ -53,7 +53,7 @@ module.exports = class AuditLoggingTemplate extends require('../../plugin') {
53
53
  ]
54
54
  })
55
55
  }
56
- if (addHelm) {
56
+ if (addHelm || addHelmUnifiedRuntime) {
57
57
  await merge({
58
58
  srv: { bindings: { auditlog: { serviceInstanceName: 'auditlog' }}},
59
59
  auditlog: {
@@ -1,6 +1,7 @@
1
1
  const cds = require('../../../cds')
2
2
  const { merge, sort } = require('../../merge')
3
3
 
4
+ const mvn = require('../../mvn')
4
5
  const { readProject } = require('../../projectReader')
5
6
  const { srv4, mtxSidecar4, approuter } = require('../../registries/mta')
6
7
 
@@ -32,6 +33,7 @@ module.exports = class extends require('../../plugin') {
32
33
  await sort('package.json', 'dependencies')
33
34
  } else if (isJava) {
34
35
  await merge(__dirname, 'files/java-cdsrc.json.hbs').into('.cdsrc.json', { with: project })
36
+ await mvn.add('APPLICATION_LOGGING')
35
37
  }
36
38
  }
37
39
 
@@ -50,7 +52,6 @@ module.exports = class extends require('../../plugin') {
50
52
  const additions = [cloudLogging], relationships = []
51
53
  const srv = srv4(srvPath)
52
54
  if (addMta) {
53
- additions.push(srv)
54
55
  relationships.push({
55
56
  insert: [cloudLogging, 'name'],
56
57
  into: [srv, 'requires', 'name']
@@ -58,14 +59,12 @@ module.exports = class extends require('../../plugin') {
58
59
  }
59
60
  if (addMultitenancy) {
60
61
  const mtxSidecar = mtxSidecar4(isJava ? 'mtx/sidecar' : 'gen/mtx/sidecar')
61
- additions.push(mtxSidecar)
62
62
  relationships.push({
63
63
  insert: [cloudLogging, 'name'],
64
64
  into: [mtxSidecar, 'requires', 'name']
65
65
  })
66
66
  }
67
67
  if (addApprouter) {
68
- additions.push(approuter)
69
68
  relationships.push({
70
69
  insert: [cloudLogging, 'name'],
71
70
  into: [approuter, 'requires', 'name']