@sap/cds-dk 6.4.0 → 6.5.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.
- package/CHANGELOG.md +28 -1
- package/bin/bind.js +30 -34
- package/bin/cds.js +14 -3
- package/bin/deploy/index.js +3 -1
- package/bin/deploy/to-sqlite.js +2 -1
- package/bin/env.js +4 -4
- package/bin/migrate.js +25 -4
- package/bin/push.js +4 -0
- package/bin/watch.js +3 -3
- package/lib/bind/KubernetesApiAccessor.js +79 -0
- package/lib/bind/{BindingManager.js → bindingManager.js} +16 -15
- package/lib/bind/bindingResolver.js +41 -68
- package/lib/bind/bindingUtil.js +93 -104
- package/lib/bind/cfKeyProvider.js +85 -74
- package/lib/bind/kubernetesCredentialsProvider.js +34 -132
- package/lib/bind/userCdsrcJsonStorageProvider.js +12 -18
- package/lib/client/params.js +2 -2
- package/lib/client/push.js +11 -6
- package/lib/client/settings_manager.js +2 -1
- package/lib/client/subscription.js +4 -1
- package/lib/import/odata/v2parser.js +32 -8
- package/lib/import/odata/v4parser.js +11 -3
- package/lib/init/constants.js +1 -1
- package/lib/init/index.js +3 -11
- package/lib/init/template/approuter/files/approuter-configmap.yaml +4 -1
- package/lib/init/template/approuter/files/approuter-mtxs-configmap.yaml +5 -1
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/approuter/files/values.yaml.hbs +1 -1
- package/lib/init/template/approuter/files/xs-app.json.hbs +2 -2
- package/lib/init/template/approuter/index.js +4 -5
- package/lib/init/template/auditlog/index.js +1 -1
- package/lib/init/template/data/index.js +1 -1
- package/lib/init/template/destinations/index.js +1 -1
- package/lib/init/template/enterprise-messaging/index.js +1 -1
- package/lib/init/template/extensibility/index.js +6 -1
- package/lib/init/template/hana/files/chart.yaml +4 -0
- package/lib/init/template/hana/files/hana.yaml +1 -0
- package/lib/init/template/hana/files/java/db/package.json +1 -1
- package/lib/init/template/hana/files/values.yaml.hbs +6 -3
- package/lib/init/template/hana/index.js +73 -21
- package/lib/init/template/helm/chart/templates/_deployment_helpers.tpl +14 -6
- package/lib/init/template/helm/chart/values.root.schema.json +10 -0
- package/lib/init/template/helm/chart/values.schema.json +11 -110
- package/lib/init/template/helm/index.js +2 -2
- package/lib/init/template/helm/subcharts/content-deployment/Chart.yaml +5 -0
- package/lib/init/template/helm/subcharts/content-deployment/templates/_helpers.tpl +207 -0
- package/lib/init/template/helm/subcharts/content-deployment/templates/job.yaml +66 -0
- package/lib/init/template/helm/subcharts/content-deployment/templates/secret.yaml +10 -0
- package/lib/init/template/helm/subcharts/content-deployment/templates/service-account.yaml +4 -0
- package/lib/init/template/helm/subcharts/content-deployment/templates/service-binding.yaml +28 -0
- package/lib/init/template/helm/subcharts/content-deployment/values-lint.yaml +5 -0
- package/lib/init/template/helm/subcharts/content-deployment/values.md +1014 -0
- package/lib/init/template/helm/subcharts/content-deployment/values.schema.json +589 -0
- package/lib/init/template/helm/subcharts/content-deployment/values.yaml +54 -0
- package/lib/init/template/helm/{subchart → subcharts/web-application}/Chart.yaml +0 -0
- package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/_helpers.tpl +56 -50
- package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/api-rule.yaml +1 -1
- package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/deployment.yaml +15 -9
- package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/network-policy.yaml +0 -0
- package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/pod-disruption-budget.yaml +2 -2
- package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/secret.yaml +0 -0
- package/lib/init/template/helm/subcharts/web-application/templates/service-account.yaml +6 -0
- package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/service-binding.yaml +4 -3
- package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/service.yaml +0 -0
- package/lib/init/template/helm/subcharts/web-application/templating.md +44 -0
- package/lib/init/template/helm/{subchart → subcharts/web-application}/values-lint.yaml +0 -0
- package/lib/init/template/helm/{subchart → subcharts/web-application}/values.md +254 -200
- package/lib/init/template/helm/{subchart → subcharts/web-application}/values.schema.json +82 -28
- package/lib/init/template/helm/{subchart → subcharts/web-application}/values.yaml +15 -5
- package/lib/init/template/html5-repo/files/chart.yaml +4 -0
- package/lib/init/template/html5-repo/files/html5-apps-deployer-configmap.yaml +14 -0
- package/lib/init/template/html5-repo/index.js +39 -7
- package/lib/init/template/kibana-logging/index.js +1 -1
- package/lib/init/template/lint/index.js +1 -1
- package/lib/init/template/multitenancy/files/saas-registry.yaml +2 -2
- package/lib/init/template/multitenancy/files/service-manager.yaml +1 -0
- package/lib/init/template/multitenancy/files/values.yaml.hbs +10 -6
- package/lib/init/template/multitenancy/index.js +21 -3
- package/lib/init/util/commandUtil.js +5 -5
- package/lib/init/util/merge.js +27 -1
- package/lib/init/util/mvnArchetypeUtil.js +1 -1
- package/lib/init/util/projectReader.js +2 -2
- package/lib/util/exec.js +3 -3
- package/lib/util/term.js +7 -7
- package/lib/watch/livereload-connect.js +187 -0
- package/lib/watch/livereload-server.js +12 -12
- package/lib/watch/node-watch.js +1 -1
- package/npm-shrinkwrap.json +191 -404
- package/package.json +2 -5
- package/lib/init/template/hana/files/hana-deployer-job.yaml +0 -49
- package/lib/init/template/html5-repo/files/_html5_deployment_helpers.tpl +0 -16
- package/lib/init/template/html5-repo/files/html5-apps-deployer-job.yaml +0 -61
- package/lib/init/util/spinner.js +0 -80
|
@@ -1,139 +1,52 @@
|
|
|
1
|
-
const
|
|
2
|
-
const os = require('os');
|
|
3
|
-
const util = require('util');
|
|
4
|
-
const yaml = require("@sap/cds-foss")("yaml");
|
|
5
|
-
const { exec } = require('../util/exec');
|
|
6
|
-
const https = require('https')
|
|
7
|
-
|
|
8
|
-
const axios = require('axios');
|
|
9
|
-
const { URL } = require('url');
|
|
10
|
-
|
|
11
|
-
const execFile = util.promisify(cp.execFile);
|
|
12
|
-
const IS_WIN = os.platform() === 'win32';
|
|
1
|
+
const KubernetesApiAccessor = require('./KubernetesApiAccessor');
|
|
13
2
|
|
|
14
3
|
class KubernetesCredentialsProvider {
|
|
15
|
-
|
|
16
|
-
constructor() {
|
|
17
|
-
this.logger = console;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
setLogger(logger) {
|
|
21
|
-
this.logger = logger;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async _kubectlRead(...args) {
|
|
25
|
-
this.logger.debug(`_kubectlRead: kubectl ${args.join(" ")}`);
|
|
26
|
-
|
|
27
|
-
const result = await execFile("kubectl", args, {
|
|
28
|
-
shell: IS_WIN,
|
|
29
|
-
stdio: ['inherit', 'pipe', 'inherit'],
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
return result.stdout.toString().trim();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
async _kubectlConfig() {
|
|
37
|
-
const configString = await this._kubectlRead('config', 'view', '--raw');
|
|
38
|
-
const config = yaml.parse(configString);
|
|
39
|
-
|
|
40
|
-
const currentContextName = config["current-context"];
|
|
41
|
-
if (!currentContextName) throw new Error(`No current context in kubectl config.`);
|
|
42
|
-
const currentContextEntry = config.contexts && config.contexts.find( entry => entry.name === currentContextName && entry.context && entry.context.cluster && entry.context.user );
|
|
43
|
-
if (!currentContextEntry) throw new Error(`Current context not found in kubectl config.`);
|
|
44
|
-
const currentContext = currentContextEntry.context;
|
|
45
|
-
const clusterEntry = config.clusters && config.clusters.find( entry => entry.name === currentContext.cluster && entry.cluster && entry.cluster.server );
|
|
46
|
-
if (!currentContextEntry) throw new Error(`Cluster ${currentContext.cluster} not found in kubectl config.`);
|
|
47
|
-
const cluster = clusterEntry.cluster;
|
|
48
|
-
const userEntry = config.users && config.users.find( entry => entry.name === currentContext.user && entry.user);
|
|
49
|
-
if (!userEntry) throw new Error(`User ${currentContext.user} not found in kubectl config.`);
|
|
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}`;
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
url: cluster.server,
|
|
66
|
-
httpsAgent: cluster['certificate-authority-data'] && new https.Agent({
|
|
67
|
-
ca: Buffer.from(cluster['certificate-authority-data'], 'base64').toString()
|
|
68
|
-
}),
|
|
69
|
-
namespace: currentContext.namespace || 'default',
|
|
70
|
-
token
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
4
|
async init() {
|
|
75
|
-
this.
|
|
5
|
+
this.api = new KubernetesApiAccessor();
|
|
76
6
|
}
|
|
77
7
|
|
|
78
|
-
async _req(urlPath) {
|
|
79
|
-
|
|
80
|
-
const url = new URL(urlPath, this.config.url);
|
|
81
|
-
this.logger.debug(`_req: ${url.toString()}`);
|
|
82
|
-
|
|
83
|
-
const result = await axios.get(url.toString(), {
|
|
84
|
-
validateStatus: status => status === 200 || status === 404,
|
|
85
|
-
headers: {
|
|
86
|
-
authorization: `Bearer ${this.config.token}`
|
|
87
|
-
},
|
|
88
|
-
httpsAgent: this.config.httpsAgent
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
if (result.status === 200) {
|
|
92
|
-
return result.data;
|
|
93
|
-
} else {
|
|
94
|
-
return null
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
8
|
async resolve(name, binding) {
|
|
100
9
|
if (!this.initPromise) {
|
|
101
10
|
this.initPromise = this.init();
|
|
102
11
|
}
|
|
103
12
|
await this.initPromise;
|
|
104
13
|
|
|
105
|
-
|
|
14
|
+
|
|
15
|
+
const {context, namespace, cluster} = await this.api.context({cluster: binding.cluster, namespace: binding.namespace});
|
|
16
|
+
let {name: bindingName, secret} = binding;
|
|
17
|
+
|
|
106
18
|
let instance;
|
|
107
19
|
let secretOrBindingResource = 'Secret';
|
|
108
20
|
|
|
109
|
-
if (!namespace) namespace = this.config.namespace;
|
|
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}".`);
|
|
111
|
-
|
|
112
21
|
if (bindingName) {
|
|
113
|
-
const bindingResource = await this.
|
|
22
|
+
const bindingResource = await this.api.get({context, namespace, kind: 'servicebindings', name: bindingName});
|
|
114
23
|
if (bindingResource === null) {
|
|
115
24
|
// Fallback to secret instead of service binding;
|
|
116
25
|
secret = bindingName;
|
|
117
26
|
bindingName = undefined;
|
|
118
27
|
secretOrBindingResource = 'Service Binding or Secret';
|
|
119
28
|
} else {
|
|
120
|
-
|
|
121
|
-
|
|
29
|
+
const { spec } = bindingResource
|
|
30
|
+
if (!spec.secretName || !spec.serviceInstanceName) {
|
|
31
|
+
throw new Error('Internal error')
|
|
32
|
+
}
|
|
33
|
+
secret = spec.secretName;
|
|
34
|
+
instance = spec.serviceInstanceName;
|
|
122
35
|
}
|
|
123
36
|
}
|
|
124
37
|
|
|
125
|
-
const secretResource = await this.
|
|
126
|
-
if (secretResource === null)
|
|
38
|
+
const secretResource = await this.api.get({context, namespace, kind: 'secrets', name: secret});
|
|
39
|
+
if (secretResource === null) throw new Error(`${secretOrBindingResource} '${secret}' not found.`);
|
|
127
40
|
|
|
128
|
-
const serviceBinding =
|
|
41
|
+
const serviceBinding = this._readBinding(name, secretResource);
|
|
129
42
|
const { credentials } = serviceBinding;
|
|
130
|
-
const vcap = {...serviceBinding, credentials: undefined}
|
|
43
|
+
const vcap = { ...serviceBinding, credentials: undefined }
|
|
131
44
|
|
|
132
45
|
const resolvedBinding = {
|
|
133
46
|
binding: {
|
|
134
47
|
type: "k8s",
|
|
135
48
|
name: bindingName,
|
|
136
|
-
cluster
|
|
49
|
+
cluster,
|
|
137
50
|
instance,
|
|
138
51
|
namespace,
|
|
139
52
|
secret: secret,
|
|
@@ -146,36 +59,25 @@ class KubernetesCredentialsProvider {
|
|
|
146
59
|
return resolvedBinding;
|
|
147
60
|
}
|
|
148
61
|
|
|
149
|
-
|
|
62
|
+
_readBinding(name, secret) {
|
|
63
|
+
const { data } = secret;
|
|
64
|
+
const properties = {};
|
|
65
|
+
for (const name in data) {
|
|
66
|
+
const value = Buffer.from(data[name], "base64").toString()
|
|
67
|
+
properties[name] = value;
|
|
68
|
+
}
|
|
150
69
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
70
|
+
let parseBinding
|
|
71
|
+
try {
|
|
72
|
+
// eslint-disable-next-line
|
|
73
|
+
parseBinding = require('@sap/cds/lib/env/serviceBindings').parseBinding;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
//
|
|
76
|
+
}
|
|
77
|
+
if (!parseBinding) throw new Error(`Please update your @sap/cds version for Kubernetes support`)
|
|
158
78
|
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
// eslint-disable-next-line
|
|
162
|
-
parseBinding = require('@sap/cds/lib/env/serviceBindings').parseBinding;
|
|
163
|
-
} catch (error) {
|
|
164
|
-
//
|
|
79
|
+
return parseBinding('/', name, properties);
|
|
165
80
|
}
|
|
166
|
-
if (!parseBinding) throwError `Please update your @sap/cds version for Kubernetes support`
|
|
167
|
-
|
|
168
|
-
return parseBinding('/', name, properties);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
*
|
|
174
|
-
* @param {string} msg
|
|
175
|
-
* @returns {never}
|
|
176
|
-
*/
|
|
177
|
-
function throwError(msg = 'Internal Error') {
|
|
178
|
-
throw new Error(msg);
|
|
179
81
|
}
|
|
180
82
|
|
|
181
83
|
module.exports = new KubernetesCredentialsProvider();
|
|
@@ -2,8 +2,11 @@ const os = require('os');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const readline = require('readline');
|
|
4
4
|
|
|
5
|
+
const DEBUG = /\b(y|all|cli)\b/.test(process.env.DEBUG) && console.debug
|
|
6
|
+
|
|
5
7
|
const fs = require('fs').promises;
|
|
6
8
|
const { highlight } = require('../util/term');
|
|
9
|
+
const { logger } = require('./bindingUtil')
|
|
7
10
|
|
|
8
11
|
const commandUtil = require('../init/util/commandUtil');
|
|
9
12
|
|
|
@@ -11,7 +14,6 @@ const CDSRC_FILENAME = '.cdsrc-private.json';
|
|
|
11
14
|
const GIT_IGNORE_FILE = '.gitignore';
|
|
12
15
|
|
|
13
16
|
class UserCdsrcJsonStorageProvider {
|
|
14
|
-
|
|
15
17
|
async askBooleanQuestion(question) {
|
|
16
18
|
const rl = readline.createInterface({
|
|
17
19
|
input: process.stdin,
|
|
@@ -31,23 +33,23 @@ class UserCdsrcJsonStorageProvider {
|
|
|
31
33
|
});
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
async ensureFileIsGitignored(file
|
|
36
|
+
async ensureFileIsGitignored(file) {
|
|
35
37
|
try {
|
|
36
38
|
await commandUtil.spawnCommand('git', ['check-ignore', '-n', '-v', file], {}, undefined, true);
|
|
37
39
|
return; // file is git ignored
|
|
38
40
|
} catch (err) {
|
|
39
41
|
if (err.code === 'ENOENT') { // git command not found
|
|
40
|
-
|
|
42
|
+
DEBUG && DEBUG('git command not found');
|
|
41
43
|
return;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
if (!err.stdLog.match(/::/)) { // included or not a git repo
|
|
45
|
-
|
|
47
|
+
DEBUG && DEBUG(err);
|
|
46
48
|
return;
|
|
47
49
|
}
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
logger.
|
|
52
|
+
logger.log(`adding entry '${file}' to ${GIT_IGNORE_FILE}.`);
|
|
51
53
|
let gitignore = await this.readFileSafely(GIT_IGNORE_FILE);
|
|
52
54
|
gitignore = gitignore + `
|
|
53
55
|
# added by cds bind
|
|
@@ -68,7 +70,7 @@ ${file}
|
|
|
68
70
|
let jsonPath = options['output-file'] || CDSRC_FILENAME;
|
|
69
71
|
|
|
70
72
|
if (jsonPath === CDSRC_FILENAME) {
|
|
71
|
-
await this.ensureFileIsGitignored(CDSRC_FILENAME
|
|
73
|
+
await this.ensureFileIsGitignored(CDSRC_FILENAME);
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
jsonPath = jsonPath.replace(/~/g, os.homedir());
|
|
@@ -77,14 +79,6 @@ ${file}
|
|
|
77
79
|
jsonPath = path.join(jsonPath, '.cdsrc.json');
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
// if (options['save-credentials'] && services.find(service => service.credentials) && process.stdin.isTTY) {
|
|
81
|
-
// const answer = await this.askBooleanQuestion(warn(`IF YOU CONTINUE, SENSITIVE DATA WILL BE WRITTEN TO AN UNENCRYPTED FILE [${jsonPath}]! Do you want to continue [y/N] ?`));
|
|
82
|
-
// if (!answer) {
|
|
83
|
-
// options.logger.info(`aborting cds bind; not writing any sensitive data to file [${jsonPath}]`);
|
|
84
|
-
// return;
|
|
85
|
-
// }
|
|
86
|
-
// }
|
|
87
|
-
|
|
88
82
|
const jsonObject = await this.readJsonSafely(jsonPath);
|
|
89
83
|
let cdsRoot;
|
|
90
84
|
if (path.basename(jsonPath) === 'package.json') {
|
|
@@ -110,14 +104,14 @@ ${file}
|
|
|
110
104
|
const { name, kind, binding, credentials } = service;
|
|
111
105
|
const requireService = section[name] = section[name] || {};
|
|
112
106
|
|
|
113
|
-
Object.assign(requireService, { binding: {...binding, vcap: undefined}, credentials, kind, vcap: { name } });
|
|
107
|
+
Object.assign(requireService, { binding: { ...binding, vcap: undefined }, credentials, kind, vcap: { name } });
|
|
114
108
|
}
|
|
115
109
|
|
|
116
|
-
await this.writeJsonFile(jsonPath, jsonObject
|
|
110
|
+
await this.writeJsonFile(jsonPath, jsonObject);
|
|
117
111
|
if (options.profile) {
|
|
118
|
-
|
|
112
|
+
logger.log(`Saving bindings to ${highlight(jsonPath)} in profile ${highlight(options.profile)}`);
|
|
119
113
|
} else {
|
|
120
|
-
|
|
114
|
+
logger.log(`Saving bindings to ${highlight(jsonPath)}`);
|
|
121
115
|
}
|
|
122
116
|
}
|
|
123
117
|
|
package/lib/client/params.js
CHANGED
|
@@ -153,7 +153,7 @@ class Param {
|
|
|
153
153
|
// CONCRETE PARAMS
|
|
154
154
|
|
|
155
155
|
/**
|
|
156
|
-
* The known params and their default values, restricted to those needed by the
|
|
156
|
+
* The known params and their default values, restricted to those needed by the MTXS Client as a whole.
|
|
157
157
|
*/
|
|
158
158
|
class BasicParams {
|
|
159
159
|
constructor() {
|
|
@@ -211,7 +211,7 @@ class AllParams extends BasicParams {
|
|
|
211
211
|
this.sync = new Param('sync', false);
|
|
212
212
|
|
|
213
213
|
// `login` PARAMS
|
|
214
|
-
this.skipToken = new Param('skipToken', false);
|
|
214
|
+
this.skipToken = new Param('skipToken', false, { internal: true });
|
|
215
215
|
|
|
216
216
|
// `logout` PARAMS
|
|
217
217
|
this.deleteSettings = new Param('deleteSettings', false);
|
package/lib/client/push.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const cds = require ('../cds'), { local } = cds.utils
|
|
1
|
+
const cds = require ('../cds'), { local } = cds.utils;
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const { join } = require('path');
|
|
4
4
|
const axios = require('axios');
|
|
@@ -10,23 +10,28 @@ const { schemaRegex } = require('./helper/url_helper');
|
|
|
10
10
|
|
|
11
11
|
module.exports = class Push extends BaseCommand {
|
|
12
12
|
|
|
13
|
-
static async run(paramValues, pushOptions) {
|
|
13
|
+
static async run(paramValues, pushOptions = {}) {
|
|
14
14
|
const params = await login(paramValues);
|
|
15
|
+
const projectFolder = params.get('projectFolder');
|
|
16
|
+
const extensionName = this.getExtensionName(projectFolder);
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
if (!('extArchive' in pushOptions)) {
|
|
19
|
+
console.log(`Building project`);
|
|
20
|
+
await cds.build({ project: projectFolder });
|
|
21
|
+
}
|
|
17
22
|
|
|
18
|
-
const
|
|
23
|
+
const { src, content } = await this.readSource(params, pushOptions);
|
|
19
24
|
const url = params.get('appUrl');
|
|
20
25
|
const subdomain = params.get('subdomain');
|
|
21
26
|
const target = Object.assign({ url }, subdomain && { subdomain });
|
|
22
|
-
console.log(`\nPushing extension '${extensionName}' from`, { src }, 'to', target);
|
|
23
27
|
|
|
28
|
+
console.log(`\nPushing extension '${extensionName}' from`, { src }, 'to', target);
|
|
24
29
|
await this.pushTgz(params, content, extensionName);
|
|
25
30
|
console.log ('Activation succeeded.\n')
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
static async readSource(params, pushOptions) {
|
|
29
|
-
let src = pushOptions?.extArchive ?? join(params.get('projectFolder'), 'gen
|
|
34
|
+
let src = pushOptions?.extArchive ?? join(params.get('projectFolder'), 'gen/extension.tgz');
|
|
30
35
|
let content;
|
|
31
36
|
if (fs.existsSync(src)) {
|
|
32
37
|
content = fs.readFileSync(src, { encoding: 'base64' });
|
|
@@ -184,6 +184,7 @@ class SettingsManager {
|
|
|
184
184
|
if (params.get('tokenStorage') === 'plain') {
|
|
185
185
|
await this._saveAuthToFile(params, params.toValueMap(Persistence.auth));
|
|
186
186
|
} else if (params.get('tokenStorage') === 'keyring') {
|
|
187
|
+
await this.checkKeytar(params);
|
|
187
188
|
await this._saveAuthToKeyring(params, params.toValueMap(Persistence.auth));
|
|
188
189
|
} else {
|
|
189
190
|
console.log('Note: the authentication token is not saved by default. To save the token for later commands, please run `cds login`.');
|
|
@@ -281,7 +282,7 @@ class SettingsManager {
|
|
|
281
282
|
}
|
|
282
283
|
}
|
|
283
284
|
|
|
284
|
-
static async checkKeytar(params, logout) {
|
|
285
|
+
static async checkKeytar(params, logout = false) {
|
|
285
286
|
if (params.get('skipToken')) {
|
|
286
287
|
return;
|
|
287
288
|
}
|
|
@@ -4,6 +4,7 @@ const { CliError } = require('./helper/errors');
|
|
|
4
4
|
const { ParamCollection } = require('./params');
|
|
5
5
|
const AuthManager = require('./auth_manager');
|
|
6
6
|
const { capitalize } = require('./helper/string_helper');
|
|
7
|
+
const { debug } = require('./helper/logging');
|
|
7
8
|
|
|
8
9
|
module.exports = class ToggleSubscription extends BaseCommand {
|
|
9
10
|
|
|
@@ -16,6 +17,7 @@ module.exports = class ToggleSubscription extends BaseCommand {
|
|
|
16
17
|
this.command = prefix + 'subscribe';
|
|
17
18
|
this.gerund = prefix + 'subscribing';
|
|
18
19
|
this.noun = prefix + 'subscription';
|
|
20
|
+
this.preposition = on ? 'to' : 'from';
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
async run(paramValues, { tenant, local } = {}) {
|
|
@@ -37,8 +39,9 @@ module.exports = class ToggleSubscription extends BaseCommand {
|
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
async runClientOnly(params, tenant) {
|
|
40
|
-
console.log(capitalize(this.gerund), { tenant },
|
|
42
|
+
console.log(capitalize(this.gerund), { tenant }, this.preposition, { url: params.get('appUrl') });
|
|
41
43
|
const url = `${params.get('appUrl')}/-/cds/deployment/${this.command}`;
|
|
44
|
+
debug(`Sending request to ${url}`);
|
|
42
45
|
try {
|
|
43
46
|
await require('axios').post(url, { tenant }, params.get('reqAuth'));
|
|
44
47
|
} catch (error) {
|
|
@@ -333,7 +333,6 @@ function _parseDocumentationTag(documentedContent) {
|
|
|
333
333
|
function _extractComplexType(complexType, parserContext) {
|
|
334
334
|
let complexTypeKey = "";
|
|
335
335
|
let baseType = "";
|
|
336
|
-
let properties;
|
|
337
336
|
let namespaceAttributes = {};
|
|
338
337
|
Object.keys(complexType).forEach((key) => {
|
|
339
338
|
if (key === "_attributes") {
|
|
@@ -344,7 +343,7 @@ function _extractComplexType(complexType, parserContext) {
|
|
|
344
343
|
complexType.open = true;
|
|
345
344
|
}
|
|
346
345
|
} else if (key === "Property") {
|
|
347
|
-
properties = complexType.Property;
|
|
346
|
+
// properties = complexType.Property;
|
|
348
347
|
} else if (key === "Documentation") {
|
|
349
348
|
parserContext.allComplexTypeDocs[complexTypeKey] = _parseDocumentationTag(complexType.Documentation);
|
|
350
349
|
}
|
|
@@ -381,6 +380,7 @@ function _getServiceEntityProperty(
|
|
|
381
380
|
documentation,
|
|
382
381
|
allowedNamespaceAttributes = -1,
|
|
383
382
|
nullable,
|
|
383
|
+
collectionKind,
|
|
384
384
|
parserContext
|
|
385
385
|
) {
|
|
386
386
|
let isCollection = -1;
|
|
@@ -458,18 +458,18 @@ function _getServiceEntityProperty(
|
|
|
458
458
|
if (isKey) {
|
|
459
459
|
propertyJson = propertyJson + '"key": true,\n';
|
|
460
460
|
}
|
|
461
|
-
if (isCollection === 1) {
|
|
461
|
+
if (isCollection === 1 || collectionKind === 1) {
|
|
462
462
|
//if annotations to be added
|
|
463
463
|
if (edmxncdsdatatype[dataType + "_a"] && cdsDataType != "cds.Date") {
|
|
464
|
-
if (dataType === 'Edm.Stream')
|
|
464
|
+
if (dataType === 'Edm.Stream')
|
|
465
465
|
propertyJson = propertyJson + '"@Core.MediaType": "application/octet-stream",\n';
|
|
466
|
-
else
|
|
466
|
+
else
|
|
467
467
|
propertyJson = propertyJson + '"@odata.Type": "' + dataType + '",\n';
|
|
468
468
|
}
|
|
469
469
|
|
|
470
470
|
//adding precision in case of DateTime datatypes
|
|
471
471
|
if ((dataType === "Edm.DateTimeOffset" || dataType === "Edm.DateTime") && cdsDataType != "cds.Date") {
|
|
472
|
-
if (precision && precision > 0){
|
|
472
|
+
if (precision && precision > 0) {
|
|
473
473
|
propertyJson = propertyJson + '"@odata.Precision": ' + precision + ',\n';
|
|
474
474
|
}
|
|
475
475
|
}
|
|
@@ -559,6 +559,10 @@ function _getServiceComplexType(complexTypeKey, complexType, parserContext, isOp
|
|
|
559
559
|
complexTypeProperty = complexType[i]._attributes;
|
|
560
560
|
if (complexType[i].Documentation)
|
|
561
561
|
complexTypeProperty.doc = _parseDocumentationTag(complexType[i].Documentation);
|
|
562
|
+
complexType.CollectionKind = 0;
|
|
563
|
+
if (complexType[i]._attributes.CollectionKind === "Bag" || complexType[i]._attributes.CollectionKind === "List") {
|
|
564
|
+
complexType.CollectionKind = 1;
|
|
565
|
+
}
|
|
562
566
|
let namespaceAttributes = namespaceAttributeFilter(complexTypeProperty, parserContext);
|
|
563
567
|
const complexTypePropertyNamespace = namespaceAttributes.length ? namespaceAttributes : -1
|
|
564
568
|
|
|
@@ -574,6 +578,7 @@ function _getServiceComplexType(complexTypeKey, complexType, parserContext, isOp
|
|
|
574
578
|
complexTypeProperty.doc,
|
|
575
579
|
complexTypePropertyNamespace,
|
|
576
580
|
complexTypeProperty.Nullable,
|
|
581
|
+
complexType.CollectionKind,
|
|
577
582
|
parserContext
|
|
578
583
|
);
|
|
579
584
|
complexTypeCSN = complexTypeCSN + complexProperty;
|
|
@@ -589,6 +594,10 @@ function _getServiceComplexType(complexTypeKey, complexType, parserContext, isOp
|
|
|
589
594
|
if (complexType.Documentation)
|
|
590
595
|
complexTypeProperty.doc = _parseDocumentationTag(complexType.Documentation);
|
|
591
596
|
|
|
597
|
+
complexType.CollectionKind = 0;
|
|
598
|
+
if (complexType._attributes.CollectionKind === "Bag" || complexTypeProperty.CollectionKind === "List") {
|
|
599
|
+
complexTypeProperty.CollectionKind = 1;
|
|
600
|
+
}
|
|
592
601
|
let namespaceAttributes = namespaceAttributeFilter(complexTypeProperty, parserContext);
|
|
593
602
|
const complexTypePropertyNamespace = namespaceAttributes.length ? namespaceAttributes : -1;
|
|
594
603
|
|
|
@@ -604,6 +613,7 @@ function _getServiceComplexType(complexTypeKey, complexType, parserContext, isOp
|
|
|
604
613
|
complexTypeProperty.doc,
|
|
605
614
|
complexTypePropertyNamespace,
|
|
606
615
|
complexTypeProperty.Nullable,
|
|
616
|
+
complexTypeProperty.CollectionKind,
|
|
607
617
|
parserContext
|
|
608
618
|
);
|
|
609
619
|
}
|
|
@@ -663,6 +673,7 @@ function _parseParameter(parameter, parserContext) {
|
|
|
663
673
|
doc,
|
|
664
674
|
parameterNamespaceAttributes,
|
|
665
675
|
parameter["_attributes"].Nullable,
|
|
676
|
+
parameter["_attributes"].CollectionKind,
|
|
666
677
|
parserContext
|
|
667
678
|
);
|
|
668
679
|
return csn;
|
|
@@ -739,7 +750,7 @@ function _getFunctionImport(functionImport, isBounded, entityKeysList, parserCon
|
|
|
739
750
|
}
|
|
740
751
|
if (functionImport[2] !== -1) {
|
|
741
752
|
let returnCsn = _getServiceEntityProperty(noValue, functionImport[2], noValue, noValue, noValue,
|
|
742
|
-
noValue, noValue, noValue, -1, noValue, parserContext);
|
|
753
|
+
noValue, noValue, noValue, -1, noValue, noValue, parserContext);
|
|
743
754
|
if (returnCsn != "") {
|
|
744
755
|
csn = csn + ", \n";
|
|
745
756
|
csn = csn + '"returns": { \n';
|
|
@@ -812,14 +823,16 @@ function _parseEntityKeys(keys) {
|
|
|
812
823
|
function _getPropertyAttributes(propAttributes, parserContext) {
|
|
813
824
|
let propOthers = [];
|
|
814
825
|
|
|
815
|
-
let namespaceAttributes = namespaceAttributeFilter(propAttributes, parserContext)
|
|
826
|
+
let namespaceAttributes = namespaceAttributeFilter(propAttributes, parserContext);
|
|
816
827
|
|
|
817
828
|
propOthers.push(propAttributes.Type);
|
|
829
|
+
|
|
818
830
|
if (propAttributes.MaxLength) {
|
|
819
831
|
propOthers.push(propAttributes.MaxLength);
|
|
820
832
|
} else {
|
|
821
833
|
propOthers.push(-1);
|
|
822
834
|
}
|
|
835
|
+
|
|
823
836
|
if (propAttributes.Precision) {
|
|
824
837
|
propOthers.push(propAttributes.Precision);
|
|
825
838
|
} else {
|
|
@@ -856,6 +869,13 @@ function _getPropertyAttributes(propAttributes, parserContext) {
|
|
|
856
869
|
propOthers.push("true");
|
|
857
870
|
}
|
|
858
871
|
|
|
872
|
+
if (propAttributes.CollectionKind === 'List' || propAttributes.CollectionKind === 'Bag') {
|
|
873
|
+
propOthers.push(1);
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
propOthers.push(-1);
|
|
877
|
+
}
|
|
878
|
+
|
|
859
879
|
return propOthers;
|
|
860
880
|
}
|
|
861
881
|
|
|
@@ -916,6 +936,7 @@ function _generateCSNEntityKeys(entityKeysList, entityPropertiesMap, parserConte
|
|
|
916
936
|
if (propAttributes[7] == "true") {
|
|
917
937
|
console.log(warn("Expected key element to be not nullable"));
|
|
918
938
|
}
|
|
939
|
+
|
|
919
940
|
let keyCsn =
|
|
920
941
|
_getServiceEntityProperty(
|
|
921
942
|
entityKeysList[i],
|
|
@@ -928,6 +949,7 @@ function _generateCSNEntityKeys(entityKeysList, entityPropertiesMap, parserConte
|
|
|
928
949
|
propAttributes[5],
|
|
929
950
|
propAttributes[6],
|
|
930
951
|
"false",
|
|
952
|
+
propAttributes[8],
|
|
931
953
|
parserContext
|
|
932
954
|
);
|
|
933
955
|
csnEntity = csnEntity + keyCsn;
|
|
@@ -958,6 +980,7 @@ function _generateCSNEntityProperties(entityKeysList, entityPropertiesMap, parse
|
|
|
958
980
|
// Include Property which are not part of keys
|
|
959
981
|
if (entityKeysList.indexOf(property) === -1) {
|
|
960
982
|
propAttributes = entityPropertiesMap[property];
|
|
983
|
+
|
|
961
984
|
let propertyString =
|
|
962
985
|
_getServiceEntityProperty(
|
|
963
986
|
property,
|
|
@@ -970,6 +993,7 @@ function _generateCSNEntityProperties(entityKeysList, entityPropertiesMap, parse
|
|
|
970
993
|
propAttributes[5],
|
|
971
994
|
propAttributes[6],
|
|
972
995
|
propAttributes[7],
|
|
996
|
+
propAttributes[8],
|
|
973
997
|
parserContext
|
|
974
998
|
);
|
|
975
999
|
csnEntity = csnEntity + propertyString;
|
|
@@ -112,7 +112,12 @@ function _isValidJSON(jsonObj, parserContext) {
|
|
|
112
112
|
function _hasEntityContainer(jsonObj, parserContext) {
|
|
113
113
|
let jsonObjEntityContainer = jsonObj.$EntityContainer;
|
|
114
114
|
try {
|
|
115
|
-
if (jsonObjEntityContainer)
|
|
115
|
+
if (jsonObjEntityContainer) {
|
|
116
|
+
// scenario: Schema Namespace="A.B.C.D" & jsonObjEntityContainer is "A.B.C.D.EntityContainerName"
|
|
117
|
+
let lastIndex = jsonObjEntityContainer.lastIndexOf('.');
|
|
118
|
+
parserContext.namespace = jsonObjEntityContainer.substring(0, lastIndex);
|
|
119
|
+
parserContext.jsonEntityContainerName = jsonObjEntityContainer.substring(lastIndex + 1);
|
|
120
|
+
}
|
|
116
121
|
if (jsonObj[parserContext.namespace][parserContext.jsonEntityContainerName] &&
|
|
117
122
|
jsonObj[parserContext.namespace][parserContext.jsonEntityContainerName] instanceof Object
|
|
118
123
|
) {
|
|
@@ -1212,6 +1217,7 @@ function _generateDocumentation(edmxAsJson, schemaData, cdsDefs, parserContext)
|
|
|
1212
1217
|
let vocabAlias = [];
|
|
1213
1218
|
let vocab = [];
|
|
1214
1219
|
let vocabNamespce = [];
|
|
1220
|
+
let controlFieldFlag = 0;
|
|
1215
1221
|
if (schemaData[0].$Annotations) {
|
|
1216
1222
|
if (edmxAsJson.$Reference) {
|
|
1217
1223
|
Object.entries(edmxAsJson.$Reference).forEach(a => {
|
|
@@ -1232,8 +1238,9 @@ function _generateDocumentation(edmxAsJson, schemaData, cdsDefs, parserContext)
|
|
|
1232
1238
|
if (annotation[1].hasOwnProperty('@Core.Description')) {
|
|
1233
1239
|
no_of_terms = true;
|
|
1234
1240
|
}
|
|
1235
|
-
if (annotation[1].hasOwnProperty('@Common.FieldControl')) {
|
|
1236
|
-
annotation[1]["@Common.FieldControl"] = { "#": annotation[1]["@Common.FieldControl"]
|
|
1241
|
+
if (annotation[1].hasOwnProperty('@Common.FieldControl') && controlFieldFlag === 0) {
|
|
1242
|
+
annotation[1]["@Common.FieldControl"] = { "#": annotation[1]["@Common.FieldControl"]}
|
|
1243
|
+
controlFieldFlag = 1;
|
|
1237
1244
|
}
|
|
1238
1245
|
if (annotation[1].hasOwnProperty('@Common.IsUpperCase')) {
|
|
1239
1246
|
annotation[1]["@Common.IsUpperCase"] = true;
|
|
@@ -1241,6 +1248,7 @@ function _generateDocumentation(edmxAsJson, schemaData, cdsDefs, parserContext)
|
|
|
1241
1248
|
_checkAnnotationTarget(annotation, cdsDefs, anno[0], no_of_terms, vocab, parserContext);
|
|
1242
1249
|
}
|
|
1243
1250
|
});
|
|
1251
|
+
controlFieldFlag = 0;
|
|
1244
1252
|
});
|
|
1245
1253
|
}
|
|
1246
1254
|
}
|
package/lib/init/constants.js
CHANGED
package/lib/init/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
|
|
3
3
|
const term = require('../util/term');
|
|
4
|
-
const Spinner = require('./util/spinner');
|
|
5
4
|
|
|
6
5
|
const { OPTIONS_PRIORITY, COMMAND_ADD, COMMAND_INIT,
|
|
7
6
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -11,7 +10,7 @@ const { OPTIONS_PRIORITY, COMMAND_ADD, COMMAND_INIT,
|
|
|
11
10
|
|
|
12
11
|
const cds = require('../cds'), { exists, readdir } = cds.utils
|
|
13
12
|
const LOG = console
|
|
14
|
-
const DEBUG = /\
|
|
13
|
+
const DEBUG = /\b(y|all|cli)\b/.test(process.env.DEBUG) && console.debug
|
|
15
14
|
|
|
16
15
|
module.exports = class CDSGenerator {
|
|
17
16
|
constructor() {
|
|
@@ -212,15 +211,8 @@ module.exports = class CDSGenerator {
|
|
|
212
211
|
|
|
213
212
|
for (const template of this.templateList) {
|
|
214
213
|
const msg = `Adding feature '${template.name}'...`;
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
await template.run();
|
|
218
|
-
} else {
|
|
219
|
-
const spinner = new Spinner();
|
|
220
|
-
await spinner.run(msg, async () => { // NOSONAR
|
|
221
|
-
return template.run();
|
|
222
|
-
});
|
|
223
|
-
}
|
|
214
|
+
LOG.info(msg);
|
|
215
|
+
await template.run();
|
|
224
216
|
}
|
|
225
217
|
|
|
226
218
|
LOG.info(`Done adding features`);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Please do not modify this file. This file may be overwritten.
|
|
2
2
|
|
|
3
|
+
{{- $defaultParameters := dict "forwardAuthToken" true }}
|
|
4
|
+
{{- $ := dict "root" . "defaultParameters" $defaultParameters }}
|
|
5
|
+
|
|
3
6
|
apiVersion: v1
|
|
4
7
|
kind: ConfigMap
|
|
5
8
|
metadata:
|
|
@@ -7,4 +10,4 @@ metadata:
|
|
|
7
10
|
namespace: "{{ .Release.Namespace }}"
|
|
8
11
|
data:
|
|
9
12
|
destinations: >
|
|
10
|
-
{{ include "cap.
|
|
13
|
+
{{ include "cap.backendDestinations" $ }}
|