@sap/cds-dk 8.7.3 → 8.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/bin/bind.js +7 -8
  3. package/bin/cds.js +2 -4
  4. package/bin/compile/index.js +1 -0
  5. package/bin/deploy/index.js +10 -5
  6. package/bin/watch.js +48 -7
  7. package/lib/bind/{cfKeyProvider.js → cf.js} +43 -53
  8. package/lib/bind/k8s.js +138 -0
  9. package/lib/bind/plugin.js +48 -0
  10. package/lib/bind/shared.js +267 -0
  11. package/lib/build/buildTaskEngine.js +1 -1
  12. package/lib/build/plugins/helm/files/subcharts/content-deployment/Chart.yaml +0 -2
  13. package/lib/build/plugins/helm/files/subcharts/web-application/Chart.yaml +1 -3
  14. package/lib/build/plugins/helm/files/subcharts/web-application/templates/service-binding.yaml +3 -0
  15. package/lib/build/plugins/helm/files/subcharts/web-application/values.schema.json +3 -0
  16. package/lib/build/plugins/helm/files/subcharts/web-application/values.yaml +2 -1
  17. package/lib/build/util.js +1 -1
  18. package/lib/compile/to-mermaid.js +1 -2
  19. package/lib/deploy/to-hana/hana.js +1 -1
  20. package/lib/deploy/to-hana/mtaUtil.js +0 -13
  21. package/lib/env/schemas/cds-rc.js +17 -30
  22. package/lib/import/common.js +20 -10
  23. package/lib/init/constants.js +1 -1
  24. package/lib/init/merge.js +41 -32
  25. package/lib/init/projectReader.js +15 -7
  26. package/lib/init/registries/mta.js +7 -1
  27. package/lib/init/template/approuter/files/package.json +1 -1
  28. package/lib/init/template/approuter/files/xs-app.json.hbs +0 -11
  29. package/lib/init/template/cloud-logging/files/package.json.hbs +12 -1
  30. package/lib/init/template/cloud-logging/files/values.yaml.hbs +0 -2
  31. package/lib/init/template/cloud-logging/index.js +8 -15
  32. package/lib/init/template/destination/files/values.yaml +4 -4
  33. package/lib/init/template/destination/index.js +1 -1
  34. package/lib/init/template/html5-repo/files/app-package.json.hbs +0 -2
  35. package/lib/init/template/html5-repo/files/containerize.yaml.hbs +25 -0
  36. package/lib/init/template/html5-repo/files/mta.yaml.hbs +1 -2
  37. package/lib/init/template/html5-repo/files/package.json.hbs +1 -1
  38. package/lib/init/template/html5-repo/files/ui-resources/package.json.hbs +11 -0
  39. package/lib/init/template/html5-repo/files/ui5-deploy.yaml.hbs +4 -7
  40. package/lib/init/template/html5-repo/files/values.yaml.hbs +6 -4
  41. package/lib/init/template/html5-repo/index.js +22 -10
  42. package/lib/init/template/http/index.js +1 -1
  43. package/lib/init/template/lint/index.js +7 -1
  44. package/lib/init/template/multitenancy/files/mta.yaml.hbs +0 -2
  45. package/lib/init/template/multitenancy/index.js +1 -1
  46. package/lib/init/template/portal/files/mta.yaml.hbs +9 -0
  47. package/lib/init/template/portal/index.js +3 -1
  48. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +119 -119
  49. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +1 -1
  50. package/lib/init/template/telemetry/index.js +0 -13
  51. package/lib/init/template/typer/files/package.json +3 -0
  52. package/lib/init/template/workzone-standard/files/cdm.json.hbs +97 -0
  53. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +23 -0
  54. package/lib/init/template/workzone-standard/index.js +70 -39
  55. package/lib/init/template/xsuaa/files/mta.yaml.hbs +2 -2
  56. package/lib/init/template/xsuaa/files/values.yaml.hbs +0 -2
  57. package/lib/init/template/xsuaa/index.js +22 -10
  58. package/lib/lint/io.js +8 -4
  59. package/lib/mtx/util/cf.js +1 -2
  60. package/lib/util/cf.js +104 -84
  61. package/lib/util/command.js +1 -2
  62. package/lib/util/git.js +1 -1
  63. package/npm-shrinkwrap.json +53 -45
  64. package/package.json +1 -1
  65. package/bin/deploy/to-cf.js +0 -0
  66. package/bin/deploy/via-mta.js +0 -0
  67. package/lib/bind/KubernetesApiAccessor.js +0 -81
  68. package/lib/bind/bindingManager.js +0 -89
  69. package/lib/bind/bindingResolver.js +0 -54
  70. package/lib/bind/bindingUtil.js +0 -129
  71. package/lib/bind/index.js +0 -127
  72. package/lib/bind/kubernetesCredentialsProvider.js +0 -84
  73. package/lib/bind/userCdsrcJsonStorageProvider.js +0 -92
  74. package/lib/init/template/destination/files/package.sidecar.json +0 -7
  75. package/lib/init/template/html5-repo/files/package.sidecar.json +0 -7
  76. package/lib/init/template/telemetry/files/package.json.hbs +0 -18
