@sap/cds-dk 6.4.1 → 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 (92) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/bin/bind.js +30 -34
  3. package/bin/cds.js +14 -3
  4. package/bin/deploy/to-sqlite.js +2 -1
  5. package/bin/env.js +4 -4
  6. package/bin/migrate.js +25 -4
  7. package/bin/push.js +4 -0
  8. package/bin/watch.js +3 -3
  9. package/lib/bind/KubernetesApiAccessor.js +79 -0
  10. package/lib/bind/{BindingManager.js → bindingManager.js} +16 -15
  11. package/lib/bind/bindingResolver.js +41 -68
  12. package/lib/bind/bindingUtil.js +93 -104
  13. package/lib/bind/cfKeyProvider.js +85 -74
  14. package/lib/bind/kubernetesCredentialsProvider.js +34 -132
  15. package/lib/bind/userCdsrcJsonStorageProvider.js +12 -18
  16. package/lib/client/params.js +2 -2
  17. package/lib/client/push.js +11 -6
  18. package/lib/client/settings_manager.js +2 -1
  19. package/lib/client/subscription.js +4 -1
  20. package/lib/import/odata/v2parser.js +32 -8
  21. package/lib/import/odata/v4parser.js +5 -2
  22. package/lib/init/constants.js +1 -1
  23. package/lib/init/index.js +3 -11
  24. package/lib/init/template/approuter/files/approuter-configmap.yaml +4 -1
  25. package/lib/init/template/approuter/files/approuter-mtxs-configmap.yaml +5 -1
  26. package/lib/init/template/approuter/files/package.json +1 -1
  27. package/lib/init/template/approuter/files/values.yaml.hbs +1 -1
  28. package/lib/init/template/approuter/files/xs-app.json.hbs +2 -2
  29. package/lib/init/template/approuter/index.js +4 -5
  30. package/lib/init/template/auditlog/index.js +1 -1
  31. package/lib/init/template/data/index.js +1 -1
  32. package/lib/init/template/destinations/index.js +1 -1
  33. package/lib/init/template/enterprise-messaging/index.js +1 -1
  34. package/lib/init/template/extensibility/index.js +6 -1
  35. package/lib/init/template/hana/files/chart.yaml +4 -0
  36. package/lib/init/template/hana/files/hana.yaml +1 -0
  37. package/lib/init/template/hana/files/java/db/package.json +1 -1
  38. package/lib/init/template/hana/files/values.yaml.hbs +6 -3
  39. package/lib/init/template/hana/index.js +73 -21
  40. package/lib/init/template/helm/chart/templates/_deployment_helpers.tpl +14 -6
  41. package/lib/init/template/helm/chart/values.root.schema.json +10 -0
  42. package/lib/init/template/helm/chart/values.schema.json +11 -110
  43. package/lib/init/template/helm/index.js +2 -2
  44. package/lib/init/template/helm/subcharts/content-deployment/Chart.yaml +5 -0
  45. package/lib/init/template/helm/subcharts/content-deployment/templates/_helpers.tpl +207 -0
  46. package/lib/init/template/helm/subcharts/content-deployment/templates/job.yaml +66 -0
  47. package/lib/init/template/helm/subcharts/content-deployment/templates/secret.yaml +10 -0
  48. package/lib/init/template/helm/subcharts/content-deployment/templates/service-account.yaml +4 -0
  49. package/lib/init/template/helm/subcharts/content-deployment/templates/service-binding.yaml +28 -0
  50. package/lib/init/template/helm/subcharts/content-deployment/values-lint.yaml +5 -0
  51. package/lib/init/template/helm/subcharts/content-deployment/values.md +1014 -0
  52. package/lib/init/template/helm/subcharts/content-deployment/values.schema.json +589 -0
  53. package/lib/init/template/helm/subcharts/content-deployment/values.yaml +54 -0
  54. package/lib/init/template/helm/{subchart → subcharts/web-application}/Chart.yaml +0 -0
  55. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/_helpers.tpl +56 -50
  56. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/api-rule.yaml +1 -1
  57. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/deployment.yaml +15 -9
  58. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/network-policy.yaml +0 -0
  59. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/pod-disruption-budget.yaml +2 -2
  60. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/secret.yaml +0 -0
  61. package/lib/init/template/helm/subcharts/web-application/templates/service-account.yaml +6 -0
  62. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/service-binding.yaml +4 -3
  63. package/lib/init/template/helm/{subchart → subcharts/web-application}/templates/service.yaml +0 -0
  64. package/lib/init/template/helm/subcharts/web-application/templating.md +44 -0
  65. package/lib/init/template/helm/{subchart → subcharts/web-application}/values-lint.yaml +0 -0
  66. package/lib/init/template/helm/{subchart → subcharts/web-application}/values.md +254 -200
  67. package/lib/init/template/helm/{subchart → subcharts/web-application}/values.schema.json +82 -28
  68. package/lib/init/template/helm/{subchart → subcharts/web-application}/values.yaml +15 -5
  69. package/lib/init/template/html5-repo/files/chart.yaml +4 -0
  70. package/lib/init/template/html5-repo/files/html5-apps-deployer-configmap.yaml +14 -0
  71. package/lib/init/template/html5-repo/index.js +39 -7
  72. package/lib/init/template/kibana-logging/index.js +1 -1
  73. package/lib/init/template/lint/index.js +1 -1
  74. package/lib/init/template/multitenancy/files/saas-registry.yaml +2 -2
  75. package/lib/init/template/multitenancy/files/service-manager.yaml +1 -0
  76. package/lib/init/template/multitenancy/files/values.yaml.hbs +10 -6
  77. package/lib/init/template/multitenancy/index.js +21 -3
  78. package/lib/init/util/commandUtil.js +5 -5
  79. package/lib/init/util/merge.js +27 -1
  80. package/lib/init/util/mvnArchetypeUtil.js +1 -1
  81. package/lib/init/util/projectReader.js +2 -2
  82. package/lib/util/exec.js +3 -3
  83. package/lib/util/term.js +7 -7
  84. package/lib/watch/livereload-connect.js +187 -0
  85. package/lib/watch/livereload-server.js +12 -12
  86. package/lib/watch/node-watch.js +1 -1
  87. package/npm-shrinkwrap.json +532 -697
  88. package/package.json +1 -4
  89. package/lib/init/template/hana/files/hana-deployer-job.yaml +0 -49
  90. package/lib/init/template/html5-repo/files/_html5_deployment_helpers.tpl +0 -16
  91. package/lib/init/template/html5-repo/files/html5-apps-deployer-job.yaml +0 -61
  92. package/lib/init/util/spinner.js +0 -80
