@sap/cds-dk 9.4.3 → 9.6.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 (168) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/bin/compile/index.js +1 -1
  3. package/bin/env.js +3 -3
  4. package/bin/export.js +1 -1
  5. package/bin/help.js +0 -1
  6. package/bin/import.js +8 -3
  7. package/bin/repl.js +7 -11
  8. package/bin/up.js +159 -23
  9. package/bin/util/containerize.js +79 -25
  10. package/bin/version.js +4 -4
  11. package/lib/bind/k8s.js +1 -1
  12. package/lib/build/plugins/helm/files/templates/_deployment_helpers.tpl +1 -1
  13. package/lib/build/plugins/helm/index.js +1 -1
  14. package/lib/build/provider/hana/2migration.js +16 -24
  15. package/lib/build/provider/hana/index.js +1 -1
  16. package/lib/build/provider/hana/template/.hdiconfig-all +7 -1
  17. package/lib/build/provider/hana/template/package.json +1 -1
  18. package/lib/build/provider/internalBuildPlugin.js +15 -1
  19. package/lib/build/provider/java/index.js +12 -1
  20. package/lib/build/provider/mtx-sidecar/index.js +2 -1
  21. package/lib/build/provider/nodejs/index.js +2 -1
  22. package/lib/build/util.js +36 -19
  23. package/lib/compile/to_openapi.js +1 -5
  24. package/lib/import/common.js +9 -2
  25. package/lib/import/odata/ParserContext.js +1 -0
  26. package/lib/import/odata/v4parser.js +57 -25
  27. package/lib/import/openapi/importOpenAPI.js +6 -1
  28. package/lib/init/constants.js +1 -1
  29. package/lib/init/index.js +20 -6
  30. package/lib/init/merge.js +16 -0
  31. package/lib/init/projectReader.js +34 -8
  32. package/lib/init/registries/mta.js +2 -2
  33. package/lib/init/template/ams/index.js +3 -3
  34. package/lib/init/template/app-frontend/files/app-package.json.hbs +1 -7
  35. package/lib/init/template/app-frontend/files/ui5.yaml.hbs +21 -1
  36. package/lib/init/template/app-frontend/index.js +2 -6
  37. package/lib/init/template/application-logging/index.js +11 -16
  38. package/lib/init/template/approuter/files/mta.yaml.hbs +0 -2
  39. package/lib/init/template/approuter/index.js +13 -10
  40. package/lib/init/template/attachments/index.js +4 -2
  41. package/lib/init/template/audit-logging/index.js +4 -2
  42. package/lib/init/template/cloud-logging/index.js +6 -5
  43. package/lib/init/template/connectivity/index.js +2 -2
  44. package/lib/init/template/console/files/package.json +5 -0
  45. package/lib/init/template/console/index.js +25 -0
  46. package/lib/init/template/containerize/index.js +4 -6
  47. package/lib/init/template/data/as-json.js +18 -20
  48. package/lib/init/template/destination/index.js +4 -2
  49. package/lib/init/template/dynatrace/files/package.json.hbs +2 -2
  50. package/lib/init/template/dynatrace/index.js +4 -4
  51. package/lib/init/template/enterprise-messaging/files/mta.yaml.hbs +3 -1
  52. package/lib/init/template/enterprise-messaging/index.js +4 -2
  53. package/lib/init/template/enterprise-messaging-shared/index.js +4 -2
  54. package/lib/init/template/github-actions/files/workflows/cf.yaml.hbs +16 -1
  55. package/lib/init/template/github-actions/files/workflows/release.yaml.hbs +2 -2
  56. package/lib/init/template/github-actions/files/workflows/test.yaml.hbs +20 -0
  57. package/lib/init/template/github-actions/index.js +7 -10
  58. package/lib/init/template/hana/files/db/package.json +2 -2
  59. package/lib/init/template/hana/files/mta.yaml.hbs +0 -4
  60. package/lib/init/template/hana/index.js +7 -9
  61. package/lib/init/template/helm/index.js +8 -10
  62. package/lib/init/template/helm-unified-runtime/index.js +1 -4
  63. package/lib/init/template/html5-repo/files/app-package.json.hbs +1 -7
  64. package/lib/init/template/html5-repo/files/ui5.yaml.hbs +21 -1
  65. package/lib/init/template/html5-repo/index.js +14 -18
  66. package/lib/init/template/ias/files/values.yaml.hbs +6 -6
  67. package/lib/init/template/ias/index.js +13 -21
  68. package/lib/init/template/java/index.js +14 -0
  69. package/lib/init/template/kafka/index.js +4 -2
  70. package/lib/init/template/{containerize → kyma}/files/containerize.yaml.hbs +17 -2
  71. package/lib/init/template/kyma/index.js +129 -5
  72. package/lib/init/template/malware-scanner/files/mta.yaml.hbs +3 -1
  73. package/lib/init/template/malware-scanner/files/values.yaml +3 -1
  74. package/lib/init/template/malware-scanner/index.js +4 -2
  75. package/lib/init/template/multitenancy/files/mta.yaml.hbs +1 -3
  76. package/lib/init/template/multitenancy/files/values.yaml.hbs +8 -0
  77. package/lib/init/template/multitenancy/index.js +15 -7
  78. package/lib/init/template/nodejs/files/.vscode/extensions.json +0 -3
  79. package/lib/init/template/nodejs/files/package.json.hbs +1 -1
  80. package/lib/init/template/pipeline/files/app-package.json.hbs +1 -7
  81. package/lib/init/template/pipeline/files/ui5.yaml.hbs +21 -1
  82. package/lib/init/template/pipeline/index.js +1 -2
  83. package/lib/init/template/portal/files/mta.yaml.hbs +5 -3
  84. package/lib/init/template/portal/index.js +8 -8
  85. package/lib/init/template/postgres/index.js +2 -2
  86. package/lib/init/template/redis/index.js +2 -3
  87. package/lib/init/template/redis-messaging/index.js +2 -3
  88. package/lib/init/template/sample/files/_i18n/i18n_de.properties +21 -0
  89. package/lib/init/template/sample/files/_i18n/i18n_en.properties +21 -0
  90. package/lib/init/template/sample/files/_i18n/i18n_fr.properties +21 -0
  91. package/lib/init/template/sample/files/_i18n/messages_de.properties +34 -0
  92. package/lib/init/template/sample/files/_i18n/messages_en.properties +34 -0
  93. package/lib/init/template/sample/files/_i18n/messages_fr.properties +34 -0
  94. package/lib/init/template/sample/files/app/_i18n/i18n.properties +13 -25
  95. package/lib/init/template/sample/files/app/_i18n/i18n_de.properties +13 -25
  96. package/lib/init/template/sample/files/app/admin-authors/fiori-service.cds +52 -0
  97. package/lib/init/template/sample/files/app/admin-authors/webapp/Component.js.hbs +3 -0
  98. package/lib/init/template/sample/files/app/admin-authors/webapp/i18n/i18n.properties +3 -0
  99. package/lib/init/template/sample/files/app/admin-authors/webapp/i18n/i18n_de.properties +3 -0
  100. package/lib/init/template/sample/files/app/admin-authors/webapp/i18n/i18n_en.properties +3 -0
  101. package/lib/init/template/sample/files/app/admin-authors/webapp/manifest.json +130 -0
  102. package/lib/init/template/sample/files/app/admin-books/fiori-service.cds +81 -88
  103. package/lib/init/template/sample/files/app/admin-books/webapp/Component.js.hbs +3 -0
  104. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n.properties +3 -3
  105. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_de.properties +3 -3
  106. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_en.properties +3 -3
  107. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +24 -48
  108. package/lib/init/template/sample/files/app/appconfig/fioriSandboxConfig.json +56 -5
  109. package/lib/init/template/sample/files/app/browse/fiori-service.cds +30 -30
  110. package/lib/init/template/sample/files/app/browse/webapp/Component.js.hbs +3 -0
  111. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n.properties +3 -4
  112. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_de.properties +3 -3
  113. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_en.properties +3 -4
  114. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +19 -24
  115. package/lib/init/template/sample/files/app/common.cds +127 -158
  116. package/lib/init/template/sample/files/app/fiori-apps.html +50 -0
  117. package/lib/init/template/sample/files/app/genres/fiori-service.cds +33 -0
  118. package/lib/init/template/sample/files/app/genres/tree-view.cds +42 -0
  119. package/lib/init/template/sample/files/app/genres/value-help.cds +6 -0
  120. package/lib/init/template/sample/files/app/genres/webapp/Component.js.hbs +3 -0
  121. package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n.properties +3 -0
  122. package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n_de.properties +3 -0
  123. package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n_en.properties +3 -0
  124. package/lib/init/template/sample/files/app/genres/webapp/manifest.json +126 -0
  125. package/lib/init/template/sample/files/app/services.cds +6 -2
  126. package/lib/init/template/sample/files/db/currencies.cds +13 -0
  127. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books.csv +5 -5
  128. package/lib/init/template/sample/files/db/data/{sap.capire.bookshop-Books_texts.csv → sap.capire.bookshop-Books.texts.csv} +4 -4
  129. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.csv +42 -15
  130. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.texts.csv +43 -0
  131. package/lib/init/template/sample/files/db/data/sap.common-Currencies.csv +12 -0
  132. package/lib/init/template/sample/files/db/data/sap.common-Currencies.texts.csv +15 -0
  133. package/lib/init/template/sample/files/db/hana/index.cds +10 -0
  134. package/lib/init/template/sample/files/db/schema.cds +35 -22
  135. package/lib/init/template/sample/files/db/sqlite/index.cds +10 -0
  136. package/lib/init/template/sample/files/package.json.hbs +14 -1
  137. package/lib/init/template/sample/files/srv/access-control.cds +3 -0
  138. package/lib/init/template/sample/files/srv/admin-service.cds +3 -2
  139. package/lib/init/template/sample/files/srv/cat-service.cds +7 -11
  140. package/lib/init/template/sample/index.js +40 -18
  141. package/lib/init/template/sqlite/files/package.json.hbs +13 -0
  142. package/lib/init/template/sqlite/index.js +1 -1
  143. package/lib/init/template/telemetry/index.js +1 -3
  144. package/lib/init/template/typescript/files/package.json +1 -1
  145. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +0 -5
  146. package/lib/init/template/workzone-standard/index.js +0 -1
  147. package/lib/init/template/xsuaa/files/mta.yaml.hbs +4 -0
  148. package/lib/init/template/xsuaa/index.js +7 -6
  149. package/lib/lint/Linter.js +10 -4
  150. package/lib/lint/eslintHelp.json +1 -1
  151. package/lib/mtx/tenant_action.js +12 -4
  152. package/lib/util/checksum.js +41 -0
  153. package/lib/util/fs.js +8 -1
  154. package/lib/util/glob.js +26 -13
  155. package/lib/util/question.js +60 -7
  156. package/npm-shrinkwrap.json +141 -118
  157. package/package.json +2 -2
  158. package/lib/init/template/app-frontend/files/ui5-deploy.yaml.hbs +0 -25
  159. package/lib/init/template/github-actions/files/cf-setup/action.yaml +0 -53
  160. package/lib/init/template/html5-repo/files/ui5-deploy.yaml.hbs +0 -25
  161. package/lib/init/template/pipeline/files/ui5-deploy.yaml.hbs +0 -25
  162. package/lib/init/template/sample/files/app/admin-books/webapp/Component.js +0 -8
  163. package/lib/init/template/sample/files/app/browse/webapp/Component.js +0 -7
  164. package/lib/init/template/sample/files/app/index.html +0 -32
  165. package/lib/init/template/sqlite/files/package.json +0 -5
  166. /package/lib/init/template/{helm → kyma}/files/Chart.yaml.hbs +0 -0
  167. /package/lib/init/template/{helm → kyma}/files/values.schema.json +0 -0
  168. /package/lib/init/template/{helm → kyma}/files/values.yaml.hbs +0 -0
