@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
@@ -39,6 +39,19 @@ modules:
39
39
  routes:
40
40
  - route: "${default-url}"
41
41
  - route: "${default-host}.cert.${default-domain}"{{/hasIas}}
42
+ {{#isNodejs}}
43
+ hooks:
44
+ - name: upgrade-all
45
+ type: task
46
+ phases:
47
+ - blue-green.application.before-start.idle
48
+ - deploy.application.before-start
49
+ parameters:
50
+ name: upgrade
51
+ memory: 512M # increase as required
52
+ disk-quota: 768M # increase as required
53
+ command: cds-mtx upgrade '*'
54
+ {{/isNodejs}}
42
55
  provides:
43
56
  - name: mtx-api
44
57
  properties:
@@ -91,7 +104,7 @@ resources:
91
104
  appName: {{appName}}-${org}-${space}
92
105
  appCallbacks:
93
106
  dependenciesCallbacks:
94
- url: {{#isJava}}~{srv-api/srv-cert-url}/mt/sms/subscriptions/tenants/{app_tid}/dependencies{{/isJava}}{{#isNodejs}}~{mtx-api/mtx-cert-url}/-/cds/sms-provisioning/dependencies{{/isNodejs}}
107
+ url: {{#isJava}}~{srv-api/srv-cert-url}/mt/sms/subscriptions/tenants/{app_tid}/dependencies{{/isJava}}{{#isNodejs}}~{mtx-api/mtx-cert-url}/-/cds/sms-provisioning/dependencies/{app_tid}{{/isNodejs}}
95
108
  subscriptionCallbacks:
96
109
  url: {{#isJava}}~{srv-api/srv-cert-url}/mt/sms/subscriptions/tenants/{app_tid}{{/isJava}}{{#isNodejs}}~{mtx-api/mtx-cert-url}/-/cds/sms-provisioning/tenant/{app_tid}{{/isNodejs}}
97
110
  async:
@@ -101,9 +114,9 @@ resources:
101
114
  displayName: {{appName}}
102
115
  description: Description for {{appName}}
103
116
  category: Category
104
- iasServiceInstanceName: {{appName}}-auth{{#hasXsuaa}}
117
+ iasServiceInstanceName: {{appName}}-ias{{#hasXsuaa}}
105
118
  saasManagerServiceInstanceName: {{appName}}-registry{{/hasXsuaa}}
106
119
  requires:
107
120
  - name: {{#isJava}}srv-api{{/isJava}}{{^isJava}}mtx-api{{/isJava}}
108
- processed-after: [{{#hasXsuaa}}{{appName}}-registry, {{/hasXsuaa}}{{appName}}-auth]
121
+ processed-after: [{{#hasXsuaa}}{{appName}}-registry, {{/hasXsuaa}}{{appName}}-ias]
109
122
  {{/hasIas}}
@@ -19,6 +19,11 @@
19
19
  {{#profile}}
20
20
  }
21
21
  {{/profile}}
22
+ {{^profileSpecified}}
23
+ ,"[with-mtx]": {
24
+ "multitenancy": true
25
+ }
26
+ {{/profileSpecified}}
22
27
  }
23
28
  {{#isNodejs}}
24
29
  }
@@ -50,7 +50,7 @@ subscription-manager:
50
50
  parameters:
51
51
  appCallbacks:
52
52
  dependenciesCallbacks:
53
- {{#literal}}url: https://{{ .Release.Name }}-sidecar-{{ .Release.Namespace }}.cert.{{ .Values.global.domain }}/-/cds/sms-provisioning/dependencies{{/literal}}
53
+ {{#literal}}url: https://{{ .Release.Name }}-sidecar-{{ .Release.Namespace }}.cert.{{ .Values.global.domain }}/-/cds/sms-provisioning/dependencies/{app_tid}{{/literal}}
54
54
  subscriptionCallbacks:
55
55
  {{#literal}}url: https://{{ .Release.Name }}-sidecar-{{ .Release.Namespace }}.cert.{{ .Values.global.domain }}/-/cds/sms-provisioning/tenant/{app_tid}{{/literal}}
56
56
  appName: {{appName}}
@@ -87,11 +87,6 @@ sidecar:
87
87
  serviceInstanceName: service-manager
88
88
  image:
89
89
  repository: {{appName}}-sidecar
90
- {{#hasIas}}
91
- expose:
92
- gateway: default/kyma-mtls-gateway
93
- {{#literal}}host: https://{{ .Release.Name }}-sidecar-{{ .Release.Namespace }}.cert.{{ .Values.global.domain }}{{/literal}}
94
- {{/hasIas}}
95
90
  resources:
96
91
  limits:
97
92
  ephemeral-storage: 1G
@@ -19,9 +19,7 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
19
19
  }
20
20
 
21
21
  static hasInProduction(env) {
22
- const { options } = cds.cli.options
23
- // REVISIT: Check if this can be simplified
24
- return options?.add?.has('mtx') || options?.add?.has('multitenancy') || !!env.requires?.multitenancy || false
22
+ return env.requires?.multitenancy
25
23
  }
26
24
 
27
25
  async run() {
@@ -44,12 +42,12 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
44
42
  async combine() {
45
43
  const project = readProject()
46
44
  const {
47
- isNodejs, isJava, hasMta, hasHelm, hasHelmUnifiedRuntime,
48
- hasXsuaa, hasIas, hasHana, hasContainerize, hasApprouter,
45
+ isNodejs, isJava, addMta, addHelm, addHelmUnifiedRuntime,
46
+ addXsuaa, hasXsuaa, addIas, hasIas, hasHana, addContainerize, hasApprouter,
49
47
  srvPath, appName
50
48
  } = project
51
49
 
52
- if (hasMta) {
50
+ if (addMta || addIas || addXsuaa) {
53
51
  const srv = srv4(srvPath)
54
52
  const mtxSidecar = mtxSidecar4(isJava ? 'mtx/sidecar' : 'gen/mtx/sidecar')
55
53
  const modules = [srv, mtxSidecar]
@@ -60,7 +58,7 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
60
58
  if (isJava) apis.push(requiredMtxSidecarApi4(srv))
61
59
  const additions = [...modules, ...services, ...apis]
62
60
  const relationships = []
63
- if (hasXsuaa) {
61
+ if (addXsuaa) {
64
62
  relationships.push({
65
63
  insert: [saasRegistry, 'name'],
66
64
  into: [isJava ? srv : mtxSidecar, 'requires', 'name']
@@ -82,7 +80,7 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
82
80
  await merge(templateMtaPath).into('mta.yaml', { with: project, additions, relationships })
83
81
  }
84
82
 
85
- if (hasHelm || hasHelmUnifiedRuntime) {
83
+ if (addHelm || addHelmUnifiedRuntime) {
86
84
  await merge(__dirname, 'files/values.yaml.hbs').into('chart/values.yaml', { with: project })
87
85
  const applicationWorkload = hasApprouter ? 'approuter' : 'srv'
88
86
  await merge({
@@ -96,7 +94,7 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
96
94
  }).into('chart/values.yaml',{ forceOverwrite: true })
97
95
  }
98
96
 
99
- if (hasContainerize) {
97
+ if (addContainerize) {
100
98
  const deletions = []
101
99
  if (hasHana) {
102
100
  deletions.push({
@@ -113,7 +111,7 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
113
111
  await merge(__dirname, '/files/containerize.yaml.hbs').into('containerize.yaml', { with: project, additions, deletions })
114
112
  }
115
113
 
116
- if (hasXsuaa) {
114
+ if (addXsuaa) {
117
115
  await merge(__dirname, 'files/xs-security.json.hbs').into('xs-security.json', {
118
116
  project,
119
117
  additions: [{
@@ -35,9 +35,9 @@ module.exports = class NotificationsTemplate extends require('../../plugin') {
35
35
 
36
36
  async combine() {
37
37
  const project = readProject()
38
- const { hasMta } = project
38
+ const { addMta } = project
39
39
 
40
- if (hasMta) {
40
+ if (addMta) {
41
41
  await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', { project, additions: [notificationTypesDeployer] })
42
42
  }
43
43
  }
@@ -37,11 +37,16 @@ modules:
37
37
  name: {{appName}}-portal-key
38
38
  config:
39
39
  content-endpoint: developer
40
- - name: {{appName}}-auth{{#hasIas}}
40
+ {{#hasXsuaa}}
41
+ - name: {{appName}}-auth
42
+ {{/hasXsuaa}}
43
+ {{#hasIas}}
44
+ - name: {{appName}}-ias
41
45
  parameters:
42
46
  config:
43
47
  credential-type: X509_GENERATED
44
- app-identifier: portal-deployer{{/hasIas}}
48
+ app-identifier: portal-deployer
49
+ {{/hasIas}}
45
50
  - name: {{appName}}-html5-repo-host
46
51
  - name: {{appName}}-app-deployer
47
52
  deployed-after:
@@ -63,3 +68,14 @@ resources:
63
68
  type: org.cloudfoundry.managed-service
64
69
  parameters:
65
70
  service: destination
71
+
72
+ - name: {{appName}}-ias
73
+ type: org.cloudfoundry.managed-service
74
+ parameters:
75
+ service: identity
76
+
77
+ - name: {{appName}}-html5-repo-host
78
+ type: org.cloudfoundry.managed-service
79
+ parameters:
80
+ service: html5-apps-repo
81
+ service-plan: app-host
@@ -28,13 +28,13 @@ module.exports = class PortalTemplate extends require('../../plugin') {
28
28
 
29
29
  async combine() {
30
30
  const project = readProject()
31
- const { appName, isJava, hasMta, srvPath, hasHtml5Repo, hasXsuaa, hasApprouter, hasMultitenancy, hasHelm, hasHelmUnifiedRuntime, hasContainerize } = project
31
+ const { appName, isJava, addMta, hasMta, srvPath, addHtml5Repo, addXsuaa, addApprouter, addMultitenancy, addHelm, addHelmUnifiedRuntime, addContainerize, hasApprouter, hasHtml5Repo } = project
32
32
 
33
- if (hasMultitenancy) {
33
+ if (addMultitenancy) {
34
34
  await merge(__dirname, 'files/package.sidecar.json').into('mtx/sidecar/package.json')
35
35
  }
36
36
 
37
- if (hasMta) {
37
+ if (addMta || (addXsuaa || addHtml5Repo || addApprouter || addMultitenancy) && hasMta) {
38
38
  const srv = srv4(srvPath)
39
39
  const additions = [srv, portal, appDeployer, portalDeployer]
40
40
  const relationships = [{
@@ -45,7 +45,7 @@ module.exports = class PortalTemplate extends require('../../plugin') {
45
45
  into: [portalDeployer, 'requires', 'name']
46
46
  }]
47
47
 
48
- if (hasXsuaa) {
48
+ if (addXsuaa) {
49
49
  additions.push(xsuaa)
50
50
  relationships.push({
51
51
  insert: [xsuaa, 'name'],
@@ -68,7 +68,7 @@ module.exports = class PortalTemplate extends require('../../plugin') {
68
68
  })
69
69
  }
70
70
 
71
- if (hasMultitenancy) {
71
+ if (addMultitenancy) {
72
72
  relationships.push({
73
73
  insert: [portal, 'name'],
74
74
  into: [isJava ? srv : mtxSidecar4('gen/mtx/sidecar'), 'requires', 'name']
@@ -80,7 +80,7 @@ module.exports = class PortalTemplate extends require('../../plugin') {
80
80
  })
81
81
  }
82
82
 
83
- if (hasContainerize) {
83
+ if (addContainerize) {
84
84
  const additions = [{
85
85
  in: 'modules',
86
86
  where: { name: `${appName}-portal-deployer` }
@@ -88,7 +88,7 @@ module.exports = class PortalTemplate extends require('../../plugin') {
88
88
  await merge(__dirname, '/files/containerize.yaml.hbs').into('containerize.yaml', { with: project, additions })
89
89
  }
90
90
 
91
- if (hasHelm || hasHelmUnifiedRuntime) {
91
+ if (addHelm || addHelmUnifiedRuntime) {
92
92
  await merge(__dirname, 'files/values.yaml.hbs').into('chart/values.yaml', { with: project })
93
93
  }
94
94
  }
@@ -1,5 +1,5 @@
1
1
  const cds = require('../../../cds')
2
- const { read } = cds.utils
2
+ const { read, exists } = cds.utils
3
3
  const { readProject } = require('../../projectReader')
4
4
  const { merge } = require('../../merge')
5
5
  const mvn = require('../../mvn')
@@ -24,16 +24,16 @@ module.exports = class PostgresTemplate extends require('../../plugin') {
24
24
  async run() {
25
25
  const project = readProject()
26
26
  const { configFile, isJava } = project
27
- project.shortcut = typeof (await read(configFile)).cds?.requires?.db !== 'object'
27
+ project.shortcut = !exists(configFile) || typeof (await read(configFile)).cds?.requires?.db !== 'object'
28
28
  await merge(__dirname, 'files/package.json.hbs').into('package.json', { project })
29
29
  if (isJava) await mvn.add('postgresql')
30
30
  }
31
31
 
32
32
  async combine() {
33
33
  const project = readProject()
34
- const { hasMta, srvPath } = project
34
+ const { addMta, srvPath } = project
35
35
 
36
- if (hasMta) {
36
+ if (addMta) {
37
37
  const srv = srv4(srvPath)
38
38
  await merge(__dirname, 'files/mta.yml.hbs').into('mta.yaml', {
39
39
  project,
@@ -27,9 +27,9 @@ module.exports = class extends require('../../plugin') {
27
27
 
28
28
  async combine() {
29
29
  const project = readProject()
30
- const { hasMta, srvPath } = project
30
+ const { addMta, srvPath } = project
31
31
 
32
- if (hasMta) {
32
+ if (addMta) {
33
33
  const srv = srv4(srvPath)
34
34
  await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', {
35
35
  project,
@@ -1,5 +1,5 @@
1
1
  const cds = require('../../../cds')
2
- const { read } = cds.utils
2
+ const { read, exists } = cds.utils
3
3
  const { readProject } = require('../../projectReader')
4
4
  const { merge } = require('../../merge')
5
5
 
@@ -28,7 +28,7 @@ module.exports = class RedisMessagingTemplate extends require('../../plugin') {
28
28
  async run() {
29
29
  const project = readProject()
30
30
  const { configFile } = project
31
- project.shortcut = typeof (await read(configFile)).cds?.requires?.messaging !== 'object'
31
+ project.shortcut = !exists(configFile) || typeof (await read(configFile)).cds?.requires?.messaging !== 'object'
32
32
  await merge(__dirname, 'files/package.json.hbs').into(configFile, { project })
33
33
  }
34
34
  }
@@ -109,8 +109,8 @@ extend service AdminService {
109
109
  }
110
110
 
111
111
  // Workaround for Fiori popups for asking user to enter a valid UUID on Create
112
- annotate AdminService.Authors : ID with @Core.Computed @odata.Type: 'Edm.String';
113
- annotate AdminService.Books : ID with @Core.Computed @odata.Type: 'Edm.String';
112
+ annotate AdminService.Authors : ID with @Core.Computed;
113
+ annotate AdminService.Books : ID with @Core.Computed;
114
114
 
115
115
  // Show Genre as drop down, not a dialog
116
116
  annotate AdminService.Books with {
@@ -1,13 +1,9 @@
1
- using {
2
- Currency,
3
- managed,
4
- cuid,
5
- sap
6
- } from '@sap/cds/common';
1
+ using { Currency, managed, sap } from '@sap/cds/common';
7
2
 
8
3
  namespace sap.capire.bookshop;
9
4
 
10
- entity Books : managed, cuid {
5
+ entity Books : managed {
6
+ key ID : Integer;
11
7
  title : localized String(111) @mandatory;
12
8
  descr : localized String(1111);
13
9
  author : Association to Authors @mandatory;
@@ -17,7 +13,8 @@ entity Books : managed, cuid {
17
13
  currency : Currency;
18
14
  }
19
15
 
20
- entity Authors : managed, cuid {
16
+ entity Authors : managed {
17
+ key ID : Integer;
21
18
  name : String(111) @mandatory;
22
19
  dateOfBirth : Date;
23
20
  dateOfDeath : Date;
@@ -0,0 +1,24 @@
1
+ {{#isESM}}
2
+ import cds from '@sap/cds'
3
+
4
+ export class AdminService extends cds.ApplicationService { init() {
5
+ {{/isESM}}
6
+ {{^isESM}}
7
+ const cds = require('@sap/cds')
8
+
9
+ module.exports = class AdminService extends cds.ApplicationService { init() {
10
+ {{/isESM}}
11
+
12
+ const { Books } = this.entities
13
+
14
+ /**
15
+ * Generate IDs for new Books drafts
16
+ */
17
+ this.before ('NEW', Books.drafts, async (req) => {
18
+ if (req.data.ID) return
19
+ const { ID:id1 } = await SELECT.one.from(Books).columns('max(ID) as ID')
20
+ const { ID:id2 } = await SELECT.one.from(Books.drafts).columns('max(ID) as ID')
21
+ req.data.ID = Math.max(id1||0, id2||0) + 1
22
+ })
23
+ return super.init()
24
+ }}
@@ -0,0 +1,43 @@
1
+ {{#isESM}}
2
+ import cds from '@sap/cds'
3
+
4
+ export class CatalogService extends cds.ApplicationService { init() {
5
+ {{/isESM}}
6
+ {{^isESM}}
7
+ const cds = require('@sap/cds')
8
+
9
+ module.exports = class CatalogService extends cds.ApplicationService { init() {
10
+ {{/isESM}}
11
+
12
+ const { Books } = cds.entities('sap.capire.bookshop')
13
+ const { ListOfBooks } = this.entities
14
+
15
+ // Add some discount for overstocked books
16
+ this.after('each', ListOfBooks, book => {
17
+ if (book.stock > 111) book.title += ` -- 11% discount!`
18
+ })
19
+
20
+ // Reduce stock of ordered books if available stock suffices
21
+ this.on('submitOrder', async req => {
22
+ let { book:id, quantity } = req.data
23
+ let book = await SELECT.one.from (Books, id, b => b.stock)
24
+
25
+ // Validate input data
26
+ if (!book) return req.error (404, `Book #${id} doesn't exist`)
27
+ if (quantity < 1) return req.error (400, `quantity has to be 1 or more`)
28
+ if (!book.stock || quantity > book.stock) return req.error (409, `${quantity} exceeds stock for book #${id}`)
29
+
30
+ // Reduce stock in database and return updated stock value
31
+ await UPDATE (Books, id) .with ({ stock: book.stock -= quantity })
32
+ return book
33
+ })
34
+
35
+ // Emit event when an order has been submitted
36
+ this.after('submitOrder', async (_,req) => {
37
+ let { book, quantity } = req.data
38
+ await this.emit('OrderedBook', { book, quantity, buyer: req.user.id })
39
+ })
40
+
41
+ // Delegate requests to the underlying generic service
42
+ return super.init()
43
+ }}
@@ -0,0 +1,16 @@
1
+ import cds from '@sap/cds'
2
+
3
+ import { Authors, Books } from '#cds-models/AdminService'
4
+
5
+ export class AdminService extends cds.ApplicationService { init(){
6
+ this.before (['NEW','CREATE'], Authors, genid)
7
+ this.before (['NEW','CREATE'], Books, genid)
8
+ return super.init()
9
+ }}
10
+
11
+ /** Generate primary keys for target entity in request */
12
+ async function genid (req:any) {
13
+ if (req.data.ID) return
14
+ const {id} = await SELECT.one.from(req.target).columns('max(ID) as id')
15
+ req.data.ID = id + 4 // Note: that is not safe! ok for this sample only.
16
+ }
@@ -0,0 +1,31 @@
1
+ import cds from '@sap/cds'
2
+
3
+ import { Books } from '#cds-models/sap/capire/bookshop'
4
+ import { submitOrder, ListOfBooks } from '#cds-models/CatalogService'
5
+
6
+ export class CatalogService extends cds.ApplicationService { async init() {
7
+
8
+ // Add some discount for overstocked books
9
+ this.after('each', ListOfBooks, book => {
10
+ if (book?.stock === undefined || book.stock === null) return
11
+ if (book?.stock > 111) book.title += ` -- 11% discount!`
12
+ })
13
+
14
+ // Reduce stock of ordered books if available stock suffices
15
+ this.on(submitOrder, async req => {
16
+ let { book:id, quantity } = req.data
17
+ let book = await SELECT.from (Books, id!, b => b.stock)
18
+
19
+ // Validate input data
20
+ if (!book) return req.error (404, `Book #${id} doesn't exist`)
21
+ if (!quantity || quantity < 1) return req.error (400, `quantity has to be 1 or more`)
22
+ if (quantity > book.stock!) return req.error (409, `${quantity} exceeds stock for book #${id}`)
23
+
24
+ // Reduce stock in database and return updated stock value
25
+ await UPDATE (Books, id) .with ({ stock: book.stock! -= quantity })
26
+ return book
27
+ })
28
+
29
+ // Delegate requests to the underlying generic service
30
+ return super.init()
31
+ }}
@@ -13,7 +13,7 @@ const { GET, POST, expect, axios } = cds.test (__dirname+'/..')
13
13
  axios.defaults.auth = { username: '{{user}}', password: '{{password}}' }
14
14
  {{/user}}
15
15
 
16
- describe('OData APIs', () => {
16
+ describe('{{serviceName}} OData APIs', () => {
17
17
 
18
18
  {{#entities}}{{=<% %>=}}
19
19
  it('serves <%name%>', async () => {
@@ -69,7 +69,7 @@ async function createNodeTest(proj) {
69
69
  .filter(([name]) => name.match(nameFilter))
70
70
  .filter(([name]) => !name.match(/[._]texts$/))
71
71
  .filter(([, e]) => !e['@cds.autoexposed']) // only consider non-autoexposed entities to reduce clutter
72
- .map(([, e]) => e)
72
+ .map(([name, e]) => {e._nameUnqualified = name; return e})
73
73
 
74
74
  let entity // use one entity only (don't want to make users test all entities)
75
75
  if (entities.length) entity = entities[0]
@@ -99,7 +99,7 @@ async function createNodeTest(proj) {
99
99
 
100
100
  proj.entities = [{
101
101
  name: entity.name,
102
- urlPath: `/${sInfo.endpoints[0].path}${entity.name}`,
102
+ urlPath: `/${sInfo.endpoints[0].path}${entity._nameUnqualified}`,
103
103
  selectString: selectElements.join(','),
104
104
  elementsString: JSON.stringify(
105
105
  Object.entries(sampleData[entity.name][0])
@@ -14,7 +14,7 @@ module.exports = class TinySampleTemplate extends require('../../plugin') {
14
14
  async run() {
15
15
  const { isJava } = readProject()
16
16
  if (isJava) {
17
- if (!cds.cli.options.add.has(JAVA)) {
17
+ if (!cds.cli.options.add?.has(JAVA)) {
18
18
  await mvn.add('tiny_sample')
19
19
  }
20
20
  } else {
@@ -2,7 +2,8 @@
2
2
  "compilerOptions": {
3
3
  "moduleResolution": "nodenext",
4
4
  "paths": {
5
- "#cds-models/*": ["./@cds-models/*"]
5
+ "#cds-models/*": ["./@cds-models/*"],
6
+ "@sap/cds": ["./node_modules/@cap-js/cds-types"]
6
7
  }
7
8
  }
8
9
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "devDependencies": {
3
- "@cap-js/cds-types": "^0.14.0",
3
+ "@cap-js/cds-types": "^0.15.0",
4
4
  "@types/node": "^22.0.0",
5
5
  "tsx": "^4",
6
6
  "typescript": "^5"
@@ -8,6 +8,9 @@
8
8
  "strict": true,
9
9
  "skipLibCheck": true,
10
10
  "sourceMap": true,
11
- "allowJs": true
11
+ "allowJs": true,
12
+ "paths": {
13
+ "@sap/cds": ["./node_modules/@cap-js/cds-types"]
14
+ }
12
15
  }
13
16
  }
@@ -11,10 +11,12 @@ modules:
11
11
  - name: {{appName}}-destinations
12
12
  type: com.sap.application.content
13
13
  requires:
14
+ {{#hasXsuaa}}
14
15
  - name: {{appName}}-auth
15
16
  parameters:
16
17
  service-key:
17
18
  name: {{appName}}-auth-key
19
+ {{/hasXsuaa}}
18
20
  - name: {{appName}}-html5-repo-host
19
21
  parameters:
20
22
  service-key:
@@ -34,11 +36,13 @@ modules:
34
36
  ServiceInstanceName: {{appName}}-html5-repo-host
35
37
  ServiceKeyName: {{appName}}-html5-repo-host-key
36
38
  sap.cloud.service: {{strippedAppName}}.service
39
+ {{#hasXsuaa}}
37
40
  - Name: {{appName}}-auth
38
41
  Authentication: OAuth2UserTokenExchange
39
42
  ServiceInstanceName: {{appName}}-auth
40
43
  ServiceKeyName: {{appName}}-auth-key
41
44
  sap.cloud.service: {{strippedAppName}}.service
45
+ {{/hasXsuaa}}
42
46
 
43
47
  - name: {{appName}}-mtx
44
48
  type: nodejs
@@ -26,21 +26,22 @@ module.exports = class extends require('../../plugin') {
26
26
  }
27
27
 
28
28
  static hasInProduction(env) {
29
- return !!env.requires?.workzone || cds.cli.options.add.has('workzone-standard')
29
+ return !!env.requires?.workzone || cds.cli.options.add?.has('workzone-standard')
30
30
  }
31
31
 
32
32
  async run() {
33
- await merge({ cds: { requires: { workzone: true } } }).into('package.json')
33
+ const project = readProject()
34
+ const conf = project.isJava ? { requires: { workzone: true }} : { cds: { requires: { workzone: true } } }
35
+ await merge(conf).into(project.configFile)
34
36
 
35
37
  if (cds.cli.options.cdm) {
36
- const project = readProject()
37
38
  await merge(join(__dirname, 'files/cdm.json.hbs')).into('app/workzone/cdm.json', { project })
38
39
  }
39
40
  }
40
41
 
41
42
  async combine() {
42
43
  const project = readProject()
43
- const { isJava, hasMta, srvPath, apps, appPath, appUIPaths, hasMultitenancy, strippedAppName } = project
44
+ const { isJava, addMta, hasMta, srvPath, apps, appPath, appUIPaths, addMultitenancy, strippedAppName } = project
44
45
 
45
46
  await Promise.all(appUIPaths.map(async p => {
46
47
  project.uiApp = p
@@ -66,7 +67,7 @@ module.exports = class extends require('../../plugin') {
66
67
  await merge(__dirname, 'files/xs-app.json.hbs').into(join(appPath, app, 'xs-app.json'), { with: project, additions, overwrites })
67
68
  }))
68
69
 
69
- if (hasMultitenancy) {
70
+ if (addMultitenancy) {
70
71
  const mtxPackageJson = await read('mtx/sidecar/package.json')
71
72
  mtxPackageJson.cds ??= {}
72
73
  mtxPackageJson.cds.requires ??= {}
@@ -77,14 +78,14 @@ module.exports = class extends require('../../plugin') {
77
78
  await write('mtx/sidecar/package.json', mtxPackageJson, { spaces: 2 })
78
79
  }
79
80
 
80
- if (hasMta) {
81
+ if (addMta || addMultitenancy && hasMta) {
81
82
  const srv = srv4(srvPath)
82
83
  const additions = [srv, destinations, destination, {
83
84
  in: [destination, 'requires'],
84
85
  where: { name: 'srv-api' }
85
86
  }]
86
87
  const relationships = []
87
- if (hasMultitenancy) {
88
+ if (addMultitenancy) {
88
89
  additions.push(html5RepoHost)
89
90
  relationships.push({
90
91
  insert: [html5RepoHost, 'name'],
@@ -92,7 +93,6 @@ module.exports = class extends require('../../plugin') {
92
93
  })
93
94
  }
94
95
 
95
-
96
96
  await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', { with: project, additions, relationships })
97
97
  }
98
98
  }
@@ -6,13 +6,11 @@ modules:
6
6
  requires:
7
7
  - name: {{appName}}-auth
8
8
 
9
- {{#hasMultitenancy}}
10
9
  - name: {{appName}}-mtx
11
10
  type: nodejs
12
11
  path: {{#isNodejs}}gen/{{/isNodejs}}mtx/sidecar
13
12
  requires:
14
13
  - name: {{appName}}-auth
15
- {{/hasMultitenancy}}
16
14
 
17
15
  resources:
18
16
  - name: {{appName}}-auth{{#servicePlan}}-{{servicePlan}}{{/servicePlan}}
@@ -6,6 +6,9 @@
6
6
  "cds": {
7
7
  {{/isNodejs}}
8
8
  "requires": {
9
+ {{#hasIas}}
10
+ "xsuaa": true,
11
+ {{/hasIas}}
9
12
  {{#profile}}
10
13
  "[{{profile}}]": {
11
14
  {{/profile}}