@sap/cds-dk 7.4.1 → 7.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 (179) hide show
  1. package/bin/activate.js +1 -1
  2. package/bin/add.js +6 -4
  3. package/bin/build.js +4 -6
  4. package/bin/cds.js +4 -1
  5. package/bin/deploy/index.js +5 -2
  6. package/bin/eval.js +1 -1
  7. package/bin/extend.js +3 -3
  8. package/bin/login.js +2 -2
  9. package/bin/logout.js +1 -1
  10. package/bin/pull.js +1 -1
  11. package/bin/push.js +2 -2
  12. package/bin/subscribe.js +2 -2
  13. package/bin/unsubscribe.js +2 -2
  14. package/bin/upgrade.js +2 -2
  15. package/bin/version.js +2 -2
  16. package/bin/watch.js +1 -1
  17. package/lib/bind/bindingUtil.js +2 -1
  18. package/lib/bind/cfKeyProvider.js +1 -1
  19. package/lib/bind/userCdsrcJsonStorageProvider.js +1 -1
  20. package/lib/build/buildPlugin.js +6 -12
  21. package/lib/build/buildTaskEngine.js +24 -19
  22. package/lib/build/buildTaskFactory.js +27 -33
  23. package/lib/build/buildTaskProvider.js +1 -1
  24. package/lib/build/buildTaskProviderFactory.js +26 -41
  25. package/lib/build/constants.js +0 -1
  26. package/lib/build/index.js +3 -3
  27. package/lib/build/plugins.js +15 -15
  28. package/lib/build/provider/edmxBuildPlugin.js +6 -7
  29. package/lib/build/provider/fiori/index.js +8 -8
  30. package/lib/build/provider/hana/2migration.js +12 -16
  31. package/lib/build/provider/hana/index.js +5 -7
  32. package/lib/build/provider/hana/template/package.json +1 -1
  33. package/lib/build/provider/internalBuildPlugin.js +0 -8
  34. package/lib/build/provider/internalBuildTaskProvider.js +17 -21
  35. package/lib/build/provider/java/index.js +2 -1
  36. package/lib/build/provider/mtx-extension/index.js +3 -3
  37. package/lib/build/provider/mtx-sidecar/index.js +2 -1
  38. package/lib/build/provider/nodejs/index.js +6 -5
  39. package/lib/compile/openapi/csdl2openapi.js +29 -15
  40. package/lib/compile/openapi/index.js +6 -6
  41. package/lib/deploy/to-hana/hana.js +2 -2
  42. package/lib/env/schema.js +2 -3
  43. package/lib/index.js +68 -28
  44. package/lib/init/add.js +17 -0
  45. package/lib/init/constants.js +5 -3
  46. package/lib/init/index.js +39 -25
  47. package/lib/init/{util/merge.js → merge.js} +32 -20
  48. package/lib/init/{util/mvn.js → mvn.js} +5 -5
  49. package/lib/init/plugin.js +85 -0
  50. package/lib/init/projectReader.js +145 -0
  51. package/lib/init/registries/mta.js +151 -0
  52. package/lib/init/template/{kibana → application-logging}/files/mta.yaml.hbs +3 -3
  53. package/lib/init/template/application-logging/index.js +49 -0
  54. package/lib/init/template/approuter/files/mta.yaml.hbs +11 -11
  55. package/lib/init/template/approuter/files/xs-app.json.hbs +2 -2
  56. package/lib/init/template/approuter/index.js +36 -81
  57. package/lib/init/template/cf-manifest/files/manifest.yml.hbs +3 -3
  58. package/lib/init/template/cf-manifest/files/services-manifest.yml.hbs +4 -4
  59. package/lib/init/template/cf-manifest/index.js +4 -4
  60. package/lib/init/template/connectivity/index.js +22 -35
  61. package/lib/init/template/data/index.js +3 -2
  62. package/lib/init/template/destination/files/values.yaml +9 -0
  63. package/lib/init/template/destination/index.js +15 -47
  64. package/lib/init/template/enterprise-messaging/index.js +26 -58
  65. package/lib/init/template/enterprise-messaging-shared/index.js +23 -39
  66. package/lib/init/template/extensibility/index.js +16 -30
  67. package/lib/init/template/file-based-messaging/index.js +7 -8
  68. package/lib/init/template/h2/index.js +4 -4
  69. package/lib/init/template/hana/files/mta.yaml.hbs +8 -8
  70. package/lib/init/template/hana/index.js +29 -115
  71. package/lib/init/template/helm/files/chart/templates/NOTES.txt +12 -0
  72. package/lib/init/template/helm/files/chart/templates/_deployment_helpers.tpl +82 -0
  73. package/lib/init/template/helm/files/chart/templates/_helpers.tpl +28 -0
  74. package/lib/init/template/helm/files/chart/values.root.schema.json +61 -0
  75. package/lib/init/template/helm/files/chart/values.schema.json +366 -0
  76. package/lib/init/template/helm/{subcharts → files}/web-application/Chart.yaml +1 -1
  77. package/lib/init/template/helm/{subcharts → files}/web-application/templates/_api.tpl +1 -1
  78. package/lib/init/template/helm/{subcharts → files}/web-application/values.yaml +1 -1
  79. package/lib/init/template/helm/index.js +84 -85
  80. package/lib/init/template/helm-unified-runtime/files/Chart.yaml.hbs +10 -0
  81. package/lib/init/template/helm-unified-runtime/files/values.yaml.hbs +26 -0
  82. package/lib/init/template/helm-unified-runtime/index.js +91 -0
  83. package/lib/init/template/helm-unified-runtime/lib/build.js +0 -0
  84. package/lib/init/template/helm-unified-runtime/package.json +4 -0
  85. package/lib/init/template/html5-repo/files/values.yaml.hbs +28 -0
  86. package/lib/init/template/html5-repo/index.js +23 -97
  87. package/lib/init/template/java/index.js +3 -3
  88. package/lib/init/template/lint/index.js +2 -2
  89. package/lib/init/template/liquibase/index.js +16 -0
  90. package/lib/init/template/local-messaging/index.js +7 -8
  91. package/lib/init/template/mta/index.js +7 -19
  92. package/lib/init/template/mtx/index.js +2 -2
  93. package/lib/init/template/multitenancy/index.js +38 -89
  94. package/lib/init/template/nodejs/index.js +5 -4
  95. package/lib/init/template/notebook/index.js +1 -1
  96. package/lib/init/template/notifications/index.js +16 -19
  97. package/lib/init/template/pipeline/index.js +1 -1
  98. package/lib/init/template/postgres/index.js +25 -31
  99. package/lib/init/template/redis-messaging/index.js +23 -27
  100. package/lib/init/template/sample/index.js +4 -4
  101. package/lib/init/template/samples/index.js +2 -2
  102. package/lib/init/template/sqlite/files/{package.json.hbs → package.json} +0 -2
  103. package/lib/init/template/sqlite/index.js +6 -10
  104. package/lib/init/template/tiny-sample/index.js +6 -6
  105. package/lib/init/template/toggles/index.js +7 -8
  106. package/lib/init/template/typer/index.js +6 -6
  107. package/lib/init/template/xsuaa/files/mta.yaml.hbs +3 -3
  108. package/lib/init/template/xsuaa/files/values.yaml.hbs +3 -3
  109. package/lib/init/template/xsuaa/index.js +27 -52
  110. package/lib/init/{util/template.js → template.js} +1 -1
  111. package/lib/{client → mtx}/activate.js +15 -11
  112. package/lib/{client → mtx}/auth_manager.js +5 -4
  113. package/lib/{client → mtx}/download_legacy.js +6 -4
  114. package/lib/{client → mtx}/extend.js +14 -9
  115. package/lib/{client → mtx}/index.js +1 -1
  116. package/lib/{client → mtx}/params.js +3 -1
  117. package/lib/{client → mtx}/pull.js +6 -4
  118. package/lib/{client → mtx}/push.js +10 -9
  119. package/lib/{client → mtx}/settings_manager.js +8 -6
  120. package/lib/{client → mtx}/tenant_action.js +9 -8
  121. package/lib/{client/helper/cf_helper.js → mtx/util/cf.js} +4 -3
  122. package/lib/{client/helper/dependency_helper.js → mtx/util/dependencies.js} +1 -0
  123. package/lib/mtx/util/errors.js +27 -0
  124. package/lib/mtx/util/legacy_ext.js +16 -0
  125. package/lib/{client/helper/url_helper.js → mtx/util/urls.js} +12 -1
  126. package/lib/util/fs.js +5 -1
  127. package/lib/util/pruneAxiosErrors.js +35 -18
  128. package/lib/util/write.js +1 -1
  129. package/npm-shrinkwrap.json +310 -270
  130. package/package.json +1 -1
  131. package/lib/client/base_command.js +0 -54
  132. package/lib/init/template/_merging/registry-mta.js +0 -304
  133. package/lib/init/template/helm/Model.d.ts +0 -53
  134. package/lib/init/template/kibana/files/package.json.hbs +0 -17
  135. package/lib/init/template/kibana/index.js +0 -54
  136. package/lib/init/template/templateBase.js +0 -100
  137. package/lib/init/util/md5.js +0 -86
  138. package/lib/init/util/projectReader.js +0 -94
  139. /package/lib/init/{util/indent.js → indent.js} +0 -0
  140. /package/lib/init/template/helm/{subcharts → files}/content-deployment/Chart.yaml +0 -0
  141. /package/lib/init/template/helm/{subcharts → files}/content-deployment/templates/_helpers.tpl +0 -0
  142. /package/lib/init/template/helm/{subcharts → files}/content-deployment/templates/job.yaml +0 -0
  143. /package/lib/init/template/helm/{subcharts → files}/content-deployment/templates/secret.yaml +0 -0
  144. /package/lib/init/template/helm/{subcharts → files}/content-deployment/templates/service-account.yaml +0 -0
  145. /package/lib/init/template/helm/{subcharts → files}/content-deployment/templates/service-binding.yaml +0 -0
  146. /package/lib/init/template/helm/{subcharts → files}/content-deployment/values.schema.json +0 -0
  147. /package/lib/init/template/helm/{subcharts → files}/content-deployment/values.yaml +0 -0
  148. /package/lib/init/template/helm/{subcharts → files}/service-instance/Chart.yaml +0 -0
  149. /package/lib/init/template/helm/{subcharts → files}/service-instance/templates/_helpers.tpl +0 -0
  150. /package/lib/init/template/helm/{subcharts → files}/service-instance/templates/serviceinstance.yaml +0 -0
  151. /package/lib/init/template/helm/{subcharts → files}/service-instance/values.schema.json +0 -0
  152. /package/lib/init/template/helm/{subcharts → files}/service-instance/values.yaml +0 -0
  153. /package/lib/init/template/helm/{subcharts → files}/web-application/templates/NOTES.txt +0 -0
  154. /package/lib/init/template/helm/{subcharts → files}/web-application/templates/_helpers.tpl +0 -0
  155. /package/lib/init/template/helm/{subcharts → files}/web-application/templates/api-rule.yaml +0 -0
  156. /package/lib/init/template/helm/{subcharts → files}/web-application/templates/deployment.yaml +0 -0
  157. /package/lib/init/template/helm/{subcharts → files}/web-application/templates/network-policy.yaml +0 -0
  158. /package/lib/init/template/helm/{subcharts → files}/web-application/templates/pod-disruption-budget.yaml +0 -0
  159. /package/lib/init/template/helm/{subcharts → files}/web-application/templates/rbac.yaml +0 -0
  160. /package/lib/init/template/helm/{subcharts → files}/web-application/templates/secret.yaml +0 -0
  161. /package/lib/init/template/helm/{subcharts → files}/web-application/templates/service-account.yaml +0 -0
  162. /package/lib/init/template/helm/{subcharts → files}/web-application/templates/service-binding.yaml +0 -0
  163. /package/lib/init/template/helm/{subcharts → files}/web-application/templates/service.yaml +0 -0
  164. /package/lib/init/template/helm/{subcharts → files}/web-application/templates/virtual-service.yaml +0 -0
  165. /package/lib/init/template/helm/{subcharts → files}/web-application/values.schema.json +0 -0
  166. /package/lib/init/template/{helm/chart → helm-unified-runtime/files}/templates/NOTES.txt +0 -0
  167. /package/lib/init/template/{helm/chart → helm-unified-runtime/files}/templates/_deployment_helpers.tpl +0 -0
  168. /package/lib/init/template/{helm/chart → helm-unified-runtime/files}/templates/_helpers.tpl +0 -0
  169. /package/lib/init/template/{helm/chart → helm-unified-runtime/files}/values.root.schema.json +0 -0
  170. /package/lib/init/template/{helm/chart → helm-unified-runtime/files}/values.schema.json +0 -0
  171. /package/lib/init/template/postgres/files/{mta.yaml.hbs → mta.yml.hbs} +0 -0
  172. /package/lib/init/{util/xml.js → xml.js} +0 -0
  173. /package/lib/{client/helper/env_helper.js → mtx/util/env.js} +0 -0
  174. /package/lib/{client/helper/fs_helper.js → mtx/util/fs.js} +0 -0
  175. /package/lib/{client/helper → mtx/util}/logging.js +0 -0
  176. /package/lib/{client/helper → mtx/util}/question.js +0 -0
  177. /package/lib/{client/helper/string_helper.js → mtx/util/strings.js} +0 -0
  178. /package/lib/util/{cfUtil.js → cf.js} +0 -0
  179. /package/lib/util/{gitUtil.js → git.js} +0 -0