@@ -262,8 +262,8 @@ function _captureAnnotationInCSN(targetString, annotationObject, csnDefsTargetOb
262
262
  }
263
263
 
264
264
  /**
265
- * Annotations need to be flattened in CSN. E.g.
266
- *
265
+ * Annotations need to be flattened in CSN. E.g.
266
+ *
267
267
  * @Core.ContentDisposition : {
268
268
  * Type : 'inline',
269
269
  * Filename: typedImageFileName
@@ -426,7 +426,7 @@ function _validateAnnotationNamespace(annotationTerm, vocabNamespaceToAlias) {
426
426
  if (aliasOrNamespace === "cds") {
427
427
  return
428
428
  }
429
-
429
+
430
430
  for (let alias of Object.values(known_vocabularies)) {
431
431
  if (alias === aliasOrNamespace) {
432
432
  return;
@@ -462,7 +462,7 @@ function _resolveAnnotationTerm(schemaDataAnnotation, parserContext) {
462
462
  }
463
463
  }
464
464
  /**
465
- * Case 3: Namespace doesn't have edmx:Reference. But it is present in known_vocabularies.
465
+ * Case 3: Namespace doesn't have edmx:Reference. But it is present in known_vocabularies.
466
466
  * Replacing it with standard alias.
467
467
  */