@@ -2,10 +2,19 @@ const cds = require('../../../cds')
2
2
  const { read } = cds.utils
3
3
  const { readProject } = require('../../projectReader')
4
4
  const { merge } = require('../../merge')
5
- const { srv4, xsuaa, mtxSidecar4 } = require('../../registries/mta')
5
+ const { srv4, xsuaa: xsuaaDefault, xsuaa4, mtxSidecar4 } = require('../../registries/mta')
6
6
 
7
7
  module.exports = class XsuaaTemplate extends require('../../plugin') {
8
8
 
9
+ options() {
10
+ return {
11
+ 'plan': {
12
+ type: 'string',
13
+ help: 'Specify the service plan.',
14
+ }
15
+ }
16
+ }
17
+
9
18
  static help() {
10
19
  return 'authentication via XSUAA'
11
20
  }
@@ -26,16 +35,20 @@ module.exports = class XsuaaTemplate extends require('../../plugin') {
26
35
  const { hasMultitenancy, hasMta, hasHelm, hasHelmUnifiedRuntime, isJava, srvPath } = project
27
36
 
28
37
  const cds = require('../../../../lib')
29
- const xsSecurity = await (async () => { try {
30
- const models = await cds.load(cds.env.folders.srv)
31
- return cds.compile.to.xsuaa(models)
32
- } catch (error) { /* ignore */ }})() ?? { scopes: [], attributes: [], 'role-templates': [], 'authorities-inheritance': false }
38
+ const xsSecurity = await (async () => {
39
+ try {
40
+ const models = await cds.load(cds.env.folders.srv)
41
+ return cds.compile.to.xsuaa(models)
42
+ } catch (error) { /* ignore */ }
43
+ })() ?? { scopes: [], attributes: [], 'role-templates': [], 'authorities-inheritance': false }
33
44
 
34
45
  const additions = xsSecurity.scopes.map(scope => ({ in: 'scopes', where: { name: scope.name } }))
35
46
  await merge(xsSecurity).into('xs-security.json', { project, additions })
36
47
 
37
48
  if (hasMta) {
38
49
  const srv = srv4(srvPath)
50
+ const { plan } = cds.cli.options
51
+ const xsuaa = plan ? xsuaa4(plan) : xsuaaDefault
39
52
  const additions = [srv, xsuaa]
40
53
  const mtxSidecar = mtxSidecar4(isJava ? 'mtx/sidecar' : 'gen/mtx/sidecar')
41
54
  if (hasMultitenancy && isJava) additions.push(mtxSidecar)
@@ -48,19 +61,18 @@ module.exports = class XsuaaTemplate extends require('../../plugin') {
48
61
  insert: [xsuaa, 'name'],
49
62
  into: [mtxSidecar, 'requires', 'name']
50
63
  })
51
-
64
+ if (plan) project.servicePlan = plan
52
65
  await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', { project, additions, overwrites, relationships })
53
66
  }
54
67
 
55
68
  if (hasHelm || hasHelmUnifiedRuntime) {
56
69
  const overwrites = hasMultitenancy ? [{
57
70
  item: [{
58
- in: 'xsuaa',
59
- where: { serviceOfferingName: 'xsuaa' }
71
+ in: 'xsuaa',
72
+ where: { serviceOfferingName: 'xsuaa' }
60
73
  }, 'parameters.tenant-mode'], withValue: 'shared'
61
- }] : []
74
+ }] : []
62
75
  await merge(__dirname, 'files/values.yaml.hbs').into('chart/values.yaml', { with: project, overwrites })
