@sap/cds-dk 4.9.6 → 6.0.2

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 (143) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/bin/activate.js +4 -4
  3. package/bin/add.js +11 -5
  4. package/bin/bind.js +16 -28
  5. package/bin/build/all.js +2 -2
  6. package/bin/build/index.js +12 -19
  7. package/bin/cds.js +16 -17
  8. package/bin/compile/index.js +12 -3
  9. package/bin/deploy/index.js +14 -14
  10. package/bin/deploy/to-hana.js +4 -4
  11. package/bin/deploy/to-sqlite.js +1 -1
  12. package/bin/env.js +13 -4
  13. package/bin/eval.js +1 -1
  14. package/bin/fix-redirects.js +1 -1
  15. package/bin/import.js +44 -179
  16. package/bin/repl.js +1 -1
  17. package/bin/watch.js +10 -15
  18. package/lib/bind/BindingManager.js +87 -0
  19. package/lib/bind/bindingResolver.js +14 -85
  20. package/lib/bind/bindingUtil.js +18 -11
  21. package/lib/bind/cfKeyProvider.js +58 -29
  22. package/lib/bind/kubernetesCredentialsProvider.js +44 -21
  23. package/lib/bind/userCdsrcJsonStorageProvider.js +12 -18
  24. package/lib/cds.js +13 -0
  25. package/lib/client/base_command.js +3 -2
  26. package/lib/client/extend.js +6 -7
  27. package/lib/client/helper/fs_helper.js +7 -8
  28. package/lib/client/settings_manager.js +14 -10
  29. package/lib/compile/index.js +9 -10
  30. package/lib/compile/openapi/csdl2openapi.js +151 -43
  31. package/lib/compile/openapi/index.js +2 -2
  32. package/lib/compile/to-xsuaa.js +3 -2
  33. package/lib/import/common.js +342 -27
  34. package/lib/import/importapi.js +81 -13
  35. package/lib/import/message.js +4 -4
  36. package/lib/import/odata/index.js +141 -0
  37. package/lib/import/{utils → odata/metadataconverter}/MetadataConverter.js +2 -2
  38. package/lib/import/{utils → odata/metadataconverter}/MetadataConverterFactory.js +1 -1
  39. package/lib/import/{utils → odata/util}/MultiMap.js +0 -0
  40. package/lib/import/{v2parser.js → odata/v2parser.js} +405 -468
  41. package/lib/import/{v4parser.js → odata/v4parser.js} +311 -322
  42. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Aggregation.V1.json +0 -0
  43. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Authorization.V1.json +0 -0
  44. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Capabilities.V1.json +0 -0
  45. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Core.V1.json +0 -0
  46. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Measures.V1.json +0 -0
  47. package/lib/import/{vocabularies → odata/vocabularies}/Org.OData.Validation.V1.json +0 -0
  48. package/lib/import/openapi/importOpenAPI.js +521 -0
  49. package/lib/import/openapi/index.js +19 -0
  50. package/lib/import/openapi/utilities.js +38 -0
  51. package/lib/index.js +1 -1
  52. package/lib/init/constants.js +7 -2
  53. package/lib/init/index.js +10 -3
  54. package/lib/init/template/_merging/existences.js +79 -19
  55. package/lib/init/template/approuter/files/mta.yaml.hbs +25 -11
  56. package/lib/init/template/approuter/files/package.json +1 -1
  57. package/lib/init/template/approuter/files/xs-app.json.hbs +7 -0
  58. package/lib/init/template/approuter/index.js +61 -54
  59. package/lib/init/template/auditlog/index.js +2 -2
  60. package/lib/init/template/data/index.js +2 -2
  61. package/lib/init/template/hana/files/mta.yaml.hbs +17 -17
  62. package/lib/init/template/hana/index.js +8 -4
  63. package/lib/init/template/helm/index.js +39 -0
  64. package/lib/init/template/helm/update/index.js +28 -0
  65. package/lib/init/template/java/index.js +7 -9
  66. package/lib/init/template/kibana-logging/index.js +5 -6
  67. package/lib/init/template/mta/index.js +4 -4
  68. package/lib/init/template/mtx/files/java/package.json +9 -9
  69. package/lib/init/template/mtx/files/mta.yaml.hbs +0 -7
  70. package/lib/init/template/mtx/files/xs-security.json.hbs +11 -20
  71. package/lib/init/template/mtx/index.js +50 -57
  72. package/lib/init/template/nodejs/files/package.json.hbs +2 -2
  73. package/lib/init/template/notebook/index.js +2 -0
  74. package/lib/init/template/templateBase.js +6 -4
  75. package/lib/init/template/xsuaa/index.js +9 -5
  76. package/lib/init/util/fsUtil.js +25 -10
  77. package/lib/init/util/logger.js +1 -1
  78. package/lib/init/util/mvnArchetypeUtil.js +4 -10
  79. package/lib/init/util/projectReader.js +16 -8
  80. package/lib/init/util/templateUtil.js +30 -34
  81. package/lib/k8s/ChartAdder.js +65 -0
  82. package/lib/k8s/ChartBuilder.js +186 -0
  83. package/lib/k8s/Context.js +71 -0
  84. package/lib/k8s/ImageUtil.js +21 -0
  85. package/lib/k8s/Model.d.ts +53 -0
  86. package/lib/k8s/auditlog/index.js +29 -0
  87. package/lib/k8s/auditlog/templates/auditlog.yaml +1 -0
  88. package/lib/k8s/buildModel.js +70 -0
  89. package/lib/k8s/connectivity/index.js +74 -0
  90. package/lib/k8s/connectivity/templates/connectivity-binding.yaml +9 -0
  91. package/lib/k8s/connectivity/templates/connectivity-proxy-info.yaml +16 -0
  92. package/lib/k8s/connectivity/templates/connectivity.yaml +1 -0
  93. package/lib/k8s/deployment/index.js +81 -0
  94. package/lib/k8s/deployment/subchart/Chart.yaml +6 -0
  95. package/lib/k8s/deployment/subchart/templates/_helpers.tpl +156 -0
  96. package/lib/k8s/deployment/subchart/templates/api-rule.yaml +24 -0
  97. package/lib/k8s/deployment/subchart/templates/deployment.yaml +110 -0
  98. package/lib/k8s/deployment/subchart/templates/network-policy.yaml +16 -0
  99. package/lib/k8s/deployment/subchart/templates/pod-disruption-budget.yaml +11 -0
  100. package/lib/k8s/deployment/subchart/templates/secret.yaml +10 -0
  101. package/lib/k8s/deployment/subchart/templates/service-binding.yaml +27 -0
  102. package/lib/k8s/deployment/subchart/templates/service.yaml +11 -0
  103. package/lib/k8s/deployment/subchart/values-lint.yaml +8 -0
  104. package/lib/k8s/deployment/subchart/values.schema.json +449 -0
  105. package/lib/k8s/deployment/subchart/values.yaml +76 -0
  106. package/lib/k8s/deployment/subchart-override/templates/_web_application_helpers.tpl +81 -0
  107. package/lib/k8s/destinations/index.js +40 -0
  108. package/lib/k8s/destinations/lib.js +16 -0
  109. package/lib/k8s/destinations/templates/destinations.yaml +1 -0
  110. package/lib/k8s/docker/push.js +8 -0
  111. package/lib/k8s/event-mesh/index.js +53 -0
  112. package/lib/k8s/event-mesh/lib.js +9 -0
  113. package/lib/k8s/event-mesh/templates/event-mesh.yaml +1 -0
  114. package/lib/k8s/hana-deployer/index.js +38 -0
  115. package/lib/k8s/hana-deployer/templates/hana-deployer-job.yaml +41 -0
  116. package/lib/k8s/helm/Release.js +80 -0
  117. package/lib/k8s/html5-apps-deployer/index.js +43 -0
  118. package/lib/k8s/html5-apps-deployer/templates/_html5_deployment_helpers.tpl +16 -0
  119. package/lib/k8s/html5-apps-deployer/templates/html5-apps-deployer-job.yaml +53 -0
  120. package/lib/k8s/html5-apps-repo-host/index.js +26 -0
  121. package/lib/k8s/html5-apps-repo-host/lib.js +16 -0
  122. package/lib/k8s/html5-apps-repo-host/templates/html5-apps-repo-host.yaml +1 -0
  123. package/lib/k8s/index.js +153 -0
  124. package/lib/k8s/modules.js +36 -0
  125. package/lib/k8s/multitenancy/index.js +24 -0
  126. package/lib/k8s/multitenancy/lib.js +25 -0
  127. package/lib/k8s/multitenancy/templates/saas-registry.yaml +19 -0
  128. package/lib/k8s/readProject.js +14 -0
  129. package/lib/k8s/resources/helm/chart/templates/NOTES.txt +11 -0
  130. package/lib/k8s/resources/helm/chart/templates/_deployment_helpers.tpl +221 -0
  131. package/lib/k8s/resources/helm/chart/templates/_helpers.tpl +167 -0
  132. package/lib/k8s/resources/helm/chart/templates/_sapcp_helpers.tpl +103 -0
  133. package/lib/k8s/util.js +186 -0
  134. package/lib/k8s/wrapFeature.js +55 -0
  135. package/lib/k8s/xsuaa/index.js +64 -0
  136. package/lib/k8s/xsuaa/lib.js +14 -0
  137. package/lib/k8s/xsuaa/templates/xsuaa.yaml +1 -0
  138. package/lib/util/exec.js +1 -1
  139. package/lib/util/write.js +1 -1
  140. package/lib/watch/node-watch.js +43 -14
  141. package/npm-shrinkwrap.json +914 -2745
  142. package/package.json +5 -5
  143. 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*(.*)/i, `CF API endpoint is missing. Use 'cf login' to login.`),
