@sap/cds-dk 7.7.2 → 7.8.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 +40 -17
- package/bin/bind.js +21 -12
- package/bin/build.js +14 -2
- package/bin/cds.js +8 -0
- package/bin/deploy/to-sqlite.js +1 -1
- package/bin/help.js +4 -1
- package/bin/version.js +54 -22
- package/lib/bind/userCdsrcJsonStorageProvider.js +1 -1
- package/lib/build/buildTaskFactory.js +2 -2
- package/lib/build/provider/hana/index.js +1 -1
- package/lib/build/provider/internalBuildTaskProvider.js +23 -11
- package/lib/build/provider/mtx/resourcesTarBuilder.js +16 -4
- package/lib/build/provider/mtx-extension/extensionCompilation.js +13 -2
- package/lib/build/provider/mtx-extension/index.js +6 -2
- package/lib/build/provider/nodejs/index.js +4 -4
- package/lib/build/util.js +7 -18
- package/lib/compile/openapi/index.js +2 -1
- package/lib/index.js +2 -2
- package/lib/init/constants.js +3 -2
- package/lib/init/index.js +9 -23
- package/lib/init/plugin.js +8 -0
- package/lib/init/registries/mta.js +6 -0
- package/lib/init/template/data/as-csv.js +60 -0
- package/lib/init/template/data/as-json.js +441 -0
- package/lib/init/template/data/index.js +116 -86
- package/lib/init/template/enterprise-messaging/files/package.json.hbs +2 -1
- package/lib/init/template/enterprise-messaging/index.js +16 -3
- package/lib/init/template/enterprise-messaging-shared/files/package.json.hbs +2 -1
- package/lib/init/template/enterprise-messaging-shared/index.js +16 -3
- package/lib/init/template/helm/files/content-deployment/Chart.yaml +1 -1
- package/lib/init/template/helm/files/content-deployment/templates/job.yaml +7 -0
- package/lib/init/template/helm/files/content-deployment/values.schema.json +17 -0
- package/lib/init/template/helm/files/content-deployment/values.yaml +6 -1
- package/lib/init/template/helm/files/service-instance/Chart.yaml +1 -1
- package/lib/init/template/helm/files/service-instance/templates/serviceinstance.yaml +4 -0
- package/lib/init/template/helm/files/service-instance/values.schema.json +17 -1
- package/lib/init/template/helm/files/service-instance/values.yaml +4 -1
- package/lib/init/template/helm/files/values.yaml.hbs +2 -2
- package/lib/init/template/helm/files/web-application/Chart.yaml +1 -1
- package/lib/init/template/helm/files/web-application/templates/api-rule.yaml +4 -0
- package/lib/init/template/helm/files/web-application/templates/network-policy.yaml +15 -0
- package/lib/init/template/helm/files/web-application/values.schema.json +41 -0
- package/lib/init/template/helm/files/web-application/values.yaml +19 -1
- package/lib/init/template/helm/index.js +5 -2
- package/lib/init/template/helm-unified-runtime/files/values.yaml.hbs +2 -2
- package/lib/init/template/helm-unified-runtime/index.js +13 -7
- package/lib/init/template/html5-repo/index.js +0 -1
- package/lib/init/template/http/index.js +182 -0
- package/lib/init/template/kafka/files/mta.yaml.hbs +14 -0
- package/lib/init/template/kafka/files/package.json.hbs +22 -0
- package/lib/init/template/kafka/files/values.yaml +10 -0
- package/lib/init/template/kafka/index.js +39 -0
- package/lib/init/template/lint/index.js +4 -4
- package/lib/init/template/mta/files/mta.yaml.hbs +4 -0
- package/lib/init/template/multitenancy/files/values.yaml.hbs +2 -2
- package/lib/util/cf.js +6 -2
- package/npm-shrinkwrap.json +269 -293
- package/package.json +1 -1
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
const cds = require('../../..')
|
|
2
2
|
const { copy, rimraf, exists, fs } = cds.utils
|
|
3
3
|
const { join } = require('path')
|
|
4
|
-
|
|
5
|
-
// cds.build?.register('helm-unified-runtime', {
|
|
6
|
-
// impl: '@cap-js-community/helm-unified-runtime/lib/build.js'
|
|
7
|
-
// })
|
|
8
|
-
|
|
9
4
|
const { merge, readProject } = cds.add ?? {}
|
|
10
5
|
|
|
11
|
-
module.exports = class HelmUnifiedRuntimeTemplate extends
|
|
6
|
+
module.exports = class HelmUnifiedRuntimeTemplate extends require('../../plugin') {
|
|
7
|
+
|
|
8
|
+
options() {
|
|
9
|
+
return {
|
|
10
|
+
'internal-unified-runtime-charts': {
|
|
11
|
+
type: 'boolean'
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
12
15
|
|
|
13
16
|
async canRun() {
|
|
14
17
|
if (cds.cli.options.force) {
|
|
@@ -37,7 +40,7 @@ module.exports = class HelmUnifiedRuntimeTemplate extends cds.add.Plugin {
|
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
async combine() {
|
|
40
|
-
const { hasDestination, hasEnterpriseMessaging, hasHana, hasMultitenancy, hasXsuaa, hasHtml5Repo, hasApprouter } = readProject()
|
|
43
|
+
const { hasDestination, hasEnterpriseMessaging, hasHana, hasMultitenancy, hasXsuaa, hasHtml5Repo, hasApprouter, hasKafka } = readProject()
|
|
41
44
|
|
|
42
45
|
if (hasApprouter) {
|
|
43
46
|
await this._mergeDependency('web-application', 'approuter')
|
|
@@ -62,6 +65,9 @@ module.exports = class HelmUnifiedRuntimeTemplate extends cds.add.Plugin {
|
|
|
62
65
|
if (hasXsuaa) {
|
|
63
66
|
await this._mergeDependency('service-instance', 'xsuaa')
|
|
64
67
|
}
|
|
68
|
+
if (hasKafka) {
|
|
69
|
+
await this._mergeDependency('service-instance', 'kafka')
|
|
70
|
+
}
|
|
65
71
|
if (hasHana) {
|
|
66
72
|
if (hasMultitenancy) {
|
|
67
73
|
await this._mergeDependency('service-instance', 'service-manager')
|
|
@@ -57,7 +57,6 @@ module.exports = class Html5RepoTemplate extends require('../../plugin') {
|
|
|
57
57
|
into: [appContent, 'requires', 'name'],
|
|
58
58
|
}]
|
|
59
59
|
if (hasApprouter) {
|
|
60
|
-
console.log('aaddd')
|
|
61
60
|
additions.push(approuter, html5Runtime)
|
|
62
61
|
relationships.push({
|
|
63
62
|
insert: [html5Runtime, 'name'],
|
|
@@ -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,39 @@
|
|
|
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, hasHelm, hasHelmUnifiedRuntime } = 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
|
+
if (hasHelm || hasHelmUnifiedRuntime) {
|
|
36
|
+
await merge(__dirname, 'files/values.yaml').into('chart/values.yaml')
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -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
|
|
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']"
|
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
|
-
|
|
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) {
|