63
-
64
76
  await merge({
65
77
  xsuaa: {
66
78
  parameters: {
package/lib/lint/io.js CHANGED
@@ -1,9 +1,13 @@
1
1
  const { read, write, exists, yaml } = require('../cds').utils;
2
+ const cds = require("../cds");
3
+
2
4
  const { readJSONC } = require("../util/fs");
3
5
  const path = require("path");
4
6
  const term = require("../util/term");
5
7
  const JSONC = require('../util/jsonc');
6
8
 
9
+ const { COMMAND_INIT } = require("../init/constants");
10
+
7
11
  module.exports = {
8
12
  /**
9
13
  * Reads ESLint config contents from file
@@ -90,7 +94,7 @@ module.exports = {
90
94
  }
91
95
  }
92
96
  if (isFilePath) {
93
- await this.writeEslintConfig(configIn, logger);
97
+ await this.writeEslintConfig(configIn, cds.cli?.command === COMMAND_INIT, logger);
94
98
  }
95
99
  return configContents;
96
100
  },
@@ -215,12 +219,12 @@ module.exports = {
215
219
  * @param {*} configPath config file path to write to
216
220
  * @returns {Promise<void>}
217
221
  */
218
- async writeEslintConfig(configPath, logger = console) {
222
+ async writeEslintConfig(configPath, force = false, logger = console) {
219
223
  let configFile = path.basename(configPath);
220
224
  switch (configFile) {
221
225
  case "eslint.config.js":
222
226
  case "eslint.config.cjs":
223
- if (exists(configPath)) {
227
+ if (!force && exists(configPath)) {
224
228
  logger.log(
225
229
  `${term.warn(
226
230
  `\n\nPlease add the following to your "${configFile}":` +
@@ -252,7 +256,7 @@ module.exports = [
252
256
  }
253
257
  break;
254
258
  case "eslint.config.mjs":
255
- if (exists(configPath)) {
259
+ if (!force && exists(configPath)) {
256
260
  logger.log(
257
261
  `${term.warn(
258
262
  `\n\nPlease add the following to your "${configFile}":` +
@@ -1,4 +1,3 @@
1
- const os = require('os');
2
1
  const { execFile } = require('child_process');
3
2
 
4
3
  const cds = require('../../cds');
@@ -139,7 +138,7 @@ async function getSubdomain(appName) {
139
138
  return resolve(errorResult(getErrorMessage(error, stdout)));
140
139
  }
141
140
 
142
- const nl = os.EOL;
141
+ const nl = '\r?\n'; //more robust than os.EOL;
143
142
  const match = new RegExp(`^System-Provided:${nl}(?:\\{${nl}((.|${nl})*?)^}|((.|${nl})*?^}))`, 'm').exec(stdout);
144
143
  if (!match) {
145
144
  return resolve(errorResult(`CF reports no system-provided environment for app '${appName}'`));
package/lib/util/cf.js CHANGED
@@ -32,7 +32,6 @@ const CF_CLIENT_MINIMUM_VERSION = 8;
32
32
  const ENCODED_COMMA = encodeURIComponent(',');
33
33
 
34
34
  module.exports = new class CfUtil {
35
- spaceInfo;
36
35
 
37
36
  async _sleep(ms) {
38
37
  return new Promise((resolve) => {
@@ -68,11 +67,11 @@ module.exports = new class CfUtil {
68
67
  }
69
68
  }
70
69
 
71
- async cfRequest(urlPath, queryObj, bodyObj) {
72
- const url = this.spaceInfo.apiEndpoint + urlPath;
70
+ async curl(urlPath, queryObj, bodyObj) {
71
+ const url = this.#target.apiEndpoint + urlPath;
73
72
 
74
73
  const options = {
75
- headers: { 'Authorization': this.spaceInfo.token }
74
+ headers: { 'Authorization': this.#target.token }
76
75
  };
77
76
 
78
77
  if (queryObj && Object.keys(queryObj).length > 0) {
@@ -96,8 +95,6 @@ module.exports = new class CfUtil {
96
95
  LOG.debug(`POST ${url}\n${JSON.stringify(options, null, 4)}\n${JSON.stringify(response.data, null, 4)}`);
97
96
  }
98
97
 
99
- // require('../../test/testUtil/recorder').record(urlPath, options.params, response.data, 'cf.json');
100
-
101
98
  return response.data;
102
99
  } catch (err) {
103
100
  if (err instanceof axios.AxiosError) {
@@ -134,111 +131,113 @@ module.exports = new class CfUtil {
134
131
  }
135
132
  }
136
133
 
134
+ #cfTargetPromise
137
135
  async _getCfTargetFromCli() {
138
- const result = await this.cfRun('target');
139
- if (result?.stdout) {
140
- return {
141
- apiEndpoint: this._extract(result.stdout, /api endpoint\s*:\s*([^\s]+)/i, `CF API endpoint is missing. Use 'cf login' to login.`),
142
- user: this._extract(result.stdout, /user\s*:\s*(.+)/i, `CF user is missing. Use 'cf login' to login.`),
143
- org: this._extract(result.stdout, /org\s*:\s*(.+)/i, `CF org is missing. Use 'cf target -o <ORG>' to specify.`),
144
- space: this._extract(result.stdout, /space\s*:\s*(.+)/i, `CF space is missing. Use 'cf target -s <SPACE>' to specify.`),
145
- };
146
- }
136
+ if (this.#cfTargetPromise) return this.#cfTargetPromise
137
+ this.#cfTargetPromise = (async () => {
138
+ const result = await this.cfRun('target')
139
+ if (result?.stdout) {
140
+ return {
141
+ apiEndpoint: this._extract(result.stdout, /api endpoint\s*:\s*([^\s]+)/i, `CF API endpoint is missing. Use 'cf login' to login.`),
142
+ user: this._extract(result.stdout, /user\s*:\s*(.+)/i, `CF user is missing. Use 'cf login' to login.`),
143
+ org: this._extract(result.stdout, /org\s*:\s*(.+)/i, `CF org is missing. Use 'cf target -o <ORG>' to specify.`),
144
+ space: this._extract(result.stdout, /space\s*:\s*(.+)/i, `CF space is missing. Use 'cf target -s <SPACE>' to specify.`),
145
+ }
146
+ }
147
+ })()
148
+ return this.#cfTargetPromise
147
149
  }
148
150
 
149
151
  #token
150
- async _getCfAuthorization() {
152
+ #tokenPromise
153
+ async _token() {
151
154
  if (this.#token) return this.#token
152
- if (!this._tokenPromise) {
153
- this._tokenPromise = this.cfRun('oauth-token')
155
+ if (!this.#tokenPromise) {
156
+ this.#tokenPromise = this.cfRun('oauth-token')
154
157
  .then(result => {
155
158
  this.#token = result.stdout
156
159
  return this.#token
157
160
  })
158
161
  .catch(err => {
159
- this._tokenPromise = null
162
+ this.#tokenPromise = null
160
163
  throw err.message
161
164
  })
162
165
  }
163
- return this._tokenPromise
164
- }
165
-
166
- async getCfTarget() {
167
- await this._checkCliVersion();
168
- const token = await this._getCfAuthorization();
169
- const target = await this._getCfTargetFromConfigFile() || await this._getCfTargetFromCli();
170
- target.token = token;
171
- return target;
166
+ return this.#tokenPromise
172
167
  }
173
168
 
174
- #checkedVersion = false
169
+ #cfVersionPromise
175
170
  async _checkCliVersion() {
176
- if (this.#checkedVersion) return
177
- this.#checkedVersion = true
178
- const result = await this.cfRun('-v');
179
- const version = result?.stdout?.match(/version.*(\d+\.\d+\.\d+)/i)
180
- if (parseInt(version?.[1]) < CF_CLIENT_MINIMUM_VERSION) {
181
- console.log(warn(`
182
- [Warning] You are using Cloud Foundry client version ${version[1]}. We recommend version ${CF_CLIENT_MINIMUM_VERSION} or higher.
183
- Deployment will stop in the near future for Cloud Foundry client versions < ${CF_CLIENT_MINIMUM_VERSION}.
184
- `));
185
- }
171
+ if (this.#cfVersionPromise) return this.#cfVersionPromise
172
+ this.#cfVersionPromise = (async () => {
173
+ const result = await this.cfRun('-v');
174
+ const version = result?.stdout?.match(/version.*(\d+\.\d+\.\d+)/i);
175
+ if (parseInt(version?.[1]) < CF_CLIENT_MINIMUM_VERSION) {
176
+ console.log(warn(`
177
+ [Warning] You are using Cloud Foundry client version ${version[1]}. We recommend version ${CF_CLIENT_MINIMUM_VERSION} or higher.
178
+ Deployment will stop in the near future for Cloud Foundry client versions < ${CF_CLIENT_MINIMUM_VERSION}.
179
+ `))
180
+ }
181
+ })()
182
+ return this.#cfVersionPromise
186
183
  }
187
184
 
188
- async getCfSpaceInfo() {
189
- if (!this.spaceInfo) {
190
- LOG.debug('getting space info');
185
+ #target
186
+ async target() {
187
+ if (!this.#target) {
188
+ await this._checkCliVersion();
189
+ const token = await this._token();
190
+ const target = await this._getCfTargetFromConfigFile() || await this._getCfTargetFromCli();
191
+ target.token = token;
192
+ this.#target = target;
191
193
 
192
- const target = await this.getCfTarget();
193
- this.spaceInfo = target;
194
194
  const { org, space } = target;
195
- const orgs = await this.cfRequest('/v3/organizations', { names: org });
195
+ const orgs = await this.curl('/v3/organizations', { names: org });
196
196
  if (!orgs?.resources?.length) {
197
197
  throw `CF org ${bold(org)} not found!`;
198
198
  }
199
-
200
199
  const orgGuid = orgs.resources[0].guid;
201
- const spaces = await this.cfRequest('/v3/spaces', { names: space, organization_guids: orgGuid });
200
+
201
+ const spaces = await this.curl('/v3/spaces', { names: space, organization_guids: orgGuid });
202
202
  if (!spaces?.resources?.length) {
203
203
  throw `CF space ${bold(space)} not found in org ${bold(org)}!`;
204
204
  }
205
205
 
206
206
  const spaceGuid = spaces.resources[0].guid;
207
207
 
208
- this.spaceInfo = {
208
+ this.#target = {
209
209
  ...target,
210
210
  orgGuid,
211
211
  spaceGuid
212
212
  }
213
213
  }
214
214
 
215
- return this.spaceInfo;
215
+ return this.#target;
216
216
  }
217
217
 
218
- async getServices(app) {
219
- const env = await this.getEnv(app)
218
+ async services(app) {
219
+ const env = await this.env(app)
220
220
  return Object.values(env.system_env_json.VCAP_SERVICES).flat();
221
221
  }
222
222
 
223
- async getEnv(app) {
224
- const { spaceGuid: space_guids, orgGuid: organization_guids } = await this.getCfSpaceInfo();
225
- const apps = await this.cfRequest('/v3/apps', { space_guids, organization_guids, names: app });
223
+ async env(app) {
224
+ const { spaceGuid: space_guids, orgGuid: organization_guids } = await this.target();
225
+ const apps = await this.curl('/v3/apps', { space_guids, organization_guids, names: app });
226
226
  if (apps.resources.length < 1) throw `Cannot find app with name '${app}'`
227
227
  const guid = apps.resources[0].guid;
228
- return await this.cfRequest('/v3/apps/' + guid + '/env', { space_guids, organization_guids, names: app });
228
+ return await this.curl('/v3/apps/' + guid + '/env', { space_guids, organization_guids, names: app });
229
229
  }
230
230
 
231
- async getService(serviceName, showMessage = true) {
232
- showMessage && console.log(`getting service ${bold(serviceName)}`);
233
- const spaceInfo = await this.getCfSpaceInfo();
231
+ async service(serviceName) {
232
+ const target = await this.target();
234
233
 
235
234
  let counter = POLL_COUNTER;
236
235
  while (counter > 0) {
237
236
  counter--;
238
- const serviceInstances = await this.cfRequest('/v3/service_instances', {
237
+ const serviceInstances = await this.curl('/v3/service_instances', {
239
238
  names: serviceName,
240
- space_guids: spaceInfo.spaceGuid,
241
- organization_guids: spaceInfo.orgGuid
239
+ space_guids: target.spaceGuid,
240
+ organization_guids: target.orgGuid
242
241
  });
243
242
  if (!serviceInstances?.resources?.length) {
244
243
  return null;
@@ -269,10 +268,8 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
269
268
  throw `Timeout occurred while getting service ${bold(serviceName)}`;
270
269
  }
271
270
 
272
-
273
271
  async getOrCreateService(serviceOfferingName, planName, serviceName, options) {
274
-
275
- const probeService = await this.getService(serviceName, false);
272
+ const probeService = await this.service(serviceName);
276
273
  if (probeService) {
277
274
  console.log(`getting service ${bold(serviceName)}`);
278
275
  return probeService;
@@ -280,12 +277,12 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
280
277
 
281
278
  console.log(`Creating service ${bold(serviceName)} - please be patient...`);
282
279
 
283
- const spaceInfo = await this.getCfSpaceInfo();
280
+ const target = await this.target();
284
281
 
285
- const servicePlan = await this.cfRequest(`/v3/service_plans`, {
282
+ const servicePlan = await this.curl(`/v3/service_plans`, {
286
283
  names: planName,
287
- space_guids: spaceInfo.spaceGuid,
288
- organization_guids: spaceInfo.orgGuid,
284
+ space_guids: target.spaceGuid,
285
+ organization_guids: target.orgGuid,
289
286
  service_offering_names: serviceOfferingName
290
287
  });
291
288
 
@@ -300,7 +297,7 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
300
297
  relationships: {
301
298
  space: {
302
299
  data: {
303
- guid: spaceInfo.spaceGuid
300
+ guid: target.spaceGuid
304
301
  }
305
302
  },
306
303
  service_plan: {
@@ -315,12 +312,12 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
315
312
  body.parameters = { ...options };
316
313
  }
317
314
 
318
- const postResult = await this.cfRequest('/v3/service_instances', undefined, body);
315
+ const postResult = await this.curl('/v3/service_instances', undefined, body);
319
316
  if (postResult?.errors) {
320
317
  throw postResult.errors[0].detail;
321
318
  }
322
319
 
323
- const newService = await this.getService(serviceName, false);
320
+ const newService = await this.service(serviceName);
324
321
  if (newService) {
325
322
  return newService;
326
323
  }
@@ -328,13 +325,11 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
328
325
  throw `Could not create service ${bold(serviceName)}`;
329
326
  }
330
327
 
331
- async _getServiceKey(serviceInstance, serviceKeyName, showMessage = true) {
332
- showMessage && console.log(`getting service key ${bold(serviceKeyName)}`);
333
-
328
+ async _getServiceKey(serviceInstance, serviceKeyName) {
334
329
  let counter = POLL_COUNTER;
335
330
  while (counter > 0) {
336
331
  counter--;
337
- const bindings = await this.cfRequest(`/v3/service_credential_bindings`, { names: serviceKeyName, service_instance_guids: serviceInstance.guid });
332
+ const bindings = await this.curl(`/v3/service_credential_bindings`, { names: serviceKeyName, service_instance_guids: serviceInstance.guid });
338
333
  if (!bindings?.resources?.length) {
339
334
  return null;
340
335
  }
@@ -347,7 +342,7 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
347
342
  break;
348
343
 
349
344
  case OPERATION_STATE_SUCCEEDED: {
350
- const keyDetails = await this.cfRequest(`/v3/service_credential_bindings/${encodeURIComponent(binding.guid)}/details`);
345
+ const keyDetails = await this.curl(`/v3/service_credential_bindings/${encodeURIComponent(binding.guid)}/details`);
351
346
  return keyDetails.credentials;
352
347
  }
353
348
 
@@ -366,14 +361,14 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
366
361
  throw `Timeout occurred while getting service key ${bold(serviceKeyName)}`;
367
362
  }
368
363
 
369
- async getOrCreateServiceKey(serviceInstance, serviceKeyName, parameters, { silent = false, create = true } = {}) {
370
- const serviceKey = await this._getServiceKey(serviceInstance, serviceKeyName, false);
371
- if (serviceKey || !create) {
372
- if (serviceKey && !silent) DEBUG?.(`getting service key ${bold(serviceKeyName)}`);
364
+ async getOrCreateServiceKey(serviceInstance, serviceKeyName, parameters, { silent = false } = {}) {
365
+ const serviceKey = await this._getServiceKey(serviceInstance, serviceKeyName);
366
+ if (serviceKey) {
367
+ DEBUG?.(`getting service key ${bold(serviceKeyName)}`)
373
368
  return serviceKey;
374
369
  }
375
370
 
376
- if (!silent) console.log(`Creating service key ${bold(serviceKeyName)} - please be patient...`);
371
+ if (!silent) console.log(`creating service key ${bold(serviceKeyName)} - please be patient...`);
377
372
 
378
373
  const body = {
379
374
  type: 'key',
@@ -388,16 +383,41 @@ Deployment will stop in the near future for Cloud Foundry client versions < ${CF
388
383
  parameters
389
384
  }
390
385
 
391
- const postResult = await this.cfRequest('/v3/service_credential_bindings', undefined, body);
386
+ const postResult = await this.curl('/v3/service_credential_bindings', undefined, body);
392
387
  if (postResult?.errors) {
393
388
  throw postResult.errors[0].detail;
394
389
  }
395
390
 
396
- const newServiceKey = await this._getServiceKey(serviceInstance, serviceKeyName, false);
391
+ const newServiceKey = await this._getServiceKey(serviceInstance, serviceKeyName);
397
392
  if (newServiceKey) {
398
393
  return newServiceKey;
399
394
  }
400
395
 
401
- throw `Could not create service key ${bold(serviceKeyName)}`;
396
+ throw `could not create service key ${bold(serviceKeyName)}`;
397
+ }
398
+
399
+ async org(orgName) {
400
+ const result = await this.curl('/v3/organizations', { names: orgName })
401
+ return result?.resources?.[0]
402
+ }
403
+
404
+ async space(orgGuid, spaceName) {
405
+ const result = await this.curl('/v3/spaces', { names: spaceName, organization_guids: orgGuid })
406
+ return result?.resources?.find(r => r.relationships.organization.data.guid === orgGuid)
407
+ }
408
+
409
+ async instance(spaceGuid, instanceName) {
410
+ const result = await this.curl('/v3/service_instances', { names: instanceName, space_guids: spaceGuid })
411
+ return result?.resources?.find(r => !!r.relationships.space.data.guid)
412
+ }
413
+
414
+ async plan(planGuid) {
415
+ const result = await this.curl('/v3/service_plans', { guids: planGuid })
416
+ return result?.resources?.[0]
417
+ }
418
+
419
+ async offering(offeringGuid) {
420
+ const result = await this.curl('/v3/service_offerings', { guids: offeringGuid })
421
+ return result?.resources?.[0]
402
422
  }
403
423
  }
@@ -1,5 +1,4 @@
1
1
  const cp = require('child_process');
2
- const os = require('os');
3
2
  const cds = require('../cds');
4
3
  const LOG = cds.log('cli'), DEBUG = cds.debug('cli');
5
4
 
@@ -102,7 +101,7 @@ class LogBuffer {
102
101
  this.result = this.result + chunkStr;
103
102
  }
104
103
 
105
- const seq = chunkStr.split(os.EOL);
104
+ const seq = chunkStr.split(/\r?\n/); // more robust than os.EOL
106
105
  this.buffer = this.buffer + seq.shift();
107
106
  for (const line of seq) {
108
107
  this.flush();
package/lib/util/git.js CHANGED
@@ -35,7 +35,7 @@ class GitUtil {
35
35
  }
36
36
  }
37
37
 
38
- console.log(`Adding entry '${file}' to ${GIT_IGNORE_FILE}.`);
38
+ console.log(`adding entry '${file}' to ${GIT_IGNORE_FILE}.`);
39
39
  let gitignore = await this.readFileSafely(gitIgnorePath);
40
40
  gitignore = gitignore + `${os.EOL}# added by cds${os.EOL}${file}${os.EOL}`;
41
41