@sap/cds-dk 6.8.3 → 7.0.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 (218) hide show
  1. package/bin/activate.js +15 -7
  2. package/bin/add.js +29 -15
  3. package/bin/bind.js +49 -116
  4. package/bin/build.js +114 -0
  5. package/bin/cds.js +67 -24
  6. package/bin/compile/index.js +2 -2
  7. package/bin/deploy/index.js +46 -39
  8. package/bin/deploy/to-hana.js +11 -8
  9. package/bin/deploy/to-postgres.js +8 -2
  10. package/bin/deploy/to-sqlite.js +0 -1
  11. package/bin/env.js +75 -41
  12. package/bin/eval.js +3 -3
  13. package/bin/extend.js +17 -10
  14. package/bin/help.js +22 -21
  15. package/bin/import.js +10 -7
  16. package/bin/init.js +38 -17
  17. package/bin/login.js +69 -55
  18. package/bin/logout.js +12 -9
  19. package/bin/migrate.js +10 -4
  20. package/bin/pull.js +12 -5
  21. package/bin/push.js +24 -13
  22. package/bin/repl.js +1 -0
  23. package/bin/run.js +25 -0
  24. package/bin/subscribe.js +13 -8
  25. package/bin/unsubscribe.js +15 -8
  26. package/bin/upgrade.js +15 -9
  27. package/bin/util/log.js +24 -0
  28. package/bin/version.js +124 -0
  29. package/bin/watch.js +1 -35
  30. package/bin/watched.js +39 -0
  31. package/lib/bind/KubernetesApiAccessor.js +2 -2
  32. package/lib/bind/bindingManager.js +1 -1
  33. package/lib/bind/bindingUtil.js +0 -9
  34. package/lib/bind/cfKeyProvider.js +10 -72
  35. package/lib/bind/index.js +114 -0
  36. package/lib/bind/kubernetesCredentialsProvider.js +2 -2
  37. package/lib/bind/userCdsrcJsonStorageProvider.js +7 -44
  38. package/lib/build/buildTaskEngine.js +355 -0
  39. package/lib/build/buildTaskFactory.js +284 -0
  40. package/lib/build/buildTaskHandler.js +240 -0
  41. package/lib/build/buildTaskProvider.js +22 -0
  42. package/lib/build/buildTaskProviderFactory.js +182 -0
  43. package/lib/build/constants.js +56 -0
  44. package/lib/build/index.js +21 -0
  45. package/lib/build/provider/buildTaskHandlerEdmx.js +83 -0
  46. package/lib/build/provider/buildTaskHandlerFeatureToggles.js +132 -0
  47. package/lib/build/provider/buildTaskHandlerInternal.js +228 -0
  48. package/lib/build/provider/buildTaskProviderInternal.js +370 -0
  49. package/lib/build/provider/fiori/index.js +167 -0
  50. package/lib/build/provider/hana/2migration.js +179 -0
  51. package/lib/build/provider/hana/index.js +522 -0
  52. package/lib/build/provider/hana/migrationtable.js +472 -0
  53. package/lib/build/provider/hana/template/.hdiconfig-all +163 -0
  54. package/lib/build/provider/hana/template/.hdiconfig-cloud +137 -0
  55. package/lib/build/provider/hana/template/.hdinamespace +4 -0
  56. package/lib/build/provider/hana/template/package.json +12 -0
  57. package/lib/build/provider/hana/template/undeploy.json +5 -0
  58. package/lib/build/provider/java/index.js +93 -0
  59. package/lib/build/provider/java-cf/index.js +1 -0
  60. package/lib/build/provider/mtx/index.js +37 -0
  61. package/lib/build/provider/mtx/resourcesTarBuilder.js +94 -0
  62. package/lib/build/provider/mtx-extension/index.js +131 -0
  63. package/lib/build/provider/mtx-sidecar/index.js +138 -0
  64. package/lib/build/provider/node-cf/index.js +1 -0
  65. package/lib/build/provider/nodejs/index.js +178 -0
  66. package/lib/build/util.js +263 -0
  67. package/lib/cds.js +9 -21
  68. package/lib/client/auth_manager.js +39 -16
  69. package/lib/client/helper/dependency_helper.js +7 -6
  70. package/lib/client/push.js +2 -1
  71. package/lib/client/settings_manager.js +10 -39
  72. package/lib/deploy/to-hana/hana.js +273 -0
  73. package/lib/deploy/to-hana/hdiDeployUtil.js +68 -0
  74. package/lib/deploy/to-hana/mtaUtil.js +171 -0
  75. package/lib/import/asyncapi/importAsyncAPI.js +3 -2
  76. package/lib/import/asyncapi/index.js +5 -2
  77. package/lib/import/asyncapi/parserContext.js +67 -26
  78. package/lib/import/asyncapi/utils.js +34 -3
  79. package/lib/import/common.js +40 -30
  80. package/lib/import/importapi.js +36 -35
  81. package/lib/index.js +12 -17
  82. package/lib/init/constants.js +36 -71
  83. package/lib/init/index.js +18 -52
  84. package/lib/init/template/_merging/registry-mta.js +21 -3
  85. package/lib/init/template/approuter/files/mta.yaml.hbs +29 -29
  86. package/lib/init/template/approuter/files/values.yaml.hbs +5 -5
  87. package/lib/init/template/approuter/files/xs-app.json.hbs +8 -2
  88. package/lib/init/template/approuter/index.js +7 -8
  89. package/lib/init/template/auditlog/files/mta.yaml.hbs +5 -5
  90. package/lib/init/template/auditlog/files/package.json.hbs +13 -13
  91. package/lib/init/template/auditlog/index.js +3 -3
  92. package/lib/init/template/cf-manifest/files/manifest.yml.hbs +22 -22
  93. package/lib/init/template/cf-manifest/files/services-manifest.yml.hbs +3 -3
  94. package/lib/init/template/connectivity/files/mta.yaml.hbs +14 -0
  95. package/lib/init/template/connectivity/files/package.json.hbs +17 -0
  96. package/lib/init/template/connectivity/index.js +21 -4
  97. package/lib/init/template/data/index.js +5 -1
  98. package/lib/init/template/destination/files/mta.yaml.hbs +14 -0
  99. package/lib/init/template/destination/files/package.json.hbs +17 -0
  100. package/lib/init/template/{destinations → destination}/index.js +19 -13
  101. package/lib/init/template/enterprise-messaging/files/event-mesh.json.hbs +3 -3
  102. package/lib/init/template/enterprise-messaging/files/mta.yaml.hbs +5 -5
  103. package/lib/init/template/enterprise-messaging/files/package.json.hbs +9 -9
  104. package/lib/init/template/enterprise-messaging/files/xs-security.json.hbs +19 -0
  105. package/lib/init/template/enterprise-messaging/index.js +27 -6
  106. package/lib/init/template/enterprise-messaging-shared/files/event-mesh.json.hbs +3 -3
  107. package/lib/init/template/enterprise-messaging-shared/files/mta.yaml.hbs +5 -5
  108. package/lib/init/template/enterprise-messaging-shared/files/package.json.hbs +9 -9
  109. package/lib/init/template/enterprise-messaging-shared/index.js +4 -5
  110. package/lib/init/template/extensibility/files/package.json.hbs +10 -10
  111. package/lib/init/template/extensibility/index.js +8 -9
  112. package/lib/init/template/file-based-messaging/files/package.json.hbs +9 -9
  113. package/lib/init/template/file-based-messaging/index.js +3 -4
  114. package/lib/init/template/hana/files/db/package.json +1 -1
  115. package/lib/init/template/hana/files/mta.yaml.hbs +21 -21
  116. package/lib/init/template/hana/files/package.json.hbs +16 -22
  117. package/lib/init/template/hana/files/values.yaml.hbs +1 -1
  118. package/lib/init/template/hana/index.js +5 -15
  119. package/lib/init/template/helm/files/Chart.yaml.hbs +4 -4
  120. package/lib/init/template/helm/files/values.yaml.hbs +6 -6
  121. package/lib/init/template/helm/index.js +5 -10
  122. package/lib/init/template/helm/subcharts/content-deployment/Chart.yaml +2 -0
  123. package/lib/init/template/helm/subcharts/content-deployment/templates/_helpers.tpl +37 -31
  124. package/lib/init/template/helm/subcharts/content-deployment/templates/job.yaml +13 -14
  125. package/lib/init/template/helm/subcharts/content-deployment/templates/secret.yaml +2 -2
  126. package/lib/init/template/helm/subcharts/content-deployment/templates/service-account.yaml +1 -1
  127. package/lib/init/template/helm/subcharts/content-deployment/templates/service-binding.yaml +3 -3
  128. package/lib/init/template/helm/subcharts/content-deployment/values.md +116 -116
  129. package/lib/init/template/helm/subcharts/service-instance/Chart.yaml +2 -0
  130. package/lib/init/template/helm/subcharts/service-instance/templates/_helpers.tpl +9 -9
  131. package/lib/init/template/helm/subcharts/service-instance/templates/serviceinstance.yaml +2 -2
  132. package/lib/init/template/helm/subcharts/service-instance/values.md +15 -15
  133. package/lib/init/template/helm/subcharts/web-application/Chart.yaml +2 -0
  134. package/lib/init/template/helm/subcharts/web-application/templates/NOTES.txt +15 -0
  135. package/lib/init/template/helm/subcharts/web-application/templates/_helpers.tpl +23 -23
  136. package/lib/init/template/helm/subcharts/web-application/templates/api-rule.yaml +4 -4
  137. package/lib/init/template/helm/subcharts/web-application/templates/deployment.yaml +17 -17
  138. package/lib/init/template/helm/subcharts/web-application/templates/network-policy.yaml +24 -3
  139. package/lib/init/template/helm/subcharts/web-application/templates/pod-disruption-budget.yaml +3 -3
  140. package/lib/init/template/helm/subcharts/web-application/templates/secret.yaml +2 -2
  141. package/lib/init/template/helm/subcharts/web-application/templates/service-account.yaml +1 -1
  142. package/lib/init/template/helm/subcharts/web-application/templates/service-binding.yaml +3 -3
  143. package/lib/init/template/helm/subcharts/web-application/templates/service.yaml +3 -3
  144. package/lib/init/template/helm/subcharts/web-application/values.md +141 -123
  145. package/lib/init/template/helm/subcharts/web-application/values.schema.json +7 -0
  146. package/lib/init/template/helm/subcharts/web-application/values.yaml +1 -0
  147. package/lib/init/template/html5-repo/index.js +4 -5
  148. package/lib/init/template/java/index.js +5 -5
  149. package/lib/init/template/kibana-logging/files/mta.yaml.hbs +5 -5
  150. package/lib/init/template/kibana-logging/files/package.json.hbs +9 -9
  151. package/lib/init/template/kibana-logging/index.js +3 -4
  152. package/lib/init/template/lint/index.js +3 -3
  153. package/lib/init/template/mta/files/mta.yaml.hbs +14 -14
  154. package/lib/init/template/mta/files/package.json +5 -0
  155. package/lib/init/template/mta/index.js +12 -13
  156. package/lib/init/template/mtx/index.js +2 -2
  157. package/lib/init/template/multitenancy/files/mta.yaml.hbs +29 -27
  158. package/lib/init/template/multitenancy/files/package.json.hbs +17 -14
  159. package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +7 -7
  160. package/lib/init/template/multitenancy/files/values.yaml.hbs +19 -19
  161. package/lib/init/template/multitenancy/index.js +14 -16
  162. package/lib/init/template/nodejs/files/package.json.hbs +1 -1
  163. package/lib/init/template/nodejs/index.js +5 -8
  164. package/lib/init/template/redis-messaging/files/mta.yaml.hbs +5 -5
  165. package/lib/init/template/redis-messaging/files/package.json.hbs +9 -9
  166. package/lib/init/template/redis-messaging/index.js +4 -5
  167. package/lib/init/template/sample/files/app/_i18n/i18n.properties +43 -0
  168. package/lib/init/template/sample/files/app/_i18n/i18n_de.properties +14 -0
  169. package/lib/init/template/sample/files/app/admin-books/fiori-service.cds +114 -0
  170. package/lib/init/template/sample/files/app/admin-books/webapp/Component.js +8 -0
  171. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n.properties +11 -0
  172. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +129 -0
  173. package/lib/init/template/sample/files/app/appconfig/fioriSandboxConfig.json +57 -0
  174. package/lib/init/template/sample/files/app/common.cds +263 -0
  175. package/lib/init/template/sample/files/app/index.html +30 -0
  176. package/lib/init/template/sample/files/app/services.cds +5 -0
  177. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Authors.csv +5 -0
  178. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books.csv +6 -0
  179. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books_texts.csv +5 -0
  180. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.csv +16 -0
  181. package/lib/init/template/sample/files/db/schema.cds +31 -0
  182. package/lib/init/template/sample/files/nodejs/admin-service.js +12 -0
  183. package/lib/init/template/sample/files/nodejs/cat-service.js +26 -0
  184. package/lib/init/template/sample/files/srv/admin-service.cds +5 -0
  185. package/lib/init/template/sample/files/srv/cat-service.cds +16 -0
  186. package/lib/init/template/sample/index.js +24 -0
  187. package/lib/init/template/sample-tiny/index.js +39 -0
  188. package/lib/init/template/samples/index.js +5 -56
  189. package/lib/init/template/sqlite/files/package.json.hbs +13 -13
  190. package/lib/init/template/sqlite/index.js +2 -2
  191. package/lib/init/template/templateBase.js +24 -1
  192. package/lib/init/template/toggles/files/package.json.hbs +10 -10
  193. package/lib/init/template/toggles/index.js +3 -3
  194. package/lib/init/template/typer/files/gitignore +1 -0
  195. package/lib/init/template/typer/files/package.json +8 -0
  196. package/lib/init/template/typer/files/xsconfig.json +9 -0
  197. package/lib/init/template/typer/index.js +36 -0
  198. package/lib/init/template/xsuaa/files/mta.yaml.hbs +12 -12
  199. package/lib/init/template/xsuaa/files/package.json.hbs +13 -13
  200. package/lib/init/template/xsuaa/files/values.yaml.hbs +8 -8
  201. package/lib/init/template/xsuaa/index.js +8 -19
  202. package/lib/init/util/merge.js +73 -59
  203. package/lib/init/util/mvn.js +21 -17
  204. package/lib/init/util/projectReader.js +49 -87
  205. package/lib/init/util/xml.js +14 -23
  206. package/lib/util/cfUtil.js +366 -0
  207. package/lib/util/command.js +3 -3
  208. package/lib/util/gitUtil.js +55 -0
  209. package/lib/util/version.js +27 -0
  210. package/npm-shrinkwrap.json +337 -275
  211. package/package.json +6 -6
  212. package/CHANGELOG.md +0 -1460
  213. package/bin/build/all.js +0 -88
  214. package/bin/build/index.js +0 -82
  215. package/lib/compile/index.js +0 -14
  216. /package/lib/init/template/{samples → sample-tiny}/files/nodejs/db/data/my.bookshop-Books.csv +0 -0
  217. /package/lib/init/template/{samples → sample-tiny}/files/nodejs/db/data-model.cds +0 -0
  218. /package/lib/init/template/{samples → sample-tiny}/files/nodejs/srv/cat-service.cds.hbs +0 -0
