@sap/cds-dk 7.6.1 → 7.7.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 (39) hide show
  1. package/bin/cds.js +1 -1
  2. package/bin/version.js +21 -20
  3. package/bin/watch.js +6 -0
  4. package/lib/build/buildTaskEngine.js +6 -4
  5. package/lib/build/buildTaskProviderFactory.js +5 -0
  6. package/lib/build/provider/edmxBuildPlugin.js +1 -1
  7. package/lib/build/provider/internalBuildTaskProvider.js +12 -10
  8. package/lib/build/provider/mtx/index.js +2 -2
  9. package/lib/build/provider/mtx/resourcesTarBuilder.js +1 -1
  10. package/lib/build/provider/mtx-extension/extensionCompilation.js +50 -0
  11. package/lib/build/provider/mtx-extension/index.js +50 -48
  12. package/lib/build/provider/mtx-sidecar/index.js +3 -3
  13. package/lib/build/util.js +9 -4
  14. package/lib/env/schema.js +1 -0
  15. package/lib/env/schemas/cds-rc.js +54 -1
  16. package/lib/import/asyncapi/parserContext.js +6 -0
  17. package/lib/init/add.js +2 -0
  18. package/lib/init/constants.js +1 -1
  19. package/lib/init/merge.js +31 -13
  20. package/lib/init/projectReader.js +9 -1
  21. package/lib/init/registries/mta.js +19 -1
  22. package/lib/init/template/approuter/files/mta.yaml.hbs +1 -1
  23. package/lib/init/template/approuter/index.js +5 -5
  24. package/lib/init/template/connectivity/index.js +3 -4
  25. package/lib/init/template/html5-repo/files/app-package.json.hbs +16 -0
  26. package/lib/init/template/html5-repo/files/mta.yaml.hbs +83 -0
  27. package/lib/init/template/html5-repo/files/package.json.hbs +17 -0
  28. package/lib/init/template/html5-repo/files/ui5.yaml.hbs +33 -0
  29. package/lib/init/template/html5-repo/files/xs-app.json.hbs +19 -0
  30. package/lib/init/template/html5-repo/index.js +56 -12
  31. package/lib/init/template/mta/files/mta.yaml.hbs +1 -0
  32. package/lib/init/template/mta/index.js +5 -0
  33. package/lib/init/template/nodejs/files/.vscode/extensions.json +1 -2
  34. package/lib/init/template/nodejs/files/README.md +1 -1
  35. package/lib/init/template/sample/files/app/index.html +8 -6
  36. package/lib/init/template/sample/index.js +12 -2
  37. package/lib/util/cf.js +1 -0
  38. package/npm-shrinkwrap.json +302 -276
  39. package/package.json +1 -1
package/lib/init/merge.js CHANGED
@@ -141,7 +141,7 @@ async function _mergeYAML(into, from, project, semantics = {}) {
141
141
  throw new Error(`Target YAML doesn't exist`)
142
142
  }
143
143
 
144
- const entryMap = new Map, templateEntryMap = new Map()
144
+ const entryMap = new Map, templateEntryMap = new Map
145
145
 
146
146
  const { additions, overwrites, deletions, relationships } = semantics