468
468
  for (const [namespace, alias] of Object.entries(known_vocabularies)) {
@@ -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);
@@ -928,21 +942,10 @@ function _generateCSNForBoundActionFunction(actionFunctionObject, kind, parserCo
928
942
  csnObj.params = {};
929
943
 
930
944
  for (let index = 0; index < parameters.length; index++) {
931
- let isCollection = parameters[index].$Collection;
932
- csnObj.params[parameters[index].$Name] = _parseParameterAndReturnTypeContent(parameters[index], parserContext);
933
- /**
934
- * binding parameter handling - only first parameter will contain the binding
935
- * parameter information, so only make change to the first parameter information.
936
- * NOTE: Edm.Stream can act as the binding parameter for action/function
937
- * We don't have any example for it, so not handled as of now
938
- */
939
- if (isBound && index === 0) {
940
- if (isCollection) {
941
- csnObj.params[parameters[index].$Name].items.type = '$self';
942
- }
943
- else {
944
- csnObj.params[parameters[index].$Name].type = '$self';
945
- }
945
+ // skip the first parameter for bound actions, which is the $self parameter
946
+ // that is only implicit in CSN
947
+ if (!isBound || index > 0) {
948
+ csnObj.params[parameters[index].$Name] = _parseParameterAndReturnTypeContent(parameters[index], parserContext);
946
949
  }
947
950
  }
948
951
  }
@@ -1335,7 +1338,7 @@ function _parseEntityContainer(csnDefs, entityContainerElements, parserContext)
1335
1338
  Object.keys(parserContext.entityContainerAnnotations[annotationTarget]).length > 0
1336
1339
  ) {
1337
1340
  entityContainerAnnotations[annotationTarget] = Object.assign(
1338
- entityContainerAnnotations[annotationTarget],
1341
+ entityContainerAnnotations[annotationTarget],
1339
1342
  possibleAnnotations
1340
1343
  );
1341
1344
  }
