clever-tools 4.7.0 → 4.8.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/bin/clever.js +11 -1
- package/package.json +2 -2
- package/src/clever-client/drains.js +18 -18
- package/src/commands/README.md +1 -0
- package/src/commands/accesslogs/accesslogs.command.js +2 -2
- package/src/commands/activity/activity.command.js +1 -1
- package/src/commands/addon/addon.create.command.js +2 -3
- package/src/commands/addon/addon.delete.command.js +2 -2
- package/src/commands/addon/addon.docs.md +2 -2
- package/src/commands/addon/addon.env.command.js +4 -6
- package/src/commands/config-provider/config-provider.command.js +0 -4
- package/src/commands/config-provider/config-provider.import.command.js +1 -1
- package/src/commands/config-provider/config-provider.list.command.js +8 -19
- package/src/commands/config-provider/config-provider.open.command.js +1 -1
- package/src/commands/console/console.command.js +3 -2
- package/src/commands/curl/curl.command.js +0 -1
- package/src/commands/database/database.backups.command.js +4 -7
- package/src/commands/database/database.backups.download.command.js +5 -7
- package/src/commands/database/database.command.js +0 -4
- package/src/commands/database/database.docs.md +2 -2
- package/src/commands/deploy/deploy.command.js +1 -1
- package/src/commands/deploy/deploy.docs.md +1 -1
- package/src/commands/drain/drain.command.js +14 -9
- package/src/commands/drain/drain.create.command.js +9 -13
- package/src/commands/drain/drain.disable.command.js +7 -7
- package/src/commands/drain/drain.docs.md +7 -1
- package/src/commands/drain/drain.enable.command.js +7 -7
- package/src/commands/drain/drain.get.command.js +12 -8
- package/src/commands/drain/drain.remove.command.js +7 -7
- package/src/commands/emails/emails.open.command.js +1 -4
- package/src/commands/global.commands.js +18 -0
- package/src/commands/global.options.js +7 -7
- package/src/commands/help/help.command.js +0 -3
- package/src/commands/k8s/k8s.command.js +0 -4
- package/src/commands/k8s/k8s.delete.command.js +2 -2
- package/src/commands/k8s/k8s.docs.md +1 -1
- package/src/commands/logs/logs.command.js +4 -5
- package/src/commands/ng/ng.docs.md +0 -1
- package/src/commands/ng/ng.get-config.command.js +3 -14
- package/src/commands/ng/ng.options.js +1 -1
- package/src/commands/notify-email/notify-email.remove.command.js +2 -2
- package/src/commands/oauth-consumers/oauth-consumers.args.js +8 -0
- package/src/commands/oauth-consumers/oauth-consumers.command.js +6 -0
- package/src/commands/oauth-consumers/oauth-consumers.create.command.js +64 -0
- package/src/commands/oauth-consumers/oauth-consumers.delete.command.js +33 -0
- package/src/commands/oauth-consumers/oauth-consumers.docs.md +130 -0
- package/src/commands/oauth-consumers/oauth-consumers.get.command.js +67 -0
- package/src/commands/oauth-consumers/oauth-consumers.list.command.js +34 -0
- package/src/commands/oauth-consumers/oauth-consumers.open.command.js +18 -0
- package/src/commands/oauth-consumers/oauth-consumers.options.js +48 -0
- package/src/commands/oauth-consumers/oauth-consumers.update.command.js +83 -0
- package/src/commands/open/open.command.js +1 -1
- package/src/commands/profile/profile.open.command.js +1 -1
- package/src/commands/restart/restart.docs.md +1 -1
- package/src/commands/ssh-keys/ssh-keys.open.command.js +1 -1
- package/src/commands/webhooks/webhooks.remove.command.js +2 -2
- package/src/lib/cliparse-patched.js +2 -0
- package/src/lib/get-command-info.js +3 -1
- package/src/lib/get-command-info.types.d.ts +4 -0
- package/src/lib/operator-commands.js +11 -25
- package/src/lib/print-items-by-owner.js +38 -0
- package/src/lib/prompts.js +23 -1
- package/src/lib/zod-utils.js +51 -0
- package/src/models/drain.js +17 -0
- package/src/models/ids-resolver.js +38 -20
- package/src/models/ng.js +2 -2
- package/src/models/notification.js +0 -4
- package/src/models/oauth-consumer.js +77 -0
- package/src/models/utils.js +1 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# 📖 `clever oauth-consumers` command reference
|
|
2
|
+
|
|
3
|
+
## ➡️ `clever oauth-consumers` <kbd>Since 4.8.0</kbd>
|
|
4
|
+
|
|
5
|
+
Manage OAuth consumers used with a Clever Cloud login
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
clever oauth-consumers
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## ➡️ `clever oauth-consumers create` <kbd>Since 4.8.0</kbd>
|
|
12
|
+
|
|
13
|
+
Create an OAuth consumer
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
clever oauth-consumers create <name> [options]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 📥 Arguments
|
|
20
|
+
|
|
21
|
+
|Name|Description|
|
|
22
|
+
|---|---|
|
|
23
|
+
|`name`|Consumer name|
|
|
24
|
+
|
|
25
|
+
### ⚙️ Options
|
|
26
|
+
|
|
27
|
+
|Name|Description|
|
|
28
|
+
|---|---|
|
|
29
|
+
|`--base-url` `<url>`|OAuth callback base URL|
|
|
30
|
+
|`-d`, `--description` `<description>`|Consumer description|
|
|
31
|
+
|`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
|
|
32
|
+
|`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
|
|
33
|
+
|`--picture` `<url>`|Application logo URL|
|
|
34
|
+
|`--rights` `<rights>`|Comma-separated list of rights (access-organisations, access-organisations-bills, access-organisations-consumption-statistics, access-organisations-credit-count, access-personal-information, manage-organisations, manage-organisations-applications, manage-organisations-members, manage-organisations-services, manage-personal-information, manage-ssh-keys, all)|
|
|
35
|
+
|`--url` `<url>`|Application home URL|
|
|
36
|
+
|
|
37
|
+
## ➡️ `clever oauth-consumers delete` <kbd>Since 4.8.0</kbd>
|
|
38
|
+
|
|
39
|
+
Delete an OAuth consumer
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
clever oauth-consumers delete <consumer-key|consumer-name> [options]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 📥 Arguments
|
|
46
|
+
|
|
47
|
+
|Name|Description|
|
|
48
|
+
|---|---|
|
|
49
|
+
|`consumer-key|consumer-name`|OAuth consumer key (or name, if unambiguous)|
|
|
50
|
+
|
|
51
|
+
### ⚙️ Options
|
|
52
|
+
|
|
53
|
+
|Name|Description|
|
|
54
|
+
|---|---|
|
|
55
|
+
|`-y`, `--yes`|Skip confirmation and proceed with deletion directly|
|
|
56
|
+
|
|
57
|
+
## ➡️ `clever oauth-consumers get` <kbd>Since 4.8.0</kbd>
|
|
58
|
+
|
|
59
|
+
Get details of an OAuth consumer
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
clever oauth-consumers get <consumer-key|consumer-name> [options]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 📥 Arguments
|
|
66
|
+
|
|
67
|
+
|Name|Description|
|
|
68
|
+
|---|---|
|
|
69
|
+
|`consumer-key|consumer-name`|OAuth consumer key (or name, if unambiguous)|
|
|
70
|
+
|
|
71
|
+
### ⚙️ Options
|
|
72
|
+
|
|
73
|
+
|Name|Description|
|
|
74
|
+
|---|---|
|
|
75
|
+
|`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
|
|
76
|
+
|`--with-secret`|Include the consumer secret in the output|
|
|
77
|
+
|
|
78
|
+
## ➡️ `clever oauth-consumers list` <kbd>Since 4.8.0</kbd>
|
|
79
|
+
|
|
80
|
+
List OAuth consumers
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
clever oauth-consumers list [options]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### ⚙️ Options
|
|
87
|
+
|
|
88
|
+
|Name|Description|
|
|
89
|
+
|---|---|
|
|
90
|
+
|`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
|
|
91
|
+
|
|
92
|
+
## ➡️ `clever oauth-consumers open` <kbd>Since 4.8.0</kbd>
|
|
93
|
+
|
|
94
|
+
Open an OAuth consumer in the Clever Cloud Console
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
clever oauth-consumers open <consumer-key|consumer-name>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 📥 Arguments
|
|
101
|
+
|
|
102
|
+
|Name|Description|
|
|
103
|
+
|---|---|
|
|
104
|
+
|`consumer-key|consumer-name`|OAuth consumer key (or name, if unambiguous)|
|
|
105
|
+
|
|
106
|
+
## ➡️ `clever oauth-consumers update` <kbd>Since 4.8.0</kbd>
|
|
107
|
+
|
|
108
|
+
Update an OAuth consumer
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
clever oauth-consumers update <consumer-key|consumer-name> [options]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 📥 Arguments
|
|
115
|
+
|
|
116
|
+
|Name|Description|
|
|
117
|
+
|---|---|
|
|
118
|
+
|`consumer-key|consumer-name`|OAuth consumer key (or name, if unambiguous)|
|
|
119
|
+
|
|
120
|
+
### ⚙️ Options
|
|
121
|
+
|
|
122
|
+
|Name|Description|
|
|
123
|
+
|---|---|
|
|
124
|
+
|`--base-url` `<url>`|OAuth callback base URL|
|
|
125
|
+
|`-d`, `--description` `<description>`|Consumer description|
|
|
126
|
+
|`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
|
|
127
|
+
|`-n`, `--name` `<name>`|Consumer name|
|
|
128
|
+
|`--picture` `<url>`|Application logo URL|
|
|
129
|
+
|`--rights` `<rights>`|Comma-separated list of rights (access-organisations, access-organisations-bills, access-organisations-consumption-statistics, access-organisations-credit-count, access-personal-information, manage-organisations, manage-organisations-applications, manage-organisations-members, manage-organisations-services, manage-personal-information, manage-ssh-keys, all)|
|
|
130
|
+
|`--url` `<url>`|Application home URL|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { getSecret as getOauthConsumerSecret } from '@clevercloud/client/esm/api/v2/oauth-consumer.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
4
|
+
import { defineOption } from '../../lib/define-option.js';
|
|
5
|
+
import { Logger } from '../../logger.js';
|
|
6
|
+
import { OAUTH_RIGHTS, resolveOauthConsumer } from '../../models/oauth-consumer.js';
|
|
7
|
+
import { sendToApi } from '../../models/send-to-api.js';
|
|
8
|
+
import { humanJsonOutputFormatOption } from '../global.options.js';
|
|
9
|
+
import { consumerKeyOrNameArg } from './oauth-consumers.args.js';
|
|
10
|
+
|
|
11
|
+
export const oauthConsumersGetCommand = defineCommand({
|
|
12
|
+
description: 'Get details of an OAuth consumer',
|
|
13
|
+
since: '4.8.0',
|
|
14
|
+
options: {
|
|
15
|
+
format: humanJsonOutputFormatOption,
|
|
16
|
+
withSecret: defineOption({
|
|
17
|
+
name: 'with-secret',
|
|
18
|
+
schema: z.boolean().default(false),
|
|
19
|
+
description: 'Include the consumer secret in the output',
|
|
20
|
+
}),
|
|
21
|
+
},
|
|
22
|
+
args: [consumerKeyOrNameArg],
|
|
23
|
+
async handler(options, keyOrName) {
|
|
24
|
+
const { format, withSecret } = options;
|
|
25
|
+
|
|
26
|
+
const oauthConsumer = await resolveOauthConsumer(keyOrName);
|
|
27
|
+
|
|
28
|
+
const secret = withSecret
|
|
29
|
+
? await getOauthConsumerSecret({ id: oauthConsumer.ownerId, key: oauthConsumer.key })
|
|
30
|
+
.then(sendToApi)
|
|
31
|
+
.then((c) => c.secret)
|
|
32
|
+
: null;
|
|
33
|
+
|
|
34
|
+
switch (format) {
|
|
35
|
+
case 'json': {
|
|
36
|
+
Logger.printJson({ ...oauthConsumer, ...(secret != null && { secret }) });
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
case 'human':
|
|
40
|
+
default: {
|
|
41
|
+
const dataToPrint = {
|
|
42
|
+
Key: oauthConsumer.key,
|
|
43
|
+
Name: oauthConsumer.name || '(unnamed)',
|
|
44
|
+
Description: oauthConsumer.description || '',
|
|
45
|
+
URL: oauthConsumer.url || '',
|
|
46
|
+
Picture: oauthConsumer.picture || '',
|
|
47
|
+
'Base URL': oauthConsumer.baseUrl || '',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (secret != null) {
|
|
51
|
+
dataToPrint.Secret = secret;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.table(dataToPrint);
|
|
55
|
+
|
|
56
|
+
if (oauthConsumer.rights) {
|
|
57
|
+
Logger.println('');
|
|
58
|
+
const rightsData = {};
|
|
59
|
+
for (const [apiName, cliName] of Object.entries(OAUTH_RIGHTS)) {
|
|
60
|
+
rightsData[cliName] = oauthConsumer.rights[apiName] ?? false;
|
|
61
|
+
}
|
|
62
|
+
console.table(rightsData);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
2
|
+
import { printItemsByOwner } from '../../lib/print-items-by-owner.js';
|
|
3
|
+
import { Logger } from '../../logger.js';
|
|
4
|
+
import { getAllConsumers } from '../../models/oauth-consumer.js';
|
|
5
|
+
import { humanJsonOutputFormatOption } from '../global.options.js';
|
|
6
|
+
|
|
7
|
+
export const oauthConsumersListCommand = defineCommand({
|
|
8
|
+
description: 'List OAuth consumers',
|
|
9
|
+
since: '4.8.0',
|
|
10
|
+
options: {
|
|
11
|
+
format: humanJsonOutputFormatOption,
|
|
12
|
+
},
|
|
13
|
+
async handler(options) {
|
|
14
|
+
const { format } = options;
|
|
15
|
+
const oauthConsumers = await getAllConsumers();
|
|
16
|
+
|
|
17
|
+
switch (format) {
|
|
18
|
+
case 'json': {
|
|
19
|
+
const consumersPerOwner = Object.groupBy(oauthConsumers, (c) => c.ownerId);
|
|
20
|
+
Logger.printJson(consumersPerOwner);
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
case 'human':
|
|
24
|
+
default: {
|
|
25
|
+
printItemsByOwner(oauthConsumers, {
|
|
26
|
+
itemName: 'OAuth consumer',
|
|
27
|
+
emptyCommand: 'clever oauth-consumers create',
|
|
28
|
+
getItemId: (c) => c.key,
|
|
29
|
+
getExtraLines: (c) => (c.url ? `URL: ${c.url}` : null),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
2
|
+
import { styleText } from '../../lib/style-text.js';
|
|
3
|
+
import { resolveOauthConsumer } from '../../models/oauth-consumer.js';
|
|
4
|
+
import { openBrowser } from '../../models/utils.js';
|
|
5
|
+
import { consumerKeyOrNameArg } from './oauth-consumers.args.js';
|
|
6
|
+
|
|
7
|
+
export const oauthConsumersOpenCommand = defineCommand({
|
|
8
|
+
description: 'Open an OAuth consumer in the Clever Cloud Console',
|
|
9
|
+
since: '4.8.0',
|
|
10
|
+
args: [consumerKeyOrNameArg],
|
|
11
|
+
async handler(options, keyOrName) {
|
|
12
|
+
const oauthConsumer = await resolveOauthConsumer(keyOrName);
|
|
13
|
+
await openBrowser(
|
|
14
|
+
`/organisations/${oauthConsumer.ownerId}/oauth-consumers/${oauthConsumer.key}`,
|
|
15
|
+
`Opening OAuth consumer ${styleText('blue', oauthConsumer.key)} in the browser…`,
|
|
16
|
+
);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineOption } from '../../lib/define-option.js';
|
|
3
|
+
import { OAUTH_RIGHTS } from '../../models/oauth-consumer.js';
|
|
4
|
+
|
|
5
|
+
export const descriptionOption = defineOption({
|
|
6
|
+
name: 'description',
|
|
7
|
+
schema: z.string().trim().min(1).optional(),
|
|
8
|
+
description: 'Consumer description',
|
|
9
|
+
aliases: ['d'],
|
|
10
|
+
placeholder: 'description',
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const urlOption = defineOption({
|
|
14
|
+
name: 'url',
|
|
15
|
+
schema: z.string().url().optional(),
|
|
16
|
+
description: 'Application home URL',
|
|
17
|
+
placeholder: 'url',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const pictureOption = defineOption({
|
|
21
|
+
name: 'picture',
|
|
22
|
+
schema: z.string().url().optional(),
|
|
23
|
+
description: 'Application logo URL',
|
|
24
|
+
placeholder: 'url',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const baseUrlOption = defineOption({
|
|
28
|
+
name: 'base-url',
|
|
29
|
+
schema: z.string().url().optional(),
|
|
30
|
+
description: 'OAuth callback base URL',
|
|
31
|
+
placeholder: 'url',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/** @type {[string, ...string[]]} */
|
|
35
|
+
const OAUTH_RIGHTS_WITH_ALL = [...Object.values(OAUTH_RIGHTS), 'all'];
|
|
36
|
+
|
|
37
|
+
const rightsSchema = z
|
|
38
|
+
.string()
|
|
39
|
+
.transform((s) => s.split(',').map((r) => r.trim()))
|
|
40
|
+
.pipe(z.array(z.enum(OAUTH_RIGHTS_WITH_ALL)))
|
|
41
|
+
.optional();
|
|
42
|
+
|
|
43
|
+
export const rightsOption = defineOption({
|
|
44
|
+
name: 'rights',
|
|
45
|
+
schema: rightsSchema,
|
|
46
|
+
description: `Comma-separated list of rights (${OAUTH_RIGHTS_WITH_ALL.join(', ')})`,
|
|
47
|
+
placeholder: 'rights',
|
|
48
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { update as updateOauthConsumer } from '@clevercloud/client/esm/api/v2/oauth-consumer.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
4
|
+
import { defineOption } from '../../lib/define-option.js';
|
|
5
|
+
import { promptTextOption } from '../../lib/prompts.js';
|
|
6
|
+
import { styleText } from '../../lib/style-text.js';
|
|
7
|
+
import { Logger } from '../../logger.js';
|
|
8
|
+
import {
|
|
9
|
+
promptRights,
|
|
10
|
+
removeReadonlyRights,
|
|
11
|
+
resolveOauthConsumer,
|
|
12
|
+
rightsFromList,
|
|
13
|
+
} from '../../models/oauth-consumer.js';
|
|
14
|
+
import { sendToApi } from '../../models/send-to-api.js';
|
|
15
|
+
import { humanJsonOutputFormatOption } from '../global.options.js';
|
|
16
|
+
import { consumerKeyOrNameArg } from './oauth-consumers.args.js';
|
|
17
|
+
import { baseUrlOption, descriptionOption, pictureOption, rightsOption, urlOption } from './oauth-consumers.options.js';
|
|
18
|
+
|
|
19
|
+
const nameOption = defineOption({
|
|
20
|
+
name: 'name',
|
|
21
|
+
schema: z.string().trim().min(1).optional(),
|
|
22
|
+
description: 'Consumer name',
|
|
23
|
+
aliases: ['n'],
|
|
24
|
+
placeholder: 'name',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const oauthConsumersUpdateCommand = defineCommand({
|
|
28
|
+
description: 'Update an OAuth consumer',
|
|
29
|
+
since: '4.8.0',
|
|
30
|
+
options: {
|
|
31
|
+
name: nameOption,
|
|
32
|
+
description: descriptionOption,
|
|
33
|
+
url: urlOption,
|
|
34
|
+
picture: pictureOption,
|
|
35
|
+
baseUrl: baseUrlOption,
|
|
36
|
+
rights: rightsOption,
|
|
37
|
+
format: humanJsonOutputFormatOption,
|
|
38
|
+
},
|
|
39
|
+
args: [consumerKeyOrNameArg],
|
|
40
|
+
async handler(options, keyOrName) {
|
|
41
|
+
const { name, description, url, picture, baseUrl, rights, format } = options;
|
|
42
|
+
|
|
43
|
+
const oauthConsumer = await resolveOauthConsumer(keyOrName);
|
|
44
|
+
|
|
45
|
+
const hasAnyOption = [name, description, url, picture, baseUrl, rights].some((v) => v != null);
|
|
46
|
+
|
|
47
|
+
const body = hasAnyOption
|
|
48
|
+
? {
|
|
49
|
+
name: name ?? oauthConsumer.name,
|
|
50
|
+
description: description ?? oauthConsumer.description,
|
|
51
|
+
url: url ?? oauthConsumer.url,
|
|
52
|
+
picture: picture ?? oauthConsumer.picture,
|
|
53
|
+
baseUrl: baseUrl ?? oauthConsumer.baseUrl,
|
|
54
|
+
rights: rights != null ? rightsFromList(rights) : removeReadonlyRights(oauthConsumer.rights),
|
|
55
|
+
}
|
|
56
|
+
: {
|
|
57
|
+
name: await promptTextOption(nameOption, oauthConsumer.name),
|
|
58
|
+
description: await promptTextOption(descriptionOption, oauthConsumer.description),
|
|
59
|
+
url: await promptTextOption(urlOption, oauthConsumer.url),
|
|
60
|
+
picture: await promptTextOption(pictureOption, oauthConsumer.picture),
|
|
61
|
+
baseUrl: await promptTextOption(baseUrlOption, oauthConsumer.baseUrl),
|
|
62
|
+
rights: await promptRights(oauthConsumer.rights),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const updatedOauthConsumer = await updateOauthConsumer(
|
|
66
|
+
{ id: oauthConsumer.ownerId, key: oauthConsumer.key },
|
|
67
|
+
body,
|
|
68
|
+
).then(sendToApi);
|
|
69
|
+
|
|
70
|
+
switch (format) {
|
|
71
|
+
case 'json': {
|
|
72
|
+
Logger.printJson(updatedOauthConsumer);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case 'human':
|
|
76
|
+
default: {
|
|
77
|
+
Logger.printSuccess(
|
|
78
|
+
`OAuth consumer ${styleText(['bold', 'green'], updatedOauthConsumer.key)} has been updated!`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
});
|
|
@@ -19,6 +19,6 @@ export const openCommand = defineCommand({
|
|
|
19
19
|
const vhost = await Domain.getBest(appId, ownerId);
|
|
20
20
|
const url = 'https://' + vhost.fqdn;
|
|
21
21
|
|
|
22
|
-
await openBrowser(url, 'Opening the application in
|
|
22
|
+
await openBrowser(url, 'Opening the application in the browser…');
|
|
23
23
|
},
|
|
24
24
|
});
|
|
@@ -7,6 +7,6 @@ export const profileOpenCommand = defineCommand({
|
|
|
7
7
|
options: {},
|
|
8
8
|
args: [],
|
|
9
9
|
async handler() {
|
|
10
|
-
await openBrowser('/users/me/information', 'Opening the profile page in
|
|
10
|
+
await openBrowser('/users/me/information', 'Opening the profile page in the browser…');
|
|
11
11
|
},
|
|
12
12
|
});
|
|
@@ -15,7 +15,7 @@ clever restart [options]
|
|
|
15
15
|
|`-a`, `--alias` `<alias>`|Short name for the application|
|
|
16
16
|
|`--app` `<app-id\|app-name>`|Application to manage by its ID (or name, if unambiguous)|
|
|
17
17
|
|`--commit` `<commit-id>`|Restart the application with a specific commit ID|
|
|
18
|
-
|`-e`, `--exit-on` `<step>`|Step at which the logs streaming is ended
|
|
18
|
+
|`-e`, `--exit-on` `<step>`|Step at which the logs streaming is ended (deploy-start, deploy-end, never) (default: deploy-end)|
|
|
19
19
|
|`--follow`|Continue to follow logs after deployment has ended *(deprecated, use `--exit-on never` instead)*|
|
|
20
20
|
|`-q`, `--quiet`|Don't show logs during deployment|
|
|
21
21
|
|`--without-cache`|Restart the application without using cache|
|
|
@@ -7,6 +7,6 @@ export const sshKeysOpenCommand = defineCommand({
|
|
|
7
7
|
options: {},
|
|
8
8
|
args: [],
|
|
9
9
|
handler() {
|
|
10
|
-
return openBrowser('/users/me/ssh-keys', 'Opening the SSH keys
|
|
10
|
+
return openBrowser('/users/me/ssh-keys', 'Opening the SSH keys page in the browser…');
|
|
11
11
|
},
|
|
12
12
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { deleteWebhook } from '@clevercloud/client/esm/api/v2/notification.js';
|
|
2
2
|
import { defineCommand } from '../../lib/define-command.js';
|
|
3
3
|
import { Logger } from '../../logger.js';
|
|
4
|
-
import {
|
|
4
|
+
import { getOwnerIdFromOrgIdOrName } from '../../models/ids-resolver.js';
|
|
5
5
|
import { sendToApi } from '../../models/send-to-api.js';
|
|
6
6
|
import { notificationIdArg } from '../global.args.js';
|
|
7
7
|
import { orgaIdOrNameOption } from '../global.options.js';
|
|
@@ -16,7 +16,7 @@ export const webhooksRemoveCommand = defineCommand({
|
|
|
16
16
|
async handler(options, notificationId) {
|
|
17
17
|
const { org } = options;
|
|
18
18
|
|
|
19
|
-
const ownerId = await
|
|
19
|
+
const ownerId = await getOwnerIdFromOrgIdOrName(org);
|
|
20
20
|
await deleteWebhook({ ownerId, id: notificationId }).then(sendToApi);
|
|
21
21
|
|
|
22
22
|
Logger.println('The notification has been successfully removed');
|
|
@@ -80,6 +80,7 @@ cliparseCommandModule.help = function (context) {
|
|
|
80
80
|
if (commandInfo.args) {
|
|
81
81
|
argumentsRows = commandInfo.args.map((arg) => {
|
|
82
82
|
let description = arg.description;
|
|
83
|
+
if (arg.enumValues) description += ` (${arg.enumValues.join(', ')})`;
|
|
83
84
|
if (arg.optional) description += ` ${styleText('dim', arg.optional)}`;
|
|
84
85
|
return [arg.name, description];
|
|
85
86
|
});
|
|
@@ -93,6 +94,7 @@ cliparseCommandModule.help = function (context) {
|
|
|
93
94
|
const aliasesPadding = opt.aliases[0].length === 2 ? '' : ' ';
|
|
94
95
|
const aliases = aliasesPadding + opt.aliases.join(', ') + placeholder;
|
|
95
96
|
let description = opt.description;
|
|
97
|
+
if (opt.enumValues) description += ` (${opt.enumValues.join(', ')})`;
|
|
96
98
|
if (opt.deprecated) description += ` ${styleText('dim', opt.deprecated)}`;
|
|
97
99
|
if (opt.required) description += ` ${styleText('dim', opt.required)}`;
|
|
98
100
|
if (opt.default) description += ` ${styleText('dim', opt.default)}`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDefault, isBoolean, isRequired } from './zod-utils.js';
|
|
1
|
+
import { getDefault, getEnumValues, isBoolean, isRequired } from './zod-utils.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @typedef {import('./get-command-info.types.d.ts').ArgumentInfo} ArgumentInfo
|
|
@@ -45,6 +45,7 @@ export function getCommandInfo(path, definition) {
|
|
|
45
45
|
name: arg.placeholder,
|
|
46
46
|
description: arg.description,
|
|
47
47
|
optional: isRequired(arg.schema) ? null : '(optional)',
|
|
48
|
+
enumValues: getEnumValues(arg.schema) ?? null,
|
|
48
49
|
};
|
|
49
50
|
})
|
|
50
51
|
: null;
|
|
@@ -78,6 +79,7 @@ export function getCommandInfo(path, definition) {
|
|
|
78
79
|
: null,
|
|
79
80
|
required: isRequired(option.schema) ? '(required)' : null,
|
|
80
81
|
default: defaultValue ? `(default: ${defaultValue})` : null,
|
|
82
|
+
enumValues: getEnumValues(option.schema) ?? null,
|
|
81
83
|
};
|
|
82
84
|
})
|
|
83
85
|
: null;
|
|
@@ -13,6 +13,8 @@ export interface ArgumentInfo {
|
|
|
13
13
|
description: string;
|
|
14
14
|
/** '(optional)' or null */
|
|
15
15
|
optional: string | null;
|
|
16
|
+
/** Enum values if the schema is a z.enum(), or null */
|
|
17
|
+
enumValues: string[] | null;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export interface OptionInfo {
|
|
@@ -30,6 +32,8 @@ export interface OptionInfo {
|
|
|
30
32
|
required: string | null;
|
|
31
33
|
/** '(default: value)' or null */
|
|
32
34
|
default: string | null;
|
|
35
|
+
/** Enum values if the schema is a z.enum(), or null */
|
|
36
|
+
enumValues: string[] | null;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
export interface CommandInfo {
|
|
@@ -15,6 +15,7 @@ import { findAddonsByAddonProvider } from '../models/ids-resolver.js';
|
|
|
15
15
|
import * as Operator from '../models/operator.js';
|
|
16
16
|
import { sendToApi } from '../models/send-to-api.js';
|
|
17
17
|
import { openBrowser } from '../models/utils.js';
|
|
18
|
+
import { printItemsByOwner } from './print-items-by-owner.js';
|
|
18
19
|
import { confirm, selectAnswer } from './prompts.js';
|
|
19
20
|
import { styleText } from './style-text.js';
|
|
20
21
|
|
|
@@ -141,30 +142,18 @@ export async function operatorNgEnable(provider, addonIdOrName) {
|
|
|
141
142
|
export async function operatorList(provider, format) {
|
|
142
143
|
const deployed = await findAddonsByAddonProvider(provider);
|
|
143
144
|
const providerName = _.capitalize(provider.replace('addon-', ''));
|
|
144
|
-
const operatorsPerOwner = _.groupBy(deployed, 'ownerId');
|
|
145
|
-
|
|
146
145
|
switch (format) {
|
|
147
|
-
case 'json':
|
|
146
|
+
case 'json': {
|
|
147
|
+
const operatorsPerOwner = Object.groupBy(deployed, (o) => o.ownerId);
|
|
148
148
|
Logger.printJson(operatorsPerOwner);
|
|
149
149
|
break;
|
|
150
|
+
}
|
|
150
151
|
case 'human':
|
|
151
152
|
default:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
)
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
Logger.println(`🔎 Found ${deployed.length} ${providerName} operator${deployed.length > 1 ? 's' : ''}:`);
|
|
160
|
-
Logger.println();
|
|
161
|
-
|
|
162
|
-
Object.values(operatorsPerOwner).forEach((operators) => {
|
|
163
|
-
Logger.println(`• ${styleText('bold', `${operators[0].ownerId} (${operators[0].ownerName})`)}`);
|
|
164
|
-
operators.forEach((operator) => {
|
|
165
|
-
Logger.println(` • ${operator.name} ${styleText('grey', `(${operator.realId})`)}`);
|
|
166
|
-
});
|
|
167
|
-
Logger.println();
|
|
153
|
+
printItemsByOwner(deployed, {
|
|
154
|
+
itemName: `${providerName} operator`,
|
|
155
|
+
emptyCommand: `clever addon create ${providerName.toLocaleLowerCase()}`,
|
|
156
|
+
getItemId: (o) => o.realId,
|
|
168
157
|
});
|
|
169
158
|
break;
|
|
170
159
|
}
|
|
@@ -180,7 +169,7 @@ export async function operatorOpen(provider, addonIdOrName) {
|
|
|
180
169
|
const operator = await Operator.getDetails(provider, addonIdOrName);
|
|
181
170
|
await openBrowser(
|
|
182
171
|
`${config.GOTO_URL}/${operator.addonId}`,
|
|
183
|
-
|
|
172
|
+
`Opening ${styleText('blue', operator.addonId)} in the browser…`,
|
|
184
173
|
);
|
|
185
174
|
}
|
|
186
175
|
|
|
@@ -194,7 +183,7 @@ export async function operatorOpenLogs(provider, addonIdOrName) {
|
|
|
194
183
|
const operator = await Operator.getDetails(provider, addonIdOrName);
|
|
195
184
|
await openBrowser(
|
|
196
185
|
`/organisations/${operator.ownerId}/applications/${operator.resources.entrypoint}/logs`,
|
|
197
|
-
|
|
186
|
+
`Opening ${styleText('blue', operator.addonId)} logs in the browser…`,
|
|
198
187
|
);
|
|
199
188
|
}
|
|
200
189
|
|
|
@@ -206,10 +195,7 @@ export async function operatorOpenLogs(provider, addonIdOrName) {
|
|
|
206
195
|
*/
|
|
207
196
|
export async function operatorOpenWebUi(provider, addonIdOrName) {
|
|
208
197
|
const operator = await Operator.getDetails(provider, addonIdOrName);
|
|
209
|
-
await openBrowser(
|
|
210
|
-
operator.accessUrl,
|
|
211
|
-
`🌐 Opening ${styleText('blue', operator.addonId)} Management interface in the browser…`,
|
|
212
|
-
);
|
|
198
|
+
await openBrowser(operator.accessUrl, `Opening ${styleText('blue', operator.addonId)} web UI in the browser…`);
|
|
213
199
|
}
|
|
214
200
|
|
|
215
201
|
/**
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Logger } from '../logger.js';
|
|
2
|
+
import { styleText } from './style-text.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Print a list of items grouped by owner with consistent formatting.
|
|
6
|
+
* Items must have ownerId, ownerName and name properties.
|
|
7
|
+
* @param {Array} items - The items to display
|
|
8
|
+
* @param {Object} options
|
|
9
|
+
* @param {string} options.itemName - Singular name for the items (e.g. "configuration provider")
|
|
10
|
+
* @param {string} options.emptyCommand - Command suggestion shown when no items are found
|
|
11
|
+
* @param {Function} options.getItemId - Function to get the item ID from an item
|
|
12
|
+
* @param {Function} [options.getExtraLines] - Function returning extra detail lines to display below the item (nulls filtered)
|
|
13
|
+
*/
|
|
14
|
+
export function printItemsByOwner(items, { itemName, emptyCommand, getItemId, getExtraLines }) {
|
|
15
|
+
if (items.length === 0) {
|
|
16
|
+
Logger.println(`🔎 No ${itemName} found, create one with ${styleText('blue', emptyCommand)} command`);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const plural = items.length > 1 ? 's' : '';
|
|
21
|
+
Logger.println(`🔎 Found ${items.length} ${itemName}${plural}:`);
|
|
22
|
+
Logger.println();
|
|
23
|
+
|
|
24
|
+
const groups = Object.groupBy(items, (item) => item.ownerId);
|
|
25
|
+
for (const group of Object.values(groups)) {
|
|
26
|
+
const { ownerId, ownerName } = group[0];
|
|
27
|
+
Logger.println(`• ${styleText('bold', `${ownerId} (${ownerName})`)}`);
|
|
28
|
+
for (const item of group) {
|
|
29
|
+
Logger.println(` • ${item.name || '(unnamed)'} ${styleText('grey', `(${getItemId(item)})`)}`);
|
|
30
|
+
if (getExtraLines != null) {
|
|
31
|
+
for (const line of [getExtraLines(item)].flat().filter(Boolean)) {
|
|
32
|
+
Logger.println(` ${line}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
Logger.println();
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/lib/prompts.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { confirm as confirmPrompt, input, password, select } from '@inquirer/prompts';
|
|
1
|
+
import { checkbox, confirm as confirmPrompt, input, password, select } from '@inquirer/prompts';
|
|
2
|
+
import { unwrapSchema } from './zod-utils.js';
|
|
2
3
|
|
|
3
4
|
export function promptSecret(message) {
|
|
4
5
|
return password({ message, mask: true }).catch(exitOnPromptError);
|
|
@@ -24,6 +25,27 @@ export function selectAnswer(message, choices) {
|
|
|
24
25
|
return select({ message, choices }).catch(exitOnPromptError);
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
export function promptCheckbox(message, choices) {
|
|
29
|
+
return checkbox({ message, choices }).catch(exitOnPromptError);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Prompt the user for a text value based on an option definition.
|
|
34
|
+
* Derives the prompt message from option.description and validation from option.schema.
|
|
35
|
+
* @param {import('./define-option.types.js').OptionDefinition} option
|
|
36
|
+
* @param {string} [defaultValue]
|
|
37
|
+
* @returns {Promise<string>}
|
|
38
|
+
*/
|
|
39
|
+
export function promptTextOption(option, defaultValue) {
|
|
40
|
+
const message = `${option.description}:`;
|
|
41
|
+
const innerSchema = unwrapSchema(option.schema);
|
|
42
|
+
const validate = (value) => {
|
|
43
|
+
const result = innerSchema.safeParse(value);
|
|
44
|
+
return result.success || result.error.issues[0]?.message || 'Invalid value';
|
|
45
|
+
};
|
|
46
|
+
return input({ message, default: defaultValue, validate }).catch(exitOnPromptError);
|
|
47
|
+
}
|
|
48
|
+
|
|
27
49
|
function exitOnPromptError(error) {
|
|
28
50
|
if (error instanceof Error && error.name === 'ExitPromptError') {
|
|
29
51
|
process.exit(1);
|