@sap/cds-dk 7.7.2 → 7.8.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 (50) hide show
  1. package/bin/add.js +42 -17
  2. package/bin/bind.js +21 -12
  3. package/bin/build.js +14 -2
  4. package/bin/deploy/to-sqlite.js +1 -1
  5. package/bin/help.js +4 -1
  6. package/bin/version.js +54 -22
  7. package/lib/bind/userCdsrcJsonStorageProvider.js +1 -1
  8. package/lib/build/buildTaskFactory.js +2 -2
  9. package/lib/build/provider/hana/index.js +1 -1
  10. package/lib/build/provider/internalBuildTaskProvider.js +19 -5
  11. package/lib/build/provider/mtx/resourcesTarBuilder.js +16 -4
  12. package/lib/build/provider/mtx-extension/extensionCompilation.js +13 -2
  13. package/lib/build/provider/mtx-extension/index.js +6 -2
  14. package/lib/build/provider/nodejs/index.js +4 -4
  15. package/lib/build/util.js +1 -12
  16. package/lib/compile/openapi/index.js +2 -1
  17. package/lib/index.js +2 -2
  18. package/lib/init/constants.js +2 -1
  19. package/lib/init/index.js +9 -23
  20. package/lib/init/plugin.js +8 -0
  21. package/lib/init/registries/mta.js +6 -0
  22. package/lib/init/template/data/as-csv.js +60 -0
  23. package/lib/init/template/data/as-json.js +441 -0
  24. package/lib/init/template/data/index.js +115 -86
  25. package/lib/init/template/helm/files/content-deployment/Chart.yaml +1 -1
  26. package/lib/init/template/helm/files/content-deployment/templates/job.yaml +7 -0
  27. package/lib/init/template/helm/files/content-deployment/values.schema.json +17 -0
  28. package/lib/init/template/helm/files/content-deployment/values.yaml +6 -1
  29. package/lib/init/template/helm/files/service-instance/Chart.yaml +1 -1
  30. package/lib/init/template/helm/files/service-instance/templates/serviceinstance.yaml +4 -0
  31. package/lib/init/template/helm/files/service-instance/values.schema.json +17 -1
  32. package/lib/init/template/helm/files/service-instance/values.yaml +4 -1
  33. package/lib/init/template/helm/files/values.yaml.hbs +2 -2
  34. package/lib/init/template/helm/files/web-application/Chart.yaml +1 -1
  35. package/lib/init/template/helm/files/web-application/templates/api-rule.yaml +4 -0
  36. package/lib/init/template/helm/files/web-application/templates/network-policy.yaml +15 -0
  37. package/lib/init/template/helm/files/web-application/values.schema.json +41 -0
  38. package/lib/init/template/helm/files/web-application/values.yaml +19 -1
  39. package/lib/init/template/helm-unified-runtime/files/values.yaml.hbs +2 -2
  40. package/lib/init/template/helm-unified-runtime/index.js +9 -6
  41. package/lib/init/template/http/index.js +182 -0
  42. package/lib/init/template/kafka/files/mta.yaml.hbs +14 -0
  43. package/lib/init/template/kafka/files/package.json.hbs +22 -0
  44. package/lib/init/template/kafka/index.js +40 -0
  45. package/lib/init/template/lint/index.js +4 -4
  46. package/lib/init/template/mta/files/mta.yaml.hbs +4 -0
  47. package/lib/init/template/multitenancy/files/values.yaml.hbs +2 -2
  48. package/lib/util/cf.js +6 -2
  49. package/npm-shrinkwrap.json +13 -13
  50. package/package.json +1 -1