147
147
  additions?.forEach(entry => {
@@ -236,7 +236,7 @@ async function _mergeYAML(into, from, project, semantics = {}) {
236
236
 
237
237
  if (!templateCollection?.items) return
238
238
 
239
- templateCollection.items.forEach((templateNode, templateIndex) => {
239
+ templateCollection.items?.forEach((templateNode, templateIndex) => {
240
240
  if (YAML.isScalar(templateNode)) {
241
241
  if (!collection.items.map(item => item.value).includes(templateNode.value)) {
242
242
  collection.add(templateNode)
@@ -413,20 +413,38 @@ async function removeFromYAMLArray(name, keyPath, aliasToRemove) {
413
413
  await writeYAML(name, yaml)
414
414
  }
415
415
 
416
+
416
417
  const path = require('path')
418
+
419
+ /**
420
+ * @param {...(string | Object | YAML.Document)} src The source to merge from.
421
+ * @returns {{
422
+ * into: (dst: (string | Object | YAML.Document), options?: {project?: Object, semantics?: MergingSemantics}) => Promise<any>
423
+ * }} An object with an 'into' method to merge the sources into the specified destination.
424
+ */
417
425
  const merge = (...src) => {
418
- return { into: (dst, { with: withProject, project, ...semantics } = {}) => {
419
- if (Object.keys(semantics).length === 0) semantics = undefined
420
- project ??= withProject // to allow { with: project } for a more fluent API
421
- const isYAML = typeof dst === 'string' && ['.yaml', '.yml', '.yaml.hbs', '.yml.hbs'].some(ext => dst.endsWith(ext))
422
- const _merge = isYAML ? _mergeYAML : _mergeJSON
423
- const target = typeof dst === 'string' ? path.resolve(cds.root, dst) : dst
424
- if (typeof src[src.length-1] === 'string') {
425
- return _merge(target, path.join(...src), project, semantics)
426
- } else {
427
- return _merge(target, ...src, project, semantics)
426
+ return {
427
+ /**
428
+ * Merges sources into the specified destination.
429
+ * @param {(string | Object | YAML.Document)} dst File path or object to merge into.
430
+ * @param {Object} [options] Merging options.
431
+ * @param {Object} [options.project] Project descriptor used for Mustache replacements.
432
+ * @param {MergingSemantics} [options.semantics] Merging algorithm semantics.
433
+ * @returns {Promise} A promise resolving to the merged object.
434
+ */
435
+ into: (dst, { with: withProject, project, ...semantics } = {}) => {
436
+ if (Object.keys(semantics).length === 0) semantics = undefined
437
+ project ??= withProject // to allow { with: project } for a more fluent API
438
+ const isYAML = typeof dst === 'string' && ['.yaml', '.yml', '.yaml.hbs', '.yml.hbs'].some(ext => dst.endsWith(ext))
439
+ const _merge = isYAML ? _mergeYAML : _mergeJSON
440
+ const target = typeof dst === 'string' ? path.resolve(cds.root, dst) : dst
441
+ if (typeof src[src.length-1] === 'string') {
442
+ return _merge(target, path.join(...src), project, semantics)
443
+ } else {
444
+ return _merge(target, ...src, project, semantics)
445
+ }
428
446
  }
429
- }}
447
+ }
430
448
  }
431
449
 
432
450
  module.exports = {
@@ -1,6 +1,6 @@
1
1
  const cds = require('../cds')
2
2
  const { join, resolve, basename } = require('path')
3
- const { fs, exists, isdir } = cds.utils
3
+ const { fs, exists, isdir, path } = cds.utils
4
4
  const { parseXml } = require('./xml')
5
5
  const { REGEX_JAVA_VERSION } = require('./constants')
6
6
  const DEBUG = cds.debug('cli')
@@ -59,6 +59,7 @@ module.exports = new class ProjectReader {
59
59
  * @property {Object} db Database configuration.
60
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
61
  * @property {string} appPath The path to the app folder.
62
+ * @property {string} approuterPath The path to the approuter folder.
62
63
  * @property {boolean} hasUIEmbedded Indicates if the project uses an embedded UI.
63
64
  * @property {boolean} hasUIModule Indicates if the project is using a modular UI.
64
65
  * @property {boolean} hasUI Indicates if the project has a UI.
@@ -112,8 +113,15 @@ module.exports = new class ProjectReader {
112
113
  return { folder, name, path }
113
114
  },
114
115
  configFile: () => exists('pom.xml') ? '.cdsrc.json' : 'package.json',
116
+ apps: () => (fs.readdirSync(path.resolve(cds.root, cds.env.folders.app))).filter(e =>
117
+ isdir(join(cds.root, cds.env.folders.app, e)) && e !== 'appconfig' && e !== '_i18n' && e !== 'router'
118
+ ).map(app => ({ app })),
119
+ appUIPaths: () => (fs.readdirSync(path.resolve(cds.root, cds.env.folders.app))).filter(e =>
120
+ isdir(join(cds.root, cds.env.folders.app, e)) && e !== 'appconfig' && e !== '_i18n' && e !== 'router'
121
+ ),
115
122
  appPath: () => env.folders.app,
116
123
  appVersion: () => appVersion,
124
+ approuterPath: () => join(env.folders.app, 'router'),
117
125
  appName: () => appName,
118
126
  cleanedAppName: () => appName.replaceAll('_', '-'),
119
127
  appId: () => appId,
@@ -122,6 +122,21 @@ const approuterExtensibility = {
122
122
  where: { source: '^/-/cds/.*', destination: 'mtx-api', authenticationType: 'none' }
123
123
  }
124
124
 
125
+ const html5RepoHost = {
126
+ in: 'resources',
127
+ where: { 'parameters.service': 'html5-apps-repo', 'parameters.service-plan': 'app-host' }
128
+ }
129
+
130
+ const html5Runtime = {
131
+ in: 'resources',
132
+ where: { 'parameters.service': 'html5-apps-repo', 'parameters.service-plan': 'app-runtime' }
133
+ }
134
+
135
+ const appContent = {
136
+ in: 'modules',
137
+ where: { type: 'com.sap.application.content' }
138
+ }
139
+
125
140
  module.exports = {
126
141
  srv4,
127
142
  approuter,
@@ -147,5 +162,8 @@ module.exports = {
147
162
  providedJavaApprouterApi4,
148
163
  providedJavaApprouterApi,
149
164
  applicationLogging,
150
- approuterExtensibility
165
+ approuterExtensibility,
166
+ html5RepoHost,
167
+ html5Runtime,
168
+ appContent
151
169
  }
@@ -19,7 +19,7 @@ modules:
19
19
 
20
20
  - name: {{appName}}
21
21
  type: approuter.nodejs
22
- path: {{& appPath}}
22
+ path: {{& approuterPath}}
23
23
  parameters:
24
24
  keep-existing-routes: true
25
25
  disk-quota: 256M
@@ -22,16 +22,16 @@ module.exports = class ApprouterTemplate extends require('../../plugin') {
22
22
  }
23
23
 
24
24
  async run() {
25
- const { appPath } = readProject()
26
- const appPackageJSONPath = join(appPath, 'package.json')
25
+ const { approuterPath } = readProject()
26
+ const appPackageJSONPath = join(approuterPath, 'package.json')
27
27
  await merge(__dirname, 'files/package.json').into(appPackageJSONPath)
28
28
  await sort(appPackageJSONPath, 'dependencies')
29
- await merge(__dirname, 'files/default-env.json').into(appPath + '/default-env.json')
29
+ await merge(__dirname, 'files/default-env.json').into(approuterPath + '/default-env.json')
30
30
  }
31
31
 
32
32
  async combine() {
33
33
  const project = readProject(this.options)
34
- const { isNodejs, isJava, hasExtensibility, hasMultitenancy, hasMta, hasHelm, hasHelmUnifiedRuntime, srvPath, appPath } = project
34
+ const { isNodejs, isJava, hasExtensibility, hasMultitenancy, hasMta, hasHelm, hasHelmUnifiedRuntime, srvPath, approuterPath } = project
35
35
 
36
36
  if (hasMta) {
37
37
  const srv = srv4(srvPath)
@@ -72,7 +72,7 @@ module.exports = class ApprouterTemplate extends require('../../plugin') {
72
72
  }
73
73
  }
74
74
 
75
- const xsAppPath = join(appPath, 'xs-app.json')
75
+ const xsAppPath = join(approuterPath, 'xs-app.json')
76
76
  const additions = hasExtensibility ? [{ ...approuterExtensibility, at: 0 }] : []
77
77
  await merge(__dirname, 'files/xs-app.json.hbs').into(xsAppPath, { project, additions })
78
78
  }
@@ -12,10 +12,9 @@ module.exports = class ConnectivityTemplate extends require('../../plugin') {
12
12
  }
13
13
 
14
14
  async run() {
15
- // Disable setting cds.requires.connectivity for now.
16
- // const project = readProject()
17
- // const { configFile } = project
18
- // await merge(__dirname, 'files', 'package.json.hbs').into(configFile, { with: project })
15
+ const project = readProject()
16
+ const { configFile } = project
17
+ await merge(__dirname, 'files', 'package.json.hbs').into(configFile, { with: project })
19
18
  }
20
19
 
21
20
  async combine() {
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "{{app}}",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "description": "<description>",
6
+ "main": "webapp/index.html",
7
+ "scripts": {
8
+ "build": "ui5 build preload --clean-dest --include-task=generateCachebusterInfo",
9
+ "start": "ui5 serve"
10
+ },
11
+ "devDependencies": {
12
+ "@ui5/cli": "^3.8.0",
13
+ "ui5-middleware-simpleproxy": "^3.2.10",
14
+ "ui5-task-zipper": "^3.1.1"
15
+ }
16
+ }
@@ -0,0 +1,83 @@
1
+ ---
2
+ modules:
3
+ - name: {{appName}}-srv
4
+ type: {{language}}
5
+ path: {{& srvPath}}
6
+
7
+ - name: {{appName}}
8
+ type: approuter.nodejs
9
+ path: {{& approuterPath}}
10
+ requires:
11
+ - name: {{appName}}-html5-runtime
12
+
13
+ - name: {{appName}}-app-content
14
+ type: com.sap.application.content
15
+ path: {{& appPath}}
16
+ requires:
17
+ - name: {{appName}}-destination
18
+ - name: {{appName}}-html5-repo-host
19
+ parameters:
20
+ content-target: true
21
+ build-parameters:
22
+ build-result: resources
23
+ requires:
24
+ {{#apps}}
25
+ - name: {{appName}}-app-{{app}}
26
+ artifacts:
27
+ - {{app}}.zip
28
+ target-path: resources/
29
+ {{/apps}}
30
+
31
+ {{#apps}}
32
+ - name: {{appName}}-app-{{app}}
33
+ type: html5
34
+ path: {{& appPath}}{{app}}
35
+ build-parameters:
36
+ build-result: dist
37
+ builder: custom
38
+ commands:
39
+ - npm ci
40
+ - npm run build
41
+ supported-platforms: []
42
+ {{/apps}}
43
+
44
+ resources:
45
+ {{#hasApprouter}}
46
+ - name: {{appName}}-html5-runtime
47
+ type: org.cloudfoundry.managed-service
48
+ parameters:
49
+ service: html5-apps-repo
50
+ service-plan: app-runtime
51
+ {{/hasApprouter}}
52
+
53
+ - name: {{appName}}-html5-repo-host
54
+ type: org.cloudfoundry.managed-service
55
+ parameters:
56
+ service: html5-apps-repo
57
+ service-plan: app-host
58
+
59
+ - name: {{appName}}-destination
60
+ type: org.cloudfoundry.managed-service
61
+ parameters:
62
+ service: destination
63
+ service-plan: lite
64
+ config:
65
+ HTML5Runtime_enabled: true
66
+ init_data:
67
+ instance:
68
+ existing_destinations_policy: update
69
+ destinations:
70
+ - Name: {{appName}}-srv
71
+ URL: ~{srv-api/srv-url}
72
+ Authentication: NoAuthentication
73
+ Type: HTTP
74
+ ProxyType: Internet
75
+ HTML5.ForwardAuthToken: true
76
+ HTML5.DynamicDestination: true
77
+ requires:
78
+ - name: srv-api
79
+ group: destinations
80
+ properties:
81
+ name: srv-api # must be used in xs-app.json as well
82
+ url: ~{srv-url}
83
+ forwardAuthToken: true
@@ -0,0 +1,17 @@
1
+ {
2
+ {{#isNodejs}}
3
+ "cds": {
4
+ {{/isNodejs}}
5
+ "requires": {
6
+ {{#profile}}
7
+ "[{{profile}}]": {
8
+ {{/profile}}
9
+ "html5-repo": true
10
+ {{#profile}}
11
+ }
12
+ {{/profile}}
13
+ }
14
+ {{#isNodejs}}
15
+ }
16
+ {{/isNodejs}}
17
+ }
@@ -0,0 +1,33 @@
1
+ ---
2
+ # yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json
3
+ specVersion: "3.0"
4
+ metadata:
5
+ name: sap.fe.cap.{{app}}
6
+ type: application
7
+
8
+ resources:
9
+ configuration:
10
+ propertiesFileSourceEncoding: UTF-8
11
+
12
+ builder:
13
+ resources:
14
+ excludes:
15
+ - "/test/**"
16
+ - "/localService/**"
17
+ customTasks:
18
+ - name: ui5-task-zipper
19
+ afterTask: generateCachebusterInfo
20
+ configuration:
21
+ archiveName: {{app}}
22
+ additionalFiles:
23
+ - xs-app.json
24
+
25
+ server:
26
+ customMiddleware:
27
+ - name: ui5-middleware-simpleproxy
28
+ mountPath: /{{app}}
29
+ afterMiddleware: compression
30
+ configuration:
31
+ baseUri: http://localhost:4004/{{app}}
32
+ username: admin # dummy credentials for local testing
33
+ password: admin # dummy credentials for local testing
@@ -0,0 +1,19 @@
1
+ {
2
+ "welcomeFile": "/index.html",
3
+ "authenticationMethod": "route",
4
+ "routes": [
5
+ {
6
+ "source": "^/{{app}}/(.*)$",
7
+ "target": "/{{app}}/$1",
8
+ "destination": "srv-api",
9
+ "authenticationType": "xsuaa",
10
+ "csrfProtection": false
11
+ },
12
+ {
13
+ "source": "^(.*)$",
14
+ "target": "$1",
15
+ "service": "html5-apps-repo-rt",
16
+ "authenticationType": "xsuaa"
17
+ }
18
+ ]
19
+ }
@@ -1,7 +1,9 @@
1
1
  const { join } = require('path')
2
- const { exists, copy } = require('../../../cds').utils
2
+ const cds = require('../../../cds')
3
+ const { copy } = cds.utils
3
4
  const { readProject } = require('../../projectReader')
4
5
  const { merge } = require('../../merge')
6
+ const { srv4, destination, html5RepoHost, html5Runtime, appContent, approuter } = require('../../registries/mta')
5
7
 
6
8
  module.exports = class Html5RepoTemplate extends require('../../plugin') {
7
9
 
@@ -9,23 +11,65 @@ module.exports = class Html5RepoTemplate extends require('../../plugin') {
9
11
  return ['destination']
10
12
  }
11
13
 
12
- async canRun() {
13
- const project = readProject()
14
- const { hasMta, hasHelm, hasHelmUnifiedRuntime } = project
15
- if (!(hasHelm || hasHelmUnifiedRuntime) && hasMta) {
16
- throw `'cds add html5-repo' is not available for Cloud Foundry yet`
17
- }
18
- return true
14
+ static hasInProduction(env) {
15
+ return !!env.requires['html5-repo']
19
16
  }
20
17
 
21
- static hasInProduction() {
22
- // REVISIT: Should be detectable without helm charts
23
- return exists(join('chart', 'templates', 'html5-apps-deployer-configmap.yaml'))
18
+ async run() {
19
+ const project = readProject()
20
+ const { apps, appPath, configFile } = project
21
+ await merge(__dirname, 'files', 'package.json.hbs').into(configFile, { with: project })
22
+
23
+ await Promise.all(apps.map(async ({app}) => {
24
+ project.app = app
25
+ await merge(__dirname, 'files/ui5.yaml.hbs').into(join(appPath, app, 'ui5.yaml'), { project })
26
+ }))
27
+ await Promise.all(apps.map(async ({app}) => {
28
+ project.app = app
29
+ await merge(__dirname, 'files/app-package.json.hbs').into(join(appPath, app, 'package.json'), { project })
30
+ }))
24
31
  }
25
32
 
26
33
  async combine() {
27
34
  const project = readProject()
28
- const { hasHelm, hasHelmUnifiedRuntime } = project
35
+ const { srvPath, appPath, hasMta, hasHelm, hasHelmUnifiedRuntime, hasApprouter, apps } = project
36
+
37
+ if (hasApprouter) {
38
+ await Promise.all(apps.map(async ({app}) => {
39
+ project.app = app
40
+ await merge(__dirname, 'files/xs-app.json.hbs').into(join(appPath, app, 'xs-app.json'), { project })
41
+ }))
42
+ }
43
+
44
+ if (hasMta) {
45
+ const srv = srv4(srvPath)
46
+ project.apps = apps
47
+ const appModules = project.apps.map(app => ( {
48
+ in: 'modules',
49
+ where: { type: 'html5', path: `${appPath}${app.app}` }
50
+ }))
51
+ const additions = [srv, appContent, destination, html5RepoHost, ...appModules]
52
+ const relationships = [{
53
+ insert: [destination, 'name'],
54
+ into: [appContent, 'requires', 'name'],
55
+ }, {
56
+ insert: [html5RepoHost, 'name'],
57
+ into: [appContent, 'requires', 'name'],
58
+ }]
59
+ if (hasApprouter) {
60
+ console.log('aaddd')
61
+ additions.push(approuter, html5Runtime)
62
+ relationships.push({
63
+ insert: [html5Runtime, 'name'],
64
+ into: [approuter, 'requires', 'name'],
65
+ })
66
+ }
67
+ await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', { with: project,
68
+ additions,
69
+ relationships
70
+ })
71
+ }
72
+
29
73
  if (hasHelm || hasHelmUnifiedRuntime) {
30
74
  await merge(__dirname, 'files/values.yaml.hbs').into('chart/values.yaml', { with: project })
31
75
  await merge({
@@ -10,6 +10,7 @@ build-parameters:
10
10
  before-all:
11
11
  - builder: custom
12
12
  commands:
13
+ - npm ci
13
14
  - npx cds build --production
14
15
  {{/isNodejs}}
15
16
  modules:
@@ -1,6 +1,7 @@
1
1
  const { join } = require('path')
2
2
  const cds = require('../../../cds')
3
3
  const { exists } = cds.utils
4
+ const { bold } = require('../../../util/term')
4
5
  const { copyRenderedYAML } = require('../../../util/fs')
5
6
  const { readProject } = require('../../projectReader')
6
7
  const { merge, sort } = require('../../merge')
@@ -19,4 +20,8 @@ module.exports = class MtaTemplate extends require('../../plugin') {
19
20
  await merge(__dirname, 'files/package.json').into('package.json')
20
21
  await sort('package.json', 'devDependencies')
21
22
  }
23
+
24
+ async finalize() {
25
+ if (!exists('pom.xml')) console.log(`\nRun ${bold('npm update --package-lock-only')} to freeze dependencies.`)
26
+ }
22
27
  }
@@ -9,8 +9,7 @@
9
9
  "esbenp.prettier-vscode",
10
10
  "mechatroner.rainbow-csv",
11
11
  "qwtel.sqlite-viewer",
12
- "humao.rest-client",
13
- "sdras.night-owl"
12
+ "humao.rest-client"
14
13
  ],
15
14
  // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
16
15
  "unwantedRecommendations": [
@@ -15,7 +15,7 @@ File or Folder | Purpose
15
15
 
16
16
  ## Next Steps
17
17
 
18
- - Open a new terminal and run `cds watch`
18
+ - Open a new terminal and run `cds watch`
19
19
  - (in VS Code simply choose _**Terminal** > Run Task > cds watch_)
20
20
  - Start adding content, for example, a [db/schema.cds](db/schema.cds).
21
21
 
@@ -15,12 +15,14 @@
15
15
  </script>
16
16
 
17
17
  <script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
18
- <script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
19
- data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
20
- data-sap-ui-compatVersion="edge"
21
- data-sap-ui-theme="sap_horizon"
22
- data-sap-ui-frameOptions="allow"
23
- ></script>
18
+ <script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
19
+ data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
20
+ data-sap-ui-compatVersion="edge"
21
+ data-sap-ui-async="true"
22
+ data-sap-ui-preload="async"
23
+ data-sap-ui-theme="sap_horizon"
24
+ data-sap-ui-frameOptions="allow"
25
+ ></script>
24
26
  <script>
25
27
  sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"))
26
28
  </script>
@@ -1,5 +1,5 @@
1
1
  const { join } = require('path')
2
- const cds = require('../../../cds'), { exists, copy } = cds.utils
2
+ const cds = require('../../../cds'), { exists, read, write, copy } = cds.utils
3
3
  const { env4, readProject } = require('../../projectReader')
4
4
 
5
5
  module.exports = class SampleTemplate extends require('../../plugin') {
@@ -13,11 +13,21 @@ module.exports = class SampleTemplate extends require('../../plugin') {
13
13
  async run() {
14
14
  const { db, srv, app } = env4('production').folders
15
15
  const language = exists('pom.xml') ? 'java' : 'nodejs'
16
+
16
17
  await Promise.all([
17
18
  copy(join(__dirname, 'files', 'db')).to(db),
18
- copy(join(__dirname, 'files', 'app')).to(app),
19
19
  copy(join(__dirname, 'files', 'srv')).to(srv),
20
20
  copy(join(__dirname, 'files', language)).to(srv)
21
21
  ])
22
+
23
+ await copy(join(__dirname, 'files', 'app')).to(app)
24
+ const { appName, appPath, appUIPaths } = readProject()
25
+
26
+ // manifest.json must be unique
27
+ await Promise.all(appUIPaths.map(async p => {
28
+ const manifest = await read(join(appPath, p, 'webapp/manifest.json'))
29
+ manifest['sap.app'].id = appName + '.' + p
30
+ await write(join(appPath, p, 'webapp/manifest.json'), manifest, { spaces: 2 })
31
+ }))
22
32
  }
23
33
  }
package/lib/util/cf.js CHANGED
@@ -192,6 +192,7 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
192
192
  async getServices(app) {
193
193
  const { spaceGuid: space_guids, orgGuid: organization_guids } = await this.getCfSpaceInfo();
194
194
  const apps = await this._cfRequest('/v3/apps', { space_guids, organization_guids, names: app });
195
+ if (apps.resources.length < 1) throw `Cannot find app with name '${app}'`
195
196
  const guid = apps.resources[0].guid;
196
197
  const env = await this._cfRequest('/v3/apps/'+guid+'/env', { space_guids, organization_guids, names: app });
197
198
  return Object.values(env.system_env_json.VCAP_SERVICES).flat();