@sap/cds-dk 7.8.2 → 7.9.1
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.
- package/bin/add.js +28 -7
- package/bin/bind.js +64 -21
- package/bin/build.js +35 -3
- package/bin/cds.js +31 -13
- package/bin/compile/index.js +44 -3
- package/bin/completion/completionFs.js +61 -0
- package/bin/completion/index.js +212 -0
- package/bin/completion/scripts/cds-bash.sh +17 -0
- package/bin/completion/scripts/cds-ps.ps1 +18 -0
- package/bin/completion/scripts/cds-zsh.sh +12 -0
- package/bin/help.js +8 -2
- package/bin/init.js +34 -4
- package/bin/subscribe.js +5 -1
- package/bin/watch.js +4 -2
- package/lib/bind/KubernetesApiAccessor.js +3 -2
- package/lib/bind/bindingResolver.js +3 -2
- package/lib/bind/bindingUtil.js +10 -8
- package/lib/bind/cfKeyProvider.js +58 -153
- package/lib/bind/index.js +46 -36
- package/lib/bind/kubernetesCredentialsProvider.js +6 -5
- package/lib/bind/userCdsrcJsonStorageProvider.js +25 -3
- package/lib/build/buildTaskEngine.js +1 -1
- package/lib/build/provider/hana/index.js +1 -1
- package/lib/build/provider/hana/template/undeploy.json +3 -1
- package/lib/build/provider/nodejs/index.js +1 -1
- package/lib/compile/openapi/index.js +5 -0
- package/lib/deploy/to-hana/hana.js +25 -7
- package/lib/env/schemas/cds-rc.js +29 -2
- package/lib/import/odata/v4parser.js +78 -25
- package/lib/init/constants.js +2 -1
- package/lib/init/index.js +18 -13
- package/lib/init/merge.js +1 -0
- package/lib/init/mvn.js +8 -47
- package/lib/init/plugin.js +0 -4
- package/lib/init/projectReader.js +17 -10
- package/lib/init/template/approuter/files/values.yaml.hbs +0 -12
- package/lib/init/template/completion/completionSetup.js +184 -0
- package/lib/init/template/completion/index.js +22 -0
- package/lib/init/template/data/as-csv.js +25 -17
- package/lib/init/template/data/as-json.js +115 -30
- package/lib/init/template/data/index.js +28 -15
- package/lib/init/template/hana/files/undeploy.json +2 -1
- package/lib/init/template/helm/files/chart/values.schema.json +1 -1
- package/lib/init/template/helm/files/content-deployment/Chart.yaml +0 -2
- package/lib/init/template/helm/files/content-deployment/values.schema.json +0 -1
- package/lib/init/template/helm/files/service-instance/Chart.yaml +0 -2
- package/lib/init/template/helm/files/service-instance/values.schema.json +0 -1
- package/lib/init/template/helm/files/web-application/Chart.yaml +0 -2
- package/lib/init/template/helm/files/web-application/values.schema.json +0 -1
- package/lib/init/template/helm-unified-runtime/files/values.schema.json +1 -1
- package/lib/init/template/http/index.js +150 -104
- package/lib/init/template/kafka/index.js +6 -0
- package/lib/init/template/lint/index.js +12 -13
- package/lib/init/template/mta/files/mta.yaml.hbs +1 -1
- package/lib/init/template/multitenancy/files/values.yaml.hbs +0 -5
- package/lib/init/template/multitenancy/index.js +13 -5
- package/lib/init/template/nodejs/files/.vscode/settings.json +11 -0
- package/lib/init/template/nodejs/files/eslint.config.js +13 -0
- package/lib/init/template/nodejs/files/package.json.hbs +3 -1
- package/lib/init/template/sample/index.js +18 -20
- package/lib/init/template/tiny-sample/index.js +1 -2
- package/lib/init/template/typer/index.js +2 -2
- package/lib/lint/Linter.js +133 -55
- package/lib/lint/checks.js +5 -6
- package/lib/lint/io.js +119 -10
- package/lib/mtx/auth_manager.js +5 -5
- package/lib/mtx/params.js +4 -2
- package/lib/mtx/settings_manager.js +1 -1
- package/lib/mtx/tenant_action.js +12 -2
- package/lib/mtx/util/logging.js +10 -4
- package/lib/util/axios.js +2 -1
- package/lib/util/cf.js +32 -37
- package/lib/util/git.js +1 -1
- package/lib/util/term.js +1 -1
- package/{bin → lib/watch}/watched.js +2 -2
- package/npm-shrinkwrap.json +34 -34
- package/package.json +2 -2
- package/lib/init/template/nodejs/files/.eslintrc +0 -26
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
],
|
|
188
188
|
"properties": {
|
|
189
189
|
"fromSecret": {
|
|
190
|
-
"description": "Name of a Kubernetes Secret, with the binding content, compliant to the SAP Kubernetes Service Binding spec
|
|
190
|
+
"description": "Name of a Kubernetes Secret, with the binding content, compliant to the SAP Kubernetes Service Binding spec",
|
|
191
191
|
"$ref": "#/definitions/KubernetesName"
|
|
192
192
|
}
|
|
193
193
|
}
|
|
@@ -1,21 +1,26 @@
|
|
|
1
|
+
const https = require('node:https')
|
|
2
|
+
const axios = require('axios')
|
|
3
|
+
const asJson = require("../data/as-json")
|
|
1
4
|
const cds = require('../../../cds')
|
|
2
5
|
const cf = require("../../../util/cf")
|
|
3
6
|
const { write, exists } = cds.utils
|
|
7
|
+
const { filterStringAsRegex } = require("../data/index")
|
|
4
8
|
|
|
5
9
|
module.exports = class HttpTemplate extends require('../../plugin') {
|
|
6
10
|
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#defaultDirName = "*"
|
|
11
|
+
#nodeHost = "http://localhost:4004"
|
|
12
|
+
#javaHost = "http://localhost:8080"
|
|
10
13
|
#defaultOutputDir = "http"
|
|
11
|
-
#defaultAuth = `Authorization: Basic
|
|
14
|
+
#defaultAuth = `Authorization: Basic alice:`
|
|
12
15
|
|
|
13
16
|
options() {
|
|
14
17
|
return {
|
|
15
18
|
'filter': {
|
|
16
19
|
type: 'string',
|
|
17
20
|
short: 'f',
|
|
18
|
-
help: `
|
|
21
|
+
help: `Filter for services or entities or actions matching the given pattern. If it contains meta
|
|
22
|
+
characters like '^' or '*', it is treated as a regular expression,
|
|
23
|
+
otherwise as an include pattern, i.e /.*pattern.*/i`
|
|
19
24
|
},
|
|
20
25
|
'for-app': {
|
|
21
26
|
type: 'string',
|
|
@@ -26,146 +31,187 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
26
31
|
type: 'string',
|
|
27
32
|
short: 'o',
|
|
28
33
|
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.'
|
|
34
|
+
},
|
|
35
|
+
'dry': {
|
|
36
|
+
type: 'boolean',
|
|
37
|
+
help: 'Print the generated requests to the console instead of writing them to a file.'
|
|
29
38
|
}
|
|
30
39
|
}
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
async run() {
|
|
34
|
-
|
|
35
|
-
const path = cds.cli.options['filter'] || this.#defaultDirName
|
|
36
|
-
const csn = await cds.load(path)
|
|
43
|
+
const csn = await cds.load(cds.env.roots).then(cds.minify)
|
|
37
44
|
cds.linked(csn)
|
|
38
45
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
let
|
|
46
|
+
const serviceInfo = cds.compile.to.serviceinfo(csn)
|
|
47
|
+
let [hostname, auth] = await this.#getHostnameAndAuth(serviceInfo)
|
|
48
|
+
let services = this.#collectServiceInfo(csn, serviceInfo)
|
|
49
|
+
if (!services) return
|
|
42
50
|
|
|
43
|
-
this.#
|
|
51
|
+
const payload = await this.#generatePayload(csn, services)
|
|
52
|
+
this.#generateRequests(services, hostname, auth, payload)
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
async #getHostnameAndAuth() {
|
|
55
|
+
async #getHostnameAndAuth(serviceInfo) {
|
|
47
56
|
if (!cds.cli.options['for-app']) {
|
|
48
|
-
|
|
57
|
+
if (serviceInfo[0].runtime === 'Java') {
|
|
58
|
+
return [this.#javaHost, null]
|
|
59
|
+
} else {
|
|
60
|
+
return [this.#nodeHost, this.#defaultAuth]
|
|
61
|
+
}
|
|
49
62
|
}
|
|
50
63
|
|
|
51
|
-
const
|
|
64
|
+
const app = cds.cli.options['for-app']
|
|
65
|
+
// Later: differentiate between CF and Kyma
|
|
66
|
+
const env = await cf.getEnv(app)
|
|
67
|
+
if (!cds.cli.options.dry) console.error('Fetching credentials from Cloud Foundry app \'' + app + '\'...')
|
|
52
68
|
const hostname = `https://${env.application_env_json.VCAP_APPLICATION.application_uris[0]}`
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
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
|
-
}
|
|
69
|
+
const xsuaaCredentials = env.system_env_json.VCAP_SERVICES?.xsuaa[0]?.credentials ?? {}
|
|
70
|
+
const { url, clientid, clientsecret, certurl, certificate, key } = xsuaaCredentials
|
|
71
|
+
if (!url) throw new Error('No XSUAA service found in the environment of ' + app)
|
|
72
|
+
|
|
73
|
+
const auth = certificate ? { maxRedirects: 0, httpsAgent: new https.Agent({ cert: certificate, key }) }
|
|
74
|
+
: { auth: { username: clientid, password: clientsecret } }
|
|
75
|
+
const authUrl = `${certurl ?? url}/oauth/token`
|
|
76
|
+
const data = `grant_type=client_credentials&client_id=${encodeURI(clientid)}`
|
|
77
|
+
const config = { method: 'POST', timeout: 5000, data, ...auth }
|
|
78
|
+
const { access_token } = (await axios(authUrl, config)).data
|
|
79
|
+
const approuterAuth = `x-approuter-authorization: bearer ${access_token}`
|
|
80
|
+
return [hostname, approuterAuth]
|
|
71
81
|
}
|
|
72
82
|
|
|
73
|
-
#
|
|
74
|
-
const
|
|
83
|
+
#collectServiceInfo(csn, serviceInfo) {
|
|
84
|
+
const services = new Map()
|
|
85
|
+
|
|
86
|
+
const filter = cds.cli.options['filter']
|
|
75
87
|
|
|
76
88
|
csn.services.map((s) => {
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
let
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
const sInfo = serviceInfo.find(info => info.name === s.name)
|
|
90
|
+
const urlPaths = sInfo?.endpoints?.map(e => e.path) ?? [sInfo?.urlPath]
|
|
91
|
+
if (!urlPaths) return
|
|
92
|
+
let entities = []
|
|
93
|
+
let actions = []
|
|
94
|
+
|
|
95
|
+
for (const c of csn.childrenOf(s.name)) {
|
|
96
|
+
if (c.name.match(filterStringAsRegex(filter))) {
|
|
97
|
+
if (c.name.includes('.texts')) continue
|
|
98
|
+
c.kind === 'entity' ? entities.push(this.#addNameAndReadOnly(c)) : c.kind === 'action' ? actions.push(c) : null
|
|
85
99
|
}
|
|
86
|
-
entityInfo.push(this.#addNameAndReadOnly(entities[e]))
|
|
87
100
|
}
|
|
88
|
-
|
|
89
|
-
|
|
101
|
+
if (entities.length === 0 && actions.length === 0) return
|
|
102
|
+
services.set(s.name, { entities, urlPaths, actions })
|
|
90
103
|
})
|
|
91
104
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
105
|
+
if (services.size === 0) {
|
|
106
|
+
console.error(`No services found for filter ${filter}`)
|
|
107
|
+
process.exitCode = 1
|
|
108
|
+
return
|
|
109
|
+
}
|
|
95
110
|
|
|
96
|
-
return
|
|
111
|
+
return services
|
|
97
112
|
}
|
|
98
113
|
|
|
99
|
-
#
|
|
100
|
-
return
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
: ["odata"]
|
|
114
|
+
#addNameAndReadOnly(e) {
|
|
115
|
+
return {
|
|
116
|
+
name: e.name,
|
|
117
|
+
readOnly: (e["@readonly"] || e["@cds.autoexpose"] || e['@odata.singleton']) ?? false,
|
|
118
|
+
}
|
|
105
119
|
}
|
|
106
120
|
|
|
107
|
-
#
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
}
|
|
121
|
+
async #generatePayload(csn, services) {
|
|
122
|
+
const payload = {}
|
|
123
|
+
for (const [, service] of services) {
|
|
124
|
+
for (const e of service.entities) {
|
|
125
|
+
payload[e.name] = []
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
-
return
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
name: e.name.split(".")[1],
|
|
134
|
-
readOnly: (e["@readonly"] || e["@cds.autoexpose"] || e['@odata.singleton']) ?? false,
|
|
128
|
+
// csn.all('entity').reduce((all, e) => { all[e.name] = []; return all }, {})
|
|
129
|
+
await asJson(payload, csn, 1)
|
|
130
|
+
for (const p in payload) {
|
|
131
|
+
delete payload[p][0].texts
|
|
132
|
+
delete payload[p][0].localized
|
|
135
133
|
}
|
|
134
|
+
return payload
|
|
136
135
|
}
|
|
137
136
|
|
|
138
|
-
#generateRequests(
|
|
139
|
-
for
|
|
137
|
+
#generateRequests(services, hostname, auth, payload) {
|
|
138
|
+
let varsDefined = false // when writing requests for multiple services to stdout, define vars only once
|
|
139
|
+
for (const [serviceName, service] of services) {
|
|
140
140
|
let requests = []
|
|
141
|
+
if (!cds.cli.options.dry || !varsDefined) {
|
|
142
|
+
requests.push(auth ? `@server = ${hostname}\n@auth = ${auth}\n` : `@server = ${hostname}\n`)
|
|
143
|
+
}
|
|
144
|
+
varsDefined = true
|
|
145
|
+
const headers = auth ? `Content-Type: application/json\n{{auth}}` : `Content-Type: application/json`
|
|
141
146
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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`)
|
|
147
|
+
service.urlPaths.forEach((urlPath) => {
|
|
148
|
+
if (service.urlPaths.length > 1) {
|
|
149
|
+
requests.push(`\n# Service endpoint: ${urlPath}`)
|
|
150
|
+
}
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
service.entities.forEach((e) => {
|
|
153
|
+
const entityNameSimple = e.name.slice(e.name.lastIndexOf('.') + 1)
|
|
154
|
+
const url = `{{server}}/${urlPath}${entityNameSimple}`
|
|
155
|
+
const requestTitle = `\n### ${e.name}`
|
|
156
|
+
requests.push(`${requestTitle}\nGET ${url}\n${headers}\n`)
|
|
157
|
+
if (e.readOnly) {
|
|
158
|
+
return
|
|
159
|
+
}
|
|
155
160
|
|
|
156
|
-
|
|
157
|
-
|
|
161
|
+
const entityData = payload[`${e.name}`] ? payload[`${e.name}`][0] : null
|
|
162
|
+
if (!entityData) return
|
|
163
|
+
// TODO handle ids that are not named ID
|
|
164
|
+
if (!Object.keys(entityData).some(k => k === "ID")) return // no need to write requests for entities without ids
|
|
165
|
+
|
|
166
|
+
const postBody = JSON.stringify(entityData, null, 2)
|
|
167
|
+
requests.push(`${requestTitle}\nPOST ${url}\n${headers}\n\n${postBody}\n`)
|
|
168
|
+
requests.push(`${requestTitle}\nPATCH ${url}/${entityData.ID}\n${headers}\n\n${postBody}\n`)
|
|
169
|
+
requests.push(`${requestTitle}\nDELETE ${url}/${entityData.ID}\n${headers}\n`)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
service.actions.forEach((a) => {
|
|
173
|
+
const url = `{{server}}/${urlPath}${a.name.split(".")[1]}`
|
|
174
|
+
|
|
175
|
+
let actionPayload = {}
|
|
176
|
+
if (a.params) {
|
|
177
|
+
for (const [key, value] of Object.entries(a.params)) {
|
|
178
|
+
actionPayload[key] = value.type === 'cds.Integer' ? 1
|
|
179
|
+
: value.type === 'string' ? 'sample string'
|
|
180
|
+
// TODO reference an object that has been generated
|
|
181
|
+
: typeof value.type === 'object' && value.type.ref ? payload[value.type.ref[0]][0][value.type.ref[1]]
|
|
182
|
+
: null
|
|
183
|
+
}
|
|
158
184
|
}
|
|
159
|
-
|
|
185
|
+
requests.push(`\n### ${a.name}\nPOST ${url}\n${headers}\n\n${JSON.stringify(actionPayload, null, 2)}\n`)
|
|
186
|
+
})
|
|
187
|
+
|
|
160
188
|
})
|
|
189
|
+
this.#write(requests, serviceName)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
161
192
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
193
|
+
#write(requests, serviceName) {
|
|
194
|
+
const http = requests.join('\n')
|
|
195
|
+
if (cds.cli.options.dry) {
|
|
196
|
+
console.log(http)
|
|
197
|
+
} else {
|
|
198
|
+
const term = require('../../../util/term')
|
|
199
|
+
const outputPath = this.#determineOutputPath(serviceName)
|
|
200
|
+
if (exists(outputPath)) {
|
|
201
|
+
if (cds.cli.options.force) {
|
|
202
|
+
console.log(term.dim(` Overwriting ${outputPath}`))
|
|
203
|
+
} else {
|
|
204
|
+
console.log(term.dim(` Skipping ${outputPath}`))
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
console.log(term.dim(` Creating ${outputPath}`))
|
|
209
|
+
}
|
|
210
|
+
write(http).to(outputPath)
|
|
165
211
|
}
|
|
166
212
|
}
|
|
167
213
|
|
|
168
|
-
#determineOutputPath(
|
|
214
|
+
#determineOutputPath(serviceName) {
|
|
169
215
|
let outputDir
|
|
170
216
|
const { out } = cds.cli.options
|
|
171
217
|
if (out) {
|
|
@@ -175,7 +221,7 @@ module.exports = class HttpTemplate extends require('../../plugin') {
|
|
|
175
221
|
} else {
|
|
176
222
|
outputDir = this.#defaultOutputDir
|
|
177
223
|
}
|
|
178
|
-
return `${outputDir}/${
|
|
224
|
+
return `${outputDir}/${serviceName}.http`
|
|
179
225
|
}
|
|
180
226
|
}
|
|
181
227
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const cds = require('../../../cds')
|
|
2
|
+
const { exists, fs, path } = cds.utils
|
|
1
3
|
const { readProject } = require('../../projectReader')
|
|
2
4
|
const { merge } = require('../../merge')
|
|
3
5
|
const { srv4, kafkaMessaging } = require('../../registries/mta')
|
|
@@ -6,6 +8,10 @@ const mvn = require('../../mvn')
|
|
|
6
8
|
module.exports = class KafkaTemplate extends require('../../plugin') {
|
|
7
9
|
|
|
8
10
|
static hasInProduction(env) {
|
|
11
|
+
if (exists('pom.xml')) {
|
|
12
|
+
if (!exists('srv/pom.xml')) return false
|
|
13
|
+
return fs.readFileSync(path.resolve(cds.root, 'srv/pom.xml'), 'utf8').includes('cds-feature-kafka')
|
|
14
|
+
}
|
|
9
15
|
return !!(env.requires?.messaging?.kind === 'kafka')
|
|
10
16
|
}
|
|
11
17
|
|
|
@@ -11,7 +11,7 @@ const cds = require("../../../cds");
|
|
|
11
11
|
const { mkdirp, exists } = cds.utils;
|
|
12
12
|
const IS_WIN = os.platform() === "win32";
|
|
13
13
|
const VSCODE_FILE_ASSOCIATIONS = {
|
|
14
|
-
"csv": [
|
|
14
|
+
"csv": ["csv", "csv (semicolon)", "tsv", "tab"]
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
module.exports = class LintTemplate extends require('../../plugin') {
|
|
@@ -43,7 +43,7 @@ module.exports = class LintTemplate extends require('../../plugin') {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
async run() {
|
|
46
|
-
// Require ESLint v>=
|
|
46
|
+
// Require ESLint v>=8.0.0
|
|
47
47
|
if (!this.isTest) {
|
|
48
48
|
this._checkEslint();
|
|
49
49
|
}
|
|
@@ -69,21 +69,20 @@ module.exports = class LintTemplate extends require('../../plugin') {
|
|
|
69
69
|
// Generate VS Code extension settings
|
|
70
70
|
const { getFileExtensions } = require("@sap/eslint-plugin-cds/lib/api");
|
|
71
71
|
const ALLOWED_FILE_EXTENSIONS = getFileExtensions();
|
|
72
|
-
|
|
72
|
+
const extensions = new Set(ALLOWED_FILE_EXTENSIONS.map((ext) => {
|
|
73
73
|
return path.extname(ext).replace('.', '');
|
|
74
|
-
})
|
|
74
|
+
}))
|
|
75
75
|
extensions.forEach((ext) => {
|
|
76
76
|
if (ext in VSCODE_FILE_ASSOCIATIONS) {
|
|
77
77
|
VSCODE_FILE_ASSOCIATIONS[ext].forEach((assoc) => {
|
|
78
|
-
extensions.
|
|
78
|
+
extensions.add(assoc)
|
|
79
79
|
})
|
|
80
80
|
}
|
|
81
81
|
});
|
|
82
82
|
await io.sanitizeVscodeSettings(
|
|
83
|
-
cds.root,
|
|
84
83
|
path.join(cds.root, ".vscode/settings.json"),
|
|
85
84
|
this.lintType,
|
|
86
|
-
extensions,
|
|
85
|
+
Array.from(extensions),
|
|
87
86
|
this.customRuleExample
|
|
88
87
|
);
|
|
89
88
|
}
|
|
@@ -124,8 +123,8 @@ module.exports = class LintTemplate extends require('../../plugin') {
|
|
|
124
123
|
_checkEslint() {
|
|
125
124
|
const eslintCmd = checks.resolveEslint(cds.root, '');
|
|
126
125
|
if (!eslintCmd) {
|
|
127
|
-
this.missingNpmDependencies["names"].push("eslint");
|
|
128
|
-
this.missingNpmDependencies["msgs"].push("ESLint v>=
|
|
126
|
+
this.missingNpmDependencies["names"].push("eslint@^9");
|
|
127
|
+
this.missingNpmDependencies["msgs"].push("ESLint v>=9.0.0");
|
|
129
128
|
}
|
|
130
129
|
return;
|
|
131
130
|
}
|
|
@@ -135,10 +134,11 @@ module.exports = class LintTemplate extends require('../../plugin') {
|
|
|
135
134
|
"@sap/eslint-plugin-cds",
|
|
136
135
|
cds.root
|
|
137
136
|
);
|
|
137
|
+
|
|
138
138
|
// Require locally installed ESLint CDS plugin
|
|
139
139
|
if (!eslintPluginLocal) {
|
|
140
|
-
this.missingNpmDependencies["names"].push("@sap/eslint-plugin-cds");
|
|
141
|
-
this.missingNpmDependencies["msgs"].push("ESLint plugin for CDS");
|
|
140
|
+
this.missingNpmDependencies["names"].push("@sap/eslint-plugin-cds@^3.0.0");
|
|
141
|
+
this.missingNpmDependencies["msgs"].push("ESLint plugin for CDS v>=3.0.0");
|
|
142
142
|
}
|
|
143
143
|
return;
|
|
144
144
|
}
|
|
@@ -153,10 +153,9 @@ module.exports = class LintTemplate extends require('../../plugin') {
|
|
|
153
153
|
const { getConfigPath } = require("@sap/eslint-plugin-cds/lib/api");
|
|
154
154
|
let configPath = getConfigPath(cds.root);
|
|
155
155
|
if (!configPath) {
|
|
156
|
-
configPath = path.join(cds.root, ".
|
|
156
|
+
configPath = path.join(cds.root, "eslint.config.js");
|
|
157
157
|
}
|
|
158
158
|
await io.sanitizeEslintConfig(configPath, this.customRuleExample);
|
|
159
|
-
return;
|
|
160
159
|
}
|
|
161
160
|
|
|
162
161
|
async _promptInstallDependencies(task) {
|
|
@@ -33,7 +33,7 @@ modules:
|
|
|
33
33
|
properties:
|
|
34
34
|
SPRING_PROFILES_ACTIVE: cloud,sandbox
|
|
35
35
|
JBP_CONFIG_COMPONENTS: "jres: ['com.sap.xs.java.buildpack.jre.SAPMachineJRE']"
|
|
36
|
-
JBP_CONFIG_SAP_MACHINE_JRE: '{ use_offline_repository: false, version:
|
|
36
|
+
JBP_CONFIG_SAP_MACHINE_JRE: '{ use_offline_repository: false, version: {{jdkVersion}}.+ }'
|
|
37
37
|
build-parameters:
|
|
38
38
|
builder: custom
|
|
39
39
|
commands:
|
|
@@ -78,7 +78,14 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
if (hasHelm || hasHelmUnifiedRuntime) {
|
|
81
|
-
|
|
81
|
+
const overwrites = hasXsuaa ? [{
|
|
82
|
+
item: [{
|
|
83
|
+
in: 'xsuaa',
|
|
84
|
+
where: { serviceOfferingName: 'xsuaa' }
|
|
85
|
+
}, 'parameters.tenant-mode'], withValue: 'shared'
|
|
86
|
+
}] : []
|
|
87
|
+
|
|
88
|
+
await merge(__dirname, 'files/values.yaml.hbs').into('chart/values.yaml', { with: project, overwrites })
|
|
82
89
|
|
|
83
90
|
const envFromObject = {
|
|
84
91
|
envFrom: [{
|
|
@@ -100,11 +107,12 @@ module.exports = class MultitenancyTemplate extends require('../../plugin') {
|
|
|
100
107
|
}
|
|
101
108
|
}
|
|
102
109
|
]
|
|
103
|
-
}
|
|
110
|
+
}
|
|
111
|
+
}).into('chart/values.yaml', { with: project })
|
|
104
112
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
await removeFromYAML(join('chart', 'values.yaml'), ['srv.expose', 'srv.networkSecurity'])
|
|
114
|
+
await copy(join(__dirname, 'files', `mtxs-configmap-${isJava ? 'java' : 'nodejs'}${hasApprouter ? '-approuter' : ''}.yaml`)).to('chart', 'templates', 'mtxs-configmap.yaml') // REVISIT: Move to build task
|
|
115
|
+
await copy(join(__dirname, 'files', 'saas-registry-secret.yaml')).to('chart', 'templates', 'saas-registry-secret.yaml') // REVISIT: Move to build task
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
if (hasXsuaa) {
|
|
@@ -1,33 +1,31 @@
|
|
|
1
1
|
const { join } = require('path')
|
|
2
2
|
const cds = require('../../../cds'), { exists, read, write, copy } = cds.utils
|
|
3
3
|
const { env4, readProject } = require('../../projectReader')
|
|
4
|
+
const mvn = require('../../mvn')
|
|
4
5
|
|
|
5
6
|
module.exports = class SampleTemplate extends require('../../plugin') {
|
|
6
7
|
|
|
7
|
-
async canRun() {
|
|
8
|
-
const { isJava } = readProject()
|
|
9
|
-
if (isJava) throw `'cds add sample' is not available for Java yet`
|
|
10
|
-
return true
|
|
11
|
-
}
|
|
12
|
-
|
|
13
8
|
async run() {
|
|
14
9
|
const { db, srv, app } = env4('production').folders
|
|
15
10
|
const language = exists('pom.xml') ? 'java' : 'nodejs'
|
|
16
11
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
if (language === 'nodejs') {
|
|
13
|
+
await Promise.all([
|
|
14
|
+
copy(join(__dirname, 'files/db')).to(db),
|
|
15
|
+
copy(join(__dirname, 'files/srv')).to(srv)
|
|
16
|
+
])
|
|
17
|
+
await copy(join(__dirname, 'files', language)).to(srv)
|
|
18
|
+
await copy(join(__dirname, 'files', 'app')).to(app)
|
|
19
|
+
const { appName, appPath, appUIPaths } = readProject()
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
// manifest.json must be unique // REVISIT: Check if relevant for `cds add sample` in Java scenario
|
|
22
|
+
await Promise.all(appUIPaths.map(async p => {
|
|
23
|
+
const manifest = await read(join(appPath, p, 'webapp/manifest.json'))
|
|
24
|
+
manifest['sap.app'].id = appName + '.' + p
|
|
25
|
+
await write(join(appPath, p, 'webapp/manifest.json'), manifest, { spaces: 2 })
|
|
26
|
+
}))
|
|
27
|
+
} else if (language === 'java') {
|
|
28
|
+
await mvn.add('sample')
|
|
29
|
+
}
|
|
32
30
|
}
|
|
33
31
|
}
|
|
@@ -11,8 +11,7 @@ module.exports = class TinySampleTemplate extends require('../../plugin') {
|
|
|
11
11
|
const { isJava } = readProject()
|
|
12
12
|
if (isJava) {
|
|
13
13
|
if (!cds.cli.options.add.has(JAVA)) {
|
|
14
|
-
await mvn.
|
|
15
|
-
await mvn.execMvn(mvn.getAddTestsCmdArgs())
|
|
14
|
+
await mvn.add('tiny_sample')
|
|
16
15
|
}
|
|
17
16
|
} else {
|
|
18
17
|
const { db, srv } = env4('production').folders
|