@@ -1,4 +1,3 @@
1
-
2
1
  const os = require('os');
3
2
  const IS_WIN = os.platform() === 'win32';
4
3
 
@@ -22,128 +21,118 @@ const PreferredKinds = [
22
21
  'xsuaa'
23
22
  ];
24
23
 
25
- function getRequiresNameFromKind(service, kind) {
26
- const requiresName = KindToRequiresNameMap[kind];
27
- if (!requiresName) throw new Error(`Unknown CDS service name for service "${service}". Please specify as argument for "cds bind".`)
28
- return requiresName;
24
+ const PLATFORM_TYPES = {
25
+ 'cf': 'Cloud Foundry',
26
+ 'k8s': 'Kubernetes'
29
27
  }
30
28
 
31
- /**
32
- * Determine kind value for services based on VCAP binding
33
- * information in the configuration.
34
- */
35
- function determineKinds(services) {
36
- const vcapServices = {}
37
-
38
- // Build VCAP_SERVICES
39
- for (const service of services) {
40
- service.kindCandidates = [ ]
29
+ class BindingUtil {
30
+ static getRequiresNameFromKind(service, kind) {
31
+ const requiresName = KindToRequiresNameMap[kind];
32
+ if (!requiresName) throw new Error(`Unknown CDS service name for service "${service}". Please specify as argument for "cds bind".`)
33
+ return requiresName;
34
+ }
41
35
 
42
- const { vcap = {} } = service.binding || {};
43
- let { label, type } = vcap;
44
- type = type || label;
45
- if (!type) continue;
36
+ /**
37
+ * Determine kind value for services based on VCAP binding
38
+ * information in the configuration.
39
+ */
40
+ static determineKinds(services) {
41
+ const vcapServices = {}
46
42
 
47
- if (!vcapServices[type]) vcapServices[type] = [];
48
- vcapServices[type].push({ credentials: { dummy: 'dummy' }, ...vcap, service });
49
- }
43
+ // Build VCAP_SERVICES
44
+ for (const service of services) {
45
+ service.kindCandidates = []
50
46
 
51
- // Auto configure services based on VCAP_SERVICES
47
+ const { vcap = {} } = service.binding || {};
48
+ let { label, type } = vcap;
49
+ type = type || label;
50
+ if (!type) continue;
52
51
 
53
- const cds = require('../cds');
54
- const env = cds.env.for('cds', process.cwd());
52
+ if (!vcapServices[type]) vcapServices[type] = [];
53
+ vcapServices[type].push({ credentials: { dummy: 'dummy' }, ...vcap, service });
54
+ }
55
55
 
56
- if (!env._find_credentials_for_required_service) {
57
- throwError `Please provide a service kind or update @sap/cds version to use the kind detection.`;
58
- }
56
+ // Auto configure services based on VCAP_SERVICES
59
57
 
60
- function findKindFor(kind, service) {
61
- const vcapService = env._find_credentials_for_required_service(kind, service, vcapServices);
62
- if (!vcapService) return;
63
- if (!Object.values(requires).find( require => require !== service && require.kind == kind ))
64
- vcapService.service.kindCandidates.push(kind);
65
- }
58
+ const cds = require('../cds');
59
+ const env = cds.env.for('cds', process.cwd());
66
60
 
67
- const requires = env.requires.kinds || {};
68
- for (const kind of Object.keys(requires)) {
69
- const requireService = requires[kind];
70
- findKindFor(kind, requireService);
71
- findKindFor(kind, { kind });
72
- }
61
+ if (!env._find_credentials_for_required_service) {
62
+ throw new Error(`Please provide a service kind or update @sap/cds version to use the kind detection.`);
63
+ }
73
64
 
74
- // Check if one "kind" per service was found
75
- for (const service of services) {
76
- const preferredKind = service.kindCandidates.find( kind => PreferredKinds.indexOf(kind) >= 0);
77
- if (preferredKind) {
78
- service.kind = preferredKind;
79
- } if (service.kindCandidates.length === 1) {
80
- service.kind = service.kindCandidates[0];
65
+ function findKindFor(kind, service) {
66
+ const vcapService = env._find_credentials_for_required_service(kind, service, vcapServices);
67
+ if (!vcapService) return;
68
+ if (!Object.values(requires).find(require => require !== service && require.kind == kind))
69
+ vcapService.service.kindCandidates.push(kind);
81
70
  }
82
71
 
83
- delete service.kindCandidates;
84
- }
85
- }
72
+ const requires = env.requires.kinds || {};
73
+ for (const kind of Object.keys(requires)) {
74
+ const requireService = requires[kind];
75
+ findKindFor(kind, requireService);
76
+ findKindFor(kind, { kind });
77
+ }
86
78
 
87
- function execAndExit(options, command, ...args) {
88
- const { spawnSync } = require('child_process');
89
- // use shell with Windows only; without output is not visible and some commands cannot be run
90
- const result = spawnSync(command, args, { stdio: 'inherit', shell: IS_WIN, ...options });
91
- process.exit(result.status);
92
- }
79
+ // Check if one "kind" per service was found
80
+ for (const service of services) {
81
+ const preferredKind = service.kindCandidates.find(kind => PreferredKinds.indexOf(kind) >= 0);
82
+ if (preferredKind) {
83
+ service.kind = preferredKind;
84
+ } if (service.kindCandidates.length === 1) {
85
+ service.kind = service.kindCandidates[0];
86
+ }
93
87
 
94
- async function checkIsJavaProject(projectPath) {
95
- const { constants, promises: fsp } = require('fs');
96
- const path = require('path');
97
- const pomXmlPath = path.join(projectPath, 'pom.xml');
98
- try {
99
- await fsp.access(pomXmlPath, constants.F_OK);
100
- return true;
101
- } catch (err) {
102
- return false;
88
+ delete service.kindCandidates;
89
+ }
103
90
  }
104
- }
105
-
106
-
107
- /**
108
- *
109
- * @param {string} msg
110
- * @returns {never}
111
- */
112
- function throwError(msg = 'Internal Error') {
113
- throw new Error(msg);
114
- }
115
91
 
92
+ static execAndExit(options, command, ...args) {
93
+ const { spawnSync } = require('child_process');
94
+ // use shell with Windows only; without output is not visible and some commands cannot be run
95
+ const result = spawnSync(command, args, { stdio: 'inherit', shell: IS_WIN, ...options });
96
+ process.exit(result.status);
97
+ }
116
98
 
117
- const PLATFORM_TYPES = {
118
- 'cf': 'Cloud Foundry',
119
- 'k8s': 'Kubernetes'
120
- }
99
+ static async checkIsJavaProject(projectPath) {
100
+ const { constants, promises: fsp } = require('fs');
101
+ const path = require('path');
102
+ const pomXmlPath = path.join(projectPath, 'pom.xml');
103
+ try {
104
+ await fsp.access(pomXmlPath, constants.F_OK);
105
+ return true;
106
+ } catch (err) {
107
+ return false;
108
+ }
109
+ }
121
110
 
122
- function platformText(binding) {
123
- return PLATFORM_TYPES[binding.type] || throwError(`Unexpected binding type "${binding.type}"`);
124
- }
111
+ static platformText(binding) {
112
+ if (!PLATFORM_TYPES[binding.type]) throw new Error(`Unexpected binding type "${binding.type}"`);
113
+ return PLATFORM_TYPES[binding.type]
114
+ }
125
115
 
126
- function bindingText(binding, highlight = t => t) {
127
- switch (binding.type) {
128
- case 'cf':
129
- return `service ${highlight(binding.instance)} key ${highlight(binding.key)}`
130
- case 'k8s':
131
- if (binding.name) {
132
- return `service binding ${highlight(binding.name)}`
133
- } else {
134
- return `secret ${highlight(binding.secret)}`
135
- }
136
- default:
137
- throwError();
116
+ static bindingText(binding, highlight = t => t) {
117
+ switch (binding.type) {
118
+ case 'cf':
119
+ if (binding.key) {
120
+ return `managed service ${highlight(binding.instance + ':' + binding.key)}`
121
+ } else {
122
+ return `user provided service ${highlight(binding.instance)}`
123
+ }
124
+ case 'k8s':
125
+ if (binding.name) {
126
+ return `service binding ${highlight(binding.name)}`
127
+ } else {
128
+ return `secret ${highlight(binding.secret)}`
129
+ }
130
+ default:
131
+ throw new Error('Internal Error');
132
+ }
138
133
  }
139
- }
140
134
 
141
- module.exports = {
142
- getRequiresNameFromKind,
143
- determineKinds,
144
- execAndExit,
145
- checkIsJavaProject,
146
- throwError,
147
- platformText,
148
- bindingText
135
+ static logger = { log: console.log }
149
136
  }
137
+
138
+ module.exports = BindingUtil
@@ -3,22 +3,20 @@ const fsp = require('fs').promises;
3
3
  const os = require('os');
4
4
  const util = require('util');
5
5
 
6
+ const DEBUG = /\b(y|all|cli)\b/.test(process.env.DEBUG) && console.debug
7
+
6
8
  const axios = require('axios');
7
9
  const { URL } = require('url');
8
10
  const path = require('path');
9
- const term = require('../util/term');
11
+ const { bold, highlight } = require('../util/term');
12
+ const { CliError } = require('../client/helper/errors');
10
13
 
11
14
  const execAsync = util.promisify(cp.exec);
12
15
  const IS_WIN = os.platform() === 'win32';
13
16
 
14
17
  class CFKeyProvider {
15
-
16
- constructor() {
17
- this.logger = console;
18
- }
19
-
20
18
  async _cfRead(...args) {
21
- this.logger.debug(`_cfRead: cf ${args}`);
19
+ DEBUG && DEBUG(`_cfRead: cf ${args}`);
22
20
 
23
21
  const result = await execAsync(`cf ${args}`, {
24
22
  shell: IS_WIN,
@@ -28,20 +26,6 @@ class CFKeyProvider {
28
26
  return result.stdout.toString().trim();
29
27
  }
30
28
 
31
- async _req(target, urlPath) {
32
- const url = new URL(urlPath, target.apiEndpoint);
33
-
34
- this.logger.debug(`_req: ${url.toString()}`);
35
-
36
- const result = await axios.get(url.toString(), {
37
- headers: {
38
- authorization: target.authorization
39
- }
40
- });
41
-
42
- return result.data;
43
- }
44
-
45
29
  extract(string, pattern, errorMsg) {
46
30
  const match = string.match(pattern);
47
31
  if (!match || !match[1]) {
@@ -63,7 +47,7 @@ class CFKeyProvider {
63
47
  }
64
48
  }
65
49
  } catch (err) {
66
- this.logger.debug(`getCfTargetFromConfigFile: ${err}`);
50
+ DEBUG && DEBUG(`getCfTargetFromConfigFile: ${err}`);
67
51
  }
68
52
  }
69
53
 
@@ -82,7 +66,7 @@ class CFKeyProvider {
82
66
  }
83
67
 
84
68
  async getCfSpaceInfo() {
85
- this.logger.debug('getting space info');
69
+ DEBUG && DEBUG('getting space info');
86
70
 
87
71
  const target = await this.getCfTarget();
88
72
  const authorization = await this.getCfAuthorization();
@@ -90,13 +74,13 @@ class CFKeyProvider {
90
74
 
91
75
 
92
76
  const { org, space } = target;
93
- const orgs = await this._req(targetWithAuth, `/v3/organizations?names=${encodeURIComponent(encodeURIComponent(org))}`);
77
+ const orgs = await BatchRequest.req(targetWithAuth, `/v3/organizations?names=${encodeURIComponent(encodeURIComponent(org))}`);
94
78
  if (!(orgs && orgs.resources.length === 1)) {
95
79
  throw new Error(`CF org ${org} not found!`);
96
80
  }
97
81
 
98
82
  const orgGuid = orgs.resources[0].guid;
99
- const spaces = await this._req(targetWithAuth, `/v3/spaces?names=${encodeURIComponent(encodeURIComponent(space))}&organization_guids=${encodeURIComponent(encodeURIComponent(orgGuid))}`)
83
+ const spaces = await BatchRequest.req(targetWithAuth, `/v3/spaces?names=${encodeURIComponent(encodeURIComponent(space))}&organization_guids=${encodeURIComponent(encodeURIComponent(orgGuid))}`)
100
84
  if (!(spaces && spaces.resources.length === 1)) {
101
85
  throw new Error(`CF space ${space} not found in org ${org}!`);
102
86
  }
@@ -107,35 +91,30 @@ class CFKeyProvider {
107
91
  }
108
92
 
109
93
  async getCfAuthorization() {
110
- this.logger.debug('getting authorization');
94
+ DEBUG && DEBUG('getting authorization');
111
95
  return this._cfRead('oauth-token');
112
96
  }
113
97
 
114
- setLogger(logger) {
115
- this.logger = logger;
116
- }
117
-
118
98
  async init() {
119
99
  const target = this.target = await this.getCfSpaceInfo();
120
- const logger = this.logger;
121
- this.getOrg = BatchRequest.create({ logger, target, url: '/v3/organizations' });
100
+ this.getOrg = BatchRequest.create({ target, url: '/v3/organizations' });
122
101
  this.getSpace = BatchRequest.create({
123
- logger, target, url: '/v3/spaces', extractors: {
102
+ target, url: '/v3/spaces', extractors: {
124
103
  organization_guids: resource => resource.relationships.organization.data.guid
125
104
  }
126
105
  });
127
106
  this.getInstance = BatchRequest.create({
128
- logger, target, url: '/v3/service_instances', extractors: {
107
+ target, url: '/v3/service_instances', extractors: {
129
108
  space_guids: resource => resource.relationships.space.data.guid
130
109
  }
131
110
  });
132
111
  this.getKey = BatchRequest.create({
133
- logger, target, url: '/v3/service_credential_bindings', extractors: {
112
+ target, url: '/v3/service_credential_bindings', extractors: {
134
113
  service_instance_guids: resource => resource.relationships.service_instance.data.guid
135
114
  }
136
115
  });
137
- this.getPlans = BatchRequest.create({ logger, target, url: '/v3/service_plans' });
138
- this.getOfferings = BatchRequest.create({ logger, target, url: '/v3/service_offerings' });
116
+ this.getPlans = BatchRequest.create({ target, url: '/v3/service_plans' });
117
+ this.getOfferings = BatchRequest.create({ target, url: '/v3/service_offerings' });
139
118
  }
140
119
 
141
120
  async resolve(name, binding) {
@@ -143,29 +122,19 @@ class CFKeyProvider {
143
122
  this.initPromise = this.init();
144
123
  }
145
124
  await this.initPromise;
125
+ this._checkApiEndpoint(name, binding)
146
126
 
147
- const target = this.target;
148
-
149
- let { instance, key, apiEndpoint, org, space } = binding;
150
- key = key || `${instance}-key`;
151
- name = name || `${instance}:${key}`;
152
-
153
- if (apiEndpoint && apiEndpoint !== target.apiEndpoint) {
154
- let message = `Current Cloud Foundry API endpoint "${target.apiEndpoint}" differs from API endpoint "${apiEndpoint}" for service binding "${name}".\n`;
155
- message += `Use ${term.bold(`cf login -a ${apiEndpoint}`)} to log in to the target API endpoint.`;
156
- console.log(message);
157
- process.exit(1);
158
- }
159
-
127
+ let { instance, org, space } = binding;
160
128
  let spaceGuid;
161
129
  if (org && space) {
130
+ // org and space can be overwritten by currently logged in org and space
162
131
  const orgObj = await this.getOrg({ names: org });
163
132
  const spaceObj = await this.getSpace({ names: space, organization_guids: orgObj.guid });
164
133
  spaceGuid = spaceObj.guid;
165
134
  } else {
166
- org = target.org;
167
- space = target.space;
168
- spaceGuid = target.spaceGuid;
135
+ org = this.target.org;
136
+ space = this.target.space;
137
+ spaceGuid = this.target.spaceGuid;
169
138
  }
170
139
 
171
140
  let instanceObj;
@@ -174,19 +143,32 @@ class CFKeyProvider {
174
143
  } catch (error) {
175
144
  throw new Error(`Service instance "${instance}" not found.`);
176
145
  }
177
- let keyObj;
146
+ switch (instanceObj.type) {
147
+ case 'managed':
148
+ return this._resolveManagedService(instanceObj, instance, binding.key || `${instance}-key`, org, space);
149
+ case 'user-provided':
150
+ return this._resolveUserProvidedService(instanceObj, instance, org, space);
151
+ default:
152
+ throw new Error(`Service type ${instanceObj.type} not supported`)
153
+ }
154
+ }
155
+
156
+ async _resolveManagedService(instanceObj, instance, key, org, space) {
157
+ let keyObj
178
158
  try {
179
159
  keyObj = await this.getKey({ names: key, service_instance_guids: instanceObj.guid });
180
160
  } catch (error) {
181
- throw new Error(`No service key "${key}" found for service instance "${instance}".`);
161
+ let message = `No service key ${highlight(key)} found for service instance ${highlight(instance)}.\n\n`;
162
+ message += `Use ${bold(`cf create-service-key ${instanceObj.name} ${key} [-c ...]`)} to create the required service key.`;
163
+ throw new CliError(message);
182
164
  }
183
165
  const planObj = await this.getPlans({ guids: instanceObj.relationships.service_plan.data.guid });
184
166
  const offeringObj = await this.getOfferings({ guids: planObj.relationships.service_offering.data.guid });
185
- const keyCredentialsObj = await req(this.logger, target, `/v3/service_credential_bindings/${encodeURIComponent(keyObj.guid)}/details`);
167
+ const keyCredentialsObj = await BatchRequest.req(this.target, `/v3/service_credential_bindings/${encodeURIComponent(keyObj.guid)}/details`);
186
168
  const resolvedBinding = {
187
169
  binding: {
188
170
  type: "cf",
189
- apiEndpoint: target.apiEndpoint,
171
+ apiEndpoint: this.target.apiEndpoint,
190
172
  org: org,
191
173
  space: space,
192
174
  instance: instance,
@@ -203,11 +185,42 @@ class CFKeyProvider {
203
185
 
204
186
  return resolvedBinding;
205
187
  }
188
+
189
+ async _resolveUserProvidedService(instanceObj, instance, org, space) {
190
+ const credentialsObj = await BatchRequest.req(this.target, `/v3/service_instances/${encodeURIComponent(instanceObj.guid)}/credentials`);
191
+ const resolvedBinding = {
192
+ binding: {
193
+ type: "cf",
194
+ apiEndpoint: this.target.apiEndpoint,
195
+ org: org,
196
+ space: space,
197
+ instance: instance,
198
+ vcap: {
199
+ label: instanceObj.type,
200
+ tags: instanceObj.tags
201
+ },
202
+ resolved: true,
203
+ },
204
+ credentials: credentialsObj
205
+ };
206
+ return resolvedBinding;
207
+ }
208
+
209
+ _checkApiEndpoint(name, binding) {
210
+ const { apiEndpoint, key, instance } = binding
211
+
212
+ // if no api endpoint is configured the currently logged in endpoint will be used
213
+ if (apiEndpoint && apiEndpoint !== this.target.apiEndpoint) {
214
+ name = `${name || instance}:${key || instance + '-key'}`
215
+ let message = `Current Cloud Foundry API endpoint ${highlight(this.target.apiEndpoint)} differs from API endpoint ${highlight(apiEndpoint)} for service binding ${highlight(name)}.\n\n`;
216
+ message += `Use ${bold(`cf login -a ${apiEndpoint}`)} to log in to the target API endpoint.`;
217
+ throw new CliError(message)
218
+ }
219
+ }
206
220
  }
207
221
 
208
222
  class BatchRequest {
209
- constructor({ logger, target, url, extractors = {} }) {
210
- this.logger = logger;
223
+ constructor({ target, url, extractors = {} }) {
211
224
  this.target = target;
212
225
  this.url = url;
213
226
  this.filters = [];
@@ -247,7 +260,7 @@ class BatchRequest {
247
260
  }
248
261
 
249
262
  const params = Object.keys(filterParams).map(
250
- name => `${name}=${encodeURIComponent(filterParams[name].map(value => encodeURIComponent(value)).join(','))}`
263
+ name => `${name}=${encodeURIComponent(filterParams[name].map(encodeURIComponent).join(','))}`
251
264
  ).join("&");
252
265
 
253
266
  const url = this.url + (this.url.match(/\?/) ? '&' : '?') + params;
@@ -279,22 +292,20 @@ class BatchRequest {
279
292
  }
280
293
 
281
294
  async httpRequest(urlPath) {
282
- return req(this.logger, this.target, urlPath);
295
+ return BatchRequest.req(this.target, urlPath);
283
296
  }
284
- }
285
-
286
- async function req(logger, target, urlPath) {
287
- const url = new URL(urlPath, target.apiEndpoint);
288
-
289
- logger.debug(`_req: ${url.toString()}`);
290
297
 
291
- const result = await axios.get(url.toString(), {
292
- headers: {
293
- authorization: target.authorization
294
- }
295
- });
298
+ static async req(target, urlPath) {
299
+ const url = new URL(urlPath, target.apiEndpoint);
300
+ DEBUG && DEBUG(`_req: ${url.toString()}`);
296
301
 
297
- return result.data;
302
+ const result = await axios.get(url.toString(), {
303
+ headers: {
304
+ authorization: target.authorization
305
+ }
306
+ });
307
+ return result.data;
308
+ }
298
309
  }
299
310
 
300
- module.exports = new CFKeyProvider();
311
+ module.exports = new CFKeyProvider()