@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.
Files changed (93) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/bin/bind.js +30 -34
  3. package/bin/cds.js +14 -3
  4. package/bin/deploy/index.js +3 -1
  5. package/bin/deploy/to-sqlite.js +2 -1
  6. package/bin/env.js +4 -4
  7. package/bin/migrate.js +25 -4
  8. package/bin/push.js +4 -0
  9. package/bin/watch.js +3 -3
  10. package/lib/bind/KubernetesApiAccessor.js +79 -0
  11. package/lib/bind/{BindingManager.js → bindingManager.js} +16 -15
  12. package/lib/bind/bindingResolver.js +41 -68
  13. package/lib/bind/bindingUtil.js +93 -104
  14. package/lib/bind/cfKeyProvider.js +85 -74
  15. package/lib/bind/kubernetesCredentialsProvider.js +34 -132
  16. package/lib/bind/userCdsrcJsonStorageProvider.js +12 -18
  17. package/lib/client/params.js +2 -2
  18. package/lib/client/push.js +11 -6
  19. package/lib/client/settings_manager.js +2 -1
  20. package/lib/client/subscription.js +4 -1
  21. package/lib/import/odata/v2parser.js +32 -8
  22. package/lib/import/odata/v4parser.js +11 -3
  23. package/lib/init/constants.js +1 -1
  24. package/lib/init/index.js +3 -11
  25. package/lib/init/template/approuter/files/approuter-configmap.yaml +4 -1
  26. package/lib/init/template/approuter/files/approuter-mtxs-configmap.yaml +5 -1
  27. package/lib/init/template/approuter/files/package.json +1 -1
  28. package/lib/init/template/approuter/files/values.yaml.hbs +1 -1
  29. package/lib/init/template/approuter/files/xs-app.json.hbs +2 -2
  30. package/lib/init/template/approuter/index.js +4 -5
  31. package/lib/init/template/auditlog/index.js +1 -1
  32. package/lib/init/template/data/index.js +1 -1
  33. package/lib/init/template/destinations/index.js +1 -1
  34. package/lib/init/template/enterprise-messaging/index.js +1 -1
  35. package/lib/init/template/extensibility/index.js +6 -1
  36. package/lib/init/template/hana/files/chart.yaml +4 -0
  37. package/lib/init/template/hana/files/hana.yaml +1 -0
  38. package/lib/init/template/hana/files/java/db/package.json +1 -1
  39. package/lib/init/template/hana/files/values.yaml.hbs +6 -3
  40. package/lib/init/template/hana/index.js +73 -21
  41. package/lib/init/template/helm/chart/templates/_deployment_helpers.tpl +14 -6
  42. package/lib/init/template/helm/chart/values.root.schema.json +10 -0
  43. package/lib/init/template/helm/chart/values.schema.json +11 -110
  44. package/lib/init/template/helm/index.js +2 -2
  45. package/lib/init/template/helm/subcharts/content-deployment/Chart.yaml +5 -0
  46. package/lib/init/template/helm/subcharts/content-deployment/templates/_helpers.tpl +207 -0
  47. package/lib/init/template/helm/subcharts/content-deployment/templates/job.yaml +66 -0
  48. package/lib/init/template/helm/subcharts/content-deployment/templates/secret.yaml +10 -0
  49. package/lib/init/template/helm/subcharts/content-deployment/templates/service-account.yaml +4 -0
  50. package/lib/init/template/helm/subcharts/content-deployment/templates/service-binding.yaml +28 -0
  51. package/lib/init/template/helm/subcharts/content-deployment/values-lint.yaml +5 -0
  52. package/lib/init/template/helm/subcharts/content-deployment/values.md +1014 -0
  53. package/lib/init/template/helm/subcharts/content-deployment/values.schema.json +589 -0
  54. package/lib/init/template/helm/subcharts/content-deployment/values.yaml +54 -0
  55. package/lib/init/template/helm/{subchart → subcharts/web-application}/Chart.yaml +0 -0
  56. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/_helpers.tpl +56 -50
  57. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/api-rule.yaml +1 -1
  58. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/deployment.yaml +15 -9
  59. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/network-policy.yaml +0 -0
  60. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/pod-disruption-budget.yaml +2 -2
  61. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/secret.yaml +0 -0
  62. package/lib/init/template/helm/subcharts/web-application/templates/service-account.yaml +6 -0
  63. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/service-binding.yaml +4 -3
  64. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/service.yaml +0 -0
  65. package/lib/init/template/helm/subcharts/web-application/templating.md +44 -0
  66. package/lib/init/template/helm/{subchart → subcharts/web-application}/values-lint.yaml +0 -0
  67. package/lib/init/template/helm/{subchart → subcharts/web-application}/values.md +254 -200
  68. package/lib/init/template/helm/{subchart → subcharts/web-application}/values.schema.json +82 -28
  69. package/lib/init/template/helm/{subchart → subcharts/web-application}/values.yaml +15 -5
  70. package/lib/init/template/html5-repo/files/chart.yaml +4 -0
  71. package/lib/init/template/html5-repo/files/html5-apps-deployer-configmap.yaml +14 -0
  72. package/lib/init/template/html5-repo/index.js +39 -7
  73. package/lib/init/template/kibana-logging/index.js +1 -1
  74. package/lib/init/template/lint/index.js +1 -1
  75. package/lib/init/template/multitenancy/files/saas-registry.yaml +2 -2
  76. package/lib/init/template/multitenancy/files/service-manager.yaml +1 -0
  77. package/lib/init/template/multitenancy/files/values.yaml.hbs +10 -6
  78. package/lib/init/template/multitenancy/index.js +21 -3
  79. package/lib/init/util/commandUtil.js +5 -5
  80. package/lib/init/util/merge.js +27 -1
  81. package/lib/init/util/mvnArchetypeUtil.js +1 -1
  82. package/lib/init/util/projectReader.js +2 -2
  83. package/lib/util/exec.js +3 -3
  84. package/lib/util/term.js +7 -7
  85. package/lib/watch/livereload-connect.js +187 -0
  86. package/lib/watch/livereload-server.js +12 -12
  87. package/lib/watch/node-watch.js +1 -1
  88. package/npm-shrinkwrap.json +191 -404
  89. package/package.json +2 -5
  90. package/lib/init/template/hana/files/hana-deployer-job.yaml +0 -49
  91. package/lib/init/template/html5-repo/files/_html5_deployment_helpers.tpl +0 -16
  92. package/lib/init/template/html5-repo/files/html5-apps-deployer-job.yaml +0 -61
  93. package/lib/init/util/spinner.js +0 -80