@@ -0,0 +1,182 @@
1
+ const cds = require('../../../cds')
2
+ const cf = require("../../../util/cf")
3
+ const { write, exists } = cds.utils
4
+
5
+ module.exports = class HttpTemplate extends require('../../plugin') {
6
+
7
+ #defaultHost = "http://localhost:4004"
8
+ #defaultPathPrefix = "/odata/v4/"
9
+ #defaultDirName = "*"
10
+ #defaultOutputDir = "http"
11
+ #defaultAuth = `Authorization: Basic yves:`
12
+
13
+ options() {
14
+ return {
15
+ 'filter': {
16
+ type: 'string',
17
+ short: 'f',
18
+ help: `Specify the path to the service definition file or directory. Default is ${this.#defaultDirName}.`
19
+ },
20
+ 'for-app': {
21
+ type: 'string',
22
+ short: 'a',
23
+ help: 'Specify the name of the app to generate requests for. If not specified, localhost and default auth will be used.'
24
+ },
25
+ 'out': {
26
+ type: 'string',
27
+ short: 'o',
28
+ help: 'The output directory. By default, an `http` dir will be created in one of the following: test/tests/__tests__, whichever already exists. If none exists, an `http` dir will be created at the root level.'
29
+ }
30
+ }
31
+ }
32
+
33
+ async run() {
34
+
35
+ const path = cds.cli.options['filter'] || this.#defaultDirName
36
+ const csn = await cds.load(path)
37
+ cds.linked(csn)
38
+
39
+ let [hostname, auth] = await this.#getHostnameAndAuth()
40
+
41
+ let endpoints = this.#collectEndpointInfo(csn)
42
+
43
+ this.#generateRequests(endpoints, hostname, auth)
44
+ }
45
+
46
+ async #getHostnameAndAuth() {
47
+ if (!cds.cli.options['for-app']) {
48
+ return [this.#defaultHost, this.#defaultAuth]
49
+ }
50
+
51
+ const env = await cf.getEnv(cds.cli.options['for-app'])
52
+ const hostname = `https://${env.application_env_json.VCAP_APPLICATION.application_uris[0]}`
53
+ const { url, clientid, clientsecret } = env.system_env_json.VCAP_SERVICES?.xsuaa[0]?.credentials || {}
54
+
55
+ const response = await fetch(`${url}/oauth/token?${new URLSearchParams({
56
+ grant_type: 'client_credentials',
57
+ response_type: 'token',
58
+ })}`, {
59
+ method: 'GET',
60
+ headers: {
61
+ 'Authorization': `Basic ${Buffer.from(`${clientid}:${clientsecret}`).toString('base64')}`
62
+ }
63
+ })
64
+ if (response.ok) {
65
+ const { access_token } = await response.json()
66
+ const auth = `x-approuter-authorization: bearer ` + access_token
67
+ return [hostname, auth]
68
+ } else {
69
+ throw new Error('Error authenticating ', { url, response })
70
+ }
71
+ }
72
+
73
+ #collectEndpointInfo(csn) {
74
+ const mapping = {}
75
+
76
+ csn.services.map((s) => {
77
+ const protocols = this.#getProtocols(s)
78
+ const paths = this.#getPaths(s, protocols)
79
+
80
+ let entityInfo = []
81
+ const entities = csn.entities(s.name)
82
+ for (const e in entities) {
83
+ if (entities[e].name.endsWith('.texts')) {
84
+ continue
85
+ }
86
+ entityInfo.push(this.#addNameAndReadOnly(entities[e]))
87
+ }
88
+
89
+ mapping[s.name] = { entities: entityInfo, paths: paths, protocols: protocols, actions: [] }
90
+ })
91
+
92
+ csn.foreach('action', (a) => {
93
+ mapping[a.name.split(".")[0]].actions.push(a)
94
+ })
95
+
96
+ return mapping
97
+ }
98
+
99
+ #getProtocols(s) {
100
+ return s["@protocol"]
101
+ ? typeof s["@protocol"] === "string"
102
+ ? [s["@protocol"]]
103
+ : s["@protocol"]
104
+ : ["odata"]
105
+ }
106
+
107
+ #getPaths(s, protocols) {
108
+ let paths = []
109
+ if (s["@path"]) {
110
+ paths.push(s["@path"])
111
+ } else {
112
+ const serviceName = s.name.replace("Service", "").toLowerCase()
113
+
114
+ let prefix = ""
115
+ if (protocols) {
116
+ for (const p of protocols) {
117
+ if (p === "odata-v2") {
118
+ prefix = "/odata/v2/"
119
+ } else if (p === "rest") {
120
+ prefix = "/rest/"
121
+ } else {
122
+ prefix = this.#defaultPathPrefix
123
+ }
124
+ paths.push(prefix + serviceName)
125
+ }
126
+ }
127
+ }
128
+ return paths
129
+ }
130
+
131
+ #addNameAndReadOnly(e) {
132
+ return {
133
+ name: e.name.split(".")[1],
134
+ readOnly: (e["@readonly"] || e["@cds.autoexpose"] || e['@odata.singleton']) ?? false,
135
+ }
136
+ }
137
+
138
+ #generateRequests(mapping, hostname, auth) {
139
+ for (const service in mapping) {
140
+ let requests = []
141
+
142
+ requests.push(`@server = ${hostname}\n@auth = ${auth}\n`)
143
+
144
+ mapping[service].entities.forEach((e) => {
145
+ for (const path of mapping[service].paths) {
146
+ const url = `{{server}}${path}/${e.name}`
147
+ const contentType = `Content-Type: application/json`
148
+ const authHeader = `{{auth}}`
149
+
150
+ requests.push(`GET ${url}\n${authHeader}\n${contentType}\n`)
151
+
152
+ if (!e.readOnly) {
153
+ const allHeaders = `${contentType}\n${authHeader}`
154
+ requests.push(`POST ${url}\n${allHeaders}\n\n{}\n`)
155
+
156
+ requests.push(`PATCH ${url}\n${allHeaders}\n\n{}\n`)
157
+ requests.push(`DELETE ${url}\n${authHeader}\n`)
158
+ }
159
+ }
160
+ })
161
+
162
+ const outputPath = this.#determineOutputPath(service)
163
+ write(requests.join("\n###\n")).to(outputPath)
164
+ console.log(`${outputPath} has been created.`)
165
+ }
166
+ }
167
+
168
+ #determineOutputPath(service) {
169
+ let outputDir
170
+ const { out } = cds.cli.options
171
+ if (out) {
172
+ outputDir = out.endsWith('/') ? out.slice(0, -1) : out
173
+ } else if (exists('test') || exists('tests') || exists('__tests__')) {
174
+ outputDir = exists('test') ? 'test/http' : (exists('tests') ? 'tests/http' : '__tests__/http')
175
+ } else {
176
+ outputDir = this.#defaultOutputDir
177
+ }
178
+ return `${outputDir}/${service}.http`
179
+ }
180
+ }
181
+
182
+
@@ -0,0 +1,14 @@
1
+ ---
2
+ modules:
3
+ - name: {{appName}}-srv
4
+ type: {{language}}
5
+ path: {{& srvPath}}
6
+ requires:
7
+ - name: {{appName}}-kafka
8
+
9
+ resources:
10
+ - name: {{appName}}-kafka
11
+ type: org.cloudfoundry.managed-service
12
+ parameters:
13
+ service: kafka
14
+ service-plan: standard
@@ -0,0 +1,22 @@
1
+ {
2
+ {{#isNodejs}}
3
+ "dependencies": {
4
+ "kafkajs": "^2"
5
+ },
6
+ "cds": {
7
+ {{/isNodejs}}
8
+ "requires": {
9
+ {{#profile}}
10
+ "[{{profile}}]": {
11
+ {{/profile}}
12
+ "messaging": {
13
+ "kind": "kafka"
14
+ }
15
+ {{#profile}}
16
+ }
17
+ {{/profile}}
18
+ }
19
+ {{#isNodejs}}
20
+ }
21
+ {{/isNodejs}}
22
+ }
@@ -0,0 +1,40 @@
1
+ const { readProject } = require('../../projectReader')
2
+ const { merge } = require('../../merge')
3
+ const { srv4, kafkaMessaging } = require('../../registries/mta')
4
+ const mvn = require('../../mvn')
5
+
6
+ module.exports = class KafkaTemplate extends require('../../plugin') {
7
+
8
+ static hasInProduction(env) {
9
+ return !!env.requires?.messaging?.kind === 'kafka'
10
+ }
11
+
12
+ async run() {
13
+ const project = readProject()
14
+ const { configFile, isNodejs, isJava } = project
15
+ if (isNodejs) await merge(__dirname, 'files/package.json.hbs').into(configFile, { with: project })
16
+ if (isJava) await mvn.add('kafka')
17
+ }
18
+
19
+ async combine() {
20
+ const project = readProject()
21
+ const { hasMta, srvPath } = project
22
+
23
+ if (hasMta) {
24
+ const srv = srv4(srvPath)
25
+ await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', {
26
+ project,
27
+ additions: [srv, kafkaMessaging],
28
+ relationships: [{
29
+ insert: [kafkaMessaging, 'name'],
30
+ into: [srv, 'requires', 'name']
31
+ }]
32
+ })
33
+ }
34
+
35
+ // REVISIT: Helm support
36
+ // if (hasHelm || hasHelmUnifiedRuntime) {
37
+ // await merge(__dirname, 'files', 'values.yaml').into('chart/values.yaml')
38
+ // }
39
+ }
40
+ }
@@ -7,12 +7,9 @@ const io = require("../../../lint/io");
7
7
  const readline = require("readline");
8
8
  const { renderAndCopy } = require('../../template')
9
9
  const { LINT_DEV, LINT_GLOBAL } = require("../../constants").OPTIONS;
10
- const { getConfigPath, getFileExtensions } = require("@sap/eslint-plugin-cds/lib/api");
11
10
  const cds = require("../../../cds");
12
11
  const { mkdirp, exists } = cds.utils;
13
-
14
12
  const IS_WIN = os.platform() === "win32";
15
- const ALLOWED_FILE_EXTENSIONS = getFileExtensions();
16
13
  const VSCODE_FILE_ASSOCIATIONS = {
17
14
  "csv": [ "csv", "csv (semicolon)", "tsv", "tab" ]
18
15
  }
@@ -70,7 +67,9 @@ module.exports = class LintTemplate extends require('../../plugin') {
70
67
  }
71
68
 
72
69
  // Generate VS Code extension settings
73
- const extensions = ALLOWED_FILE_EXTENSIONS.map((ext) => {
70
+ const { getFileExtensions } = require("@sap/eslint-plugin-cds/lib/api");
71
+ const ALLOWED_FILE_EXTENSIONS = getFileExtensions();
72
+ const extensions = ALLOWED_FILE_EXTENSIONS.map((ext) => {
74
73
  return path.extname(ext).replace('.', '');
75
74
  })
76
75
  extensions.forEach((ext) => {
@@ -151,6 +150,7 @@ module.exports = class LintTemplate extends require('../../plugin') {
151
150
  }
152
151
 
153
152
  async _addEslintPluginConfig() {
153
+ const { getConfigPath } = require("@sap/eslint-plugin-cds/lib/api");
154
154
  let configPath = getConfigPath(cds.root);
155
155
  if (!configPath) {
156
156
  configPath = path.join(cds.root, ".eslintrc.json");
@@ -20,12 +20,16 @@ modules:
20
20
  {{#isNodejs}}
21
21
  parameters:
22
22
  buildpack: nodejs_buildpack
23
+ readiness-health-check-type: http
24
+ readiness-health-check-http-endpoint: /health
23
25
  build-parameters:
24
26
  builder: npm
25
27
  {{/isNodejs}}
26
28
  {{#isJava}}
27
29
  parameters:
28
30
  buildpack: sap_java_buildpack
31
+ readiness-health-check-type: http
32
+ readiness-health-check-http-endpoint: /
29
33
  properties:
30
34
  SPRING_PROFILES_ACTIVE: cloud,sandbox
31
35
  JBP_CONFIG_COMPONENTS: "jres: ['com.sap.xs.java.buildpack.jre.SAPMachineJRE']"
@@ -65,6 +65,6 @@ sidecar:
65
65
  memory: 500M
66
66
  health:
67
67
  liveness:
68
- path: /
68
+ path: /health
69
69
  readiness:
70
- path: /
70
+ path: /health
package/lib/util/cf.js CHANGED
@@ -190,12 +190,16 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
190
190
  }
191
191
 
192
192
  async getServices(app) {
193
+ const env = await this.getEnv(app)
194
+ return Object.values(env.system_env_json.VCAP_SERVICES).flat();
195
+ }
196
+
197
+ async getEnv(app) {
193
198
  const { spaceGuid: space_guids, orgGuid: organization_guids } = await this.getCfSpaceInfo();
194
199
  const apps = await this._cfRequest('/v3/apps', { space_guids, organization_guids, names: app });
195
200
  if (apps.resources.length < 1) throw `Cannot find app with name '${app}'`
196
201
  const guid = apps.resources[0].guid;
197
- const env = await this._cfRequest('/v3/apps/'+guid+'/env', { space_guids, organization_guids, names: app });
198
- return Object.values(env.system_env_json.VCAP_SERVICES).flat();
202
+ return await this._cfRequest('/v3/apps/' + guid + '/env', { space_guids, organization_guids, names: app });
199
203
  }
200
204
 
201
205
  async getService(serviceName, showMessage = true) {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "7.7.2",
3
+ "version": "7.8.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@sap/cds-dk",
9
- "version": "7.7.2",
9
+ "version": "7.8.0",
10
10
  "license": "SEE LICENSE IN LICENSE",
11
11
  "dependencies": {
12
12
  "@sap/cds": "^7",
@@ -215,7 +215,7 @@
215
215
  }
216
216
  },
217
217
  "node_modules/@sap/cds": {
218
- "version": "7.7.2",
218
+ "version": "7.8.0",
219
219
  "license": "SEE LICENSE IN LICENSE",
220
220
  "dependencies": {
221
221
  "@cap-js/cds-types": "<1",
@@ -232,7 +232,7 @@
232
232
  }
233
233
  },
234
234
  "node_modules/@sap/cds-compiler": {
235
- "version": "4.7.6",
235
+ "version": "4.8.0",
236
236
  "license": "SEE LICENSE IN LICENSE",
237
237
  "dependencies": {
238
238
  "antlr4": "4.9.3"
@@ -268,7 +268,7 @@
268
268
  }
269
269
  },
270
270
  "node_modules/@sap/cds-mtxs": {
271
- "version": "1.16.0",
271
+ "version": "1.17.0",
272
272
  "license": "SEE LICENSE IN LICENSE",
273
273
  "dependencies": {
274
274
  "@sap/hdi-deploy": "^4",
@@ -556,10 +556,10 @@
556
556
  "license": "MIT"
557
557
  },
558
558
  "node_modules/axios": {
559
- "version": "1.6.7",
559
+ "version": "1.6.8",
560
560
  "license": "MIT",
561
561
  "dependencies": {
562
- "follow-redirects": "^1.15.4",
562
+ "follow-redirects": "^1.15.6",
563
563
  "form-data": "^4.0.0",
564
564
  "proxy-from-env": "^1.1.0"
565
565
  }
@@ -857,7 +857,7 @@
857
857
  }
858
858
  },
859
859
  "node_modules/cookie": {
860
- "version": "0.5.0",
860
+ "version": "0.6.0",
861
861
  "license": "MIT",
862
862
  "engines": {
863
863
  "node": ">= 0.6"
@@ -959,7 +959,7 @@
959
959
  }
960
960
  },
961
961
  "node_modules/detect-libc": {
962
- "version": "2.0.2",
962
+ "version": "2.0.3",
963
963
  "license": "Apache-2.0",
964
964
  "optional": true,
965
965
  "engines": {
@@ -1231,7 +1231,7 @@
1231
1231
  }
1232
1232
  },
1233
1233
  "node_modules/express": {
1234
- "version": "4.18.3",
1234
+ "version": "4.19.2",
1235
1235
  "license": "MIT",
1236
1236
  "dependencies": {
1237
1237
  "accepts": "~1.3.8",
@@ -1239,7 +1239,7 @@
1239
1239
  "body-parser": "1.20.2",
1240
1240
  "content-disposition": "0.5.4",
1241
1241
  "content-type": "~1.0.4",
1242
- "cookie": "0.5.0",
1242
+ "cookie": "0.6.0",
1243
1243
  "cookie-signature": "1.0.6",
1244
1244
  "debug": "2.6.9",
1245
1245
  "depd": "2.0.0",
@@ -1382,7 +1382,7 @@
1382
1382
  "license": "ISC"
1383
1383
  },
1384
1384
  "node_modules/follow-redirects": {
1385
- "version": "1.15.5",
1385
+ "version": "1.15.6",
1386
1386
  "funding": [
1387
1387
  {
1388
1388
  "type": "individual",
@@ -3037,7 +3037,7 @@
3037
3037
  }
3038
3038
  },
3039
3039
  "node_modules/tar": {
3040
- "version": "6.2.0",
3040
+ "version": "6.2.1",
3041
3041
  "devOptional": true,
3042
3042
  "license": "ISC",
3043
3043
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "7.7.2",
3
+ "version": "7.8.0",
4
4
  "description": "Command line client and development toolkit for the SAP Cloud Application Programming Model",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [