@sap/cds-dk 9.3.2 → 9.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/bin/bind.js +1 -1
  3. package/bin/cds.js +3 -0
  4. package/bin/compile/index.js +53 -3
  5. package/bin/data-products/get-csn-via-dp-cli.js +41 -0
  6. package/bin/data-products/get-csn.js +76 -0
  7. package/bin/data-products/import.js +114 -0
  8. package/bin/data-products/index.js +30 -0
  9. package/bin/data-products/merge-csns.js +46 -0
  10. package/bin/data-products/prepare-csn.js +282 -0
  11. package/bin/data-products/prepare-files.js +69 -0
  12. package/bin/export.js +240 -0
  13. package/bin/help/export.md +37 -0
  14. package/bin/help/import.md +11 -0
  15. package/bin/help/minify.md +23 -0
  16. package/bin/import.js +13 -3
  17. package/bin/inspect.js +226 -0
  18. package/bin/minify.js +123 -0
  19. package/bin/up.js +2 -2
  20. package/bin/util/cli.js +132 -0
  21. package/bin/util/log.js +2 -0
  22. package/bin/version.js +2 -1
  23. package/bin/watch.js +21 -3
  24. package/lib/bind/shared.js +3 -1
  25. package/lib/build/buildTaskEngine.js +2 -1
  26. package/lib/build/plugins/helm/files/templates/NOTES.txt +1 -1
  27. package/lib/build/plugins/helm/index.js +1 -2
  28. package/lib/build/provider/internalBuildTaskProvider.js +3 -3
  29. package/lib/build/provider/nodejs/index.js +62 -3
  30. package/lib/deploy/index.js +1 -1
  31. package/lib/deploy/to-hana/hana.js +18 -16
  32. package/lib/import/odata/v2parser.js +2 -2
  33. package/lib/import/odata/v4parser.js +21 -2
  34. package/lib/import/odata.package.json +4 -4
  35. package/lib/init/constants.js +4 -1
  36. package/lib/init/index.js +6 -5
  37. package/lib/init/mvn.js +3 -2
  38. package/lib/init/projectReader.js +65 -55
  39. package/lib/init/registries/mta.js +6 -0
  40. package/lib/init/template/ams/files/manifest.yml.hbs +1 -1
  41. package/lib/init/template/ams/files/mta.yaml.hbs +1 -1
  42. package/lib/init/template/ams/index.js +7 -6
  43. package/lib/init/template/app-frontend/files/app-package.json.hbs +20 -0
  44. package/lib/init/template/app-frontend/files/index.html.hbs +34 -0
  45. package/lib/init/template/app-frontend/files/manifest.json +13 -0
  46. package/lib/init/template/app-frontend/files/mta.yaml.hbs +64 -0
  47. package/lib/init/template/app-frontend/files/package.json.hbs +17 -0
  48. package/lib/init/template/app-frontend/files/ui5-deploy.yaml.hbs +25 -0
  49. package/lib/init/template/app-frontend/files/ui5.yaml.hbs +6 -0
  50. package/lib/init/template/app-frontend/files/values.yaml.hbs +20 -0
  51. package/lib/init/template/app-frontend/files/xs-app.json.hbs +17 -0
  52. package/lib/init/template/app-frontend/index.js +98 -0
  53. package/lib/init/template/application-logging/index.js +13 -10
  54. package/lib/init/template/approuter/files/mta.yaml.hbs +0 -2
  55. package/lib/init/template/approuter/index.js +15 -9
  56. package/lib/init/template/attachments/index.js +7 -13
  57. package/lib/init/template/audit-logging/files/package.json.hbs +5 -0
  58. package/lib/init/template/audit-logging/index.js +4 -4
  59. package/lib/init/template/cloud-logging/index.js +13 -10
  60. package/lib/init/template/connectivity/index.js +3 -3
  61. package/lib/init/template/destination/index.js +6 -6
  62. package/lib/init/template/dynatrace/index.js +13 -10
  63. package/lib/init/template/enterprise-messaging/index.js +5 -5
  64. package/lib/init/template/enterprise-messaging-shared/index.js +4 -4
  65. package/lib/init/template/extensibility/index.js +2 -2
  66. package/lib/init/template/file-based-messaging/index.js +2 -2
  67. package/lib/init/template/github-actions/index.js +3 -3
  68. package/lib/init/template/hana/index.js +19 -22
  69. package/lib/init/template/handler/index.js +1 -0
  70. package/lib/init/template/helm/index.js +34 -21
  71. package/lib/init/template/html5-repo/files/containerize.yaml.hbs +0 -1
  72. package/lib/init/template/html5-repo/files/mta.yaml.hbs +10 -2
  73. package/lib/init/template/html5-repo/index.js +8 -6
  74. package/lib/init/template/http/index.js +31 -10
  75. package/lib/init/template/ias/files/manifest.yml.hbs +1 -1
  76. package/lib/init/template/ias/files/mta.yaml.hbs +9 -5
  77. package/lib/init/template/ias/files/values.yaml.hbs +14 -2
  78. package/lib/init/template/ias/index.js +8 -8
  79. package/lib/init/template/kafka/index.js +3 -3
  80. package/lib/init/template/local-messaging/index.js +2 -2
  81. package/lib/init/template/malware-scanner/index.js +3 -7
  82. package/lib/init/template/mta/files/mta.yaml.hbs +10 -0
  83. package/lib/init/template/multitenancy/files/mta.yaml.hbs +16 -3
  84. package/lib/init/template/multitenancy/files/package.json.hbs +5 -0
  85. package/lib/init/template/multitenancy/files/values.yaml.hbs +1 -6
  86. package/lib/init/template/multitenancy/index.js +8 -10
  87. package/lib/init/template/notifications/index.js +2 -2
  88. package/lib/init/template/portal/files/mta.yaml.hbs +18 -2
  89. package/lib/init/template/portal/index.js +7 -7
  90. package/lib/init/template/postgres/index.js +4 -4
  91. package/lib/init/template/redis/index.js +2 -2
  92. package/lib/init/template/redis-messaging/index.js +2 -2
  93. package/lib/init/template/sample/files/app/admin-books/fiori-service.cds +2 -2
  94. package/lib/init/template/sample/files/db/schema.cds +5 -8
  95. package/lib/init/template/sample/files/javascript/admin-service.js.hbs +24 -0
  96. package/lib/init/template/sample/files/javascript/cat-service.js.hbs +43 -0
  97. package/lib/init/template/sample/files/typescript/admin-service.ts.hbs +16 -0
  98. package/lib/init/template/sample/files/typescript/cat-service.ts.hbs +31 -0
  99. package/lib/init/template/test/files/test/test.xs.hbs +1 -1
  100. package/lib/init/template/test/index.js +2 -2
  101. package/lib/init/template/tiny-sample/index.js +1 -1
  102. package/lib/init/template/typer/files/xsconfig.json +2 -1
  103. package/lib/init/template/typescript/files/package.json +1 -1
  104. package/lib/init/template/typescript/files/tsconfig.json +4 -1
  105. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +4 -0
  106. package/lib/init/template/workzone-standard/index.js +8 -8
  107. package/lib/init/template/xsuaa/files/mta.yaml.hbs +0 -2
  108. package/lib/init/template/xsuaa/files/package.json.hbs +3 -0
  109. package/lib/init/template/xsuaa/index.js +23 -18
  110. package/lib/lint/eslintHelp.json +1 -1
  111. package/lib/util/term.js +2 -4
  112. package/npm-shrinkwrap.json +28 -28
  113. package/package.json +1 -1