54
- user: this.extract(result, /user\s*:\s*(.*)/i, `CF user is missing. Use 'cf login' to login.`),
55
- org: this.extract(result, /org\s*:\s*(.*)/i, `CF org is missing. Use 'cf target -o <ORG> to specify.`),
56
- space: this.extract(result, /space\s*:\s*(.*)/i, `CF space is missing. Use 'cf target -s <SPACE>' to specify.`),
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.getCfTargetFromCli();
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({logger, target, url: '/v3/spaces', extractors: {
99
- organization_guids: resource => resource.relationships.organization.data.guid
100
- }});
101
- this.getInstance = BatchRequest.create({logger, target, url: '/v3/service_instances', extractors: {
102
- space_guids: resource => resource.relationships.space.data.guid
103
- }});
104
- this.getKey = BatchRequest.create({logger, target, url: '/v3/service_credential_bindings', extractors: {
105
- service_instance_guids: resource => resource.relationships.service_instance.data.guid
106
- }});
107
- this.getPlans = BatchRequest.create({logger, target, url: '/v3/service_plans' });
108
- this.getOfferings = BatchRequest.create({logger, target, url: '/v3/service_offerings' });
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( (_resolve, _reject) => {
222
+ const promise = new Promise((_resolve, _reject) => {
194
223
  resolve = _resolve;
195
- reject = _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( value => encodeURIComponent(value) ).join(',')}`
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( key => `${key}: ${filter[key]}` ).join(', ');
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( resource => this.extractors[name](resource) === filter.filter[name] );
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 && entry.user.token );
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: user.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/servicecatalog.k8s.io/v1beta1/namespaces/${encodeURIComponent(namespace)}/servicebindings/${encodeURIComponent(bindingName)}`);
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.instanceRef && bindingResource.spec.instanceRef.name || throwError();
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 credentials = {};
111
- for (const name in secretResource.data) {
112
- let value = secretResource.data[name];
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
- if (instance) {
134
- const serviceInstanceResource = await this._req(`/apis/servicecatalog.k8s.io/v1beta1/namespaces/${encodeURIComponent(namespace)}/serviceinstances/${encodeURIComponent(instance)}`);
149
+ }
135
150
 
136
- resolvedBinding.binding.vcap = {
137
- label: serviceInstanceResource.spec.clusterServiceClassExternalName,
138
- plan: serviceInstanceResource.spec.clusterServicePlanExternalName
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
- return resolvedBinding;
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
- // TODO transition to fs → need to mock fs and fse in test (cf. MTX client)
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 fse.writeFile(GIT_IGNORE_FILE, gitignore, 'utf8');
56
+ await fs.writeFile(GIT_IGNORE_FILE, gitignore, 'utf8');
58
57
  }
59
58
 
60
59
  async readFileSafely(file) {
61
60
  try {
62
- return await fse.readFile(file, 'utf8');
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 fse.stat(jsonPath);
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
- cdsRoot.features = cdsRoot.features || {};
100
- cdsRoot.features[profileKey] = cdsRoot.features[profileKey] || {};
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, vcap } = service;
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 fse.readJSON(filePath) || {};
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 fse.outputJSON(filePath, content, {
138
- spaces: 2
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 { fse } = require('@sap/cds-foss');
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
- return fse.pathExists(path.join(folder, 'package.json'));
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) {
@@ -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
- fse.mkdirpSync(targetDirectory);
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
- fse.mkdirpSync(path.dirname(normalizedFilepath));
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 fse.mkdirp(path.join(params.get('projectFolder'), DB_FOLDER));
125
- await fse.mkdirp(path.join(params.get('projectFolder'), SRV_FOLDER));
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 = await fse.pathExists(targetDirectory);
150
+ const targetExists = fs.existsSync(targetDirectory);
152
151
  if (!targetExists || await this.promptTemplateOverwrite()) {
153
152
  if (targetExists) {
154
- await fse.remove(targetDirectory);
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
- filePaths.forEach((filePath) => {
7
- const fileContent = fs.readFileSync(filePath);
8
- resultMap.set(toUnixPath(path.relative(root, filePath)), fileContent.toString('utf-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
- absoluteDir = fs.realpathSync(absoluteDir);
33
- if (fs.statSync(absoluteDir)) {
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 fse.outputJson(CONFIG.paths.settings, paramValuesByFolder, { spaces: 2 });
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 fse.outputJson(CONFIG.paths.auth, allAuthValues, { spaces: 2 });
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 fse.outputJson(CONFIG.paths.auth, allAuthValues, { spaces: 2 });
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
- const settingsByFolder = await fse.readJson(CONFIG.paths.settings);
438
-
439
- if (!settingsByFolder) {
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
- const allAuth = await fse.readJson(CONFIG.paths.auth);
465
-
466
- if (!allAuth) {
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
  }
@@ -1,14 +1,13 @@
1
- const cds = require ('@sap/cds')
1
+ const cds = require('../cds')
2
+ const comp_to = cds.compile.to
3
+ const { defineProperty } = Object
2
4
 
3
- // cds.compile.to ...
4
- 'xsuaa' in cds.compile.to || Object.defineProperties (cds.compile.to,{
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