clever-tools 3.6.0 → 3.7.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.
package/README.md CHANGED
@@ -70,9 +70,3 @@ If you want to know more or if you need to release a new version, please read [R
70
70
  ## License
71
71
 
72
72
  This project is licensed under the [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html).
73
-
74
- We're using a CJS bundled version of a file from [curlconverter](https://github.com/curlconverter/curlconverter):
75
-
76
- * The project is licensed with [MIT](https://github.com/curlconverter/curlconverter/blob/master/LICENSE)
77
- * The copyright is at the top the file `vendors/curlconverter-parse`
78
- * The details are explained in [README_VENDORS.md](vendors/README_VENDORS.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clever-tools",
3
- "version": "3.6.0",
3
+ "version": "3.7.0",
4
4
  "description": "Command Line Interface for Clever Cloud.",
5
5
  "main": "bin/clever.js",
6
6
  "keywords": [
@@ -27,10 +27,10 @@
27
27
  "dependencies": {
28
28
  "@clevercloud/client": "^8.1.2",
29
29
  "clf-date": "^0.2.0",
30
- "cliparse": "^0.3.3",
30
+ "cliparse": "0.3.5",
31
31
  "colors": "1.4.0",
32
32
  "common-env": "^6.4.0",
33
- "curlconverter": "^4.9.0",
33
+ "curlconverter": "^3.21.0",
34
34
  "duration-js": "^4.0.0",
35
35
  "eventsource": "^1.1.0",
36
36
  "iso8601-duration": "^2.1.2",
@@ -78,7 +78,6 @@
78
78
  "pkg-fetch": "3.5.2"
79
79
  },
80
80
  "scripts": {
81
- "bundle-curlconverter": "rollup -c rollup-curlconverter.config.js",
82
81
  "pretest": "npm run lint",
83
82
  "test": "grunt test",
84
83
  "lint": "eslint bin src scripts",
@@ -22,14 +22,14 @@ function getSameCommitPolicyOption () {
22
22
  const availablePolicies = ['error', 'ignore', 'restart', 'rebuild'];
23
23
  return cliparse.option('same-commit-policy', {
24
24
  aliases: ['p'],
25
- metavar: 'same-commit-policy',
25
+ metavar: 'policy',
26
26
  parser: (policy) => {
27
27
  return availablePolicies.includes(policy)
28
28
  ? cliparse.parsers.success(policy)
29
- : cliparse.parsers.error(`The output policy must be one of ${availablePolicies.join(', ')}`);
29
+ : cliparse.parsers.error(`the policy must be one of ${availablePolicies.join(', ')}`);
30
30
  },
31
31
  default: 'error',
32
- description: `Which policy to apply when the local commit is the same as the remote one. Available policies are (${availablePolicies.join(', ')})`,
32
+ description: `What to do when local and remote commit are identical (${availablePolicies.join(', ')})`,
33
33
  complete () {
34
34
  return cliparse.autocomplete.words(availablePolicies);
35
35
  },
@@ -31,10 +31,11 @@ function getColoredState (state, isLast) {
31
31
  // We use examples of maximum width text to have a clean display
32
32
  const formatActivityTable = formatTable([
33
33
  moment().format(),
34
+ 47,
34
35
  'IN PROGRESS',
35
36
  'downscale',
36
- // a git commit id is 40 chars long
37
- 40,
37
+ // a git commit id is 8 chars long
38
+ 8,
38
39
  0,
39
40
  ]);
40
41
 
@@ -42,9 +43,10 @@ function formatActivityLine (event) {
42
43
  return formatActivityTable([
43
44
  [
44
45
  moment(event.date).format(),
46
+ event.uuid,
45
47
  getColoredState(event.state, event.isLast),
46
48
  event.action,
47
- event.commit || 'not specified',
49
+ event?.commit?.substring(0, 8) ?? 'N/A',
48
50
  event.cause,
49
51
  ],
50
52
  ]);
@@ -77,11 +79,11 @@ function handleEvent (previousEvent, event) {
77
79
  }
78
80
 
79
81
  function onEvent (previousEvent, newEvent) {
80
- const { event, date, data: { state, action, commit, cause } } = newEvent;
82
+ const { event, date, data: { uuid, state, action, commit, cause } } = newEvent;
81
83
  if (event !== 'DEPLOYMENT_ACTION_BEGIN' && event !== 'DEPLOYMENT_ACTION_END') {
82
84
  return previousEvent;
83
85
  }
84
- return handleEvent(previousEvent, { date, state, action, commit, cause, isLast: true });
86
+ return handleEvent(previousEvent, { date, uuid, state, action, commit, cause, isLast: true });
85
87
  }
86
88
 
87
89
  async function activity (params) {
@@ -15,22 +15,6 @@ const { sendToApi } = require('../models/send-to-api.js');
15
15
  const { toNameEqualsValueString } = require('@clevercloud/client/cjs/utils/env-vars.js');
16
16
  const { resolveAddonId } = require('../models/ids-resolver.js');
17
17
 
18
- function getPlan (inputPlan, providerName) {
19
-
20
- // Return user plan
21
- if (inputPlan !== '') {
22
- return inputPlan;
23
- }
24
-
25
- // Choose a default plan based on the add-on provider
26
- switch (providerName) {
27
- case 'kv':
28
- return 'alpha';
29
- default:
30
- return 'dev';
31
- }
32
- }
33
-
34
18
  async function list (params) {
35
19
  const { org: orgaIdOrName, format } = params.options;
36
20
 
@@ -74,7 +58,7 @@ async function create (params) {
74
58
  const [providerName, name] = params.args;
75
59
  const {
76
60
  link: linkedAppAlias,
77
- plan: inputPlan,
61
+ plan: planName,
78
62
  region,
79
63
  yes: skipConfirmation,
80
64
  org: orgaIdOrName,
@@ -82,7 +66,6 @@ async function create (params) {
82
66
  } = params.options;
83
67
  const version = params.options['addon-version'];
84
68
  const addonOptions = parseAddonOptions(params.options.option);
85
- const planName = getPlan(inputPlan, providerName);
86
69
 
87
70
  const ownerId = (orgaIdOrName != null)
88
71
  ? await Organisation.getId(orgaIdOrName)
@@ -143,8 +126,8 @@ function displayAddon (format, addon, providerName, message) {
143
126
  if (providerName === 'kv') {
144
127
  const materiaMessage = [
145
128
  '',
146
- colors.yellow(`/!\\ The MateriaDB ${providerName.toUpperCase()} provider is in Alpha testing phase, don't store sensitive or production grade data`),
147
- 'You can easily use MateriaDB KV with \'redis-cli\', with such commands:',
129
+ colors.yellow(`/!\\ The Materia ${providerName.toUpperCase()} provider is in Alpha testing phase, don't store sensitive or production grade data`),
130
+ 'You can easily use Materia KV with \'redis-cli\', with such commands:',
148
131
  colors.blue(`source <(clever addon env ${addon.id} -F shell)`),
149
132
  colors.blue('redis-cli -h $KV_HOST -p $KV_PORT --tls'),
150
133
  ].join('\n');
@@ -5,7 +5,7 @@ const { loadOAuthConf, conf } = require('../models/configuration.js');
5
5
  const { addOauthHeader } = require('@clevercloud/client/cjs/oauth.js');
6
6
  const Logger = require('../logger.js');
7
7
  const colors = require('colors/safe');
8
- const curlParser = require('../../vendors/curlconverter-parse.js');
8
+ const curlconverter = require('curlconverter');
9
9
 
10
10
  async function loadTokens () {
11
11
  const tokens = await loadOAuthConf();
@@ -49,7 +49,12 @@ async function curl () {
49
49
  return;
50
50
  }
51
51
 
52
- const requestParams = await parseCurlCommand(['curl', ...curlArgs]);
52
+ // WARNING: Version 3.x of curlconverter uses a fork of yargs.
53
+ // For "reasons", when the command has a help param, it stops the parsing and triggers a very short and localized version of some help (WAT?).
54
+ // That's why we remove the help param before parsing and "reinstate" it before execution.
55
+ const curlArgsNoHelp = curlArgs.filter((arg) => arg !== '--help' && arg !== '-h');
56
+ const curlCommand = ['curl', ...curlArgsNoHelp].join(' ');
57
+ const requestParams = await parseCurlCommand(curlCommand);
53
58
 
54
59
  // We only allow request to the respective API_HOST
55
60
  if (!requestParams.url.startsWith(conf.API_HOST)) {
@@ -78,23 +83,15 @@ async function curl () {
78
83
 
79
84
  async function parseCurlCommand (curlCommand) {
80
85
 
81
- const [request] = curlParser.parse(curlCommand);
82
- const url = request.urls[0];
86
+ const jsonString = curlconverter.toJsonString(curlCommand);
87
+ const curlRequestParams = JSON.parse(jsonString);
83
88
 
84
89
  return {
85
- method: url.method.toString(),
86
- url: url.urlWithoutQueryArray.toString(),
87
- headers: transformCurlConverterWordsToObject(request.headers.headers),
88
- queryParams: transformCurlConverterWordsToObject(url.queryDict),
90
+ method: curlRequestParams.method,
91
+ url: curlRequestParams.url,
92
+ headers: curlRequestParams.headers,
93
+ queryParams: curlRequestParams.queries,
89
94
  };
90
95
  }
91
96
 
92
- function transformCurlConverterWordsToObject (words = []) {
93
- return Object.fromEntries(
94
- words.map(([key, value]) => {
95
- return [key.toString(), value.toString()];
96
- }),
97
- );
98
- }
99
-
100
97
  module.exports = { curl };
@@ -26,7 +26,7 @@ async function getProvider (providerName) {
26
26
  const providers = await listProviders();
27
27
  const provider = providers.find((p) => p.id === providerName);
28
28
  if (provider == null) {
29
- throw new Error('invalid provider name');
29
+ throw new Error(`Invalid provider name. Available providers: ${providers.map((p) => p.id).join(', ')}`);
30
30
  }
31
31
  return provider;
32
32
  }
@@ -136,22 +136,17 @@ function validateAddonVersionAndOptions (region, version, addonOptions, provider
136
136
  async function create ({ ownerId, name, providerName, planName, region, skipConfirmation, version, addonOptions }) {
137
137
 
138
138
  // TODO: We should be able to use it without {}
139
- const providers = await listProviders();
139
+ const provider = await getProvider(providerName);
140
140
 
141
- const provider = providers.find((p) => p.id === providerName);
142
- if (provider == null) {
143
- throw new Error('invalid provider name');
144
- }
145
141
  if (!provider.regions.includes(region)) {
146
- throw new Error(`invalid region name. Available regions: ${provider.regions.join(', ')}`);
142
+ throw new Error(`Invalid region name. Available regions: ${provider.regions.join(', ')}`);
147
143
  }
148
-
149
- const plan = provider.plans.find((p) => p.slug.toLowerCase() === planName.toLowerCase());
150
- if (plan == null) {
151
- const availablePlans = provider.plans.map((p) => p.slug);
152
- throw new Error(`invalid plan name. Available plans: ${availablePlans.join(', ')}`);
144
+ if (provider.plans.length === 0) {
145
+ throw new Error(`No plans available for provider ${providerName}`);
153
146
  }
154
147
 
148
+ const plan = getPlan(planName, provider.plans);
149
+
155
150
  const providerInfos = await getProviderInfos(provider.id);
156
151
  const planType = plan.features.find(({ name }) => name.toLowerCase() === 'type');
157
152
 
@@ -289,6 +284,20 @@ function parseAddonOptions (options) {
289
284
  }, {});
290
285
  }
291
286
 
287
+ function getPlan (planName, plans) {
288
+ // if no plan specified, pick the cheapest one
289
+ if (planName == null || planName === '') {
290
+ return plans.sort((p1, p2) => p1.price - p2.price)[0];
291
+ }
292
+
293
+ const plan = plans.find((p) => p.slug.toLowerCase() === planName.toLowerCase());
294
+ if (plan == null) {
295
+ const availablePlans = plans.map((p) => p.slug);
296
+ throw new Error(`Invalid plan name. Available plans: ${availablePlans.join(', ')}`);
297
+ }
298
+ return plan;
299
+ }
300
+
292
301
  module.exports = {
293
302
  completePlan,
294
303
  completeRegion,