@sap/cds-dk 9.7.2 → 9.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/CHANGELOG.md +42 -2
  2. package/bin/bind.js +7 -2
  3. package/bin/build.js +14 -4
  4. package/bin/cds.js +2 -2
  5. package/bin/completion/completionFs.js +1 -1
  6. package/bin/completion/index.js +2 -2
  7. package/bin/data-products/get-csn-via-dp-cli.js +1 -1
  8. package/bin/debug.js +1 -1
  9. package/bin/deploy/index.js +3 -2
  10. package/bin/gen.js +1 -1
  11. package/bin/help.js +1 -1
  12. package/bin/import.js +3 -0
  13. package/bin/minify.js +1 -1
  14. package/bin/mock.js +1 -1
  15. package/bin/repl.js +212 -39
  16. package/bin/test.js +0 -2
  17. package/bin/up.js +27 -3
  18. package/bin/util/containerize.js +1 -3
  19. package/bin/version.js +30 -9
  20. package/bin/watch.js +20 -4
  21. package/lib/bind/cf.js +2 -2
  22. package/lib/bind/k8s.js +0 -1
  23. package/lib/bind/plugin.js +2 -0
  24. package/lib/bind/shared.js +6 -1
  25. package/lib/build/constants.js +0 -8
  26. package/lib/build/index.js +569 -25
  27. package/lib/build/{provider/edmxBuildPlugin.js → plugins/edmx.js} +6 -15
  28. package/lib/build/{provider/featureToggleBuildPlugin.js → plugins/featureToggles.js} +8 -27
  29. package/lib/build/{provider → plugins}/fiori/index.js +27 -40
  30. package/lib/build/{provider → plugins}/hana/index.js +67 -85
  31. package/lib/build/{provider → plugins}/hana/migrationtable.js +4 -3
  32. package/lib/build/plugins/helm/index.js +6 -6
  33. package/lib/build/{provider/internalBuildPlugin.js → plugins/internal.js} +7 -31
  34. package/lib/build/{provider → plugins}/java/index.js +18 -26
  35. package/lib/build/plugins/mtx/index.js +41 -0
  36. package/lib/build/plugins/mtx/resourcesTarBuilder.js +41 -0
  37. package/lib/build/{provider → plugins}/mtx-extension/extensionCompilation.js +1 -1
  38. package/lib/build/{provider → plugins}/mtx-extension/index.js +35 -44
  39. package/lib/build/{provider → plugins}/mtx-sidecar/index.js +17 -3
  40. package/lib/build/{provider → plugins}/nodejs/index.js +29 -28
  41. package/lib/build/{plugin.js → plugins/plugin.js} +57 -60
  42. package/lib/build/plugins.js +11 -8
  43. package/lib/build/util.js +0 -8
  44. package/lib/compile/to-xsuaa.js +2 -2
  45. package/lib/deploy/to-hana/hana.js +64 -56
  46. package/lib/import/common.js +1 -1
  47. package/lib/import/odata/metadataconverter/MetadataConverter.js +4 -3
  48. package/lib/import/odata/v2parser.js +28 -28
  49. package/lib/import/odata/v4parser.js +37 -2
  50. package/lib/import/openapi/index.js +1 -1
  51. package/lib/init/constants.js +1 -2
  52. package/lib/init/template/app-frontend/index.js +1 -1
  53. package/lib/init/template/approuter/files/package.json +1 -1
  54. package/lib/init/template/completion/completionSetup.js +1 -1
  55. package/lib/init/template/data/as-csv.js +1 -1
  56. package/lib/init/template/github-actions/index.js +1 -1
  57. package/lib/init/template/handler/index.js +1 -1
  58. package/lib/init/template/helm/index.js +4 -4
  59. package/lib/init/template/kyma/index.js +3 -3
  60. package/lib/init/template/lint/index.js +7 -21
  61. package/lib/init/template/multitenancy/files/package.json.hbs +0 -5
  62. package/lib/init/template/postgres/files/containerize.yaml.hbs +15 -0
  63. package/lib/init/template/postgres/files/package.json.hbs +35 -0
  64. package/lib/init/template/postgres/files/values.yaml.hbs +23 -0
  65. package/lib/init/template/postgres/index.js +34 -9
  66. package/lib/init/template/test/files/package.json +1 -1
  67. package/lib/init/template/typescript/files/package.json +1 -1
  68. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +1 -0
  69. package/lib/lint/eslintHelp.json +1 -1
  70. package/lib/lint/io.js +9 -13
  71. package/lib/mtx/pull.js +1 -1
  72. package/lib/mtx/settings_manager.js +11 -5
  73. package/lib/mtx/tenant_action.js +1 -1
  74. package/lib/mtx/util/fs.js +2 -2
  75. package/lib/util/fs.js +3 -3
  76. package/lib/util/git.js +1 -1
  77. package/lib/util/glob.js +1 -1
  78. package/lib/watch/hot-reload.js +202 -0
  79. package/lib/watch/livereload-server.js +6 -4
  80. package/lib/watch/node-watch.js +5 -1
  81. package/lib/watch/watched.js +7 -3
  82. package/lib/watch/watcher.js +41 -3
  83. package/npm-shrinkwrap.json +46 -31
  84. package/package.json +1 -1
  85. package/lib/build/buildTaskEngine.js +0 -377
  86. package/lib/build/buildTaskFactory.js +0 -198
  87. package/lib/build/buildTaskProvider.js +0 -38
  88. package/lib/build/buildTaskProviderFactory.js +0 -233
  89. package/lib/build/provider/internalBuildTaskProvider.js +0 -227
  90. package/lib/build/provider/java-cf/index.js +0 -1
  91. package/lib/build/provider/mtx/index.js +0 -36
  92. package/lib/build/provider/mtx/resourcesTarBuilder.js +0 -102
  93. package/lib/build/provider/node-cf/index.js +0 -1
  94. package/lib/init/template/lint/files/.eslint/rules/no-entity-moo.js +0 -40
  95. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/fixed/schema.cds +0 -4
  96. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/invalid/schema.cds +0 -4
  97. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/rule.test.js +0 -15
  98. package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/valid/schema.cds +0 -6
  99. package/lib/init/types.d.ts +0 -23
  100. /package/lib/build/{provider → plugins}/hana/2migration.js +0 -0
  101. /package/lib/build/{provider → plugins}/hana/template/.hdiconfig-all +0 -0
  102. /package/lib/build/{provider → plugins}/hana/template/.hdiconfig-cloud +0 -0
  103. /package/lib/build/{provider → plugins}/hana/template/.hdinamespace +0 -0
  104. /package/lib/build/{provider → plugins}/hana/template/package.json +0 -0
  105. /package/lib/build/{provider → plugins}/hana/template/undeploy.json +0 -0