@@ -143,7 +143,7 @@ class InternalBuildTaskProvider extends BuildTaskProvider {
143
143
  sidecarEnv = cds.env.for("cds", sidecarPath)
144
144
  }
145
145
  if (
146
- cds.env.profiles?.includes('with-mtx-sidecar') || // new presets
146
+ cds.env.profiles?.includes('with-mtx-sidecar') || // new presets // FIXME: BAD hard-coded dependency to profile
147
147
  cds.env.requires["cds.xt.ModelProviderService"]?.external // for compatibility with former mtxs presets
148
148
  ) {
149
149
  DEBUG?.("MTX app with sidecar")
@@ -161,7 +161,7 @@ class InternalBuildTaskProvider extends BuildTaskProvider {
161
161
  }
162
162
 
163
163
  if (sidecarEnv?.requires["cds.xt.ModelProviderService"]?._in_sidecar || sidecarEnv?.profiles?.includes('mtx-sidecar')) {
164
- throw new BuildError(`MTX sidecar requires profile 'with-mtx-sidecar' in project root configuration.`)
164
+ throw new BuildError(`MTX sidecar requires profile 'with-mtx-sidecar' in project root configuration.`) // FIXME: BAD hard-coded dependency to profile
165
165
  }
166
166
  if (cds.env.requires["cds.xt.ModelProviderService"]?._in_sidecar || cds.env.profiles?.includes('mtx-sidecar')) {
167
167
  // cds build is executed in sidecar folder
@@ -222,7 +222,7 @@ class InternalBuildTaskProvider extends BuildTaskProvider {
222
222
  */
223
223
  static _isMtxs() {
224
224
  return cds.env.requires.toggles
225
- || cds.env.profiles.includes('with-mtx-sidecar')
225
+ || cds.env.profiles.includes('with-mtx-sidecar') // FIXME: BAD hard-coded dependency to profile
226
226
  || cds.env.requires['cds.xt.ModelProviderService']
227
227
  || (typeof cds.env.requires.multitenancy === "object")
228
228
  }
@@ -6,10 +6,13 @@ const EdmxBuildPlugin = require('../edmxBuildPlugin')
6
6
  const { BuildError, getWorkspaces, findWorkspaceRoot } = require('../../util')
7
7
  const { OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM, ODATA_VERSION_V2, FOLDER_GEN, CONTENT_EDMX, CONTENT_PACKAGELOCK_JSON,
8
8
  CONTENT_NPMRC, CONTENT_CDSRC_JSON, CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON, FLAVOR_LOCALIZED_EDMX,
9
- OPTION_WS_PACK} = require('../../constants')
9
+ OPTION_WS_PACK} = require('../../constants');
10
10
  const { WARNING } = EdmxBuildPlugin
11
- const LOG = cds.log('cli|build'), DEBUG = cds.debug('cli|build')
11
+ const LOG = cds.log('cli|build')
12
+ const DEBUG = cds.debug('cli|build')
12
13
  const NODE_ENGINE_VERSION = '>=20'
14
+ const { exec } = require('node:child_process')
15
+ const pexec = require('node:util').promisify(exec);
13
16
 
14
17
  /**
15
18
  * @param {T | T[]} x
@@ -84,9 +87,65 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
84
87
  await this._packWorkspaceDependencies(destRoot)
85
88
  }
86
89
  }
90
+
91
+ await this.#cleanupPackageJson(destRoot)
92
+
87
93
  return this._result
88
94
  }
89
95
 
96
+ /**
97
+ * Removes devDeps that are invalid semver, pointing to workspace or file locations,
98
+ * and also removes workspace entries. If needed, creates a new package-lock.json in build destination.
99
+ */
100
+ async #cleanupPackageJson() {
101
+ /** @param {string | undefined} version */
102
+ const valid = version => version && !version.match('^(?:workspace|file):')
103
+
104
+ const packageJsonPath = path.join(this.task.dest, 'package.json')
105
+ try {
106
+ const packageJson = await cds.utils.read(packageJsonPath)
107
+
108
+ DEBUG?.(`Sanitizing the package.json for "${this.task.src}'...`)
109
+
110
+ let modified = false
111
+ // remove the workspace configuration
112
+ if (packageJson.workspaces) {
113
+ delete packageJson.workspaces
114
+ modified = true
115
+ }
116
+ // remove the workspace dev dependencies and the cds-plugin-ui5
117
+ if (packageJson.devDependencies) {
118
+ packageJson.devDependencies = Object.entries(packageJson.devDependencies)
119
+ .reduce((acc, [dep, version]) => {
120
+ if (valid(version)) {
121
+ acc[dep] = version
122
+ }
123
+ return acc
124
+ }, {})
125
+ modified = true
126
+ }
127
+ // overwrite the package.json if it was modified only
128
+ if (modified) {
129
+ await cds.utils.write(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8')
130
+ // update the package-lock.json if it exists
131
+ if (cds.utils.exists(path.join(this.task.dest, 'package-lock.json'))) {
132
+ DEBUG?.(`Updating the package-lock.json for '${this.task.src}'...`)
133
+ // run the npm install --package-lock-only to only update the package-lock.json
134
+ // without installing the dependencies to node_modules
135
+ try {
136
+ await pexec('npm install --package-lock-only', { cwd: this.task.dest })
137
+ } catch (e) {
138
+ DEBUG?.(`Failed to update the package-lock.json for '${this.task.src}'! Error: ${e.code} - ${e.message}`)
139
+ }
140
+ }
141
+ }
142
+ } catch (e) {
143
+ // ENOENT: package.json not found -> this is okay for monorepos
144
+ if (e.code !== 'ENOENT') throw e
145
+ }
146
+
147
+ }
148
+
90
149
  async clean() {
91
150
  // staging build content is deleted by BuildTaskEngine
92
151
  if (cds.env.build.target === '.') {
@@ -186,7 +245,7 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
186
245
  async _packWorkspaceDependencies(dest) {
187
246
  const wsRoot = findWorkspaceRoot(cds.root)
188
247
  const appPkg = require(path.join(dest, 'package.json'))
189
- const allWorkspaces = await getWorkspaces(wsRoot, true, false);
248
+ const allWorkspaces = await getWorkspaces(wsRoot, true, false)
190
249
  let workspaces
191
250
  if (this.task.options.workspaces) {
192
251
  const relevantWorkspaces = new Set(arrayify(this.task.options.workspaces))
@@ -1,5 +1,5 @@
1
1
  module.exports = {
2
2
  options: /** @type{const} */(['--to', '--for', '--out', '--on', '--tunnel-address', '--vcap-file', '--delta-from']),
3
- flags: /** @type{const} */(['--no-save', '--auto-undeploy', '--dry', '--xdry', '--with-mocks', '--store-credentials', '--no-build', '--model-only', '--script', '--with-auto-schema-evolution']),
3
+ flags: /** @type{const} */(['--no-save', '--auto-undeploy', '--dry', '--xdry', '--with-mocks', '--store-credentials', '--no-build', '--model-only', '--script', '--with-auto-schema-evolution', '--resolve-bindings']),
4
4
  shortcuts: ['-2', '-4', '-o', '-n']
5
5
  }
@@ -30,7 +30,8 @@ const allowList = [
30
30
  '--tunnel-address',
31
31
  '--vcap-file',
32
32
  '--with-mocks',
33
- '--no-build'
33
+ '--no-build',
34
+ '--resolve-bindings'
34
35
  ]
35
36
 
36
37
  module.exports = new class HanaDeployer {
@@ -190,7 +191,22 @@ spec:
190
191
  serviceName ??= cfServiceInstanceName;
191
192
  serviceKeyName ??= cfServiceInstanceKeyName
192
193
  if (!vcapEnv.VCAP_SERVICES || serviceName) {
193
- vcapEnv.VCAP_SERVICES = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
194
+ const base = process.env.VCAP_SERVICES ? JSON.parse(process.env.VCAP_SERVICES) : (vcapEnv.VCAP_SERVICES ?? {});
195
+ const hanas = Array.isArray(base.hana) ? base.hana : [];
196
+ let hanaTagged = hanas.filter(s => s?.name === cfServiceInstanceName || (Array.isArray(s?.tags) && s.tags.includes('hana')));
197
+ if (hanaTagged.length === 0) {
198
+ const entry = { name: cfServiceInstanceName, label: 'hana', tags: ['hana'], credentials: {} };
199
+ hanas.push(entry);
200
+ hanaTagged = [entry];
201
+ }
202
+ for (const entry of hanaTagged) {
203
+ entry.name ??= cfServiceInstanceName;
204
+ entry.tags = Array.from(new Set([...entry.tags || [], 'hana']));
205
+ entry.credentials = { ...(entry.credentials || {}), ...serviceKey };
206
+ }
207
+ base.hana = hanas;
208
+ vcapEnv.VCAP_SERVICES = base;
209
+ process.env.VCAP_SERVICES = JSON.stringify(base);
194
210
  }
195
211
 
196
212
  if (!hasVCAPEnv) {
@@ -229,7 +245,6 @@ spec:
229
245
  return buildResult;
230
246
  }
231
247
 
232
-
233
248
  async _getOrCreateCFService(currentModelFolder, serviceName, serviceKeyName, tunnelAddress) {
234
249
 
235
250
  // get from param
@@ -297,19 +312,6 @@ spec:
297
312
  return buildResult[0].result;
298
313
  }
299
314
 
300
- _getVCAPServicesEntry(serviceInstanceName, serviceKey) {
301
- return {
302
- hana: [
303
- {
304
- name: serviceInstanceName,
305
- tags: ['hana'],
306
- credentials: serviceKey
307
- }
308
- ]
309
- };
310
- }
311
-
312
-
313
315
  _injectTunnelAddress(credentials, tunnelAddress) {
314
316
  if (!/\w+:\d+/.test(tunnelAddress)) {
315
317
  throw `Invalid tunnel address '${tunnelAddress}' - must be in form 'host:port'`;
@@ -10,7 +10,7 @@ let versionInfo = require('../../../package.json').version;
10
10
  const { warn } = require('../../util/term');
11
11
 
12
12
  const edmxncdsdatatype = {
13
- "Edm.String": "cds.LargeString",
13
+ "Edm.String": "cds.String",
14
14
  "Edm.Boolean": "cds.Boolean",
15
15
  "Edm.Int16": "cds.Integer",
16
16
  "Edm.Int32": "cds.Integer",
@@ -592,7 +592,7 @@ function _getServiceEntityProperty(
592
592
  }
593
593
 
594
594
  if (defaultValue !== -1 && defaultValue) {
595
- propertyJson = propertyJson + ', \n"default": {\n"val": "' + defaultValue + '"\n}';
595
+ propertyJson = propertyJson + ', \n"default": {\n"val": ' + JSON.stringify(defaultValue) + '\n}';
596
596
  }
597
597
 
598
598
  if (propertyName)
@@ -33,7 +33,7 @@ const edmToCdsTypeMapping = {
33
33
  'Edm.Int16': 'cds.Integer',
34
34
  'Edm.Int32': 'cds.Integer',
35
35
  'Edm.Int64': 'cds.Integer64',
36
- 'Edm.String': 'cds.LargeString',
36
+ 'Edm.String': 'cds.String',
37
37
  'Edm.TimeOfDay': 'cds.Time',
38
38
  };
39
39
 
@@ -261,7 +261,26 @@ function _captureAnnotationInCSN(targetString, annotationObject, csnDefsTargetOb
261
261
  annotationObject = { "=": annotationObject.$Path };
262
262
  }
263
263
 
264
- csnDefsTargetObject[targetString] = annotationObject;
264
+ /**
265
+ * Annotations need to be flattened in CSN. E.g.
266
+ *
267
+ * @Core.ContentDisposition : {
268
+ * Type : 'inline',
269
+ * Filename: typedImageFileName
270
+ * }
271
+ *
272
+ * is represented as:
273
+ *
274
+ * @Core.ContentDisposition.Type : 'inline',
275
+ * @Core.ContentDisposition.Filename : {'=': 'typedImageFileName'},
276
+ */
277
+ if (annotationObject instanceof Object && !(annotationObject instanceof Array) && !(Object.keys(annotationObject).length === 1 && (Object.keys(annotationObject)[0] === '=' || Object.keys(annotationObject)[0] === 'xpr' || Object.keys(annotationObject)[0] === '#'))) {
278
+ Object.keys(annotationObject).forEach(innerTargetString => {
279
+ _captureAnnotationInCSN(targetString + '.' + innerTargetString, annotationObject[innerTargetString], csnDefsTargetObject);
280
+ })
281
+ } else {
282
+ csnDefsTargetObject[targetString] = annotationObject;
283
+ }
265
284
  }
266
285
 
267
286
  function _replaceSpecialCharacters(text) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "dependencies": {
3
- "@sap-cloud-sdk/connectivity": "^3",
4
- "@sap-cloud-sdk/http-client": "^3",
5
- "@sap-cloud-sdk/resilience": "^3"
3
+ "@sap-cloud-sdk/connectivity": "^4",
4
+ "@sap-cloud-sdk/http-client": "^4",
5
+ "@sap-cloud-sdk/resilience": "^4"
6
6
  }
7
- }
7
+ }
@@ -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.3.0',
8
+ MAVEN_ARCHETYPE_VERSION: '4.4.1',
9
9
 
10
10
  OPTIONS: Object.freeze({
11
11
  COMPLETION: 'completion',
@@ -54,8 +54,11 @@ module.exports = {
54
54
  CONNECTIVITY: 'connectivity',
55
55
  DESTINATION: 'destination',
56
56
  HTML5_REPO: 'html5-repo',
57
+ APP_FRONTEND: 'app-frontend',
57
58
  PORTAL: 'portal',
58
59
  APPLICATION_LOGGING: 'application-logging',
60
+ CLOUD_LOGGING: 'cloud-logging',
61
+ TELEMETRY: 'telemetry',
59
62
  AUDIT_LOGGING: 'audit-logging',
60
63
  NOTIFICATIONS: 'notifications',
61
64
  WORKZONE: 'workzone',
package/lib/init/index.js CHANGED
@@ -161,9 +161,7 @@ module.exports = class CDSGenerator {
161
161
  if (!cds.cli.options.dry) console.log(`adding ${term.bold(facet)}`)
162
162
  const template = this.templateList[i]
163
163
  await template.run()
164
- await template.combine()
165
164
  }
166
-
167
165
  DEBUG && console.log()
168
166
  DEBUG && console.log('plugins active for production:')
169
167
  const fromDk = readdirSync(join(__dirname, './template'))
@@ -176,7 +174,7 @@ module.exports = class CDSGenerator {
176
174
  for (const plugin of sorted) {
177
175
  const Plugin = registered.get(plugin) ?? require('./template/' + plugin)
178
176
  const template = new Plugin()
179
- const inProduction = Plugin.hasInProduction(env4('production')) || cds.cli.argv.includes(plugin) || cds.cli.options.add?.has(plugin)
177
+ const inProduction = Plugin.hasInProduction(env4('production')) || cds.cli.argv[0]?.includes(plugin) || cds.cli.options.add?.has(plugin)
180
178
  DEBUG && console.log(inProduction ? '✅' : '❌', plugin)
181
179
  if (inProduction) await template.combine()
182
180
  }
@@ -233,21 +231,24 @@ module.exports = class CDSGenerator {
233
231
  const templates = new Map
234
232
  for (let plugin of plugins) {
235
233
  // Compat
234
+ if (plugin === 'appfront') plugin = 'app-front'
236
235
  if (plugin === 'helm' && cds.cli.options['internal-unified-runtime-charts']) {
237
236
  plugin = 'helm-unified-runtime'
238
- cds.cli.options.add.delete('helm')
239
- cds.cli.options.add.add('helm-unified-runtime')
237
+ cds.cli.options.add?.delete('helm')
238
+ cds.cli.options.add?.add('helm-unified-runtime')
240
239
  }
241
240
  if (plugin === 'sample-tiny') plugin = 'tiny-sample'
242
241
  if (plugin in { 'kibana-logging': 1, 'kibana': 1 }) plugin = 'application-logging'
243
242
  if (plugin === 'postgresql') plugin = 'postgres'
244
243
  if (plugin === 'gha') plugin = 'github-actions'
244
+ if (plugin === 'app-front') plugin = 'app-frontend'
245
245
 
246
246
  if (!templates.has(plugin)) {
247
247
  const template = await this._createTemplate(plugin)
248
248
  if (await template.canRun()) {
249
249
  templates.set(plugin, template)
250
250
  const dependencies = await template.requires()
251
+ dependencies?.forEach(d => cds.cli.options.add?.add(d))
251
252
  dependencies?.forEach(d => plugins.add(d))
252
253
  } else {
253
254
  throw 'cannot run plugin \'' + plugin + '\'' + (DEBUG ? 'Does not satisfy the canRun() function:\n ' + template.canRun : '')
package/lib/init/mvn.js CHANGED
@@ -21,8 +21,9 @@ module.exports.add = async function (feature, featureOptions={}) {
21
21
  }
22
22
  params.set(`-Dfeature`, feature)
23
23
 
24
- const profile = cds.cli.options.for
25
- params.set(`-Dprofile`, profile === 'production' ? 'cloud' : profile ?? 'default')
24
+ const profile = cds.cli.options.for === 'production' ? 'cloud' : cds.cli.options.for ?? 'default'
25
+ params.set(`-Dprofile`, profile)
26
+ if (feature === 'mtx' && profile === 'default') params.set('-Dprofile', 'with-mtx')
26
27
 
27
28
  Object.entries(featureOptions).forEach(([k,v]) => params.set(`-Doptions.${k}`, v))
28
29
 
@@ -60,58 +60,6 @@ module.exports = new class ProjectReader {
60
60
  }
61
61
  }
62
62
 
63
- /**
64
- * @typedef {Object} ProjectConfig
65
- * @property {string} appVersion The version of the application.
66
- * @property {string} appName The name of the application.
67
- * @property {string} appId The ID of the application.
68
- * @property {string} appDescription The description of the application.
69
- * @property {'java' | 'nodejs'} language The programming language of the project.
70
- * @property {string} srvPath The path to the server module.
71
- * @property {string} archiveName The name of the archive.
72
- * @property {Object} db Database configuration.
73
- * @property {string} configFile The name of the configuration file. This is typically the `package.json` for Node.js and `.cdsrc.json` for Java.
74
- * @property {string} appPath The path to the app folder.
75
- * @property {string} approuterPath The path to the approuter folder.
76
- * @property {boolean} hasUIEmbedded Indicates if the project uses an embedded UI.
77
- * @property {boolean} hasUIModule Indicates if the project is using a modular UI.
78
- * @property {boolean} hasUI Indicates if the project has a UI.
79
- * @property {boolean} hasUI5 Indicates if the project is a UI5 project.
80
- * @property {boolean} isUI5 Indicates if the project is a UI5 project.
81
- * @property {boolean} isJava Indicates if the project is using Java.
82
- * @property {boolean} isNodejs Indicates if the project is using Node.js.
83
- * @property {boolean} isESM Indicates if the project is an ESM project (type 'module')
84
- * @property {boolean} hasEsmStyle Indicates if code supports ESM-style imports and exports. This is true for ESM and Typescript projects.
85
- * @property {boolean} isTypescript Indicates if the project is a Typescript project.
86
- * @property {boolean} hasTyper Indicates if code supports CDS typer.
87
- * @property {boolean} hasHelmUnifiedRuntime Indicates if the project uses Unified Runtime Helm deployment.
88
- * @property {boolean} hasHelm Indicates if the project uses CAP Helm deployment.
89
- * @property {boolean} hasMta Indicates if the project uses MTA deployment.
90
- * @property {boolean} hasConnectivity Indicates if the project uses connectivity.
91
- * @property {boolean} hasMultitenancy Indicates if the project is multitenant.
92
- * @property {boolean} hasToggles Indicates if the project uses feature toggles.
93
- * @property {boolean} hasExtensibility Indicates if the project is extensible.
94
- * @property {boolean} hasXsuaa Indicates if the project uses XSUAA.
95
- * @property {boolean} hasHana Indicates if the project uses HANA.
96
- * @property {boolean} hasPostgres Indicates if the project uses PostgreSQL.
97
- * @property {boolean} hasEnterpriseMessaging Indicates if the project uses SAP BTP Event Mesh.
98
- * @property {boolean} hasAttachments Indicates if the project uses SAP BTP Object Store Service.
99
- * @property {boolean} hasApprouter Indicates if the project uses the SAP Application Router.
100
- * @property {boolean} hasHtml5Repo Indicates if the project uses the SAP BTP HTML5 Application Repository.
101
- * @property {boolean} hasPortal Indicates if the project uses the SAP Cloud Portal service.
102
- * @property {boolean} hasWorkzoneStandard Indicates if the project uses the SAP Build Work Zone, Standard Edition service.
103
- * @property {boolean} hasDestination Indicates if the project uses the SAP BTP Destination service.
104
- * @property {boolean} hasMTXRoute Indicates if the approuter should have a multitenant route.
105
- * @property {boolean} hasMTXRouteJava Indicates if the approuter should have a multitenant subscription route (java).
106
- * @property {boolean} hasMalwareScanner Indicates if the project uses the SAP Malware Scanning service.
107
- * @property {boolean} hasDynatrace Indicates if the project uses Dynatrace.
108
- * @property {boolean} hasCloudLogging Indicates if the project uses Cloud Logging.
109
- * @property {String} extendsApp Indicates which project is extended if any.
110
- * @property {String} githubSlug GitHub org/repo.
111
- * @property {boolean} isMonorepoMicroservice Indicates if the project is a monorepo microservice.
112
- * @property {String} npmWorkspaceRoot The root path of the monorepo workspace.
113
- */
114
-
115
63
  /**
116
64
  * Reads the project configuration and constructs a project object.
117
65
  * This function dynamically resolves project properties and returns an object with these properties.
@@ -128,7 +76,7 @@ module.exports = new class ProjectReader {
128
76
  const _isESM = () => pkgJson().type === 'module'
129
77
  const _isTypescript = () => !!pkgJson().dependencies?.typescript || !!pkgJson().devDependencies?.typescript
130
78
  const _hasTyper = () => !!pkgJson().dependencies?.['@cap-js/cds-typer'] || !!pkgJson().devDependencies?.['@cap-js/cds-typer']
131
- const _inProd = plugin => require(`./template/${plugin}`).hasInProduction(env) || cds.cli.argv.includes(plugin) || cds.cli.options.add?.has(plugin)
79
+ const _inProd = plugin => require(`./template/${plugin}`).hasInProduction(env) || cds.cli.argv[0]?.includes(plugin) || cds.cli.options.add?.has(plugin)
132
80
  const _uiEmbedded = () => exists(join(env.folders.app, 'xs-app.json'))
133
81
  const _uiModule = () => !_uiEmbedded() && isdir(env.folders.app) && fs.readdirSync(resolve(cds.root, env.folders.app)).length > 0
134
82
  const _isJava = () => this.isJava
@@ -141,6 +89,7 @@ module.exports = new class ProjectReader {
141
89
  devDependencies: () => pkgJson().devDependencies,
142
90
  has: x => _inProd(x),
143
91
  profile: () => cds.cli.options?.for,
92
+ profileSpecified: () => !!cds.cli.options.for,
144
93
  language: () => _isJava() ? 'java' : 'nodejs',
145
94
  isJava: () => _isJava(),
146
95
  isNodejs: () => !_isJava(),
@@ -222,8 +171,15 @@ module.exports = new class ProjectReader {
222
171
  const defined = {}
223
172
  const _get = property => { const p = property
224
173
  if (p in reserved) return reserved[p]()
225
- for (const prefix of ['has', 'is']) if (p.startsWith(prefix) && p.length > prefix.length) {
226
- return _inProd(p.slice(prefix.length).replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase())
174
+ for (const prefix of ['has', 'is', 'add']) if (p.startsWith(prefix) && p.length > prefix.length) {
175
+ const facet = p.slice(prefix.length).replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
176
+ const inProd = _inProd(facet)
177
+ if (prefix === 'add') {
178
+ const adding = facet => cds.cli.argv[0]?.includes(facet) || cds.cli.options.add?.has(facet)
179
+ const caller = new Error().stack.split('\n')[3].replace(/\\/g, '/').match(/lib\/init\/template\/([^/]+)\/index\.js/)[1]
180
+ return inProd && (adding(facet) || adding(caller))
181
+ }
182
+ return inProd
227
183
  }
228
184
  }
229
185
  return new Proxy({}, {
@@ -287,3 +243,57 @@ function _normalizeSpringBootCfg(obj) {
287
243
  })
288
244
  return obj
289
245
  }
246
+
247
+ /* Types */
248
+
249
+ /**
250
+ * @typedef {Object} ProjectConfig
251
+ * @property {string} appVersion The version of the application.
252
+ * @property {string} appName The name of the application.
253
+ * @property {string} appId The ID of the application.
254
+ * @property {string} appDescription The description of the application.
255
+ * @property {'java' | 'nodejs'} language The programming language of the project.
256
+ * @property {string} srvPath The path to the server module.
257
+ * @property {string} archiveName The name of the archive.
258
+ * @property {Object} db Database configuration.
259
+ * @property {string} configFile The name of the configuration file. This is typically the `package.json` for Node.js and `.cdsrc.json` for Java.
260
+ * @property {string} appPath The path to the app folder.
261
+ * @property {string} approuterPath The path to the approuter folder.
262
+ * @property {boolean} hasUIEmbedded Indicates if the project uses an embedded UI.
263
+ * @property {boolean} hasUIModule Indicates if the project is using a modular UI.
264
+ * @property {boolean} hasUI Indicates if the project has a UI.
265
+ * @property {boolean} hasUI5 Indicates if the project is a UI5 project.
266
+ * @property {boolean} isUI5 Indicates if the project is a UI5 project.
267
+ * @property {boolean} isJava Indicates if the project is using Java.
268
+ * @property {boolean} isNodejs Indicates if the project is using Node.js.
269
+ * @property {boolean} isESM Indicates if the project is an ESM project (type 'module')
270
+ * @property {boolean} hasEsmStyle Indicates if code supports ESM-style imports and exports. This is true for ESM and Typescript projects.
271
+ * @property {boolean} isTypescript Indicates if the project is a Typescript project.
272
+ * @property {boolean} hasTyper Indicates if code supports CDS typer.
273
+ * @property {boolean} hasHelmUnifiedRuntime Indicates if the project uses Unified Runtime Helm deployment.
274
+ * @property {boolean} hasHelm Indicates if the project uses CAP Helm deployment.
275
+ * @property {boolean} hasMta Indicates if the project uses MTA deployment.
276
+ * @property {boolean} hasConnectivity Indicates if the project uses connectivity.
277
+ * @property {boolean} hasMultitenancy Indicates if the project is multitenant.
278
+ * @property {boolean} hasToggles Indicates if the project uses feature toggles.
279
+ * @property {boolean} hasExtensibility Indicates if the project is extensible.
280
+ * @property {boolean} hasXsuaa Indicates if the project uses XSUAA.
281
+ * @property {boolean} hasHana Indicates if the project uses HANA.
282
+ * @property {boolean} hasPostgres Indicates if the project uses PostgreSQL.
283
+ * @property {boolean} hasEnterpriseMessaging Indicates if the project uses SAP BTP Event Mesh.
284
+ * @property {boolean} hasAttachments Indicates if the project uses SAP BTP Object Store Service.
285
+ * @property {boolean} hasApprouter Indicates if the project uses the SAP Application Router.
286
+ * @property {boolean} hasHtml5Repo Indicates if the project uses the SAP BTP HTML5 Application Repository.
287
+ * @property {boolean} hasPortal Indicates if the project uses the SAP Cloud Portal service.
288
+ * @property {boolean} hasWorkzoneStandard Indicates if the project uses the SAP Build Work Zone, Standard Edition service.
289
+ * @property {boolean} hasDestination Indicates if the project uses the SAP BTP Destination service.
290
+ * @property {boolean} hasMTXRoute Indicates if the approuter should have a multitenant route.
291
+ * @property {boolean} hasMTXRouteJava Indicates if the approuter should have a multitenant subscription route (java).
292
+ * @property {boolean} hasMalwareScanner Indicates if the project uses the SAP Malware Scanning service.
293
+ * @property {boolean} hasDynatrace Indicates if the project uses Dynatrace.
294
+ * @property {boolean} hasCloudLogging Indicates if the project uses Cloud Logging.
295
+ * @property {String} extendsApp Indicates which project is extended if any.
296
+ * @property {String} githubSlug GitHub org/repo.
297
+ * @property {boolean} isMonorepoMicroservice Indicates if the project is a monorepo microservice.
298
+ * @property {String} npmWorkspaceRoot The root path of the monorepo workspace.
299
+ */
@@ -10,6 +10,11 @@ const srv4 = isJava ? srvJava4 : srvNode4
10
10
 
11
11
  const approuter = { in: 'modules', where: { type: 'approuter.nodejs' } }
12
12
 
13
+ const appFront = {
14
+ in: 'resources',
15
+ where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'app-front' }
16
+ }
17
+
13
18
  const xsuaa = {
14
19
  in: 'resources',
15
20
  where: { type: 'org.cloudfoundry.managed-service', 'parameters.service': 'xsuaa', 'parameters.service-plan': 'application' }
@@ -233,6 +238,7 @@ module.exports = {
233
238
  applicationLogging,
234
239
  approuterMTXRoute,
235
240
  approuterMTXRouteJava,
241
+ appFront,
236
242
  html5RepoHost,
237
243
  html5Runtime,
238
244
  appDeployer,
@@ -18,7 +18,7 @@ applications:
18
18
  buildpack: nodejs_buildpack
19
19
  command: (npm start && echo "This application may now be stopped to free resources." || echo "AMS Policy Deployment unsuccessful.") && sleep infinity
20
20
  services:
21
- - name: {{appName}}-auth
21
+ - name: {{appName}}-ias
22
22
  parameters:
23
23
  credential-type: X509_GENERATED
24
24
  app-identifier: ams-policy-deployer
@@ -29,7 +29,7 @@ modules:
29
29
  command: npm start
30
30
  memory: 512M
31
31
  requires:
32
- - name: {{appName}}-auth
32
+ - name: {{appName}}-ias
33
33
  parameters:
34
34
  config:
35
35
  credential-type: X509_GENERATED
@@ -14,7 +14,8 @@ module.exports = class AmsTemplate extends require('../../plugin') {
14
14
  }
15
15
 
16
16
  static hasInProduction(env) {
17
- return env.requires?.auth?.ams || readProject().devDependencies?.['@sap/ams']
17
+ const { dependencies, devDependencies } = readProject()
18
+ return env.requires?.auth?.ams || devDependencies?.['@sap/ams-dev'] || dependencies?.['@sap/ams']
18
19
  }
19
20
 
20
21
  async run() {
@@ -27,21 +28,21 @@ module.exports = class AmsTemplate extends require('../../plugin') {
27
28
 
28
29
  async combine() {
29
30
  const project = readProject()
30
- const { hasMta, hasCfManifest, hasHelm, hasHelmUnifiedRuntime, hasContainerize } = project
31
+ const { addMta, addCfManifest, addHelm, addHelmUnifiedRuntime, addContainerize } = project
31
32
 
32
- if (hasMta) {
33
+ if (addMta) {
33
34
  await this.#combineWithMta(project)
34
35
  }
35
36
 
36
- if (hasCfManifest) {
37
+ if (addCfManifest) {
37
38
  await this.#combineWithCfManifest(project)
38
39
  }
39
40
 
40
- if (hasHelm || hasHelmUnifiedRuntime) {
41
+ if (addHelm || addHelmUnifiedRuntime) {
41
42
  await this.#combineWithHelm(project)
42
43
  }
43
44
 
44
- if (hasContainerize) {
45
+ if (addContainerize) {
45
46
  await this.#combineWithContainerize(project)
46
47
  }
47
48
  }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "{{app}}"{{#hasUI5}},
3
+ "version": "1.0.0",
4
+ "main": "webapp/index.html",
5
+ "scripts": {
6
+ "build": "ui5 build preload --clean-dest --config ui5-deploy.yaml",
7
+ "build-local": "ui5 build preload --clean-dest",
8
+ "start": "ui5 serve"
9
+ },
10
+ "devDependencies": {
11
+ "@ui5/cli": "^4",
12
+ "ui5-task-zipper": "^3"
13
+ },
14
+ "ui5": {
15
+ "dependencies": [
16
+ "ui5-task-zipper"
17
+ ]
18
+ }
19
+ {{/hasUI5}}
20
+ }
@@ -0,0 +1,34 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{appName}} - {{app}}</title>
7
+ <style>
8
+ html, body, body > div, #container, #container-uiarea {
9
+ height: 100%;
10
+ }
11
+ </style>
12
+ <script
13
+ id="sap-ui-bootstrap"
14
+ src="https://sapui5.hana.ondemand.com/1.140.0/resources/sap-ui-core.js"
15
+ data-sap-ui-theme="sap_horizon"
16
+ data-sap-ui-resourceroots='{
17
+ "{{appName}}.{{app}}": "./"
18
+ }'
19
+ data-sap-ui-oninit="module:sap/ui/core/ComponentSupport"
20
+ data-sap-ui-compatVersion="edge"
21
+ data-sap-ui-async="true"
22
+ data-sap-ui-frameOptions="trusted"
23
+ ></script>
24
+ </head>
25
+ <body class="sapUiBody sapUiSizeCompact" id="content">
26
+ <div
27
+ data-sap-ui-component
28
+ data-name="{{appName}}.{{app}}"
29
+ data-id="container"
30
+ data-settings='{"id" : "{{appName}}.{{app}}" }'
31
+ data-handle-validation="true"
32
+ ></div>
33
+ </body>
34
+ </html>
@@ -0,0 +1,13 @@
1
+ {
2
+ "_version": "1.49.0",
3
+ "sap.app": {
4
+ "id": "{{appName}}",
5
+ "applicationVersion": {
6
+ "version": "1.0.0"
7
+ },
8
+ "type": "application",
9
+ "title": "{{appTitle}}",
10
+ "description": "{{appDescription}}",
11
+ "i18n": "i18n/i18n.properties"
12
+ }
13
+ }