@@ -1438,7 +1441,7 @@ function _collectBoundActionFunction(isAction, currNamespace, elementName, eleme
1438
1441
 
1439
1442
  // binding parameter type should be within the available namespaces in the scope of a document.
1440
1443
  if (entityTypeNamespace !== schemaName && entityTypeNamespace !== parserContext.schemaAliasToNamespace[schemaName]) {
1441
-
1444
+
1442
1445
  // checking if the namespace is available in other schema of the document
1443
1446
  if (parserContext.schemaToSchemaDataIndex.hasOwnProperty(schemaName)) {
1444
1447
  entityTypeNamespace = schemaName;
@@ -1653,12 +1656,41 @@ function _generateCSN(edmxAsJson, ignorePersistenceSkip, parserContext, importer
1653
1656
  csn.definitions[parserContext.primarySchema].kind = 'service';
1654
1657
  csn.definitions[parserContext.primarySchema]['@cds.external'] = true;
1655
1658
 
1659
+ if (parserContext.schemaDetails[parserContext.primarySchema]?.containment) {
1660
+ csn.definitions[parserContext.primarySchema]['@odata.containment'] = true;
1661
+ }
1662
+
1656
1663
  // Step 8: parse entity container contents and their annotations
1657
1664
  let schemaData = schemaDataList[parserContext.schemaToSchemaDataIndex[parserContext.primarySchema]];
1658
1665
  let entityContainerElements = schemaData[parserContext.entityContainerName];
1659
1666
  csn.definitions = Object.assign(_parseEntityContainer(csn.definitions, entityContainerElements, parserContext), csn.definitions);
1660
1667
  }
1661
1668
 
1669
+
1670
+ // Step 9: 2. Step for generating $ContainsTarget & $Partner links
1671
+ // The containing composition is already generated. Now the Keys: [] has to be removed
1672
+ // on the backlink association because per OData spec, OData does not expose the foreign key fields
1673
+ // used by the backlink and thus it is save to assume they must be generated for proper mocking
1674
+ //
1675
+ // This happens after Step 8 because that adjusts the names of entity container entities where
1676
+ // entity type and entity set differ in naming
1677
+ for (const [eName, entity] of Object.entries(csn.definitions)) {
1678
+ if (entity.kind !== 'entity') continue;
1679
+ const containingCompositions = Object.keys(entity.elements).filter(e =>
1680
+ entity.elements[e].type === 'cds.Composition' &&
1681
+ entity.elements[e].on
1682
+ );
1683
+ for (const comp of containingCompositions) {
1684
+ const compTarget = csn.definitions[entity.elements[comp].target];
1685
+ const backLink = Object.keys(compTarget.elements).find(e =>
1686
+ compTarget.elements[e].type === 'cds.Association' &&
1687
+ compTarget.elements[e].target === eName
1688
+ )
1689
+ if (compTarget.elements[backLink]?.keys) {
1690
+ delete compTarget.elements[backLink].keys
1691
+ }
1692
+ }
1693
+ }
1662
1694
  return JSON.stringify(csn, null, 4);
1663
1695
  }
1664
1696
 
@@ -1670,7 +1702,7 @@ function _generateEDMX2JSON(edmx, name_space) {
1670
1702
  const v4Conv = MetadataConverterFactory.createEdmxV40XmlToV40Json(name_space);
1671
1703
  v4Conv.execute(inputBuffer, (error, json, missingReferencedDocuments) => {
1672
1704
  if (missingReferencedDocuments?.length > 0) {
1673
- console.log(missingReferencedDocuments);
1705
+ console.log(missingReferencedDocuments);
1674
1706
  }
1675
1707
 
1676
1708
  if (json) {
@@ -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.6.0',
9
9
 
10
10
  OPTIONS: Object.freeze({
11
11
  COMPLETION: 'completion',
package/lib/init/index.js CHANGED
@@ -230,18 +230,32 @@ module.exports = class CDSGenerator {
230
230
  async _fillTemplateList(plugins) {
231
231
  const templates = new Map
232
232
  for (let plugin of plugins) {
233
+ // cds 10 remove
234
+ if(plugin === "helm" || plugin === "helm-unified-runtime") {
235
+ cds.cli.options.add?.add("kyma")
236
+ cds.cli.argv[0] += ",kyma"
237
+ }
233
238
  // Compat
234
- if (plugin === 'appfront') plugin = 'app-front'
235
239
  if (plugin === 'helm' && cds.cli.options['internal-unified-runtime-charts']) {
240
+ console.warn(term.warn('"--internal-unified-runtime-charts" is deprecated, please use "cds add kyma --unified-runtime" instead'))
236
241
  plugin = 'helm-unified-runtime'
237
242
  cds.cli.options.add?.delete('helm')
238
243
  cds.cli.options.add?.add('helm-unified-runtime')
239
244
  }
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'
245
+ const _alias = (alias, preferred) => {
246
+ if (plugin === alias) {
247
+ plugin = preferred
248
+ cds.cli.options.add?.delete(alias)
249
+ cds.cli.options.add?.add(preferred)
250
+ cds.cli.argv[0] = cds.cli.argv[0].replace(alias, preferred)
251
+ }
252
+ }
253
+ _alias('appfront', 'app-frontend')
254
+ _alias('app-front', 'app-frontend')
255
+ _alias('gha', 'github-actions')
256
+ _alias('postgresql', 'postgres')
257
+ _alias('kibana', 'application-logging')
258
+ _alias('kibana-logging', 'application-logging')
245
259
 
246
260
  if (!templates.has(plugin)) {
247
261
  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)
@@ -123,16 +123,19 @@ module.exports = new class ProjectReader {
123
123
  const inbounds = manifest?.['sap.app']?.crossNavigation?.inbounds
124
124
  const [firstInbound] = Object.values(inbounds ?? { intent: { semanticObject: 'Books', action: 'display' } })
125
125
  const vizId = firstInbound.semanticObject + '-' + firstInbound.action
126
- // Try to read archiveName from ui5-deploy.yaml if it exists
126
+ // Try to read archiveName from ui5.yaml if it exists
127
127
  let archiveName = ''
128
- const ui5DeployPath = path.resolve(cds.root, cds.env.folders.app, app, 'ui5-deploy.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')
128
+ const ui5YamlPaths = [
129
+ path.resolve(cds.root, cds.env.folders.app, app, 'ui5-deploy.yaml'),
130
+ path.resolve(cds.root, cds.env.folders.app, app, 'ui5.yaml')
131
+ ]
132
+ for (const yamlPath of ui5YamlPaths) {
133
+ if (exists(yamlPath)) {
134
+ const ui5Deploy = cds.load.yaml(yamlPath)
135
+ const zipperTask = ui5Deploy?.builder?.customTasks?.find?.(t => t.name === 'ui5-task-zipper')
134
136
  if (zipperTask?.configuration?.archiveName) {
135
137
  archiveName = `${zipperTask.configuration.archiveName}.zip`
138
+ break
136
139
  }
137
140
  }
138
141
  }
@@ -159,6 +162,7 @@ module.exports = new class ProjectReader {
159
162
  hasUIEmbedded: _uiEmbedded,
160
163
  hasUIModule: _uiModule,
161
164
  hasUI: () => _uiEmbedded() || _uiModule(),
165
+ hasUI5: () => _uiEmbedded() || _uiModule(),
162
166
  literal: () => () => text => text,
163
167
  hasMTXRoute: () => _inProd('extensibility') || (_inProd('multitenancy') && !_isJava()),
164
168
  hasMTXRouteJava: () => (_inProd('helm-unified-runtime') || _inProd('helm')) && _isJava() && _inProd('multitenancy'),
@@ -175,7 +179,28 @@ module.exports = new class ProjectReader {
175
179
  return this.npmWorkspaceRoot() !== cds.root
176
180
  },
177
181
  hasRoles: () => { }, roles: () => { }, // for xs-security generation
178
- hasCustomRoleCollections: () => exists('xs-security.json') && !!JSON.parse(fs.readFileSync(path.join(cds.root, 'xs-security.json')))['role-collections']
182
+ hasCustomRoleCollections: () => exists('xs-security.json') && !!JSON.parse(fs.readFileSync(path.join(cds.root, 'xs-security.json')))['role-collections'],
183
+ k8s: () => {
184
+ if (!exists('chart/values.yaml')) return {}
185
+
186
+ const values = cds.load.yaml(join(cds.root, 'chart/values.yaml'))
187
+
188
+ let containerize = {}
189
+ if (exists('containerize.yaml'))
190
+ containerize = cds.load.yaml(join(cds.root, 'containerize.yaml'))
191
+
192
+ return {
193
+ values: values ?? {},
194
+ containerize: containerize ?? {}
195
+ }
196
+ },
197
+ hasHelmUnifiedRuntime: () =>
198
+ exists(join(cds.root, 'chart', 'Chart.yaml')) &&
199
+ (() => {
200
+ const chartYaml = cds.load.yaml(join(cds.root, 'chart', 'Chart.yaml'))
201
+ return chartYaml.annotations?.['app.kubernetes.io/managed-by'] === 'cds-dk/helm'
202
+ && chartYaml.dependencies?.find(dep => dep.name === 'web-application')?.repository !== undefined
203
+ })()
179
204
  }
180
205
 
181
206
  // Automatically creates availability checks for `cds add` commands.
@@ -287,6 +312,7 @@ function _normalizeSpringBootCfg(obj) {
287
312
  * @property {boolean} hasTyper Indicates if code supports CDS typer.
288
313
  * @property {boolean} hasHelmUnifiedRuntime Indicates if the project uses Unified Runtime Helm deployment.
289
314
  * @property {boolean} hasHelm Indicates if the project uses CAP Helm deployment.
315
+ * @property {boolean} hasKyma Indicates if the project uses Kyma.
290
316
  * @property {boolean} hasMta Indicates if the project uses MTA deployment.
291
317
  * @property {boolean} hasConnectivity Indicates if the project uses connectivity.
292
318
  * @property {boolean} hasMultitenancy Indicates if the project is multitenant.
@@ -152,9 +152,9 @@ const requiredAppApi4 = module => ({
152
152
  where: { name: 'app-api' }
153
153
  })
154
154
 
155
- const requiredMtxSidecarApi4 = srv => ({
155
+ const requiredMtxSidecarApi4 = (srv, name) => ({
156
156
  in: [srv, 'requires'],
157
- where: { 'properties.CDS_MULTITENANCY_SIDECAR_URL': '~{mtx-url}' }
157
+ where: { 'properties.CDS_MULTITENANCY_SIDECAR_URL': name }
158
158
  })
159
159
 
160
160
  const requiredJavaApprouterApi4 = srv => ({
@@ -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() {
@@ -28,7 +28,7 @@ module.exports = class AmsTemplate extends require('../../plugin') {
28
28
 
29
29
  async combine() {
30
30
  const project = readProject()
31
- const { addMta, addCfManifest, addHelm, addHelmUnifiedRuntime, addContainerize } = project
31
+ const { addMta, addCfManifest, addContainerize, addKyma } = project
32
32
 
33
33
  if (addMta) {
34
34
  await this.#combineWithMta(project)
@@ -38,7 +38,7 @@ module.exports = class AmsTemplate extends require('../../plugin') {
38
38
  await this.#combineWithCfManifest(project)
39
39
  }
40
40
 
41
- if (addHelm || addHelmUnifiedRuntime) {
41
+ if (addKyma) {
42
42
  await this.#combineWithHelm(project)
43
43
  }
44
44
 
@@ -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,26 @@
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
+ relativePaths: true
21
+ additionalFiles:
22
+ - xs-app.json
23
+ {{#isTypescript}}
24
+ - name: ui5-tooling-transpile-task
25
+ afterTask: replaceVersion
26
+ {{/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) {
@@ -83,14 +79,14 @@ module.exports = class extends require('../../plugin') {
83
79
  }
84
80
 
85
81
  // When Helm support is added
86
- // if (hasContainerize) {
82
+ // if (hasContainerize || hasKyma) {
87
83
  // const additions = [{
88
84
  // in: 'modules',
89
85
  // where: { name: `${project.appName}-content-deployer` }
90
86
  // }]
91
87
  // await merge(__dirname, '/files/containerize.yaml.hbs').into('containerize.yaml', { with: project, additions })
92
88
  // }
93
- // if (hasHelm || hasHelmUnifiedRuntime) {
89
+ // if (hasKyma) {
94
90
  // await renderAndCopy(join(__dirname, 'files/ui-resources'), 'ui-resources', project)
95
91
  // await merge(__dirname, 'files/values.yaml.hbs').into('chart/values.yaml', { with: project })
96
92
  // }
@@ -14,8 +14,8 @@ module.exports = class ApplicationLoggingTemplate extends require('../../plugin'
14
14
  }
15
15
 
16
16
  async canRun() {
17
- const { hasMta, hasHelm, hasHelmUnifiedRuntime } = readProject()
18
- if ((hasHelmUnifiedRuntime || hasHelm) && !hasMta) throw `'cds add application-logging' is not available for Kyma yet`
17
+ const { hasMta, hasKyma } = readProject()
18
+ if (hasKyma && !hasMta) throw `'cds add application-logging' is not available for Kyma yet`
19
19
  return true
20
20
  }
21
21
 
@@ -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
@@ -11,6 +11,7 @@ const {
11
11
  approuterMTXRoute,
12
12
  approuterMTXRouteJava // xs-app.json config
13
13
  } = require('../../registries/mta')
14
+ const KymaTemplate = require('../kyma')
14
15
 
15
16
  module.exports = class ApprouterTemplate extends require('../../plugin') {
16
17
 
@@ -35,30 +36,28 @@ module.exports = class ApprouterTemplate extends require('../../plugin') {
35
36
  const {
36
37
  isNodejs, isJava,
37
38
  addIas, addXsuaa, hasXsuaa, hasIas, addMultitenancy, hasMTXRoute, hasMTXRouteJava,
38
- addMta, addHelm, addHelmUnifiedRuntime, hasMta, hasHelm, hasHelmUnifiedRuntime,
39
- addContainerize, srvPath, approuterPath
39
+ addMta, hasMta, hasMultitenancy,
40
+ addContainerize, addKyma, hasKyma, srvPath, approuterPath
40
41
  } = project
41
42
 
42
43
 
43
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
53
  const relationships = []
53
54
  if (hasXsuaa) {
54
- additions.push(xsuaa)
55
55
  relationships.push({
56
56
  insert: [xsuaa, 'name'],
57
57
  into: [approuter, 'requires', 'name']
58
58
  })
59
59
  }
60
60
  if (hasIas) {
61
- additions.push(ias)
62
61
  relationships.push({
63
62
  insert: [ias, 'name'],
64
63
  into: [approuter, 'requires', 'name']
@@ -69,11 +68,15 @@ module.exports = class ApprouterTemplate extends require('../../plugin') {
69
68
  })
70
69
  }
71
70
 
72
- if (addHelm || addHelmUnifiedRuntime || addMultitenancy && (hasHelm || hasHelmUnifiedRuntime)) {
71
+ if(addKyma) {
72
+ await KymaTemplate.mergeDependency('web-application', 'approuter')
73
+ }
74
+
75
+ if (addKyma || (addMultitenancy && hasKyma)) {
73
76
  await merge(__dirname, 'files', 'values.yaml.hbs').into('chart/values.yaml', { with: project })
74
77
  }
75
78
 
76
- if (addContainerize) {
79
+ if (addContainerize || addKyma) {
77
80
  const additions = [{
78
81
  in: 'modules',
79
82
  where: { name: `${project.appName}-approuter` }
@@ -2,6 +2,7 @@ const { readProject } = require('../../projectReader')
2
2
  const { merge } = require('../../merge')
3
3
  const { srv4, attachments } = require('../../registries/mta')
4
4
  const { mvn } = require('../../add')
5
+ const KymaTemplate = require('../kyma')
5
6
 
6
7
  module.exports = class AttachmentsTemplate extends require('../../plugin') {
7
8
 
@@ -26,7 +27,7 @@ module.exports = class AttachmentsTemplate extends require('../../plugin') {
26
27
 
27
28
  async combine() {
28
29
  const project = readProject()
29
- const { addMta, addHelm, addHelmUnifiedRuntime, srvPath } = project
30
+ const { addMta, addKyma, srvPath } = project
30
31
 
31
32
  if (addMta) {
32
33
  const srv = srv4(srvPath)
@@ -40,8 +41,9 @@ module.exports = class AttachmentsTemplate extends require('../../plugin') {
40
41
  })
41
42
  }
42
43
 
43
- if (addHelm || addHelmUnifiedRuntime) {
44
+ if (addKyma) {
44
45
  await merge(__dirname, 'files', 'values.yaml').into('chart/values.yaml')
46
+ await KymaTemplate.mergeDependency('service-instance', 'attachments')
45
47
  }
46
48
  }
47
49
  }
@@ -4,6 +4,7 @@ const { readProject } = require('../../projectReader')
4
4
  const { merge } = require('../../merge')
5
5
  const { srv4, auditlog } = require('../../registries/mta')
6
6
  const mvn = require('../../mvn')
7
+ const KymaTemplate = require('../kyma')
7
8
 
8
9
  module.exports = class AuditLoggingTemplate extends require('../../plugin') {
9
10
 
@@ -33,7 +34,7 @@ module.exports = class AuditLoggingTemplate extends require('../../plugin') {
33
34
 
34
35
  async combine() {
35
36
  const project = readProject()
36
- const { addMta, addHelm, addHelmUnifiedRuntime, addMultitenancy, srvPath } = project
37
+ const { addMta, addKyma, addMultitenancy, srvPath } = project
37
38
  const { plan = 'premium' } = cds.cli.options
38
39
  project.servicePlan = plan
39
40
 
@@ -53,7 +54,7 @@ module.exports = class AuditLoggingTemplate extends require('../../plugin') {
53
54
  ]
54
55
  })
55
56
  }
56
- if (addHelm || addHelmUnifiedRuntime) {
57
+ if (addKyma) {
57
58
  await merge({
58
59
  srv: { bindings: { auditlog: { serviceInstanceName: 'auditlog' }}},
59
60
  auditlog: {
@@ -61,6 +62,7 @@ module.exports = class AuditLoggingTemplate extends require('../../plugin') {
61
62
  servicePlanName: plan
62
63
  }
63
64
  }).into('chart/values.yaml')
65
+ await KymaTemplate.mergeDependency('service-instance', 'auditlog')
64
66
  }
65
67
  }
66
68
  }