@@ -1,139 +1,52 @@
1
- const cp = require('child_process');
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.config = await this._kubectlConfig();
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
- let {cluster, namespace, name: bindingName, secret } = binding;
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._req(`/apis/services.cloud.sap.com/v1/namespaces/${encodeURIComponent(namespace)}/servicebindings/${encodeURIComponent(bindingName)}`);
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
- secret = bindingResource.spec.secretName || throwError();
121
- instance = bindingResource.spec.serviceInstanceName || throwError();
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._req(`/api/v1/namespaces/${encodeURIComponent(namespace)}/secrets/${encodeURIComponent(secret)}`);
126
- if (secretResource === null) throwError(`${secretOrBindingResource} '${secret}' not found.`);
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 = readBinding(name, secretResource);
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: this.config.url,
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
- 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
- }
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
- let parseBinding
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, logger = console) {
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
- logger.debug('git command not found');
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
- logger.debug(err);
47
+ DEBUG && DEBUG(err);
46
48
  return;
47
49
  }
48
50
  }
49
51
 
50
- logger.info(`adding entry '${file}' to ${GIT_IGNORE_FILE}.`);
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, options.logger);
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, options.logger);
110
+ await this.writeJsonFile(jsonPath, jsonObject);
117
111
  if (options.profile) {
118
- options.logger.info(`Saving bindings to ${highlight(jsonPath)} in profile ${highlight(options.profile)}`);
112
+ logger.log(`Saving bindings to ${highlight(jsonPath)} in profile ${highlight(options.profile)}`);
119
113
  } else {
120
- options.logger.info(`Saving bindings to ${highlight(jsonPath)}`);
114
+ logger.log(`Saving bindings to ${highlight(jsonPath)}`);
121
115
  }
122
116
  }
123
117
 
@@ -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 app as a whole.
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);
@@ -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
- const { src, content } = await this.readSource(params, pushOptions);
18
+ if (!('extArchive' in pushOptions)) {
19
+ console.log(`Building project`);
20
+ await cds.build({ project: projectFolder });
21
+ }
17
22
 
18
- const extensionName = this.getExtensionName(params.get('projectFolder'));
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', 'extension.tgz');
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 }, 'from', { url: params.get('appUrl') });
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) [parserContext.namespace, parserContext.jsonEntityContainerName] = jsonObjEntityContainer.split('.');
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
  }
@@ -1,6 +1,6 @@
1
1
  const os = require('os');
2
2
 
3
- const MAVEN_ARCHETYPE_VERSION = '1.30.0';
3
+ const MAVEN_ARCHETYPE_VERSION = '1.30.1';
4
4
 
5
5
  const constants = {
6
6
  IS_WIN: (os.platform() === 'win32'),
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 = /\bcli\b/.test(process.env.DEBUG) && console.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
- if (DEBUG) {
216
- LOG.info(msg);
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.approuterDestinations" . }}
13
+ {{ include "cap.backendDestinations" $ }}