@@ -40,7 +40,7 @@ const edmxncdsdatatype = {
40
40
  };
41
41
 
42
42
  const extendedPrimitiveTypes = [
43
- "Edm.Duration",
43
+ "Edm.Duration",
44
44
  "Edm.Geography",
45
45
  "Edm.GeographyPoint",
46
46
  "Edm.GeographyLineString",
@@ -84,7 +84,7 @@ function _initialize(parserContext) {
84
84
  function namespaceAttributeFilter(attributes, parserContext) {
85
85
  let namespaceAttributes = parserContext.allowAllNamespaces
86
86
  ? Object.keys(attributes).filter((attr) => attr.includes(':'))
87
- : Object.keys(attributes).filter((attr) =>
87
+ : Object.keys(attributes).filter((attr) =>
88
88
  attr.includes(':') && parserContext.allowedNamespaces.includes(attr.split(':')[0]));
89
89
 
90
90
 
@@ -104,7 +104,7 @@ function namespaceAttributeFilter(attributes, parserContext) {
104
104
  return namespaceAttributes;
105
105
  }
106
106
 
107
- //Function to generate CSN for namespace attributes
107
+ //Function to generate CSN for namespace attributes
108
108
  function _generateNamespaceAttributes(attributes, isBefore) {
109
109
  let csn = "";
110
110
  attributes.forEach((attr) => {
@@ -309,7 +309,7 @@ function _extractFunctionImport(functionImport, parserContext) {
309
309
  Object.keys(functionImport).forEach((key) => {
310
310
  if (key === "_attributes") {
311
311
  if (functionImport[key]["sap:action-for"]) {
312
- //storing bound function imports
312
+ //storing bound function imports
313
313
  entityName = _extractEntityFromNamespace(functionImport[key]["sap:action-for"]);
314
314
  boundFunctionImports = (parserContext.allFunctionImportsMap[entityName])
315
315
  ? parserContext.allFunctionImportsMap[entityName] : [];
@@ -338,7 +338,7 @@ function _checkFunctionImports(functionImports, parserContext) {
338
338
  throw new Error(messages.UNRESOLVED_TYPE + `'${functionImports._attributes.EntitySet}'`);
339
339
  }
340
340
  }
341
- }
341
+ }
342
342
 
343
343
  //extracting the function imports from the metadata
344
344
  function _extractAllFunctionImports(functionImports, parserContext) {
@@ -554,9 +554,9 @@ function _getServiceEntityProperty(
554
554
  }
555
555
  // if annotations to be added
556
556
  if (edmxncdsdatatype[dataType + "_a"] && cdsDataType != "cds.Date") {
557
- if (dataType === 'Edm.Stream')
557
+ if (dataType === 'Edm.Stream')
558
558
  propertyJson = propertyJson + ',\n"@Core.MediaType": "application/octet-stream"';
559
- else
559
+ else
560
560
  propertyJson = propertyJson + ',\n"@odata.Type": "' + dataType + '"';
561
561
  }
562
562
 
@@ -732,7 +732,7 @@ function _getServiceComplexTypes(parserContext) {
732
732
  complexTypeKey += i;
733
733
  i++;
734
734
  }
735
-
735
+
736
736
  //Rename complexType in parserContext for downstream FunctionImport logic
737
737
  parserContext.allComplexTypes[complexTypeKey] = parserContext.allComplexTypes[originalName];
738
738
  parserContext.allComplexTypes[complexTypeKey]['_attributes'].Name = complexTypeKey
@@ -763,7 +763,7 @@ function _parseParameter(parameter, parserContext) {
763
763
  let namespaceAttributes = namespaceAttributeFilter(parameter._attributes, parserContext);
764
764
  const parameterNamespaceAttributes = namespaceAttributes.length ? namespaceAttributes : -1;
765
765
 
766
- csn = csn +
766
+ csn = csn +
767
767
  _getServiceEntityProperty(
768
768
  parameter["_attributes"].Name,
769
769
  parameter["_attributes"].Type,
@@ -796,7 +796,7 @@ function _parseParametersFunctionImport(parameters, entityKeysList, parserContex
796
796
  if (parameters.length) {
797
797
  for (let i = 0; i < parameters.length; i++) {
798
798
  // check if the parameter is a key of the entity type or not
799
- // If yes then that paramater is ignored in CSN
799
+ // If yes then that paramater is ignored in CSN
800
800
  let isKey = _checkParameterKey(parameters[i], entityKeysList);
801
801
  if (isKey == false) {
802
802
  parameterCsn = _parseParameter(parameters[i], parserContext);
@@ -822,7 +822,6 @@ function _parseParametersFunctionImport(parameters, entityKeysList, parserContex
822
822
  }
823
823
 
824
824
  function _getFunctionImport(functionImport, isBound, entityKeysList, parserContext) {
825
- let noValue;
826
825
  let csn = "";
827
826
  csn = csn + '"' + functionImport[0] + '": { \n';
828
827
  csn = csn + '"kind": "';
@@ -832,7 +831,7 @@ function _getFunctionImport(functionImport, isBound, entityKeysList, parserConte
832
831
  } else if (functionImport[1] === "POST") {
833
832
  csn = csn + "action";
834
833
  } else {
835
- let message = functionImport[0].substring(functionImport[0].lastIndexOf(".") + 1) + " has kind " +
834
+ let message = functionImport[0].substring(functionImport[0].lastIndexOf(".") + 1) + " has kind " +
836
835
  functionImport[1] + " which is not supported.";
837
836
  console.log(warn(message));
838
837
  return "";
@@ -853,9 +852,10 @@ function _getFunctionImport(functionImport, isBound, entityKeysList, parserConte
853
852
  }
854
853
  // if return type exist
855
854
  if (functionImport[2] !== -1) {
856
- let returnValue = _getServiceEntityProperty(noValue, functionImport[2], noValue, noValue, noValue, noValue,
855
+ let noValue = undefined
856
+ let returnValue = _getServiceEntityProperty(noValue, functionImport[2], noValue, noValue, noValue, noValue,
857
857
  noValue, noValue, noValue, noValue, -1, noValue, parserContext);
858
-
858
+
859
859
  if (returnValue !== "") {
860
860
  csn = csn + ", \n";
861
861
  csn = csn + '"returns": { \n';
@@ -982,7 +982,7 @@ function _getPropertyAttributes(propAttributes, parserContext) {
982
982
  } else {
983
983
  propOthers.push("true");
984
984
  }
985
-
985
+
986
986
  if (propAttributes.DefaultValue) {
987
987
  propOthers.push(propAttributes.DefaultValue);
988
988
  } else {
@@ -1178,7 +1178,7 @@ function _getCSNMultiplicity(associationEnds, toRole, entityName, parserContext)
1178
1178
  toRole === attributes.Role &&
1179
1179
  entityName === attributes.Type
1180
1180
  ) {
1181
-
1181
+
1182
1182
  multiplicity = attributes.Multiplicity;
1183
1183
  stop = true;
1184
1184
  }
@@ -1233,7 +1233,7 @@ function _getCSNAssociatedEntitySet(
1233
1233
  let associationName = relationshipName.replace(parserContext.serviceNamespace + '.', '');
1234
1234
  let association = parserContext.allAssociations[associationName];
1235
1235
  let isAssociationSetMissing = false;
1236
-
1236
+
1237
1237
  // if association set is missing, use association to extract the informations
1238
1238
  if (associationSet === undefined) {
1239
1239
  isAssociationSetMissing = true;
@@ -1272,7 +1272,7 @@ function _findAssociationType(relationshipName, fromRole, parserContext) {
1272
1272
  const associationName = relationshipName.replace(parserContext.serviceNamespace + '.', '');
1273
1273
  const association = parserContext.allAssociations[associationName];
1274
1274
  if (association === undefined) throw new Error(`'${associationName}' ` + messages.MISSING_ASSOCIATION);
1275
-
1275
+
1276
1276
  const associationEnds = association.End;
1277
1277
  let associationType;
1278
1278
 
@@ -1398,7 +1398,7 @@ function _constructServiceEntity(
1398
1398
  let csnProperties;
1399
1399
  let hasProperties;
1400
1400
  csnEntity = csnEntity + '"kind": "entity",\n';
1401
- csnEntity = csnEntity + '"@cds.external": true,\n';
1401
+ csnEntity = csnEntity + '"@cds.external": true,\n';
1402
1402
  if (ignorePersistenceSkip === false) {
1403
1403
  csnEntity = csnEntity + '"@cds.persistence.skip": true,\n';
1404
1404
  }
@@ -1474,10 +1474,10 @@ function _constructServiceEntity(
1474
1474
  }
1475
1475
  }
1476
1476
 
1477
- // if the function import is bound to entitySet
1478
- if (parserContext.allFunctionImportsMap[entityName]) {
1477
+ // if the function import is bound to entitySet
1478
+ if (parserContext.allFunctionImportsMap[entityName]) {
1479
1479
  csnEntity = csnEntity + ', \n "actions": {\n';
1480
- csnEntity = csnEntity +
1480
+ csnEntity = csnEntity +
1481
1481
  _getAllFunctionImports(parserContext.allFunctionImportsMap[entityName],
1482
1482
  1, entityKeysList, parserContext
1483
1483
  );
@@ -1545,7 +1545,7 @@ function _parseServiceEntity(entityName, entity, ignorePersistenceSkip, parserCo
1545
1545
  // adding blob element if entity has m:HasStream as true
1546
1546
  const checkProperty = entityAttributes["allowedNamespaceAttributes"].filter(e => e["m.HasStream"] === 'true');
1547
1547
  if (checkProperty.length === 1) _addBlobElement(entityPropertiesMap);
1548
-
1548
+
1549
1549
  return _constructServiceEntity(
1550
1550
  entityName,
1551
1551
  entityKeysList,
@@ -1686,7 +1686,7 @@ function _generateEDMXJSON2CSN(jsonObj, ignorePersistenceSkip, parserContext) {
1686
1686
 
1687
1687
  // Construct CSN entity based on EDMX entity
1688
1688
  let entityJson = jsonObj["edmx:Edmx"]["edmx:DataServices"].Schema.EntityType;
1689
- // if entityJson exists(ignoring the case of no entity) and is not an array, convert it to an array to handle the case of single entity
1689
+ // if entityJson exists(ignoring the case of no entity) and is not an array, convert it to an array to handle the case of single entity
1690
1690
  if (entityJson && !Array.isArray(entityJson)) entityJson = [entityJson];
1691
1691
  const definitions = [];
1692
1692
 
@@ -1734,7 +1734,7 @@ function _generateEDMXJSON2CSN(jsonObj, ignorePersistenceSkip, parserContext) {
1734
1734
 
1735
1735
  // if there are unbound function imports
1736
1736
  if (Object.keys(parserContext.allFunctionImports).length > 0) {
1737
- // passing 0 for unbound actions/functions and 0 for entityKeysList
1737
+ // passing 0 for unbound actions/functions and 0 for entityKeysList
1738
1738
  definitions.push(
1739
1739
  _getAllFunctionImports(parserContext.allFunctionImports, 0, 0, parserContext)
1740
1740
  );
@@ -1743,7 +1743,7 @@ function _generateEDMXJSON2CSN(jsonObj, ignorePersistenceSkip, parserContext) {
1743
1743
  try {
1744
1744
  csn.definitions = JSON.parse(`{${definitions.join(",")}}`);
1745
1745
  return JSON.stringify(csn, null, 4);
1746
- } catch (error) {
1746
+ } catch {
1747
1747
  throw new Error("Unable to convert to csn file.");
1748
1748
  }
1749
1749
  }
@@ -1755,7 +1755,7 @@ function getEdmxv2CSN(edmx2jsonModel, ignorePersistenceSkip, mockServerUc, conte
1755
1755
  const parserContext = {};
1756
1756
  _initialize(parserContext);
1757
1757
  parserContext.mockServerUc = mockServerUc
1758
-
1758
+
1759
1759
  if (!context.include_all_namespaces && context.namespaces.length) {
1760
1760
  parserContext.allowedNamespaces = parserContext.allowedNamespaces.concat(context.namespaces)
1761
1761
  }
@@ -1771,7 +1771,7 @@ function getEdmxv2CSN(edmx2jsonModel, ignorePersistenceSkip, mockServerUc, conte
1771
1771
  let namespaceAlias = edmx2jsonModel["edmx:Edmx"]["edmx:DataServices"].Schema._attributes.Alias;
1772
1772
  // replace the namespace alias value with original namespace value
1773
1773
  if (namespaceAlias) {
1774
- edmx2jsonModel = common.replaceAliasValue(JSON.stringify(edmx2jsonModel),
1774
+ edmx2jsonModel = common.replaceAliasValue(JSON.stringify(edmx2jsonModel),
1775
1775
  parserContext.serviceNamespace, namespaceAlias);
1776
1776
  }
1777
1777
  csnDataModel = _generateEDMXJSON2CSN(
@@ -88,7 +88,7 @@ function _isJson(edmx) {
88
88
  try {
89
89
  JSON.parse(edmx);
90
90
  isJson = true;
91
- } catch (err) {
91
+ } catch {
92
92
  isJson = false;
93
93
  }
94
94
  return isJson;
@@ -792,7 +792,7 @@ function _parsePropertyContent(csnObj, elementObj, parserContext) {
792
792
 
793
793
  if (['EnumType', 'TypeDefinition'].includes(dataKind)) {
794
794
  csnObj.elements[objKey] = _generateStructuredDataTypeCsn(dataType, contentObj,
795
- (keySet.includes(dataType) ? isPrimaryKey : !isPrimaryKey),
795
+ (keySet.includes(objKey) ? isPrimaryKey : !isPrimaryKey),
796
796
  dataKind, 'property', parserContext
797
797
  );
798
798
  }
@@ -1713,6 +1713,41 @@ function _generateCSN(edmxAsJson, ignorePersistenceSkip, parserContext, importer
1713
1713
  if (compTarget.elements[backLink]?.keys && !entityKeys.some(key => compTarget.elements[backLink + '_' + key])) {
1714
1714
  delete compTarget.elements[backLink].keys
1715
1715
  }
1716
+
1717
+ // 3. View with parameters handling. In OData if the entity has a Composition Set with a backlink Parameters and every
1718
+ // other element is a key, its a View with Parameters. Import it as such to ensure proper outbound handling
1719
+ if (
1720
+ comp === 'Set' && backLink === 'Parameters' &&
1721
+ Object.keys(entity.elements).filter(e => entity.elements[e].key).length + 1 === Object.keys(entity.elements).length
1722
+ ) {
1723
+ const actualEntity = csn.definitions[entity.elements[comp].target];
1724
+ const actualEntityName = entity.elements[comp].target
1725
+ // Adjust entity so that the current elements are parameters, remove Set, remove key flags
1726
+ delete entity.elements[comp];
1727
+ entity.params = {}
1728
+ for (const ele in entity.elements) {
1729
+ delete entity.elements[ele].key;
1730
+ entity.params[ele] = entity.elements[ele];
1731
+ }
1732
+ delete entity.elements
1733
+ // Move compTarget out of service
1734
+ // Get rid of service prefix
1735
+ let nameWithoutService = eName.split(parserContext.primarySchema).pop();
1736
+ // Get rid of leading dot
1737
+ nameWithoutService = nameWithoutService.substring(1, nameWithoutService.length)
1738
+ csn.definitions[nameWithoutService] = actualEntity;
1739
+ delete csn.definitions[actualEntityName];
1740
+ delete csn.definitions[nameWithoutService].elements['Parameters'];
1741
+ // Adjust entity to * query from compTarget
1742
+ entity.query = {
1743
+ SELECT: {
1744
+ from: {
1745
+ ref: [nameWithoutService]
1746
+ },
1747
+ columns: ["*"]
1748
+ }
1749
+ }
1750
+ }
1716
1751
  }
1717
1752
  }
1718
1753
  return JSON.stringify(csn, null, 4);
@@ -8,7 +8,7 @@ function openAPI2csn(source) {
8
8
  try {
9
9
  const fileContentToJSON = JSON.parse(source);
10
10
  csn = importOpenAPI(fileContentToJSON);
11
- } catch (e) {
11
+ } catch {
12
12
  throw new Error(messages.INVALID_OPENAPI_FILE);
13
13
  }
14
14
  return csn;
@@ -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.7.0',
8
+ MAVEN_ARCHETYPE_VERSION: '4.8.0',
9
9
 
10
10
  OPTIONS: Object.freeze({
11
11
  COMPLETION: 'completion',
@@ -67,7 +67,6 @@ module.exports = {
67
67
  DATA: 'data',
68
68
  HTTP: 'http',
69
69
  LINT: 'lint',
70
- LINT_DEV: 'lint:dev',
71
70
  PIPELINE: 'pipeline',
72
71
  GITHUB_ACTIONS: 'github-actions',
73
72
  }),
@@ -9,7 +9,7 @@ const { exists } = require('../../../cds').utils
9
9
  module.exports = class extends require('../../plugin') {
10
10
 
11
11
  static help() {
12
- return 'SAP BTP AppFront service' // TODO: Product name?
12
+ return 'SAP BTP Application Frontend service'
13
13
  }
14
14
 
15
15
  static hasInProduction(env) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "approuter",
3
3
  "dependencies": {
4
- "@sap/approuter": "^20.0.0"
4
+ "@sap/approuter": "^21.0.0"
5
5
  },
6
6
  "scripts": {
7
7
  "start": "node node_modules/@sap/approuter/approuter.js"
@@ -166,7 +166,7 @@ fi
166
166
  let profileFileExists = true;
167
167
  try {
168
168
  profile = await fsp.readFile(profileFile, 'utf8');
169
- } catch (err) {
169
+ } catch {
170
170
  profileFileExists = false;
171
171
  await fsp.mkdir(path.dirname(profileFile), { recursive: true });
172
172
  }
@@ -13,7 +13,7 @@ module.exports = async (data={}, csn, { headerOnly=false, separator=',' }={}) =>
13
13
  // So loop as long as there is some non-string data left.
14
14
  while (Object.values(data).some(d => typeof d === 'object')) {
15
15
  Object.entries(data)
16
- .filter(([_, value]) => typeof value === 'object')
16
+ .filter(([, value]) => typeof value === 'object')
17
17
  .map(([name]) => csn.definitions[name])
18
18
  .forEach(entity => {
19
19
  const entityData = data[entity.name]
@@ -39,7 +39,7 @@ module.exports = class GitHubActionTemplate extends require('../../plugin') {
39
39
  let githubSlug
40
40
  try {
41
41
  githubSlug = execSync('git remote get-url origin', { stdio: 'pipe' }).toString().split('/').slice(-2).join('/').trim().replace(/\.git$/, '')
42
- } catch (error) {
42
+ } catch {
43
43
  githubSlug = '<org/repo>'
44
44
  // console.log(`\ncould not determine GitHub URL for the project – replace '<org/repo>' in ${release}`, DEBUG ? error : '')
45
45
  }
@@ -162,7 +162,7 @@ async function loadModel() {
162
162
  }
163
163
  }
164
164
 
165
- // TODO remove w/ cds 10, copied from cds-utils.js
165
+ // cds10: remove, copied from cds-utils.js
166
166
  /**
167
167
  * Polyfill for cds.utils.merge
168
168
  * Simple helper to deep-merge two or more objects.
@@ -40,15 +40,15 @@ module.exports = class HelmTemplate extends require('../../plugin') {
40
40
  }
41
41
 
42
42
  async run() {
43
- console.warn(term.warn(`The "cds add helm" command is deprecated and will be removed in the next major release. Please use the "cds add kyma" command instead.`))
43
+ console.log(term.warn(`The "cds add helm" command is deprecated and will be removed in the next major release. Please use the "cds add kyma" command instead.`))
44
44
  const project = readProject()
45
- let imagePullSecret, registry, domain, tag
45
+ let registry, domain, tag
46
46
  if (!HelmTemplate.hasInProduction() && !project.isBas /* don't run this for the BAS Yeoman generator */) {
47
47
  try {
48
48
  const domainGuess = execSync(`kubectl config view --minify --output jsonpath="{.clusters[*].cluster.server}"`, { stdio: "pipe" }).toString()
49
49
  const domainStartIndex = domainGuess.indexOf('api.')
50
50
  if (domainStartIndex !== -1) domain = domainGuess.substring(domainStartIndex + 4)
51
- } catch (error) { /* ignore */ }
51
+ } catch { /* ignore */ }
52
52
 
53
53
  if (exists('containerize.yaml')) {
54
54
  const { repository: r, tag: t } = yaml.parse(join(cds.root, 'containerize.yaml'))
@@ -62,7 +62,7 @@ module.exports = class HelmTemplate extends require('../../plugin') {
62
62
  }
63
63
  project.registry = registry ?? '<your-container-registry-server>'
64
64
  project.domain = domain ?? '<cluster-domain>'
65
- project.imagePullSecret = imagePullSecret ?? 'docker-registry'
65
+ project.imagePullSecret = 'docker-registry'
66
66
  project.tag = tag ?? 'latest'
67
67
  await copy(join(__dirname, '../kyma/files/values.schema.json')).to('chart/values.schema.json')
68
68
  await merge(__dirname, '../kyma/files/Chart.yaml.hbs').into('chart/Chart.yaml', { with: project })
@@ -46,13 +46,13 @@ module.exports = class KymaTemplate extends require('../../plugin') {
46
46
 
47
47
  async run() {
48
48
  const project = readProject()
49
- let imagePullSecret, registry, domain, tag
49
+ let registry, domain, tag
50
50
  if (!KymaTemplate.hasInProduction() && !project.isBas /* don't run this for the BAS Yeoman generator */) {
51
51
  try {
52
52
  const domainGuess = execSync(`kubectl config view --minify --output jsonpath="{.clusters[*].cluster.server}"`, { stdio: "pipe" }).toString()
53
53
  const domainStartIndex = domainGuess.indexOf('api.')
54
54
  if (domainStartIndex !== -1) domain = domainGuess.substring(domainStartIndex + 4)
55
- } catch (error) { /* ignore */ }
55
+ } catch { /* ignore */ }
56
56
 
57
57
  // cds10: remove
58
58
  if (exists('containerize.yaml')) {
@@ -67,7 +67,7 @@ module.exports = class KymaTemplate extends require('../../plugin') {
67
67
  }
68
68
  project.registry = registry ?? '<your-container-registry-server>'
69
69
  project.domain = domain ?? '<cluster-domain>'
70
- project.imagePullSecret = imagePullSecret ?? 'docker-registry'
70
+ project.imagePullSecret = 'docker-registry'
71
71
  project.tag = tag ?? 'latest'
72
72
  await copy(join(__dirname, 'files', 'values.schema.json')).to('chart/values.schema.json')
73
73
  await merge(__dirname, 'files/Chart.yaml.hbs').into('chart/Chart.yaml', { with: project })
@@ -18,7 +18,7 @@ const ESLINT_LANGS_FOR_FILE_TYPE = {
18
18
  * Searches for ESLint config file types (in order or precedence)
19
19
  * and returns corresponding directory (usually project's root dir)
20
20
  * https://eslint.org/docs/user-guide/configuring#configuration-file-formats
21
- * @param {string} currentDir start here and search until root dir
21
+ * @param {string} currentDir start here
22
22
  * @returns {string} dir containing ESLint config file (empty if not exists)
23
23
  */
24
24
  function getConfigPath(currentDir = '.') {
@@ -27,15 +27,11 @@ function getConfigPath(currentDir = '.') {
27
27
  'eslint.config.cjs',
28
28
  'eslint.config.mjs'
29
29
  ]
30
- let configDir = path.resolve(currentDir)
31
- while (configDir !== path.resolve(configDir, '..')) {
32
- for (const configFile of configFiles) {
33
- const configPath = path.join(configDir, configFile)
34
- if (fs.existsSync(configPath) && fs.statSync(configPath).isFile()) {
35
- return configPath
36
- }
30
+ for (const configFile of configFiles) {
31
+ const configPath = path.join(currentDir, configFile)
32
+ if (fs.existsSync(configPath) && fs.statSync(configPath).isFile()) {
33
+ return configPath
37
34
  }
38
- configDir = path.join(configDir, '..')
39
35
  }
40
36
  return ''
41
37
  }
@@ -46,14 +42,6 @@ module.exports = class LintTemplate extends require('../../plugin') {
46
42
  return 'configure cds lint'
47
43
  }
48
44
 
49
- constructor() {
50
- super()
51
- const customPath = path.join(cds.root, '.eslint')
52
- this.docsPath = path.join(customPath, 'docs')
53
- this.rulesPath = path.join(customPath, 'rules')
54
- this.testsPath = path.join(customPath, 'tests')
55
- }
56
-
57
45
  async run() {
58
46
  // Require local ESLint plugin installation and corresponding config
59
47
  await this.#addEslintPluginConfig()
@@ -62,8 +50,7 @@ module.exports = class LintTemplate extends require('../../plugin') {
62
50
  const extensions = new Set(Object.values(ESLINT_LANGS_FOR_FILE_TYPE).flat())
63
51
  await io.sanitizeVscodeSettings(
64
52
  path.join(cds.root, '.vscode/settings.json'),
65
- Array.from(extensions),
66
- this.customRuleExample
53
+ Array.from(extensions)
67
54
  )
68
55
 
69
56
  await merge(path.join(__dirname, 'files', 'package.json')).into('package.json')
@@ -72,8 +59,7 @@ module.exports = class LintTemplate extends require('../../plugin') {
72
59
  }
73
60
 
74
61
  async #addEslintPluginConfig() {
75
- const configPath = getConfigPath(cds.root)
76
- ?? path.join(cds.root, 'eslint.config.mjs')
62
+ const configPath = getConfigPath(cds.root) || path.join(cds.root, 'eslint.config.mjs')
77
63
  await io.sanitizeEslintConfig(configPath)
78
64
  }
79
65
  }
@@ -5,12 +5,7 @@
5
5
  },
6
6
  "cds": {
7
7
  {{/isNodejs}}
8
- {{#isJava}}
9
- "profiles": ["with-mtx-sidecar", "java"],
10
- {{/isJava}}
11
- {{#isNodejs}}
12
8
  "profile": "with-mtx-sidecar",
13
- {{/isNodejs}}
14
9
  "requires": {
15
10
  {{#profile}}
16
11
  "[{{profile}}]": {
@@ -0,0 +1,15 @@
1
+ ---
2
+ modules:
3
+ - name: {{appName}}-postgres-deployer
4
+ build-parameters:
5
+ buildpack:
6
+ {{#hasHelmUnifiedRuntime}}
7
+ builder: proxy-unified-runtime-dmz.int.repositories.cloud.sap/builder/noble
8
+ {{/hasHelmUnifiedRuntime}}
9
+ {{^hasHelmUnifiedRuntime}}
10
+ type: nodejs
11
+ builder: builder-jammy-base
12
+ {{/hasHelmUnifiedRuntime}}
13
+ path: gen/pg
14
+ env:
15
+ BP_NODE_RUN_SCRIPTS: ""
@@ -1,5 +1,40 @@
1
1
  {
2
+ {{#isNodejs}}
2
3
  "dependencies": {
3
4
  "@cap-js/postgres": "^2"
5
+ }{{#profile}},
6
+ "cds": {
7
+ "requires": {
8
+ "[{{profile}}]": {
9
+ {{#shortcut}}
10
+ "db": "postgres"
11
+ {{/shortcut}}
12
+ {{^shortcut}}
13
+ "db": {
14
+ "kind": "postgres"
15
+ }
16
+ {{/shortcut}}
17
+ }
18
+ }
4
19
  }
20
+ {{/profile}}
21
+ {{/isNodejs}}
22
+ {{#isJava}}
23
+ "requires": {
24
+ {{#profile}}
25
+ "[{{profile}}]": {
26
+ {{/profile}}
27
+ {{#shortcut}}
28
+ "db": "postgres"
29
+ {{/shortcut}}
30
+ {{^shortcut}}
31
+ "db": {
32
+ "kind": "postgres"
33
+ }
34
+ {{/shortcut}}
35
+ {{#profile}}
36
+ }
37
+ {{/profile}}
38
+ }
39
+ {{/isJava}}
5
40
  }
@@ -0,0 +1,23 @@
1
+ ---
2
+ srv:
3
+ bindings:
4
+ postgres:
5
+ serviceInstanceName: postgres
6
+ postgres-db-deployer:
7
+ image:
8
+ repository: {{appName}}-postgres-deployer
9
+ bindings:
10
+ postgres:
11
+ serviceInstanceName: postgres
12
+ resources:
13
+ limits:
14
+ cpu: 2000m
15
+ memory: 1G
16
+ requests:
17
+ cpu: 1000m
18
+ memory: 1G
19
+ postgres:
20
+ serviceOfferingName: postgresql-db
21
+ servicePlanName: standard
22
+ parameters:
23
+ allow_access: "{{KYMA_GATEWAY_IP}}"
@@ -1,9 +1,11 @@
1
1
  const cds = require('../../../cds')
2
2
  const { read, exists } = cds.utils
3
3
  const { readProject } = require('../../projectReader')
4
- const { merge } = require('../../merge')
4
+ const { merge, sort } = require('../../merge')
5
5
  const mvn = require('../../mvn')
6
6
  const { srv4, postgres, postgresDeployer } = require('../../registries/mta')
7
+ const KymaTemplate = require('../kyma')
8
+ const { execSync } = require('child_process')
7
9
 
8
10
  module.exports = class PostgresTemplate extends require('../../plugin') {
9
11
 
@@ -11,12 +13,6 @@ module.exports = class PostgresTemplate extends require('../../plugin') {
11
13
  return 'database support for PostgreSQL'
12
14
  }
13
15
 
14
- async canRun() {
15
- const { hasMta, hasKyma } = readProject()
16
- if (hasKyma && !hasMta) throw `'cds add postgres' is not available for Kyma yet`
17
- return true
18
- }
19
-
20
16
  static hasInProduction(env) {
21
17
  return env.requires?.db?.kind === 'postgres' || readProject().dependencies?.['@cap-js/postgres']
22
18
  }
@@ -25,13 +21,15 @@ module.exports = class PostgresTemplate extends require('../../plugin') {
25
21
  const project = readProject()
26
22
  const { configFile, isJava } = project
27
23
  project.shortcut = !exists(configFile) || typeof (await read(configFile)).cds?.requires?.db !== 'object'
28
- await merge(__dirname, 'files/package.json.hbs').into('package.json', { project })
24
+ await merge(__dirname, 'files/package.json.hbs').into(configFile, { with: project, forceOverwrite: true }) // REVISIT: forceOverwrite: true as default?
29
25
  if (isJava) await mvn.add('postgresql')
26
+
27
+ await sort('package.json', 'dependencies')
30
28
  }
31
29
 
32
30
  async combine() {
33
31
  const project = readProject()
34
- const { addMta, srvPath } = project
32
+ const { addMta, srvPath, hasMultitenancy, addKyma, hasKyma, addMultitenancy } = project
35
33
 
36
34
  if (addMta) {
37
35
  const srv = srv4(srvPath)
@@ -47,5 +45,32 @@ module.exports = class PostgresTemplate extends require('../../plugin') {
47
45
  }]
48
46
  })
49
47
  }
48
+
49
+ if (!hasMultitenancy && addKyma) {
50
+ const clusterIPGuess = execSync(`kubectl --namespace kyma-system get configmap kyma-info -o json | jq -r '.data["cloud.natGatewayIps"]'`, { stdio: "pipe" }).toString()
51
+ project['KYMA_GATEWAY_IP'] = clusterIPGuess.replace(/\n/g, '')
52
+ await merge(__dirname, 'files', 'values.yaml.hbs').into('chart/values.yaml', { with: project })
53
+ await KymaTemplate.mergeDependency('service-instance', 'postgres')
54
+ await KymaTemplate.mergeDependency('content-deployment', 'postgres-deployer')
55
+ }
56
+
57
+ if (addMultitenancy && hasKyma) {
58
+ await merge('chart/values.yaml')
59
+ .remove(['postgres-deployer', 'srv.bindings.db', 'postgres'])
60
+
61
+ await merge('chart/Chart.yaml')
62
+ .remove([
63
+ { dependencies: { alias: 'postgres' } },
64
+ { dependencies: { alias: 'postgres-deployer' } }
65
+ ])
66
+ }
67
+
68
+ if (addKyma && !hasMultitenancy) {
69
+ const additions = [{
70
+ in: 'modules',
71
+ where: { name: `${project.appName}-postgres-deployer` }
72
+ }]
73
+ await merge(__dirname, '/files/containerize.yaml.hbs').into('containerize.yaml', { with: project, additions })
74
+ }
50
75
  }
51
76
  }