@@ -1,20 +1,21 @@
1
1
  const YAML = require('@sap/cds-foss').yaml
2
- const { copy, read, write, exists } = require('../../cds').utils
3
- const { readYAML, writeYAML, readJSON } = require('../../util/fs')
2
+ const cds = require('../cds')
3
+ const { copy, read, write, exists } = cds.utils
4
+ const { readYAML, writeYAML, readJSON } = require('../util/fs')
4
5
  const { EOL } = require('node:os')
5
- const { detectIndent } = require('../util/indent')
6
+ const { detectIndent } = require('./indent')
6
7
 
7
8
  function _isObject(item) {
8
9
  return item && typeof item === 'object' && !Array.isArray(item)
9
10
  }
10
11
 
11
- function _deepMerge(target, source) {
12
+ function _mergeObject(target, source) {
12
13
  const unique = array => [...new Set(array.map(JSON.stringify))].map(JSON.parse)
13
14
  if (_isObject(target) && _isObject(source)) {
14
15
  for (const key in source) {
15
16
  if (_isObject(source[key])) {
16
17
  if (!target[key]) Object.assign(target, { [key]: source[key] })
17
- else _deepMerge(target[key], source[key])
18
+ else _mergeObject(target[key], source[key])
18
19
  } else if (Array.isArray(source[key]) && Array.isArray(target[key])) {
19
20
  target[key] = unique([...source[key], ...target[key]])
20
21
  } else {
@@ -56,9 +57,7 @@ async function mergeGitignore (into, from) {
56
57
  /**
57
58
  * @typedef {Object} MatchedNode
58
59
  * @property {string | MatchedNode} in - key path or matched node to start search from
59
- * @property {Object[]} where - list of constraints
60
- * @property {string} where[].property - property key path
61
- * @property {string} where[].isEqualTo - comparison string
60
+ * @property {Object} where - list of constraints
62
61
  */
63
62
 
64
63
  /**
@@ -97,7 +96,7 @@ async function mergeGitignore (into, from) {
97
96
  * @param {MergingSemantics} [semantics] - merging algorithm semantics
98
97
  * @returns {Promise<JSON>} - merged JSON
99
98
  */
100
- async function mergeJSON(into, from, project, semantics) {
99
+ async function _mergeJSON(into, from, project, semantics) {
101
100
  const source = typeof from === 'string' ? await readJSON(from, project) : from
102
101
 
103
102
  let result
@@ -107,11 +106,11 @@ async function mergeJSON(into, from, project, semantics) {
107
106
  const target = await read(into)
108
107
  const targetYAML = YAML.parseDocument(YAML.stringify(target))
109
108
  const sourceYAML = YAML.parseDocument(YAML.stringify(source))
110
- const resultYAML = await mergeYAML(targetYAML, sourceYAML, project, semantics)
109
+ const resultYAML = await _mergeYAML(targetYAML, sourceYAML, project, semantics)
111
110
  result = YAML.parse(YAML.stringify(resultYAML))
112
111
  } else {
113
112
  const target = typeof into === 'string' ? await read(into, 'json') : into
114
- result = _deepMerge(target, source)
113
+ result = _mergeObject(target, source)
115
114
  }
116
115
  // we need to stringify result on site to be able to apply the appropriate indentation.
117
116
  // cds-utils.write is currently hardcoded to use spaces for indentation.
@@ -130,7 +129,7 @@ async function mergeJSON(into, from, project, semantics) {
130
129
  * @returns {Promise<YAML.Document>} - merged YAML
131
130
  * @throws {Error} Throws an error if empty target YAML is passed in
132
131
  */
133
- async function mergeYAML(into, from, project, semantics = {}) {
132
+ async function _mergeYAML(into, from, project, semantics = {}) {
134
133
  const target = typeof into === 'string' ? await readYAML(into) : into instanceof YAML.Document ? into : new YAML.Document(into)
135
134
  const source = typeof from === 'string' ? await readYAML(from, project) : from instanceof YAML.Document ? from : new YAML.Document(from)
136
135
 
@@ -166,16 +165,14 @@ async function mergeYAML(into, from, project, semantics = {}) {
166
165
 
167
166
  const _validateWhere = (node, dict, index) => {
168
167
  const _addToMap = entries => entries?.filter(entry => {
169
- let where = entry.in
170
- if (typeof where === 'string') return true // just a key path on the document root
171
- else where = where[0] // a more complex list of constraints, a sequence/array is involved
172
- const neededParent = dict.get(where)?.node
168
+ if (typeof entry.in === 'string') return true // just a key path on the document root
169
+ const neededParent = dict.get(entry.in[0])?.node
173
170
  return collectionStack.includes(neededParent) // lookbehind in parent collection stack
174
171
  // REVISIT: Only look behind until sequence is reached?
175
172
  }).forEach(item => {
176
173
  const json = JSON.parse(String(node))
177
- const whereFulfilled = item.where.every(constraint =>
178
- constraint.isEqualTo === _getProperty(json, constraint.property)
174
+ const whereFulfilled = Object.entries(item.where).every(([key, value]) =>
175
+ _getProperty(json, key) === value
179
176
  )
180
177
  if (whereFulfilled) {
181
178
  const [collection] = collectionStack
@@ -415,11 +412,26 @@ async function removeFromYAMLArray(name, keyPath, aliasToRemove) {
415
412
  await writeYAML(name, yaml)
416
413
  }
417
414
 
415
+ const path = require('path')
416
+ const merge = (...src) => {
417
+ return { into: (dst, { with: withProject, project, ...semantics } = {}) => {
418
+ if (Object.keys(semantics).length === 0) semantics = undefined
419
+ project ??= withProject // to allow { with: project } for a more fluent API
420
+ const isYAML = typeof dst === 'string' && ['.yaml', '.yml', '.yaml.hbs', '.yml.hbs'].some(ext => dst.endsWith(ext))
421
+ const _merge = isYAML ? _mergeYAML : _mergeJSON
422
+ const target = typeof dst === 'string' ? path.resolve(cds.root, dst) : dst
423
+ if (typeof src[src.length-1] === 'string') {
424
+ return _merge(target, path.join(...src), project, semantics)
425
+ } else {
426
+ return _merge(target, ...src, project, semantics)
427
+ }
428
+ }}
429
+ }
430
+
418
431
  module.exports = {
432
+ merge,
419
433
  sort,
420
434
  mergeGitignore,
421
- mergeJSON,
422
- mergeYAML,
423
435
  removeFromYAML,
424
436
  removeFromYAMLArray
425
437
  }
@@ -1,16 +1,16 @@
1
1
 
2
- const { URLS, MAVEN_ARCHETYPE_VERSION, OPTIONS: { JAVA_MVN, JAVA_PACKAGE, SAMPLES, HANA }, REGEX_JAVA_PACKAGE } = require('../constants');
2
+ const { URLS, MAVEN_ARCHETYPE_VERSION, OPTIONS: { JAVA_MVN, JAVA_PACKAGE, SAMPLES, HANA }, REGEX_JAVA_PACKAGE } = require('./constants');
3
3
 
4
4
  const cp = require('child_process');
5
5
  const util = require('util');
6
6
  const execAsync = util.promisify(cp.exec);
7
- const term = require('../../util/term');
8
- const cmd = require('../../util/command');
7
+ const term = require('../util/term');
8
+ const cmd = require('../util/command');
9
9
 
10
10
  const DEFAULT_GROUP_ID = 'customer';
11
11
 
12
12
  const LOG = console
13
- const cds = require('../../cds')
13
+ const cds = require('../cds')
14
14
  const DEBUG = cds.debug('cli')
15
15
 
16
16
  class MvnArchetypeUtil {
@@ -58,7 +58,7 @@ class MvnArchetypeUtil {
58
58
  params.set(`com.sap.cds:cds-maven-plugin:${MAVEN_ARCHETYPE_VERSION}:add`);
59
59
  params.set(`-Dfeature`, feature)
60
60
  const profile = cds.cli.options.for
61
- params.set(`-Dprofile`, profile === 'production' ? 'cloud' : profile)
61
+ params.set(`-Dprofile`, profile === 'production' ? 'cloud' : profile ?? 'default')
62
62
 
63
63
  const { options } = cds.cli
64
64
  if (options[JAVA_MVN]) {
@@ -0,0 +1,85 @@
1
+ const { join } = require('path')
2
+ const cds = require('../cds')
3
+ const { readdir } = cds.utils
4
+ const DEBUG = cds.debug('add')
5
+ const { env4 } = require('./projectReader')
6
+
7
+ module.exports = class Plugin {
8
+
9
+ constructor() {
10
+ this.name = this.constructor.name.replace(/template/i, '').replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
11
+ }
12
+
13
+ /**
14
+ * Gets the required templates.
15
+ * @returns {string[]} An array of strings representing required plugins.
16
+ * Returns an empty array by default.
17
+ * In general, avoid such dependencies if possible.
18
+ */
19
+ requires() { return [] }
20
+
21
+ /**
22
+ * Determines whether a plugin can be run.
23
+ * @returns {Promise<boolean>} A promise that resolves to `true` if the template can be run.
24
+ */
25
+ async canRun() { return true }
26
+
27
+ /**
28
+ * Determines if the plugin has already been added to the project.
29
+ * @param {import('@sap/cds').env} env - The env for the 'production' profile.
30
+ * @returns {boolean} - Returns `true` if the template has been added, otherwise `false`.
31
+ */
32
+ static hasInProduction(env) { // eslint-disable-line no-unused-vars
33
+ return false
34
+ }
35
+
36
+ /**
37
+ * Runs the plugin.
38
+ */
39
+ async run() { }
40
+
41
+ /**
42
+ * Runs the combine operations for interoperability with other plugins.
43
+ */
44
+ async combine() { }
45
+
46
+
47
+
48
+
49
+
50
+ /* ––––––––––––
51
+ Yeoman Generator
52
+ –––––––––––– */
53
+
54
+ /**
55
+ * Runs the finalize operations.
56
+ */
57
+ async finalize() { } // REVISIT: Required for yo-generator, check if needed any more?
58
+
59
+ /* ––––––––––––
60
+ Private APIs
61
+ –––––––––––– */
62
+
63
+ async combineSupported() {
64
+ DEBUG && console.log()
65
+ DEBUG && console.log('Plugins active for production:')
66
+ const fromDk = await readdir(join(__dirname, './template'))
67
+ const { registered } = require('./add')
68
+ const processPlugins = async (plugins) => {
69
+ for (const plugin of plugins) {
70
+ const Plugin = registered.get(plugin) ?? require('./template/' + plugin)
71
+ const template = new Plugin()
72
+ const inProduction = Plugin.hasInProduction(env4('production'))
73
+ DEBUG && console.log(inProduction ? '✅' : '❌', plugin)
74
+ if (inProduction) await template.combine()
75
+ }
76
+ }
77
+
78
+ if (DEBUG) console.log(`\n ${fromDk.length} from @sap/cds-dk:`)
79
+ await processPlugins(fromDk)
80
+ if (DEBUG) console.log(`\n ${registered.size} from plugins:`)
81
+ await processPlugins(registered.keys())
82
+
83
+ DEBUG && console.log()
84
+ }
85
+ }
@@ -0,0 +1,145 @@
1
+ const cds = require('../cds')
2
+ const { join, resolve, basename } = require('path')
3
+ const { fs, exists, isdir } = cds.utils
4
+ const { parseXml } = require('./xml')
5
+ const { REGEX_JAVA_VERSION } = require('./constants')
6
+ const DEBUG = cds.debug('cli')
7
+
8
+ module.exports = new class ProjectReader {
9
+
10
+ constructor() {
11
+ this.readProject = this.readProject.bind(this)
12
+
13
+ // 1. Use project name and static default values
14
+ this.appVersion = cds.cli.options.add?.has('java') ? '1.0.0-SNAPSHOT' : '1.0.0'
15
+ this.appName = this.appId = basename(cds.root)
16
+ this.appDescription = 'A simple CAP project.'
17
+
18
+ // 2. Use either pom.xml or package.json
19
+ if (exists('pom.xml')) {
20
+ const { artifactId = [], description = [], version = [], properties = [] } = parseXml(resolve(cds.root, 'pom.xml')) ?? {}
21
+ const v = version[0] === '${revision}' ? properties[0]?.revision[0] : version[0]
22
+ if (v && REGEX_JAVA_VERSION.test(v)) this.appVersion = v
23
+ if (artifactId[0]) this.appName = this.appId = artifactId[0].split(/-parent/)[0]
24
+ if (description[0]) this.appDescription = description[0]
25
+ }
26
+ else if (exists('package.json')) {
27
+ const { name, version, description } = JSON.parse(fs.readFileSync(join(cds.root, 'package.json')))
28
+ this.appVersion = version
29
+ const segments = name.trim().replace(/@/g, '').split('/').map(encodeURIComponent)
30
+ this.appName = segments[segments.length - 1]
31
+ this.appId = segments.join('.')
32
+ this.appDescription = description ?? this.appDescription
33
+ }
34
+ }
35
+
36
+ // REVISIT: There should an API provided by cds.env
37
+ /**
38
+ * Returns cds.env using 'production' profile by default as mta deployment is executed with having production profile set.
39
+ */
40
+ env4(profile = 'production') {
41
+ const cdsEnv = process.env.CDS_ENV
42
+ process.env.CDS_ENV = profile
43
+ try {
44
+ return cds.env.for('cds')
45
+ } finally {
46
+ cdsEnv ? process.env.CDS_ENV = cdsEnv : delete process.env.CDS_ENV
47
+ }
48
+ }
49
+
50
+ /**
51
+ * @typedef {Object} ProjectConfig
52
+ * @property {string} appVersion The version of the application.
53
+ * @property {string} appName The name of the application.
54
+ * @property {string} appId The ID of the application.
55
+ * @property {string} appDescription The description of the application.
56
+ * @property {string} language The programming language of the project.
57
+ * @property {string} srvPath The path to the server module.
58
+ * @property {string} archiveName The name of the archive.
59
+ * @property {Object} db Database configuration.
60
+ * @property {string} configFile The name of the configuration file. This is typically the `package.json` for Node.js and `.cdsrc.json` for Java.
61
+ * @property {string} appPath The path to the app folder.
62
+ * @property {boolean} hasUIEmbedded Indicates if the project uses an embedded UI.
63
+ * @property {boolean} hasUIModule Indicates if the project is using a modular UI.
64
+ * @property {boolean} hasUI Indicates if the project has a UI.
65
+ * @property {boolean} isJava Indicates if the project is using Java.
66
+ * @property {boolean} isNodejs Indicates if the project is using Node.js.
67
+ * @property {boolean} hasHelmUnifiedRuntime Indicates if the project uses Unified Runtime Helm deployment.
68
+ * @property {boolean} hasHelm Indicates if the project uses CAP Helm deployment.
69
+ * @property {boolean} hasMta Indicates if the project uses MTA deployment.
70
+ * @property {boolean} hasConnectivity Indicates if the project uses connectivity.
71
+ * @property {boolean} hasMultitenancy Indicates if the project is multitenant.
72
+ * @property {boolean} hasToggles Indicates if the project uses feature toggles.
73
+ * @property {boolean} hasExtensibility Indicates if the project is extensible.
74
+ * @property {boolean} hasXsuaa Indicates if the project uses XSUAA.
75
+ * @property {boolean} hasHana Indicates if the project uses HANA.
76
+ * @property {boolean} hasPostgres Indicates if the project uses PostgreSQL.
77
+ * @property {boolean} hasEnterpriseMessaging Indicates if the project uses SAP BTP Event Mesh.
78
+ * @property {boolean} hasApprouter Indicates if the project uses the SAP Application Router.
79
+ * @property {boolean} hasHtml5Repo Indicates if the project uses the SAP BTP HTML5 Application Repository.
80
+ * @property {boolean} hasDestination Indicates if the project uses the SAP BTP Destination service.
81
+ *
82
+ */
83
+
84
+ /**
85
+ * Reads the project configuration and constructs a project object.
86
+ * This function dynamically resolves project properties and returns an object with these properties.
87
+ *
88
+ * @returns {ProjectConfig} An object representing the project configuration.
89
+ */
90
+ readProject() {
91
+ const env = this.env4('production')
92
+ const { appVersion, appName, appId, appDescription } = this
93
+ DEBUG?.({ env })
94
+
95
+ const _inProd = plugin => require(`./template/${plugin}`).hasInProduction(env)
96
+ const reserved = {
97
+ has: x => _inProd(x),
98
+ shortcut: () => true,
99
+ profile: () => cds.cli.options?.for,
100
+ language: () => exists('pom.xml') ? 'java' : 'nodejs',
101
+ isJava: () => exists('pom.xml') || cds.cli.options?.add?.has('java'),
102
+ isNodejs: () => !exists('pom.xml') && !cds.cli.options?.add?.has('java'),
103
+ srvPath: () => join(env.build.target, env.folders.srv.replace(/\/$/, '')).replace(/\\/g, '/'),
104
+ archiveName: () => {
105
+ const pom = parseXml(resolve(cds.root, env.folders.srv, 'pom.xml'))
106
+ return (pom?.artifactId?.[0] ?? basename(cds.root)) + '-exec.' + pom?.packaging?.[0]
107
+ },
108
+ db: () => {
109
+ const folder = env.folders.db
110
+ const name = folder.replace(/\/$/, '')
111
+ const path = join(env.build.target, name).replace(/\\/g, '/')
112
+ return { folder, name, path }
113
+ },
114
+ configFile: () => exists('pom.xml') ? '.cdsrc.json' : 'package.json',
115
+ appPath: () => env.folders.app,
116
+ appVersion: () => appVersion,
117
+ appName: () => appName,
118
+ cleanedAppName: () => appName.replaceAll('_', '-'),
119
+ appId: () => appId,
120
+ appDescription: () => appDescription,
121
+ hasUIEmbedded: () => exists(join(env.folders.app, 'xs-app.json')),
122
+ hasUIModule: () => !this.hasUIEmbedded && isdir(env.folders.app) && fs.readdirSync(resolve(cds.root, env.folders.app)).length > 0,
123
+ hasUI: () => this.hasUIEmbedded || this.hasUIModule
124
+ }
125
+
126
+ const project = (() => {
127
+ const defined = {}
128
+ return new Proxy({}, {
129
+ get(_, p) { return _get(p) ?? defined[p] },
130
+ set(_, p, value) { defined[p] = value; return true },
131
+ has(_, p) { return _get(p) ?? p in defined }
132
+ })
133
+ })()
134
+
135
+ const _get = property => { const p = property
136
+ if (p in reserved) return reserved[p]()
137
+ for (const prefix of ['has', 'is']) if (p.startsWith(prefix) && p.length > prefix.length) {
138
+ return _inProd(p.slice(prefix.length).replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase())
139
+ }
140
+ }
141
+
142
+ DEBUG?.({ project })
143
+ return project
144
+ }
145
+ }
@@ -0,0 +1,151 @@
1
+ // Registry of sequence matchers in mta.yaml files
2
+ const cds = require('../../cds')
3
+
4
+ const srvNode4 = path => ({ in: 'modules', where: { type: 'nodejs', path } })
5
+ const srvJava4 = path => ({ in: 'modules', where: { type: 'java', path } })
6
+ const srv4 = cds.env['project-nature'] === 'java' || cds.cli.options?.add?.has('java') ? srvJava4 : srvNode4
7
+
8
+ const approuter = { in: 'modules', where: { type: 'approuter.nodejs' } }
9
+
10
+ const xsuaa = {
11
+ in: 'resources',
12
+ where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'xsuaa' }
13
+ }
14
+
15
+ const destination = {
16
+ in: 'resources',
17
+ where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'destination' }
18
+ }
19
+
20
+ const connectivity = {
21
+ in: 'resources',
22
+ where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'connectivity' }
23
+ }
24
+
25
+ const redis = {
26
+ in: 'resources',
27
+ where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'redis-cache' }
28
+ }
29
+
30
+ const enterpriseMessaging = {
31
+ in: 'resources',
32
+ where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'enterprise-messaging' }
33
+ }
34
+
35
+ const applicationLogging = {
36
+ in: 'resources',
37
+ where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'application-logs' }
38
+ }
39
+
40
+ const hdbDeployer = {
41
+ in: 'modules',
42
+ where: { type: 'hdb' }
43
+ }
44
+
45
+ const hdiContainer = {
46
+ in: 'resources',
47
+ where: { type: 'com.sap.xs.hdi-container' }
48
+ }
49
+
50
+ const notificationTypesDeployer = {
51
+ in: 'modules',
52
+ where: { type: 'nodejs', name: 'notification-content-deployment' }
53
+ }
54
+
55
+ const serviceManager = {
56
+ in: 'resources',
57
+ where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'service-manager', 'parameters.service-plan': 'container' }
58
+ }
59
+
60
+ const postgres = {
61
+ in: 'resources',
62
+ where: { 'parameters.service': 'postgresql-db' }
63
+ }
64
+
65
+ const postgresDeployer = {
66
+ in: 'modules',
67
+ where: { type: 'nodejs', path: 'gen/pg' }
68
+ }
69
+
70
+ const saasRegistry = {
71
+ in: 'resources',
72
+ where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'saas-registry' }
73
+ }
74
+
75
+ const srvApi4 = srv => ({
76
+ in: [srv, 'provides'],
77
+ where: { name: 'srv-api', 'properties.srv-url': '${default-url}' }
78
+ })
79
+
80
+ const mtxSidecar4 = path => ({
81
+ in: 'modules',
82
+ where: { type: 'nodejs', path }
83
+ })
84
+
85
+ const providedMtxApiSidecar4 = mtxSidecar => ({
86
+ in: [mtxSidecar, 'provides'],
87
+ where: { name: 'mtx-api', 'properties.mtx-url': '${default-url}' }
88
+ })
89
+
90
+ const requiredMtxApi = {
91
+ in: [approuter, 'requires'],
92
+ where: { 'properties.name': 'mtx-api', 'properties.url': '~{mtx-url}' }
93
+ }
94
+
95
+ const providedAppApi = {
96
+ in: [approuter, 'provides'],
97
+ where: { name: 'app-api', 'properties.app-protocol': '${protocol}', 'properties.app-uri': '${default-uri}' }
98
+ }
99
+
100
+ const requiredAppApi4 = module => ({
101
+ in: [module, 'requires'],
102
+ where: { name: 'app-api', 'properties.SUBSCRIPTION_URL': '~{app-protocol}://\\${tenant_subdomain}-~{app-uri}' }
103
+ })
104
+
105
+ const requiredMtxSidecarApi4 = srv => ({
106
+ in: [srv, 'requires'],
107
+ where: { 'properties.CDS_MULTITENANCY_SIDECAR_URL': '~{mtx-url}' }
108
+ })
109
+
110
+ const providedJavaApprouterApi4 = srv => ({
111
+ in: [srv, 'requires'],
112
+ where: { 'properties.CDS_MULTITENANCY_APPUI_URL': '~{url}' }
113
+ })
114
+
115
+ const providedJavaApprouterApi = {
116
+ in: [approuter, 'requires'],
117
+ where: { 'properties.url': '${default-url}' }
118
+ }
119
+
120
+ const approuterExtensibility = {
121
+ in: 'routes',
122
+ where: { source: '^/-/cds/.*', destination: 'mtx-api', authenticationType: 'none' }
123
+ }
124
+
125
+ module.exports = {
126
+ srv4,
127
+ approuter,
128
+ xsuaa,
129
+ destination,
130
+ connectivity,
131
+ enterpriseMessaging,
132
+ hdbDeployer,
133
+ hdiContainer,
134
+ notificationTypesDeployer,
135
+ serviceManager,
136
+ postgres,
137
+ postgresDeployer,
138
+ saasRegistry,
139
+ redis,
140
+ srvApi4,
141
+ mtxSidecar4,
142
+ providedMtxApiSidecar4,
143
+ requiredMtxApi,
144
+ requiredMtxSidecarApi4,
145
+ providedAppApi,
146
+ requiredAppApi4,
147
+ providedJavaApprouterApi4,
148
+ providedJavaApprouterApi,
149
+ applicationLogging,
150
+ approuterExtensibility
151
+ }
@@ -6,13 +6,13 @@ modules:
6
6
  requires:
7
7
  - name: {{appName}}-logging
8
8
 
9
- {{#isMultitenant}}
9
+ {{#hasMultitenancy}}
10
10
  - name: {{appName}}-mtx
11
11
  type: nodejs
12
12
  path: {{#isNodejs}}gen/{{/isNodejs}}mtx/sidecar
13
13
  requires:
14
14
  - name: {{appName}}-logging
15
- {{/isMultitenant}}
15
+ {{/hasMultitenancy}}
16
16
 
17
17
  {{#hasApprouter}}
18
18
  - name: {{appName}}
@@ -26,4 +26,4 @@ resources:
26
26
  type: org.cloudfoundry.managed-service
27
27
  parameters:
28
28
  service: application-logs
29
- service-plan: lite
29
+ service-plan: standard
@@ -0,0 +1,49 @@
1
+ const { readProject } = require('../../projectReader')
2
+ const { merge } = require('../../merge')
3
+ const { srv4, mtxSidecar4, applicationLogging, approuter } = require('../../registries/mta')
4
+
5
+ module.exports = class ApplicationLoggingTemplate extends require('../../plugin') {
6
+
7
+ static hasInProduction(env) {
8
+ return !!env.features.kibana_formatter
9
+ }
10
+
11
+ async canRun() {
12
+ const { hasMta, hasHelm, hasHelmUnifiedRuntime, isJava } = readProject()
13
+ if ((hasHelmUnifiedRuntime || hasHelm) && !hasMta) throw `'cds add application-logging' is not available for Kyma yet`
14
+ if (isJava) throw `'cds add application-logging' is not available for Java yet`
15
+ return true
16
+ }
17
+
18
+ async combine() {
19
+ const project = readProject()
20
+ const { hasMta, hasApprouter, isJava, hasMultitenancy, srvPath } = project
21
+
22
+ if (hasMta) {
23
+ const srv = srv4(srvPath)
24
+ const additions = [srv, applicationLogging]
25
+ const relationships = [{
26
+ insert: [applicationLogging, 'name'],
27
+ into: [srv, 'requires', 'name']
28
+ }]
29
+ if (hasMultitenancy) {
30
+ const mtxSidecar = mtxSidecar4(isJava ? 'mtx/sidecar' : 'gen/mtx/sidecar')
31
+ additions.push(mtxSidecar)
32
+ relationships.push({
33
+ insert: [applicationLogging, 'name'],
34
+ into: [mtxSidecar, 'requires', 'name']
35
+ })
36
+ }
37
+ if (hasApprouter) {
38
+ additions.push(approuter)
39
+ relationships.push({
40
+ insert: [applicationLogging, 'name'],
41
+ into: [approuter, 'requires', 'name']
42
+ })
43
+ }
44
+ await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', {
45
+ with: project, additions, relationships
46
+ })
47
+ }
48
+ }
49
+ }
@@ -4,10 +4,10 @@ modules:
4
4
  type: {{language}}
5
5
  path: {{& srvPath}}
6
6
  {{#isJava}}
7
- {{#isMultitenant}}
7
+ {{#hasMultitenancy}}
8
8
  properties:
9
9
  CDS_MULTITENANCY_APPUI_TENANTSEPARATOR: "-"
10
- {{/isMultitenant}}
10
+ {{/hasMultitenancy}}
11
11
  {{/isJava}}
12
12
  requires:
13
13
  - name: {{appName}}-auth
@@ -24,10 +24,10 @@ modules:
24
24
  keep-existing-routes: true
25
25
  disk-quota: 256M
26
26
  memory: 256M
27
- {{#isMultitenant}}
27
+ {{#hasMultitenancy}}
28
28
  properties:
29
29
  TENANT_HOST_PATTERN: "^(.*)-${default-uri}"
30
- {{/isMultitenant}}
30
+ {{/hasMultitenancy}}
31
31
  requires:
32
32
  - name: srv-api
33
33
  group: destinations
@@ -35,14 +35,14 @@ modules:
35
35
  name: srv-api # must be used in xs-app.json as well
36
36
  url: ~{srv-url}
37
37
  forwardAuthToken: true
38
- {{#isMultitenant}}
38
+ {{#hasMultitenancy}}
39
39
  - name: mtx-api
40
40
  group: destinations
41
41
  properties:
42
42
  name: mtx-api # must be used in xs-app.json as well
43
43
  url: ~{mtx-url}
44
- {{/isMultitenant}}
45
- {{#isMultitenant}}
44
+ {{/hasMultitenancy}}
45
+ {{#hasMultitenancy}}
46
46
  {{#isNodejs}}
47
47
  provides:
48
48
  - name: app-api
@@ -50,7 +50,7 @@ modules:
50
50
  app-protocol: ${protocol}
51
51
  app-uri: ${default-uri}
52
52
  {{/isNodejs}}
53
- {{/isMultitenant}}
53
+ {{/hasMultitenancy}}
54
54
  {{#isJava}}
55
55
  provides:
56
56
  - name: app-api
@@ -58,7 +58,7 @@ modules:
58
58
  url: ${default-url}
59
59
  {{/isJava}}
60
60
 
61
- {{#isMultitenant}}
61
+ {{#hasMultitenancy}}
62
62
  - name: {{appName}}-mtx
63
63
  type: nodejs
64
64
  path: {{#isNodejs}}gen/{{/isNodejs}}mtx/sidecar
@@ -66,7 +66,7 @@ modules:
66
66
  - name: app-api
67
67
  properties:
68
68
  SUBSCRIPTION_URL: ~{app-protocol}://\${tenant_subdomain}-~{app-uri}
69
- {{/isMultitenant}}
69
+ {{/hasMultitenancy}}
70
70
 
71
71
  resources:
72
72
  - name: {{appName}}-auth
@@ -77,4 +77,4 @@ resources:
77
77
  path: ./xs-security.json
78
78
  config:
79
79
  xsappname: {{appName}}-${org}-${space}
80
- tenant-mode: {{#isMultitenant}}shared{{/isMultitenant}}{{^isMultitenant}}dedicated{{/isMultitenant}}
80
+ tenant-mode: {{#hasMultitenancy}}shared{{/hasMultitenancy}}{{^hasMultitenancy}}dedicated{{/hasMultitenancy}}