avo-cli 4.2.0.beta.5 → 4.3.0.beta.5
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.
- checksums.yaml +4 -4
- data/README.md +19 -6
- data/lib/avo/cli/version.rb +1 -1
- data/payload/README.md +19 -6
- data/payload/dist/base-command.js +9 -5
- data/payload/dist/commands/list.js +1 -1
- data/payload/dist/commands/logout.js +1 -1
- data/payload/dist/commands/schema.js +30 -16
- data/payload/dist/schema.js +1 -1
- data/payload/dist/table.js +2 -1
- data/payload/oclif.manifest.json +6 -4
- data/payload/package.json +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e16520471450b18019b924ef115ead5d10506a57b20604bf6c5c2e001a86ce06
|
|
4
|
+
data.tar.gz: 1cefc1c773b788d3a82694971d5b045440c7887faf8149b3a3d248c44de343af
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 58ea6fc978c6505b0812271ab03a937ad3145c15121314fa14c254e671b31fa1a7173e88842ae3b0bbdd82dbd5b185fd4b3dc5fd142c23fcf2fc7056b3604827
|
|
7
|
+
data.tar.gz: e649adb3ca3dc4c03c703172afe49ba56bde22d5e070ab1c9e5b4a944188a168db22a398a52ba1abcd4239fec5616e79fa29677694e0a5395f2166b1c8e97701
|
data/README.md
CHANGED
|
@@ -61,6 +61,18 @@ avo delete users 5 # no confirmation
|
|
|
61
61
|
Field formats, errors and exit codes are in the
|
|
62
62
|
[docs](https://docs.avohq.io/4.0/cli.html#send-the-right-value-for-a-field).
|
|
63
63
|
|
|
64
|
+
To describe the API to another tool, export what the token may call as an
|
|
65
|
+
OpenAPI 3.1 document and import the file into Swagger UI, Postman, Redocly or
|
|
66
|
+
a client generator:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
avo schema --format openapi > openapi.json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The document is built for the token that asked: a read-only token gets one
|
|
73
|
+
with no create, update or delete operations. See
|
|
74
|
+
[OpenAPI document](https://docs.avohq.io/4.0/rest-api.html#openapi-document).
|
|
75
|
+
|
|
64
76
|
<!-- commands -->
|
|
65
77
|
* [`avo create RESOURCE`](#avo-create-resource)
|
|
66
78
|
* [`avo delete RESOURCE ID`](#avo-delete-resource-id)
|
|
@@ -284,15 +296,14 @@ EXAMPLES
|
|
|
284
296
|
|
|
285
297
|
## `avo logout`
|
|
286
298
|
|
|
287
|
-
Delete the saved host and token. AVO_API_HOST, AVO_API_TOKEN and AVO_API_VERSION
|
|
299
|
+
Delete the saved host and token. AVO_API_HOST, AVO_API_TOKEN and AVO_API_VERSION set in the environment keep working
|
|
288
300
|
|
|
289
301
|
```
|
|
290
302
|
USAGE
|
|
291
303
|
$ avo logout
|
|
292
304
|
|
|
293
305
|
DESCRIPTION
|
|
294
|
-
Delete the saved host and token. AVO_API_HOST, AVO_API_TOKEN and AVO_API_VERSION
|
|
295
|
-
working
|
|
306
|
+
Delete the saved host and token. AVO_API_HOST, AVO_API_TOKEN and AVO_API_VERSION set in the environment keep working
|
|
296
307
|
|
|
297
308
|
EXAMPLES
|
|
298
309
|
$ avo logout
|
|
@@ -305,7 +316,7 @@ List the resources the API serves, or the fields of one resource on one view
|
|
|
305
316
|
```
|
|
306
317
|
USAGE
|
|
307
318
|
$ avo schema [RESOURCE] [--api-version <value>] [--host <value>] [--token <value>] [--verbose] [--format
|
|
308
|
-
table|json] [--view <value>]
|
|
319
|
+
table|json|openapi] [--view <value>]
|
|
309
320
|
|
|
310
321
|
ARGUMENTS
|
|
311
322
|
[RESOURCE] A resource by its route key; with none, the resources the API serves are listed
|
|
@@ -322,8 +333,8 @@ CONNECTION FLAGS
|
|
|
322
333
|
|
|
323
334
|
OUTPUT FLAGS
|
|
324
335
|
--format=<option> [default: table] Output format; `json` prints the body exactly as the server sent it, indented on a
|
|
325
|
-
terminal
|
|
326
|
-
<options: table|json>
|
|
336
|
+
terminal; `openapi` prints the OpenAPI document of what the token may call
|
|
337
|
+
<options: table|json|openapi>
|
|
327
338
|
--verbose Log each request and response line to stderr
|
|
328
339
|
|
|
329
340
|
DESCRIPTION
|
|
@@ -334,6 +345,8 @@ EXAMPLES
|
|
|
334
345
|
|
|
335
346
|
$ avo schema --format json
|
|
336
347
|
|
|
348
|
+
$ avo schema --format openapi > openapi.json
|
|
349
|
+
|
|
337
350
|
$ avo schema users
|
|
338
351
|
|
|
339
352
|
$ avo schema users --view show
|
data/lib/avo/cli/version.rb
CHANGED
data/payload/README.md
CHANGED
|
@@ -61,6 +61,18 @@ avo delete users 5 # no confirmation
|
|
|
61
61
|
Field formats, errors and exit codes are in the
|
|
62
62
|
[docs](https://docs.avohq.io/4.0/cli.html#send-the-right-value-for-a-field).
|
|
63
63
|
|
|
64
|
+
To describe the API to another tool, export what the token may call as an
|
|
65
|
+
OpenAPI 3.1 document and import the file into Swagger UI, Postman, Redocly or
|
|
66
|
+
a client generator:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
avo schema --format openapi > openapi.json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The document is built for the token that asked: a read-only token gets one
|
|
73
|
+
with no create, update or delete operations. See
|
|
74
|
+
[OpenAPI document](https://docs.avohq.io/4.0/rest-api.html#openapi-document).
|
|
75
|
+
|
|
64
76
|
<!-- commands -->
|
|
65
77
|
* [`avo create RESOURCE`](#avo-create-resource)
|
|
66
78
|
* [`avo delete RESOURCE ID`](#avo-delete-resource-id)
|
|
@@ -284,15 +296,14 @@ EXAMPLES
|
|
|
284
296
|
|
|
285
297
|
## `avo logout`
|
|
286
298
|
|
|
287
|
-
Delete the saved host and token. AVO_API_HOST, AVO_API_TOKEN and AVO_API_VERSION
|
|
299
|
+
Delete the saved host and token. AVO_API_HOST, AVO_API_TOKEN and AVO_API_VERSION set in the environment keep working
|
|
288
300
|
|
|
289
301
|
```
|
|
290
302
|
USAGE
|
|
291
303
|
$ avo logout
|
|
292
304
|
|
|
293
305
|
DESCRIPTION
|
|
294
|
-
Delete the saved host and token. AVO_API_HOST, AVO_API_TOKEN and AVO_API_VERSION
|
|
295
|
-
working
|
|
306
|
+
Delete the saved host and token. AVO_API_HOST, AVO_API_TOKEN and AVO_API_VERSION set in the environment keep working
|
|
296
307
|
|
|
297
308
|
EXAMPLES
|
|
298
309
|
$ avo logout
|
|
@@ -305,7 +316,7 @@ List the resources the API serves, or the fields of one resource on one view
|
|
|
305
316
|
```
|
|
306
317
|
USAGE
|
|
307
318
|
$ avo schema [RESOURCE] [--api-version <value>] [--host <value>] [--token <value>] [--verbose] [--format
|
|
308
|
-
table|json] [--view <value>]
|
|
319
|
+
table|json|openapi] [--view <value>]
|
|
309
320
|
|
|
310
321
|
ARGUMENTS
|
|
311
322
|
[RESOURCE] A resource by its route key; with none, the resources the API serves are listed
|
|
@@ -322,8 +333,8 @@ CONNECTION FLAGS
|
|
|
322
333
|
|
|
323
334
|
OUTPUT FLAGS
|
|
324
335
|
--format=<option> [default: table] Output format; `json` prints the body exactly as the server sent it, indented on a
|
|
325
|
-
terminal
|
|
326
|
-
<options: table|json>
|
|
336
|
+
terminal; `openapi` prints the OpenAPI document of what the token may call
|
|
337
|
+
<options: table|json|openapi>
|
|
327
338
|
--verbose Log each request and response line to stderr
|
|
328
339
|
|
|
329
340
|
DESCRIPTION
|
|
@@ -334,6 +345,8 @@ EXAMPLES
|
|
|
334
345
|
|
|
335
346
|
$ avo schema --format json
|
|
336
347
|
|
|
348
|
+
$ avo schema --format openapi > openapi.json
|
|
349
|
+
|
|
337
350
|
$ avo schema users
|
|
338
351
|
|
|
339
352
|
$ avo schema users --view show
|
|
@@ -47,16 +47,20 @@ export class BaseCommand extends ConnectionCommand {
|
|
|
47
47
|
options: ['table', 'json'],
|
|
48
48
|
}),
|
|
49
49
|
};
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
printedJson(flags, response) {
|
|
53
|
-
if (flags.format !== 'json')
|
|
54
|
-
return false;
|
|
50
|
+
// The body as the server sent it, whatever the flag that asked for it.
|
|
51
|
+
printBody(response) {
|
|
55
52
|
const body = terminal().tty && response.body !== undefined ? indented(response.body) : response.raw;
|
|
56
53
|
// `this.log` always appends a newline, which would double the one a body ends with.
|
|
57
54
|
process.stdout.write(body);
|
|
58
55
|
if (!body.endsWith('\n'))
|
|
59
56
|
process.stdout.write('\n');
|
|
57
|
+
}
|
|
58
|
+
// Answers whether it printed. The record commands call it ahead of their shape check, so json relays a body they
|
|
59
|
+
// could not table; `schema` cannot, since its loaders check the shape as they fetch.
|
|
60
|
+
printedJson(flags, response) {
|
|
61
|
+
if (flags.format !== 'json')
|
|
62
|
+
return false;
|
|
63
|
+
this.printBody(response);
|
|
60
64
|
return true;
|
|
61
65
|
}
|
|
62
66
|
}
|
|
@@ -34,7 +34,7 @@ export default class List extends RecordCommand {
|
|
|
34
34
|
if (this.printedJson(flags, response))
|
|
35
35
|
return;
|
|
36
36
|
const { pagination, records } = parseRecords(response);
|
|
37
|
-
const layout = records.length === 0 ? undefined : await layoutTable(records, { columns: flags.fields });
|
|
37
|
+
const layout = records.length === 0 ? undefined : await layoutTable(records, { columns: flags.fields, overflow: 'wrap' });
|
|
38
38
|
this.log(layout?.text ?? `No ${args.resource}.`);
|
|
39
39
|
if (typeof pagination?.page !== 'number')
|
|
40
40
|
return;
|
|
@@ -2,7 +2,7 @@ import { Command } from '@oclif/core';
|
|
|
2
2
|
import { fileIn, remove } from '../store.js';
|
|
3
3
|
// Not a `BaseCommand`: it makes no request, so the connection flags would be on `--help` doing nothing.
|
|
4
4
|
export default class Logout extends Command {
|
|
5
|
-
static description = 'Delete the saved host and token. AVO_API_HOST, AVO_API_TOKEN and AVO_API_VERSION
|
|
5
|
+
static description = 'Delete the saved host and token. AVO_API_HOST, AVO_API_TOKEN and AVO_API_VERSION set in the environment keep working';
|
|
6
6
|
static examples = ['<%= config.bin %> logout'];
|
|
7
7
|
async run() {
|
|
8
8
|
// Nothing to read from it: parsing is what refuses a flag this command does not have, instead of ignoring it.
|
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../base-command.js';
|
|
3
|
-
import { CliError } from '../errors.js';
|
|
3
|
+
import { CliError, wrongShape } from '../errors.js';
|
|
4
4
|
import { DEFAULT_VIEW, fetchResourceSchema, loadSchema } from '../schema.js';
|
|
5
5
|
import { hiddenColumnsNote, layoutTable } from '../table.js';
|
|
6
|
-
|
|
7
|
-
// (the server could not tell) stays, and a row left with nothing prints `-` rather than `{}`. `--format json` keeps all.
|
|
8
|
-
function shownOptions(options) {
|
|
9
|
-
if (options?.required !== false)
|
|
10
|
-
return options;
|
|
11
|
-
const rest = { ...options };
|
|
12
|
-
delete rest.required;
|
|
13
|
-
return Object.keys(rest).length === 0 ? undefined : rest;
|
|
14
|
-
}
|
|
6
|
+
import { paint, terminal } from '../terminal.js';
|
|
15
7
|
// One verb, two answers: with no resource, the resources the API serves and
|
|
16
8
|
// how far the token reaches on each; with one, its fields on one view.
|
|
17
9
|
export default class Schema extends BaseCommand {
|
|
@@ -24,11 +16,18 @@ export default class Schema extends BaseCommand {
|
|
|
24
16
|
static examples = [
|
|
25
17
|
'<%= config.bin %> schema',
|
|
26
18
|
'<%= config.bin %> schema --format json',
|
|
19
|
+
'<%= config.bin %> schema --format openapi > openapi.json',
|
|
27
20
|
'<%= config.bin %> schema users',
|
|
28
21
|
'<%= config.bin %> schema users --view show',
|
|
29
22
|
'<%= config.bin %> schema --host http://localhost:3000/api --token avo_...',
|
|
30
23
|
];
|
|
31
24
|
static flags = {
|
|
25
|
+
// `openapi` is this verb's alone: the document describes the whole API, so no record command could print it.
|
|
26
|
+
format: {
|
|
27
|
+
...BaseCommand.baseFlags.format,
|
|
28
|
+
description: `${BaseCommand.baseFlags.format.description}; \`openapi\` prints the OpenAPI document of what the token may call`,
|
|
29
|
+
options: ['table', 'json', 'openapi'],
|
|
30
|
+
},
|
|
32
31
|
// No `options`: the view names are the API's to know, not this flag's.
|
|
33
32
|
view: Flags.string({
|
|
34
33
|
description: `Which view's fields: create/update list what a write may send, with their field_options; index/show what a record reads back (default: ${DEFAULT_VIEW})`,
|
|
@@ -38,11 +37,23 @@ export default class Schema extends BaseCommand {
|
|
|
38
37
|
async run() {
|
|
39
38
|
const { args, flags } = await this.parse(Schema);
|
|
40
39
|
const { resource } = args;
|
|
41
|
-
//
|
|
40
|
+
// Usage errors, so they come before the host and token are looked for.
|
|
41
|
+
if (flags.format === 'openapi' && (resource !== undefined || flags.view)) {
|
|
42
|
+
throw CliError.usage('--format openapi takes no resource and no --view: the document is the whole API.');
|
|
43
|
+
}
|
|
42
44
|
if (resource === undefined && flags.view) {
|
|
43
45
|
throw CliError.usage('--view needs a resource: `schema users --view show`.');
|
|
44
46
|
}
|
|
45
47
|
const client = this.client(flags);
|
|
48
|
+
if (flags.format === 'openapi') {
|
|
49
|
+
const response = await client.get(['_openapi']);
|
|
50
|
+
// A wrong host can answer 200 with a web page, which `> openapi.json` would save as the document.
|
|
51
|
+
if (response.body?.openapi === undefined) {
|
|
52
|
+
throw wrongShape(response.url, 'an OpenAPI document');
|
|
53
|
+
}
|
|
54
|
+
this.printBody(response);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
46
57
|
if (resource === undefined) {
|
|
47
58
|
await this.listResources(client, flags);
|
|
48
59
|
return;
|
|
@@ -52,18 +63,21 @@ export default class Schema extends BaseCommand {
|
|
|
52
63
|
if (this.printedJson(flags, response))
|
|
53
64
|
return;
|
|
54
65
|
const { fields } = schema;
|
|
66
|
+
const view = schema.view ?? asked;
|
|
55
67
|
if (fields.length === 0) {
|
|
56
|
-
this.log(`No fields on the ${
|
|
68
|
+
this.log(`No fields on the ${view} view of ${resource}.`);
|
|
57
69
|
return;
|
|
58
70
|
}
|
|
59
71
|
// Headed `field id`, `field type`, `field options`, as a person reads them; `--format json` keeps the keys.
|
|
60
72
|
// Wrapped, never cut: the CLI checks nothing against the view (decisions.md), so a truncated cell is the only
|
|
61
73
|
// place a reader could have seen the `options` before sending.
|
|
62
|
-
const rows = fields.map((field) =>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
74
|
+
const rows = fields.map((field) => Object.fromEntries(Object.entries(field).map(([key, value]) => [key.replaceAll('_', ' '), value])));
|
|
75
|
+
const term = terminal();
|
|
76
|
+
// Writes point at what a record reads back, reads at what a write may send: the view a reader most likely wants next.
|
|
77
|
+
const next = view === 'create' || view === 'update' ? 'show' : 'create';
|
|
78
|
+
this.log(`${paint(term, 'bold', resource)} ${paint(term, 'cyan', `${view} view`)}`);
|
|
66
79
|
await this.printTable(rows, { overflow: 'wrap' });
|
|
80
|
+
this.log(paint(term, 'dim', `Try: ${this.config.bin} schema ${resource} --view ${next}`));
|
|
67
81
|
}
|
|
68
82
|
async listResources(client, flags) {
|
|
69
83
|
const { response, schema } = await loadSchema(client);
|
data/payload/dist/schema.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { wrongShape } from './errors.js';
|
|
2
2
|
// The one view name held here; every other is the server's to know, and a list would refuse a view a newer API serves.
|
|
3
|
-
// It is sent explicitly
|
|
3
|
+
// It is sent explicitly, so what comes back is never the server's default under another name.
|
|
4
4
|
export const DEFAULT_VIEW = 'create';
|
|
5
5
|
// `source` names where the payload came from, so a shape error can say what did not answer.
|
|
6
6
|
function parseSchema(payload, source) {
|
data/payload/dist/table.js
CHANGED
|
@@ -54,7 +54,8 @@ export function formatCell(value) {
|
|
|
54
54
|
}
|
|
55
55
|
if (typeof value === 'object')
|
|
56
56
|
return JSON.stringify(value);
|
|
57
|
-
|
|
57
|
+
// A terminal draws a tab out to the next stop and acts on an ESC, so either one moves every border after the cell.
|
|
58
|
+
return String(value).replaceAll(/\s*\p{Cc}[\s\p{Cc}]*/gu, ' ').trim() || '-';
|
|
58
59
|
}
|
|
59
60
|
// Colour only where it carries something: a name is what the eye looks for, a boolean is scanned down a column, and
|
|
60
61
|
// an empty cell should fade behind real data. Read off the value, not the cell, so a string `"true"` stays plain.
|
data/payload/oclif.manifest.json
CHANGED
|
@@ -548,7 +548,7 @@
|
|
|
548
548
|
"logout": {
|
|
549
549
|
"aliases": [],
|
|
550
550
|
"args": {},
|
|
551
|
-
"description": "Delete the saved host and token. AVO_API_HOST, AVO_API_TOKEN and AVO_API_VERSION
|
|
551
|
+
"description": "Delete the saved host and token. AVO_API_HOST, AVO_API_TOKEN and AVO_API_VERSION set in the environment keep working",
|
|
552
552
|
"examples": [
|
|
553
553
|
"<%= config.bin %> logout"
|
|
554
554
|
],
|
|
@@ -580,6 +580,7 @@
|
|
|
580
580
|
"examples": [
|
|
581
581
|
"<%= config.bin %> schema",
|
|
582
582
|
"<%= config.bin %> schema --format json",
|
|
583
|
+
"<%= config.bin %> schema --format openapi > openapi.json",
|
|
583
584
|
"<%= config.bin %> schema users",
|
|
584
585
|
"<%= config.bin %> schema users --view show",
|
|
585
586
|
"<%= config.bin %> schema --host http://localhost:3000/api --token avo_..."
|
|
@@ -620,7 +621,7 @@
|
|
|
620
621
|
"type": "boolean"
|
|
621
622
|
},
|
|
622
623
|
"format": {
|
|
623
|
-
"description": "Output format; `json` prints the body exactly as the server sent it, indented on a terminal",
|
|
624
|
+
"description": "Output format; `json` prints the body exactly as the server sent it, indented on a terminal; `openapi` prints the OpenAPI document of what the token may call",
|
|
624
625
|
"helpGroup": "OUTPUT",
|
|
625
626
|
"name": "format",
|
|
626
627
|
"default": "table",
|
|
@@ -628,7 +629,8 @@
|
|
|
628
629
|
"multiple": false,
|
|
629
630
|
"options": [
|
|
630
631
|
"table",
|
|
631
|
-
"json"
|
|
632
|
+
"json",
|
|
633
|
+
"openapi"
|
|
632
634
|
],
|
|
633
635
|
"type": "option"
|
|
634
636
|
},
|
|
@@ -786,5 +788,5 @@
|
|
|
786
788
|
]
|
|
787
789
|
}
|
|
788
790
|
},
|
|
789
|
-
"version": "4.
|
|
791
|
+
"version": "4.3.0.beta.5"
|
|
790
792
|
}
|
data/payload/package.json
CHANGED