@sap/cds-dk 4.9.7 → 6.0.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/CHANGELOG.md +56 -0
- package/bin/activate.js +4 -4
- package/bin/add.js +11 -5
- package/bin/bind.js +16 -28
- package/bin/build/all.js +2 -2
- package/bin/build/index.js +12 -19
- package/bin/cds.js +16 -17
- package/bin/compile/index.js +12 -3
- package/bin/deploy/index.js +14 -14
- package/bin/deploy/to-hana.js +4 -4
- package/bin/deploy/to-sqlite.js +1 -1
- package/bin/env.js +13 -4
- package/bin/eval.js +1 -1
- package/bin/fix-redirects.js +1 -1
- package/bin/import.js +44 -179
- package/bin/repl.js +1 -1
- package/bin/watch.js +10 -15
- package/lib/bind/BindingManager.js +87 -0
- package/lib/bind/bindingResolver.js +14 -85
- package/lib/bind/bindingUtil.js +18 -11
- package/lib/bind/cfKeyProvider.js +58 -29
- package/lib/bind/kubernetesCredentialsProvider.js +44 -21
- package/lib/bind/userCdsrcJsonStorageProvider.js +12 -18
- package/lib/cds.js +13 -0
- package/lib/client/base_command.js +3 -2
- package/lib/client/extend.js +6 -7
- package/lib/client/helper/fs_helper.js +7 -8
- package/lib/client/settings_manager.js +14 -10
- package/lib/compile/index.js +9 -10
- package/lib/compile/openapi/csdl2openapi.js +151 -43
- package/lib/compile/openapi/index.js +2 -2
- package/lib/compile/to-xsuaa.js +3 -2
- package/lib/import/common.js +342 -27
- package/lib/import/importapi.js +81 -13
- package/lib/import/message.js +4 -4
- package/lib/import/odata/index.js +141 -0
- package/lib/import/{utils → odata/metadataconverter}/MetadataConverter.js +2 -2
- package/lib/import/{utils → odata/metadataconverter}/MetadataConverterFactory.js +1 -1
- package/lib/import/{utils → odata/util}/MultiMap.js +0 -0
- package/lib/import/{v2parser.js → odata/v2parser.js} +405 -468
- package/lib/import/{v4parser.js → odata/v4parser.js} +311 -322
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Aggregation.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Authorization.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Capabilities.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Core.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Measures.V1.json +0 -0
- package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Validation.V1.json +0 -0
- package/lib/import/openapi/importOpenAPI.js +521 -0
- package/lib/import/openapi/index.js +19 -0
- package/lib/import/openapi/utilities.js +38 -0
- package/lib/index.js +1 -1
- package/lib/init/constants.js +7 -2
- package/lib/init/index.js +10 -3
- package/lib/init/template/_merging/existences.js +79 -19
- package/lib/init/template/approuter/files/mta.yaml.hbs +25 -11
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/approuter/files/xs-app.json.hbs +7 -0
- package/lib/init/template/approuter/index.js +61 -54
- package/lib/init/template/auditlog/index.js +2 -2
- package/lib/init/template/data/index.js +2 -2
- package/lib/init/template/hana/files/mta.yaml.hbs +17 -17
- package/lib/init/template/hana/index.js +8 -4
- package/lib/init/template/helm/index.js +39 -0
- package/lib/init/template/helm/update/index.js +28 -0
- package/lib/init/template/java/index.js +7 -9
- package/lib/init/template/kibana-logging/index.js +5 -6
- package/lib/init/template/mta/index.js +4 -4
- package/lib/init/template/mtx/files/java/package.json +9 -9
- package/lib/init/template/mtx/files/mta.yaml.hbs +0 -7
- package/lib/init/template/mtx/files/xs-security.json.hbs +11 -20
- package/lib/init/template/mtx/index.js +50 -57
- package/lib/init/template/nodejs/files/package.json.hbs +2 -2
- package/lib/init/template/notebook/index.js +2 -0
- package/lib/init/template/templateBase.js +6 -4
- package/lib/init/template/xsuaa/index.js +9 -5
- package/lib/init/util/fsUtil.js +25 -10
- package/lib/init/util/logger.js +1 -1
- package/lib/init/util/mvnArchetypeUtil.js +4 -10
- package/lib/init/util/projectReader.js +16 -8
- package/lib/init/util/templateUtil.js +30 -34
- package/lib/k8s/ChartAdder.js +65 -0
- package/lib/k8s/ChartBuilder.js +186 -0
- package/lib/k8s/Context.js +71 -0
- package/lib/k8s/ImageUtil.js +21 -0
- package/lib/k8s/Model.d.ts +53 -0
- package/lib/k8s/auditlog/index.js +29 -0
- package/lib/k8s/auditlog/templates/auditlog.yaml +1 -0
- package/lib/k8s/buildModel.js +70 -0
- package/lib/k8s/connectivity/index.js +74 -0
- package/lib/k8s/connectivity/templates/connectivity-binding.yaml +9 -0
- package/lib/k8s/connectivity/templates/connectivity-proxy-info.yaml +16 -0
- package/lib/k8s/connectivity/templates/connectivity.yaml +1 -0
- package/lib/k8s/deployment/index.js +81 -0
- package/lib/k8s/deployment/subchart/Chart.yaml +6 -0
- package/lib/k8s/deployment/subchart/templates/_helpers.tpl +156 -0
- package/lib/k8s/deployment/subchart/templates/api-rule.yaml +24 -0
- package/lib/k8s/deployment/subchart/templates/deployment.yaml +110 -0
- package/lib/k8s/deployment/subchart/templates/network-policy.yaml +16 -0
- package/lib/k8s/deployment/subchart/templates/pod-disruption-budget.yaml +11 -0
- package/lib/k8s/deployment/subchart/templates/secret.yaml +10 -0
- package/lib/k8s/deployment/subchart/templates/service-binding.yaml +27 -0
- package/lib/k8s/deployment/subchart/templates/service.yaml +11 -0
- package/lib/k8s/deployment/subchart/values-lint.yaml +8 -0
- package/lib/k8s/deployment/subchart/values.schema.json +449 -0
- package/lib/k8s/deployment/subchart/values.yaml +76 -0
- package/lib/k8s/deployment/subchart-override/templates/_web_application_helpers.tpl +81 -0
- package/lib/k8s/destinations/index.js +40 -0
- package/lib/k8s/destinations/lib.js +16 -0
- package/lib/k8s/destinations/templates/destinations.yaml +1 -0
- package/lib/k8s/docker/push.js +8 -0
- package/lib/k8s/event-mesh/index.js +53 -0
- package/lib/k8s/event-mesh/lib.js +9 -0
- package/lib/k8s/event-mesh/templates/event-mesh.yaml +1 -0
- package/lib/k8s/hana-deployer/index.js +38 -0
- package/lib/k8s/hana-deployer/templates/hana-deployer-job.yaml +41 -0
- package/lib/k8s/helm/Release.js +80 -0
- package/lib/k8s/html5-apps-deployer/index.js +43 -0
- package/lib/k8s/html5-apps-deployer/templates/_html5_deployment_helpers.tpl +16 -0
- package/lib/k8s/html5-apps-deployer/templates/html5-apps-deployer-job.yaml +53 -0
- package/lib/k8s/html5-apps-repo-host/index.js +26 -0
- package/lib/k8s/html5-apps-repo-host/lib.js +16 -0
- package/lib/k8s/html5-apps-repo-host/templates/html5-apps-repo-host.yaml +1 -0
- package/lib/k8s/index.js +153 -0
- package/lib/k8s/modules.js +36 -0
- package/lib/k8s/multitenancy/index.js +24 -0
- package/lib/k8s/multitenancy/lib.js +25 -0
- package/lib/k8s/multitenancy/templates/saas-registry.yaml +19 -0
- package/lib/k8s/readProject.js +14 -0
- package/lib/k8s/resources/helm/chart/templates/NOTES.txt +11 -0
- package/lib/k8s/resources/helm/chart/templates/_deployment_helpers.tpl +221 -0
- package/lib/k8s/resources/helm/chart/templates/_helpers.tpl +167 -0
- package/lib/k8s/resources/helm/chart/templates/_sapcp_helpers.tpl +103 -0
- package/lib/k8s/util.js +186 -0
- package/lib/k8s/wrapFeature.js +55 -0
- package/lib/k8s/xsuaa/index.js +64 -0
- package/lib/k8s/xsuaa/lib.js +14 -0
- package/lib/k8s/xsuaa/templates/xsuaa.yaml +1 -0
- package/lib/util/exec.js +1 -1
- package/lib/util/write.js +1 -1
- package/lib/watch/node-watch.js +43 -14
- package/npm-shrinkwrap.json +1221 -2743
- package/package.json +5 -5
- package/lib/import/index.js +0 -235
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
const cp = require('child_process');
|
|
2
|
+
const fsp = require('fs').promises;
|
|
2
3
|
const os = require('os');
|
|
3
4
|
const util = require('util');
|
|
4
5
|
|
|
5
6
|
const axios = require('axios');
|
|
6
7
|
const { URL } = require('url');
|
|
8
|
+
const path = require('path');
|
|
7
9
|
|
|
8
10
|
const execAsync = util.promisify(cp.exec);
|
|
9
11
|
const IS_WIN = os.platform() === 'win32';
|
|
@@ -47,20 +49,41 @@ class CFKeyProvider {
|
|
|
47
49
|
return match[1];
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
async getCfTargetFromConfigFile() {
|
|
53
|
+
const cfHome = process.env.CF_HOME || process.env.cf_home || path.join(os.homedir(), '.cf');
|
|
54
|
+
try {
|
|
55
|
+
const fileContent = await fsp.readFile(path.join(cfHome, 'config.json'));
|
|
56
|
+
const config = JSON.parse(fileContent);
|
|
57
|
+
if (config) {
|
|
58
|
+
return {
|
|
59
|
+
apiEndpoint: this.extract(config.Target, /\s*(.+)\s*/, `CF API endpoint is missing. Use 'cf login' to login.`),
|
|
60
|
+
org: this.extract(config.OrganizationFields.Name, /\s*(.+)\s*/, `CF org is missing. Use 'cf target -o <ORG> to specify.`),
|
|
61
|
+
space: this.extract(config.SpaceFields.Name, /\s*(.+)\s*/, `CF space is missing. Use 'cf target -s <SPACE>' to specify.`)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch (err) {
|
|
65
|
+
this.logger.debug(`getCfTargetFromConfigFile: ${err}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
50
69
|
async getCfTargetFromCli() {
|
|
51
70
|
const result = await this._cfRead('target');
|
|
52
71
|
return {
|
|
53
|
-
apiEndpoint: this.extract(result, /api endpoint\s*:\s*(
|
|
54
|
-
user: this.extract(result, /user\s*:\s*(
|
|
55
|
-
org: this.extract(result, /org\s*:\s*(
|
|
56
|
-
space: this.extract(result, /space\s*:\s*(
|
|
72
|
+
apiEndpoint: this.extract(result, /api endpoint\s*:\s*([^\s]+)/i, `CF API endpoint is missing. Use 'cf login' to login.`),
|
|
73
|
+
user: this.extract(result, /user\s*:\s*(.+)/i, `CF user is missing. Use 'cf login' to login.`),
|
|
74
|
+
org: this.extract(result, /org\s*:\s*(.+)/i, `CF org is missing. Use 'cf target -o <ORG> to specify.`),
|
|
75
|
+
space: this.extract(result, /space\s*:\s*(.+)/i, `CF space is missing. Use 'cf target -s <SPACE>' to specify.`),
|
|
57
76
|
};
|
|
58
77
|
}
|
|
59
78
|
|
|
79
|
+
async getCfTarget() {
|
|
80
|
+
return await this.getCfTargetFromConfigFile() || await this.getCfTargetFromCli();
|
|
81
|
+
}
|
|
82
|
+
|
|
60
83
|
async getCfSpaceInfo() {
|
|
61
84
|
this.logger.debug('getting space info');
|
|
62
85
|
|
|
63
|
-
const target = await this.
|
|
86
|
+
const target = await this.getCfTarget();
|
|
64
87
|
const authorization = await this.getCfAuthorization();
|
|
65
88
|
const targetWithAuth = Object.assign({}, target, { authorization });
|
|
66
89
|
|
|
@@ -94,18 +117,24 @@ class CFKeyProvider {
|
|
|
94
117
|
async init() {
|
|
95
118
|
const target = this.target = await this.getCfSpaceInfo();
|
|
96
119
|
const logger = this.logger;
|
|
97
|
-
this.getOrg = BatchRequest.create({logger, target, url: '/v3/organizations'});
|
|
98
|
-
this.getSpace = BatchRequest.create({
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
this.
|
|
120
|
+
this.getOrg = BatchRequest.create({ logger, target, url: '/v3/organizations' });
|
|
121
|
+
this.getSpace = BatchRequest.create({
|
|
122
|
+
logger, target, url: '/v3/spaces', extractors: {
|
|
123
|
+
organization_guids: resource => resource.relationships.organization.data.guid
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
this.getInstance = BatchRequest.create({
|
|
127
|
+
logger, target, url: '/v3/service_instances', extractors: {
|
|
128
|
+
space_guids: resource => resource.relationships.space.data.guid
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
this.getKey = BatchRequest.create({
|
|
132
|
+
logger, target, url: '/v3/service_credential_bindings', extractors: {
|
|
133
|
+
service_instance_guids: resource => resource.relationships.service_instance.data.guid
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
this.getPlans = BatchRequest.create({ logger, target, url: '/v3/service_plans' });
|
|
137
|
+
this.getOfferings = BatchRequest.create({ logger, target, url: '/v3/service_offerings' });
|
|
109
138
|
}
|
|
110
139
|
|
|
111
140
|
async resolve(name, binding) {
|
|
@@ -116,7 +145,7 @@ class CFKeyProvider {
|
|
|
116
145
|
|
|
117
146
|
const target = this.target;
|
|
118
147
|
|
|
119
|
-
let {instance, key, apiEndpoint, org, space} = binding;
|
|
148
|
+
let { instance, key, apiEndpoint, org, space } = binding;
|
|
120
149
|
key = key || `${instance}-key`;
|
|
121
150
|
name = name || `${instance}:${key}`;
|
|
122
151
|
|
|
@@ -138,17 +167,17 @@ class CFKeyProvider {
|
|
|
138
167
|
let instanceObj;
|
|
139
168
|
try {
|
|
140
169
|
instanceObj = await this.getInstance({ names: instance, space_guids: spaceGuid });
|
|
141
|
-
} catch(error) {
|
|
170
|
+
} catch (error) {
|
|
142
171
|
throw new Error(`Service instance "${instance}" not found.`);
|
|
143
172
|
}
|
|
144
173
|
let keyObj;
|
|
145
174
|
try {
|
|
146
|
-
keyObj = await this.getKey({names: key, service_instance_guids: instanceObj.guid });
|
|
147
|
-
} catch(error) {
|
|
175
|
+
keyObj = await this.getKey({ names: key, service_instance_guids: instanceObj.guid });
|
|
176
|
+
} catch (error) {
|
|
148
177
|
throw new Error(`No service key "${key}" found for service instance "${instance}".`);
|
|
149
178
|
}
|
|
150
|
-
const planObj = await this.getPlans({guids: instanceObj.relationships.service_plan.data.guid});
|
|
151
|
-
const offeringObj = await this.getOfferings({guids: planObj.relationships.service_offering.data.guid });
|
|
179
|
+
const planObj = await this.getPlans({ guids: instanceObj.relationships.service_plan.data.guid });
|
|
180
|
+
const offeringObj = await this.getOfferings({ guids: planObj.relationships.service_offering.data.guid });
|
|
152
181
|
const keyCredentialsObj = await req(this.logger, target, `/v3/service_credential_bindings/${encodeURIComponent(keyObj.guid)}/details`);
|
|
153
182
|
const resolvedBinding = {
|
|
154
183
|
binding: {
|
|
@@ -173,7 +202,7 @@ class CFKeyProvider {
|
|
|
173
202
|
}
|
|
174
203
|
|
|
175
204
|
class BatchRequest {
|
|
176
|
-
constructor({logger, target, url, extractors = {}}) {
|
|
205
|
+
constructor({ logger, target, url, extractors = {} }) {
|
|
177
206
|
this.logger = logger;
|
|
178
207
|
this.target = target;
|
|
179
208
|
this.url = url;
|
|
@@ -190,9 +219,9 @@ class BatchRequest {
|
|
|
190
219
|
async request(filter) {
|
|
191
220
|
let resolve;
|
|
192
221
|
let reject;
|
|
193
|
-
const promise = new Promise(
|
|
222
|
+
const promise = new Promise((_resolve, _reject) => {
|
|
194
223
|
resolve = _resolve;
|
|
195
|
-
reject
|
|
224
|
+
reject = _reject;
|
|
196
225
|
});
|
|
197
226
|
this.filters.push({ filter, resolve, reject });
|
|
198
227
|
|
|
@@ -214,7 +243,7 @@ class BatchRequest {
|
|
|
214
243
|
}
|
|
215
244
|
|
|
216
245
|
const params = Object.keys(filterParams).map(
|
|
217
|
-
name => `${name}=${filterParams[name].map(
|
|
246
|
+
name => `${name}=${filterParams[name].map(value => encodeURIComponent(value)).join(',')}`
|
|
218
247
|
).join("&");
|
|
219
248
|
|
|
220
249
|
const url = this.url + (this.url.match(/\?/) ? '&' : '?') + params;
|
|
@@ -228,12 +257,12 @@ class BatchRequest {
|
|
|
228
257
|
}
|
|
229
258
|
return;
|
|
230
259
|
}
|
|
231
|
-
const fmt = filter => Object.keys(filter).map(
|
|
260
|
+
const fmt = filter => Object.keys(filter).map(key => `${key}: ${filter[key]}`).join(', ');
|
|
232
261
|
const resources = data.resources;
|
|
233
262
|
for (const filter of filters) {
|
|
234
263
|
let filteredResources = resources;
|
|
235
264
|
for (const name of Object.keys(filter.filter)) {
|
|
236
|
-
filteredResources = filteredResources.filter(
|
|
265
|
+
filteredResources = filteredResources.filter(resource => this.extractors[name](resource) === filter.filter[name]);
|
|
237
266
|
}
|
|
238
267
|
if (filteredResources.length === 1) {
|
|
239
268
|
filter.resolve(filteredResources[0]);
|
|
@@ -2,6 +2,8 @@ const cp = require('child_process');
|
|
|
2
2
|
const os = require('os');
|
|
3
3
|
const util = require('util');
|
|
4
4
|
const yaml = require("@sap/cds-foss")("yaml");
|
|
5
|
+
const { exec } = require('../util/exec');
|
|
6
|
+
const https = require('https')
|
|
5
7
|
|
|
6
8
|
const axios = require('axios');
|
|
7
9
|
const { URL } = require('url');
|
|
@@ -43,14 +45,29 @@ class KubernetesCredentialsProvider {
|
|
|
43
45
|
const clusterEntry = config.clusters && config.clusters.find( entry => entry.name === currentContext.cluster && entry.cluster && entry.cluster.server );
|
|
44
46
|
if (!currentContextEntry) throw new Error(`Cluster ${currentContext.cluster} not found in kubectl config.`);
|
|
45
47
|
const cluster = clusterEntry.cluster;
|
|
46
|
-
const userEntry = config.users && config.users.find( entry => entry.name === currentContext.user && entry.user
|
|
48
|
+
const userEntry = config.users && config.users.find( entry => entry.name === currentContext.user && entry.user);
|
|
47
49
|
if (!userEntry) throw new Error(`User ${currentContext.user} not found in kubectl config.`);
|
|
48
50
|
const user = userEntry.user;
|
|
51
|
+
let token;
|
|
52
|
+
if (user.token) {
|
|
53
|
+
token = user.token;
|
|
54
|
+
} else if (user.exec) {
|
|
55
|
+
const resultJson = await exec({result: 'stdout'}, user.exec.command, ...user.exec.args);
|
|
56
|
+
const result = JSON.parse(resultJson);
|
|
57
|
+
token = result.status && result.status.token;
|
|
58
|
+
} else {
|
|
59
|
+
throwError `Unsupported authentication method for kubectl context ${currentContextName}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!token) throwError `kubectl authentication failed for kubectl context ${currentContextName}`;
|
|
49
63
|
|
|
50
64
|
return {
|
|
51
65
|
url: cluster.server,
|
|
66
|
+
httpsAgent: cluster['certificate-authority-data'] && new https.Agent({
|
|
67
|
+
ca: Buffer.from(cluster['certificate-authority-data'], 'base64').toString()
|
|
68
|
+
}),
|
|
52
69
|
namespace: currentContext.namespace || 'default',
|
|
53
|
-
token
|
|
70
|
+
token
|
|
54
71
|
}
|
|
55
72
|
}
|
|
56
73
|
|
|
@@ -67,7 +84,8 @@ class KubernetesCredentialsProvider {
|
|
|
67
84
|
validateStatus: status => status === 200 || status === 404,
|
|
68
85
|
headers: {
|
|
69
86
|
authorization: `Bearer ${this.config.token}`
|
|
70
|
-
}
|
|
87
|
+
},
|
|
88
|
+
httpsAgent: this.config.httpsAgent
|
|
71
89
|
});
|
|
72
90
|
|
|
73
91
|
if (result.status === 200) {
|
|
@@ -92,7 +110,7 @@ class KubernetesCredentialsProvider {
|
|
|
92
110
|
if (cluster && cluster !== this.config.url) throw new Error(`Current Kubernetes cluster "${this.config.url}" different than Kubernetes cluster "${cluster}" for service binding "${name}".`);
|
|
93
111
|
|
|
94
112
|
if (bindingName) {
|
|
95
|
-
const bindingResource = await this._req(`/apis/
|
|
113
|
+
const bindingResource = await this._req(`/apis/services.cloud.sap.com/v1/namespaces/${encodeURIComponent(namespace)}/servicebindings/${encodeURIComponent(bindingName)}`);
|
|
96
114
|
if (bindingResource === null) {
|
|
97
115
|
// Fallback to secret instead of service binding;
|
|
98
116
|
secret = bindingName;
|
|
@@ -100,21 +118,16 @@ class KubernetesCredentialsProvider {
|
|
|
100
118
|
secretOrBindingResource = 'Service Binding or Secret';
|
|
101
119
|
} else {
|
|
102
120
|
secret = bindingResource.spec.secretName || throwError();
|
|
103
|
-
instance = bindingResource.spec.
|
|
121
|
+
instance = bindingResource.spec.serviceInstanceName || throwError();
|
|
104
122
|
}
|
|
105
123
|
}
|
|
106
124
|
|
|
107
125
|
const secretResource = await this._req(`/api/v1/namespaces/${encodeURIComponent(namespace)}/secrets/${encodeURIComponent(secret)}`);
|
|
108
126
|
if (secretResource === null) throwError(`${secretOrBindingResource} '${secret}' not found.`);
|
|
109
127
|
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
value = Buffer.from(value, "base64").toString(); // secret values are base64 encoded
|
|
114
|
-
// TODO: TESTIT
|
|
115
|
-
if (value && value[0] === '{') try { value = JSON.parse(value); } catch(e) {/* ignored */} // secret values may contain JSON objects
|
|
116
|
-
credentials[name] = value;
|
|
117
|
-
}
|
|
128
|
+
const serviceBinding = readBinding(name, secretResource);
|
|
129
|
+
const { credentials } = serviceBinding;
|
|
130
|
+
const vcap = {...serviceBinding, credentials: undefined}
|
|
118
131
|
|
|
119
132
|
const resolvedBinding = {
|
|
120
133
|
binding: {
|
|
@@ -125,23 +138,33 @@ class KubernetesCredentialsProvider {
|
|
|
125
138
|
namespace,
|
|
126
139
|
secret: secret,
|
|
127
140
|
resolved: true,
|
|
141
|
+
vcap
|
|
128
142
|
},
|
|
129
143
|
credentials
|
|
130
144
|
};
|
|
131
145
|
|
|
146
|
+
return resolvedBinding;
|
|
147
|
+
}
|
|
132
148
|
|
|
133
|
-
|
|
134
|
-
const serviceInstanceResource = await this._req(`/apis/servicecatalog.k8s.io/v1beta1/namespaces/${encodeURIComponent(namespace)}/serviceinstances/${encodeURIComponent(instance)}`);
|
|
149
|
+
}
|
|
135
150
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
151
|
+
function readBinding(name, secret) {
|
|
152
|
+
const { data } = secret;
|
|
153
|
+
const properties = {};
|
|
154
|
+
for (const name in data) {
|
|
155
|
+
const value = Buffer.from(data[name], "base64").toString()
|
|
156
|
+
properties[name] = value;
|
|
157
|
+
}
|
|
141
158
|
|
|
142
|
-
|
|
159
|
+
let parseBinding
|
|
160
|
+
try {
|
|
161
|
+
parseBinding = require('@sap/cds/lib/env/serviceBindings').parseBinding;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
//
|
|
143
164
|
}
|
|
165
|
+
if (!parseBinding) throwError `Please update your @sap/cds version for Kubernetes support`
|
|
144
166
|
|
|
167
|
+
return parseBinding('/', name, properties);
|
|
145
168
|
}
|
|
146
169
|
|
|
147
170
|
|
|
@@ -2,8 +2,7 @@ const os = require('os');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const readline = require('readline');
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
const { fse } = require('@sap/cds-foss');
|
|
5
|
+
const fs = require('fs').promises;
|
|
7
6
|
const { highlight } = require('../util/term');
|
|
8
7
|
|
|
9
8
|
const commandUtil = require('../init/util/commandUtil');
|
|
@@ -54,12 +53,12 @@ class UserCdsrcJsonStorageProvider {
|
|
|
54
53
|
# added by cds bind
|
|
55
54
|
${file}
|
|
56
55
|
`;
|
|
57
|
-
await
|
|
56
|
+
await fs.writeFile(GIT_IGNORE_FILE, gitignore, 'utf8');
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
async readFileSafely(file) {
|
|
61
60
|
try {
|
|
62
|
-
return await
|
|
61
|
+
return await fs.readFile(file, 'utf8');
|
|
63
62
|
} catch (err) {
|
|
64
63
|
return '';
|
|
65
64
|
}
|
|
@@ -73,7 +72,7 @@ ${file}
|
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
jsonPath = jsonPath.replace(/~/g, os.homedir());
|
|
76
|
-
// const stat = await
|
|
75
|
+
// const stat = await .stat(jsonPath);
|
|
77
76
|
if (path.extname(jsonPath) !== '.json') {
|
|
78
77
|
jsonPath = path.join(jsonPath, '.cdsrc.json');
|
|
79
78
|
}
|
|
@@ -96,9 +95,8 @@ ${file}
|
|
|
96
95
|
}
|
|
97
96
|
|
|
98
97
|
const profileKey = `[${options.profile}]`;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
cdsRoot.features[profileKey].emulate_vcap_services = true;
|
|
98
|
+
const features = cdsRoot.features && cdsRoot.features[profileKey];
|
|
99
|
+
if (features) delete features.emulate_vcap_services;
|
|
102
100
|
|
|
103
101
|
cdsRoot.requires = cdsRoot.requires || {};
|
|
104
102
|
let section;
|
|
@@ -109,12 +107,10 @@ ${file}
|
|
|
109
107
|
}
|
|
110
108
|
|
|
111
109
|
for (const service of services) {
|
|
112
|
-
const { name, kind, binding, credentials
|
|
110
|
+
const { name, kind, binding, credentials } = service;
|
|
113
111
|
const requireService = section[name] = section[name] || {};
|
|
114
112
|
|
|
115
|
-
Object.assign(requireService, { binding, credentials });
|
|
116
|
-
if (kind) requireService.kind = kind;
|
|
117
|
-
if (vcap) requireService.vcap = vcap;
|
|
113
|
+
Object.assign(requireService, { binding: {...binding, vcap: undefined}, credentials, kind, vcap: { name } });
|
|
118
114
|
}
|
|
119
115
|
|
|
120
116
|
await this.writeJsonFile(jsonPath, jsonObject, options.logger);
|
|
@@ -127,18 +123,16 @@ ${file}
|
|
|
127
123
|
|
|
128
124
|
async readJsonSafely(filePath) {
|
|
129
125
|
try {
|
|
130
|
-
return await
|
|
126
|
+
return JSON.parse(await fs.readFile(filePath));
|
|
131
127
|
} catch (err) {
|
|
132
128
|
return {};
|
|
133
129
|
}
|
|
134
130
|
}
|
|
135
131
|
|
|
136
132
|
async writeJsonFile(filePath, content) {
|
|
137
|
-
await
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
await fse.chmod(filePath, 0o600); // -rw-------
|
|
133
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true })
|
|
134
|
+
await fs.writeFile(filePath, JSON.stringify(content, null, 2));
|
|
135
|
+
await fs.chmod(filePath, 0o600); // -rw-------
|
|
142
136
|
}
|
|
143
137
|
}
|
|
144
138
|
|
package/lib/cds.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** @type { import('@sap/cds/apis/cds') } */
|
|
2
|
+
const cds = module.exports = require(_local ('@sap/cds'))
|
|
3
|
+
|
|
4
|
+
// for internal use in cds-dk only
|
|
5
|
+
cds.utils._log = require(_local ('@sap/cds/bin/utils/log'))
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
function _local (id) {
|
|
9
|
+
return require.resolve (id, {paths:[
|
|
10
|
+
process.cwd(), // project-local module is preferred
|
|
11
|
+
__dirname // otherwise from our own dependencies
|
|
12
|
+
]})
|
|
13
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const
|
|
2
|
+
const fs = require('fs').promises;
|
|
3
3
|
|
|
4
4
|
const Logger = require('./helper/logging');
|
|
5
5
|
|
|
@@ -11,7 +11,8 @@ module.exports = class BaseCommand {
|
|
|
11
11
|
static get CUSTOM_FILE_URL_SUFFIX() { return CUSTOM_FILE_URL_SUFFIX; }
|
|
12
12
|
|
|
13
13
|
static async checkProject(folder) {
|
|
14
|
-
|
|
14
|
+
const exists = await fs.access(path.join(folder, 'package.json')).then(() => true).catch(() => false)
|
|
15
|
+
return exists;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
static async handleHttpError(error, params) {
|
package/lib/client/extend.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const axios = require('axios');
|
|
4
|
-
const { fse } = require('@sap/cds-foss');
|
|
5
4
|
|
|
6
5
|
const AuthManager = require('./auth_manager');
|
|
7
6
|
const BaseCommand = require('./base_command');
|
|
@@ -25,7 +24,7 @@ module.exports = class Extend extends BaseCommand {
|
|
|
25
24
|
static get COMMAND_URL_SUFFIX() { return COMMAND_URL_SUFFIX; }
|
|
26
25
|
|
|
27
26
|
static async storeFiles(content, targetDirectory, filter) {
|
|
28
|
-
|
|
27
|
+
fs.mkdirSync(targetDirectory, { recursive: true });
|
|
29
28
|
targetDirectory = fs.realpathSync(targetDirectory)
|
|
30
29
|
const writtenFiles = new Set();
|
|
31
30
|
const filesToWrite = new Map();
|
|
@@ -37,7 +36,7 @@ module.exports = class Extend extends BaseCommand {
|
|
|
37
36
|
}
|
|
38
37
|
for (const [filepath, content] of [...filesToWrite.entries()]) {
|
|
39
38
|
const normalizedFilepath = normalizePath(filepath);
|
|
40
|
-
|
|
39
|
+
fs.mkdirSync(path.dirname(normalizedFilepath), { recursive: true });
|
|
41
40
|
fs.writeFileSync(normalizedFilepath, content);
|
|
42
41
|
writtenFiles.add(path.relative(process.cwd(), normalizedFilepath));
|
|
43
42
|
Extend._logWrittenFile(normalizedFilepath);
|
|
@@ -121,8 +120,8 @@ module.exports = class Extend extends BaseCommand {
|
|
|
121
120
|
|
|
122
121
|
await Extend.storeFiles(content, params.get('projectFolder'));
|
|
123
122
|
|
|
124
|
-
await
|
|
125
|
-
await
|
|
123
|
+
await fs.promises.mkdir(path.join(params.get('projectFolder'), DB_FOLDER), { recursive: true });
|
|
124
|
+
await fs.promises.mkdir(path.join(params.get('projectFolder'), SRV_FOLDER), { recursive: true });
|
|
126
125
|
|
|
127
126
|
Logger.log(`\nPut database extension models into ${DB_FOLDER} folder.`);
|
|
128
127
|
Logger.log(`Put service extension models into ${SRV_FOLDER} folder.`);
|
|
@@ -148,10 +147,10 @@ module.exports = class Extend extends BaseCommand {
|
|
|
148
147
|
static async storeTemplateFilesToTpl(templates, projectFolder) {
|
|
149
148
|
if (templates && templates.length > 0) {
|
|
150
149
|
const targetDirectory = path.join(projectFolder, TEMPLATE_FOLDER);
|
|
151
|
-
const targetExists =
|
|
150
|
+
const targetExists = fs.existsSync(targetDirectory);
|
|
152
151
|
if (!targetExists || await this.promptTemplateOverwrite()) {
|
|
153
152
|
if (targetExists) {
|
|
154
|
-
await
|
|
153
|
+
await fs.promises.rm(targetDirectory, { force: true, recursive: true });
|
|
155
154
|
}
|
|
156
155
|
Logger.log(`Writing template files to ${TEMPLATE_FOLDER} folder`);
|
|
157
156
|
await Extend.storeTemplateFiles(templates, targetDirectory);
|
|
@@ -3,11 +3,10 @@ const path = require('path');
|
|
|
3
3
|
|
|
4
4
|
async function collectFileContent(filePaths, root) {
|
|
5
5
|
const resultMap = new Map();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return resultMap;
|
|
6
|
+
return Promise.all(
|
|
7
|
+
filePaths.map(filePath => fs.promises.readFile(filePath)
|
|
8
|
+
.then(content => resultMap.set(toUnixPath(path.relative(root, filePath)), content.toString('utf-8'))))
|
|
9
|
+
).then(() => resultMap);
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
/**
|
|
@@ -29,8 +28,8 @@ function _traverseFileSystem(root, sourceDir, files, filter) {
|
|
|
29
28
|
if (!fs.existsSync(absoluteDir)) {
|
|
30
29
|
return;
|
|
31
30
|
}
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
try {
|
|
32
|
+
absoluteDir = fs.realpathSync(absoluteDir);
|
|
34
33
|
fs.readdirSync(absoluteDir)
|
|
35
34
|
.map(subDirEntry => path.join(absoluteDir, subDirEntry))
|
|
36
35
|
.forEach(entry => {
|
|
@@ -43,7 +42,7 @@ function _traverseFileSystem(root, sourceDir, files, filter) {
|
|
|
43
42
|
files.set(toUnixPath(path.relative(root, entry)), fileContent.toString('utf-8'));
|
|
44
43
|
}
|
|
45
44
|
});
|
|
46
|
-
}
|
|
45
|
+
} catch(error) { /* ignored */}
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
/**
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const os = require('os');
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const { fse } = require('@sap/cds-foss');
|
|
5
4
|
|
|
6
5
|
const requireGlobal = require('./helper/dependency_helper');
|
|
7
6
|
const keytar = requireGlobal('keytar');
|
|
@@ -385,7 +384,8 @@ class SettingsManager {
|
|
|
385
384
|
|
|
386
385
|
static async _saveAllSettingsToFile(paramValuesByFolder) {
|
|
387
386
|
Logger.debug(`Saving all settings to ${CONFIG.paths.settings}`);
|
|
388
|
-
await
|
|
387
|
+
await fs.promises.mkdir(path.dirname(CONFIG.paths.settings), { recursive: true })
|
|
388
|
+
await fs.promises.writeFile(CONFIG.paths.settings, JSON.stringify(paramValuesByFolder, null, 2));
|
|
389
389
|
Logger.debug('Saved settings');
|
|
390
390
|
}
|
|
391
391
|
|
|
@@ -396,14 +396,16 @@ class SettingsManager {
|
|
|
396
396
|
|
|
397
397
|
if (authValues !== null) {
|
|
398
398
|
(allAuthValues[appUrl] || (allAuthValues[appUrl] = {}))[params.get('subdomain')] = authValues;
|
|
399
|
-
await
|
|
399
|
+
await fs.promises.mkdir(path.dirname(CONFIG.paths.auth), { recursive: true })
|
|
400
|
+
await fs.promises.writeFile(CONFIG.paths.auth, JSON.stringify(allAuthValues, null, 2));
|
|
400
401
|
notifyLoggedIn(params)(`Saved authentication data to ${location}`);
|
|
401
402
|
} else if (allAuthValues[appUrl]) {
|
|
402
403
|
delete allAuthValues[appUrl][params.get('subdomain')];
|
|
403
404
|
if (Object.keys(allAuthValues[appUrl]).length === 0) {
|
|
404
405
|
delete allAuthValues[appUrl];
|
|
405
406
|
}
|
|
406
|
-
await
|
|
407
|
+
await fs.promises.mkdir(path.dirname(CONFIG.paths.auth), { recursive: true })
|
|
408
|
+
await fs.promises.writeFile(CONFIG.paths.auth, JSON.stringify(allAuthValues, null, 2));
|
|
407
409
|
Logger.log(`Deleted authentication data from ${location}`);
|
|
408
410
|
} else {
|
|
409
411
|
notifyLoggedIn(params)(`No authentication data to delete from ${location}`);
|
|
@@ -434,9 +436,10 @@ class SettingsManager {
|
|
|
434
436
|
return emptyData(all);
|
|
435
437
|
}
|
|
436
438
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
439
|
+
let settingsByFolder
|
|
440
|
+
try {
|
|
441
|
+
settingsByFolder = JSON.parse((await fs.promises.readFile(CONFIG.paths.settings)).toString());
|
|
442
|
+
} catch (err) {
|
|
440
443
|
Logger.debug('Empty settings file');
|
|
441
444
|
return emptyData(all);
|
|
442
445
|
}
|
|
@@ -461,9 +464,10 @@ class SettingsManager {
|
|
|
461
464
|
return emptyData(all);
|
|
462
465
|
}
|
|
463
466
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
+
let allAuth
|
|
468
|
+
try {
|
|
469
|
+
allAuth = JSON.parse((await fs.promises.readFile(CONFIG.paths.auth)).toString());
|
|
470
|
+
} catch (err) {
|
|
467
471
|
Logger.debug('Empty authentication-data file');
|
|
468
472
|
return emptyData(all);
|
|
469
473
|
}
|
package/lib/compile/index.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
const cds = require
|
|
1
|
+
const cds = require('../cds')
|
|
2
|
+
const comp_to = cds.compile.to
|
|
3
|
+
const { defineProperty } = Object
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
'
|
|
5
|
-
'xsuaa': {enumerable:true, get:()=> require('./to-xsuaa')},
|
|
6
|
-
'openapi': {enumerable:true, get:()=> require('./openapi')},
|
|
7
|
-
'edmx-v2': {enumerable:true, value: (csn,o) => cds.compile.to.edmx(csn,{...o,flavor:'v2'}) },
|
|
8
|
-
'edmx-v4': {enumerable:true, value: (csn,o) => cds.compile.to.edmx(csn,{...o,flavor:'v4'}) },
|
|
9
|
-
'edmx-w4': {enumerable:true, value: (csn,o) => cds.compile.to.edmx(csn,{...o,flavor:'w4'}) },
|
|
10
|
-
'edmx-x4': {enumerable:true, value: (csn,o) => cds.compile.to.edmx(csn,{...o,flavor:'x4'}) },
|
|
11
|
-
})
|
|
5
|
+
'xsuaa' in comp_to || defineProperty (comp_to, 'xsuaa', {enumerable:true, get:()=> require('./to-xsuaa')})
|
|
6
|
+
'openapi' in comp_to || defineProperty (comp_to, 'openapi', {enumerable:true, get:()=> require('./openapi')})
|
|
12
7
|
|
|
8
|
+
'edmx-v2' in comp_to || defineProperty (comp_to, 'edmx-v2', {enumerable:true, value: (csn,o) => comp_to.edmx(csn,{...o,flavor:'v2'})})
|
|
9
|
+
'edmx-v4' in comp_to || defineProperty (comp_to, 'edmx-v4', {enumerable:true, value: (csn,o) => comp_to.edmx(csn,{...o,flavor:'v4'})})
|
|
10
|
+
'edmx-w4' in comp_to || defineProperty (comp_to, 'edmx-w4', {enumerable:true, value: (csn,o) => comp_to.edmx(csn,{...o,flavor:'w4'})})
|
|
11
|
+
'edmx-x4' in comp_to || defineProperty (comp_to, 'edmx-x4', {enumerable:true, value: (csn,o) => comp_to.edmx(csn,{...o,flavor:'x4'})})
|
|
13
12
|
|
|
14
13
|
module.exports = cds
|