@@ -0,0 +1,366 @@
1
+ // eslint-disable-next-line no-console
2
+
3
+ const cp = require('child_process');
4
+ const fsp = require('fs').promises;
5
+ const os = require('os');
6
+ const path = require('path');
7
+ const util = require('util');
8
+
9
+ const IS_WIN = os.platform() === 'win32';
10
+ const execAsync = util.promisify(cp.exec);
11
+
12
+ const cds = require('../cds')
13
+
14
+ const LOG = cds.log ? cds.log('deploy') : console;
15
+ const DEBUG = cds.debug('deploy');
16
+
17
+ const { bold, warn } = require('./term');
18
+ const { CliError } = require('../client/helper/errors');
19
+
20
+ const CF_COMMAND = 'cf';
21
+
22
+ const POLL_COUNTER = 40;
23
+ const POLL_DELAY = 2500; //ms
24
+
25
+ const OPERATION_STATE_INITIAL = 'initial';
26
+ const OPERATION_STATE_IN_PROGRESS = 'in progress';
27
+ const OPERATION_STATE_FAILED = 'failed';
28
+ const OPERATION_STATE_SUCCEEDED = 'succeeded';
29
+
30
+ const CF_CLIENT_MINIMUM_VERSION = 8;
31
+
32
+
33
+ class CfUtil {
34
+
35
+ _clear() {
36
+ this.spaceInfo = null;
37
+ }
38
+
39
+ async _sleep(ms) {
40
+ return new Promise((resolve) => {
41
+ setTimeout(resolve, ms);
42
+ });
43
+ }
44
+
45
+ async _cfRun(...args) {
46
+ args = args.map(arg => arg.replace(/"/g, '\\"'));
47
+ const cmdLine = `${CF_COMMAND} "${args.join('" "')}"`;
48
+ DEBUG && console.time(cmdLine);
49
+ LOG.debug('>>>', cmdLine);
50
+
51
+ try {
52
+ const result = await execAsync(cmdLine, {
53
+ shell: IS_WIN,
54
+ stdio: ['inherit', 'pipe', 'inherit']
55
+ });
56
+ result.stdout = result.stdout?.trim();
57
+ result.stderr = result.stderr?.trim();
58
+ return result;
59
+ } catch (err) {
60
+ err.stdout = err.stdout?.trim();
61
+ err.stderr = err.stderr?.trim();
62
+ throw err;
63
+ } finally {
64
+ // eslint-disable-next-line no-console
65
+ DEBUG && console.timeEnd(cmdLine);
66
+ }
67
+ }
68
+
69
+ async _cfRequest(urlPath, queryObj, bodyObj) {
70
+ if (queryObj) {
71
+ const entries = Object.entries(queryObj);
72
+ const queryStr = entries.map(([key, value]) => {
73
+ // commas cause problems in cf curl when not double encoded
74
+ value = value.replace(/,/g, encodeURIComponent(','));
75
+ return `${key}=${encodeURIComponent(value)}`;
76
+ }).join('&');
77
+
78
+ urlPath = urlPath + `?${queryStr}`;
79
+ }
80
+
81
+ // cf curl PATH [-iv] [-X METHOD] [-H HEADER]... [-d DATA] [--output FILE]
82
+ const args = ['curl', urlPath];
83
+ if (bodyObj) {
84
+ args.push('-d');
85
+ args.push(JSON.stringify(bodyObj)); // cfRun uses spawn so no special handling for quotes on cli required
86
+ }
87
+
88
+ const result = await this._cfRun(...args);
89
+ let response = {};
90
+ if (result.stdout) {
91
+ response = JSON.parse(result.stdout);
92
+ } else if (result.stderr) {
93
+ response = { errors: [{ title: result.stderr }] };
94
+ }
95
+
96
+ if (response.errors) {
97
+ const errorMessage = response.errors.map((entry) => `${entry.title || ''}: ${entry.detail || ''} (${entry.code || ''})`).join('\n');
98
+ throw new Error(errorMessage);
99
+ }
100
+
101
+ return response;
102
+ }
103
+
104
+ _extract(string, pattern, errorMsg) {
105
+ const match = string.match(pattern);
106
+ if (match?.[1]) {
107
+ return match[1];
108
+ }
109
+ throw new Error(errorMsg);
110
+ }
111
+
112
+ async getCfTargetFromConfigFile() {
113
+ const cfHome = process.env.CF_HOME || process.env.cf_home || path.join(os.homedir(), '.cf');
114
+ try {
115
+ const fileContent = await fsp.readFile(path.join(cfHome, 'config.json'));
116
+ const config = JSON.parse(fileContent);
117
+ if (config) {
118
+ return {
119
+ apiEndpoint: this._extract(config.Target, /\s*(.+)\s*/, `CF API endpoint is missing. Use 'cf login' to login.`),
120
+ org: this._extract(config.OrganizationFields.Name, /\s*(.+)\s*/, `CF org is missing. Use 'cf target -o <ORG> to specify.`),
121
+ space: this._extract(config.SpaceFields.Name, /\s*(.+)\s*/, `CF space is missing. Use 'cf target -s <SPACE>' to specify.`)
122
+ }
123
+ }
124
+ } catch (err) {
125
+ LOG.debug(`getCfTargetFromConfigFile: ${err}`);
126
+ }
127
+ }
128
+
129
+ async getCfTargetFromCli() {
130
+ const result = await this._cfRun('target');
131
+ if (result?.stdout) {
132
+ return {
133
+ apiEndpoint: this._extract(result.stdout, /api endpoint\s*:\s*([^\s]+)/i, `CF API endpoint is missing. Use 'cf login' to login.`),
134
+ user: this._extract(result.stdout, /user\s*:\s*(.+)/i, `CF user is missing. Use 'cf login' to login.`),
135
+ org: this._extract(result.stdout, /org\s*:\s*(.+)/i, `CF org is missing. Use 'cf target -o <ORG> to specify.`),
136
+ space: this._extract(result.stdout, /space\s*:\s*(.+)/i, `CF space is missing. Use 'cf target -s <SPACE>' to specify.`),
137
+ };
138
+ }
139
+ }
140
+
141
+ async getCfAuthorization() {
142
+ DEBUG?.('getting authorization');
143
+ try {
144
+ return (await this._cfRun('oauth-token')).stdout;
145
+ } catch (err) {
146
+ throw new CliError(err.message);
147
+ }
148
+ }
149
+
150
+ async getCfTarget() {
151
+ await this.checkCliVersion();
152
+ await this.getCfAuthorization();
153
+ return await this.getCfTargetFromConfigFile() || await this.getCfTargetFromCli();
154
+ }
155
+
156
+ async checkCliVersion() {
157
+ const result = await this._cfRun('-v');
158
+ const version = result?.stdout?.match(/version.*(\d+\.\d+\.\d+)/i)
159
+ if (parseInt(version?.[1]) < CF_CLIENT_MINIMUM_VERSION) {
160
+ console.log(warn(`
161
+ [Warning] You are using Cloud Foundry client version ${version[1]}. We recommend version ${CF_CLIENT_MINIMUM_VERSION} or higher.
162
+ Deployment will stop in the near future for Cloud Foundry client versions < ${CF_CLIENT_MINIMUM_VERSION}.
163
+ `));
164
+ }
165
+ }
166
+
167
+ async getCfSpaceInfo() {
168
+ if (!this.spaceInfo) {
169
+ LOG.debug('getting space info');
170
+
171
+ const target = await this.getCfTarget();
172
+
173
+ const { org, space } = target;
174
+ const orgs = await this._cfRequest(`/v3/organizations`, { names: org });
175
+ if (!orgs?.resources?.length) {
176
+ throw new Error(`CF org ${bold(org)} not found!`);
177
+ }
178
+
179
+ const orgGuid = orgs.resources[0].guid;
180
+ const spaces = await this._cfRequest(`/v3/spaces`, { names: space, organization_guids: orgGuid });
181
+ if (!spaces?.resources?.length) {
182
+ throw new Error(`CF space ${bold(space)} not found in org ${bold(org)}!`);
183
+ }
184
+
185
+ const spaceGuid = spaces.resources[0].guid;
186
+
187
+ this.spaceInfo = Object.assign({}, target, { orgGuid, spaceGuid });
188
+ }
189
+
190
+ return this.spaceInfo;
191
+ }
192
+
193
+ async getService(serviceName, showMessage = true) {
194
+ showMessage && console.log(`Getting service ${bold(serviceName)}`);
195
+ const spaceInfo = await this.getCfSpaceInfo();
196
+
197
+ let counter = POLL_COUNTER;
198
+ while (counter > 0) {
199
+ counter--;
200
+ const serviceInstances = await this._cfRequest('/v3/service_instances', {
201
+ names: serviceName,
202
+ space_guids: spaceInfo.spaceGuid,
203
+ organization_guids: spaceInfo.orgGuid
204
+ });
205
+ if (!serviceInstances?.resources?.length) {
206
+ return null;
207
+ }
208
+
209
+ const serviceInstance = serviceInstances.resources[0];
210
+ switch (serviceInstance?.last_operation?.state?.toLowerCase()) {
211
+ case OPERATION_STATE_INITIAL:
212
+ case OPERATION_STATE_IN_PROGRESS:
213
+ await this._sleep(POLL_DELAY);
214
+ break;
215
+
216
+ case OPERATION_STATE_SUCCEEDED:
217
+ return serviceInstance;
218
+
219
+ case OPERATION_STATE_FAILED:
220
+ throw new Error(`The returned service reported state '${OPERATION_STATE_FAILED}'.\n${JSON.stringify(serviceInstance, null, 4)}`);
221
+
222
+ default:
223
+ console.error(`Unsupported server response state '${serviceInstance?.last_operation?.state}'. Waiting for next response.`);
224
+ break;
225
+ }
226
+ }
227
+
228
+ throw new Error(`Timeout occurred while getting service ${bold(serviceName)}`);
229
+ }
230
+
231
+
232
+ async getOrCreateService(serviceOfferingName, planName, serviceName, options) {
233
+
234
+ const probeService = await this.getService(serviceName, false);
235
+ if (probeService) {
236
+ console.log(`Getting service ${bold(serviceName)}`);
237
+ return probeService;
238
+ }
239
+
240
+ console.log(`Creating service ${bold(serviceName)} - please be patient...`);
241
+
242
+ const spaceInfo = await this.getCfSpaceInfo();
243
+
244
+ const servicePlan = await this._cfRequest(`/v3/service_plans`, {
245
+ names: planName,
246
+ space_guids: spaceInfo.spaceGuid,
247
+ organization_guids: spaceInfo.orgGuid,
248
+ service_offering_names: serviceOfferingName
249
+ });
250
+
251
+ if (!servicePlan?.resources?.length) {
252
+ throw new Error(`No service plans found`);
253
+ }
254
+
255
+ const body = {
256
+ type: 'managed',
257
+ name: serviceName,
258
+ tags: [serviceOfferingName],
259
+ relationships: {
260
+ space: {
261
+ data: {
262
+ guid: spaceInfo.spaceGuid
263
+ }
264
+ },
265
+ service_plan: {
266
+ data: {
267
+ guid: servicePlan.resources[0].guid
268
+ }
269
+ }
270
+ }
271
+ }
272
+
273
+ if (options) {
274
+ body.parameters = { ...options };
275
+ }
276
+
277
+ const postResult = await this._cfRequest('/v3/service_instances', undefined, body);
278
+ if (postResult?.errors) {
279
+ throw new Error(postResult.errors[0].detail);
280
+ }
281
+
282
+ const newService = await this.getService(serviceName, false);
283
+ if (newService) {
284
+ return newService;
285
+ }
286
+
287
+ throw new Error(`Could not create service ${bold(serviceName)}`);
288
+ }
289
+
290
+
291
+ async getServiceKey(serviceInstance, serviceKeyName, showMessage = true) {
292
+ showMessage && console.log(`Getting service key ${bold(serviceKeyName)}`);
293
+
294
+ let counter = POLL_COUNTER;
295
+ while (counter > 0) {
296
+ counter--;
297
+ const bindings = await this._cfRequest(`/v3/service_credential_bindings`, { names: serviceKeyName, service_instance_guids: serviceInstance.guid });
298
+ if (!bindings?.resources?.length) {
299
+ return null;
300
+ }
301
+
302
+ const binding = bindings.resources[0];
303
+ switch (binding?.last_operation?.state?.toLowerCase()) {
304
+ case OPERATION_STATE_INITIAL:
305
+ case OPERATION_STATE_IN_PROGRESS:
306
+ await this._sleep(POLL_DELAY);
307
+ break;
308
+
309
+ case OPERATION_STATE_SUCCEEDED: {
310
+ const keyDetails = await this._cfRequest(`/v3/service_credential_bindings/${encodeURIComponent(binding.guid)}/details`);
311
+ return keyDetails.credentials;
312
+ }
313
+
314
+ case OPERATION_STATE_FAILED:
315
+ throw new Error(`The returned binding reported state '${OPERATION_STATE_FAILED}'.\n${JSON.stringify(binding, null, 4)}`);
316
+
317
+ default:
318
+ console.error(`Unsupported server response state '${binding?.last_operation?.state}'. Waiting for next response.`);
319
+ break;
320
+ }
321
+ }
322
+
323
+ throw new Error(`Timeout occurred while getting service key ${bold(serviceKeyName)}`);
324
+ }
325
+
326
+
327
+ async getOrCreateServiceKey(serviceInstance, serviceKeyName) {
328
+
329
+ const serviceKey = await this.getServiceKey(serviceInstance, serviceKeyName, false);
330
+ if (serviceKey) {
331
+ console.log(`Getting service key ${bold(serviceKeyName)}`);
332
+ return serviceKey;
333
+ }
334
+
335
+ console.log(`Creating service key ${bold(serviceKeyName)} - please be patient...`);
336
+
337
+ const body = {
338
+ type: 'key',
339
+ name: serviceKeyName,
340
+ relationships: {
341
+ service_instance: {
342
+ data: {
343
+ guid: serviceInstance.guid
344
+ }
345
+ }
346
+ },
347
+ parameters: {
348
+ permissions: 'development'
349
+ }
350
+ }
351
+
352
+ const postResult = await this._cfRequest('/v3/service_credential_bindings', undefined, body);
353
+ if (postResult?.errors) {
354
+ throw new Error(postResult.errors[0].detail);
355
+ }
356
+
357
+ const newServiceKey = await this.getServiceKey(serviceInstance, serviceKeyName, false);
358
+ if (newServiceKey) {
359
+ return newServiceKey;
360
+ }
361
+
362
+ throw new Error(`Could not create service key ${bold(serviceKeyName)}`);
363
+ }
364
+ }
365
+
366
+ module.exports = new CfUtil();
@@ -21,7 +21,7 @@ class CommandUtil {
21
21
  *
22
22
  * @returns Promise
23
23
  */
24
- async spawnCommand(cmd, args, options = {}, collectResult = false) {
24
+ async spawnCommand(cmd, args, options = {}, collectResult = false, showOutput = true) {
25
25
  args = this._sanitizeArgs(args);
26
26
  return new Promise((resolve, reject) => {
27
27
  DEBUG?.(`[spawn] ${cmd} ${args.join(' ')}`);
@@ -31,14 +31,14 @@ class CommandUtil {
31
31
  const child = this._getChildProcess(cmd, args, options);
32
32
 
33
33
  // use inplace methods to ensure logger is called in context
34
- const stdLogBuffer = new LogBuffer((chunk) => console.log(chunk), collectResult);
34
+ const stdLogBuffer = new LogBuffer(showOutput || DEBUG ? (chunk) => console.log(chunk) : undefined, collectResult);
35
35
  child.stdout.on('data', (chunk) => {
36
36
  stdLogBuffer.handleChunk(chunk);
37
37
  });
38
38
 
39
39
  // use inplace methods to ensure logger is called in context
40
40
  // collect error log in case of Win to handle command not found (ENOENT),
41
- const errLogBuffer = new LogBuffer((chunk) => console.error(chunk), (IS_WIN || collectResult));
41
+ const errLogBuffer = new LogBuffer(showOutput || DEBUG ? (chunk) => console.error(chunk) : undefined, (IS_WIN || collectResult));
42
42
  child.stderr.on('data', (chunk) => {
43
43
  errLogBuffer.handleChunk(chunk);
44
44
  });
@@ -0,0 +1,55 @@
1
+ const cp = require('child_process');
2
+ const path = require('path');
3
+ const fs = require('fs').promises;
4
+ const util = require('util');
5
+ const os = require('os');
6
+
7
+ const cds = require('../cds');
8
+ const DEBUG = cds.debug('cli');
9
+
10
+ const GIT_IGNORE_FILE = '.gitignore';
11
+
12
+ class GitUtil {
13
+
14
+ constructor() {
15
+ this.execAsync = util.promisify(cp.exec);
16
+ }
17
+
18
+ async ensureFileIsGitignored(file, currentFolder = cds.root) {
19
+ const gitIgnorePath = path.join(currentFolder, GIT_IGNORE_FILE);
20
+
21
+ try {
22
+ await this.execAsync(`git check-ignore -n -v ${file}`, {
23
+ cwd: currentFolder
24
+ });
25
+ return; // file is git ignored, nothing to do
26
+ } catch (err) {
27
+ if (err.code === 'ENOENT') { // git command not found
28
+ DEBUG?.('git command not found');
29
+ return;
30
+ }
31
+
32
+ if (!err.stdout.match(/::/)) { // included or not a git repo
33
+ DEBUG?.(err.message);
34
+ return;
35
+ }
36
+ }
37
+
38
+ console.log(`adding entry '${file}' to ${GIT_IGNORE_FILE}.`);
39
+ let gitignore = await this.readFileSafely(gitIgnorePath);
40
+ gitignore = gitignore + `${os.EOL}# added by cds${os.EOL}${file}${os.EOL}`;
41
+
42
+ await fs.mkdir(path.dirname(gitIgnorePath), { recursive: true });
43
+ await fs.writeFile(gitIgnorePath, gitignore, 'utf8');
44
+ }
45
+
46
+ async readFileSafely(file) {
47
+ try {
48
+ return await fs.readFile(file, 'utf8');
49
+ } catch (err) {
50
+ return '';
51
+ }
52
+ }
53
+ }
54
+
55
+ module.exports = new GitUtil();
@@ -0,0 +1,27 @@
1
+ const { join } = require('path')
2
+
3
+ function _oldMtx(cds = require('../cds')) {
4
+ function _hasMtxDependency() {
5
+ try {
6
+ return require(join(cds.root, 'package.json'))?.dependencies?.['@sap/cds-mtx']
7
+ } catch(e) {
8
+ return false
9
+ }
10
+ }
11
+ return (_hasMtxDependency() || process.env.OLD_MTX?.toLowerCase() === 'true') ?? false
12
+ }
13
+
14
+ function checkMtx(cds) {
15
+ if (_oldMtx(cds)) {
16
+ const { CliError } = require('../client/helper/errors')
17
+ throw new CliError(`Package @sap/cds-mtx is deprecated and incompatible with @sap/cds-dk >= 7.
18
+ To continue using it, install version 6 of @sap/cds-dk in your project with:
19
+ npm i @sap/cds-dk@6
20
+ However, it is RECOMMENDED to migrate to @sap/cds-mtxs instead.
21
+ See https://cap.cloud.sap/docs/guides/multitenancy/old-mtx-migration for more.`)
22
+ }
23
+ }
24
+
25
+ module.exports = {
26
+ checkMtx
27
+ }