@hardfin/cli 0.0.2-dev.5 → 0.0.2-dev.6
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 +65 -1
- package/dist/cli.js +1168 -6
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -7,7 +7,8 @@ It is built for people at a terminal and for agents that call Hardfin on their b
|
|
|
7
7
|
|
|
8
8
|
## Status
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
The CLI is early. `hardfin api` and the generated commands work against an API key, and
|
|
11
|
+
`hardfin login` is not built yet.
|
|
11
12
|
|
|
12
13
|
## Install
|
|
13
14
|
|
|
@@ -21,6 +22,69 @@ Previews of unreleased work are published from the `dev` branch.
|
|
|
21
22
|
npm install -g @hardfin/cli@dev
|
|
22
23
|
```
|
|
23
24
|
|
|
25
|
+
## The command surface
|
|
26
|
+
|
|
27
|
+
Most commands are generated from the published API document rather than written by hand.
|
|
28
|
+
|
|
29
|
+
| Piece | Holds |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| `hardfin api` | A call to any endpoint, written by hand, and the escape hatch when no generated command fits |
|
|
32
|
+
| `src/command/surface.generated.ts` | Every endpoint as a command, rewritten by the generator |
|
|
33
|
+
| `surface-overrides.json` | The operations whose generated name is wrong |
|
|
34
|
+
| `scripts/generate-surface.mjs` | The generator |
|
|
35
|
+
|
|
36
|
+
### How an endpoint becomes a command
|
|
37
|
+
|
|
38
|
+
The first path segment is the noun. Every later literal segment nests under it. The method
|
|
39
|
+
and the shape of the last segment decide the verb.
|
|
40
|
+
|
|
41
|
+
| Endpoint | Command |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `GET /asset` | `hardfin asset list` |
|
|
44
|
+
| `POST /asset` | `hardfin asset create` |
|
|
45
|
+
| `GET /asset/{assetKey}` | `hardfin asset get <assetKey>` |
|
|
46
|
+
| `PATCH /asset/{assetKey}` | `hardfin asset update <assetKey>` |
|
|
47
|
+
| `PATCH /asset/{assetKey}/accounting` | `hardfin asset accounting update <assetKey>` |
|
|
48
|
+
|
|
49
|
+
A path parameter becomes a positional argument. A query parameter becomes a flag, named in
|
|
50
|
+
kebab case, and an array parameter becomes a flag you repeat. An enum parameter carries its
|
|
51
|
+
values, so a wrong value fails locally with exit code 2 rather than at the API.
|
|
52
|
+
|
|
53
|
+
### Overrides
|
|
54
|
+
|
|
55
|
+
Two endpoints sometimes want one name. `DELETE /asset/{assetKey}/ownership` and
|
|
56
|
+
`DELETE /asset/{assetKey}/ownership/{segmentKey}` both generate `asset ownership delete`, so
|
|
57
|
+
the generator stops and names the pair.
|
|
58
|
+
|
|
59
|
+
Settle it in `surface-overrides.json`, keyed by `operationId`:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"assetClearAssetActiveOwnership": {
|
|
64
|
+
"command": ["asset", "ownership", "clear"],
|
|
65
|
+
"summary": "Clear the ownership an asset holds today"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
An override for an `operationId` the document no longer publishes fails the generator. That
|
|
71
|
+
is deliberate, because a silently dropped override renames a command nobody meant to rename.
|
|
72
|
+
|
|
73
|
+
### Regenerating
|
|
74
|
+
|
|
75
|
+
The Surface workflow runs each weekday, reads `reference/core.openapi.yaml` from the
|
|
76
|
+
`hardfinhq/api-spec` repository through a read-only deploy key, and opens a pull request when
|
|
77
|
+
the generated file changes. It needs the `API_SPEC_READ_DEPLOY_KEY` secret.
|
|
78
|
+
|
|
79
|
+
Run it by hand against a local document:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
npm run generate-surface -- ../api-spec/reference/core.openapi.yaml
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The document is bundled, meaning its external files are inlined, but `$ref` pointers within
|
|
86
|
+
it remain. The generator follows those pointers itself.
|
|
87
|
+
|
|
24
88
|
## Releasing
|
|
25
89
|
|
|
26
90
|
This section is for anyone who merges a pull request in this repository. It tells you where
|
package/dist/cli.js
CHANGED
|
@@ -282,14 +282,14 @@ async function runApi(input) {
|
|
|
282
282
|
writeFailure("a path is required, such as /customer", input.isJSON);
|
|
283
283
|
return ExitCode.USAGE;
|
|
284
284
|
}
|
|
285
|
-
const query = toQuery(input.flags["field"]);
|
|
285
|
+
const query = toQuery$1(input.flags["field"]);
|
|
286
286
|
if (query === void 0) {
|
|
287
287
|
writeFailure("each --field is key=value, such as -f limit=50", input.isJSON);
|
|
288
288
|
return ExitCode.USAGE;
|
|
289
289
|
}
|
|
290
290
|
let body;
|
|
291
291
|
if (typeof input.flags["input"] === "string") {
|
|
292
|
-
body = toBody(input.flags["input"]);
|
|
292
|
+
body = toBody$1(input.flags["input"]);
|
|
293
293
|
if (body === void 0) {
|
|
294
294
|
writeFailure(`${input.flags["input"]} does not hold JSON`, input.isJSON);
|
|
295
295
|
return ExitCode.USAGE;
|
|
@@ -315,7 +315,7 @@ async function runApi(input) {
|
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
317
|
/** toQuery folds the repeated --field flags into query parameters. */
|
|
318
|
-
function toQuery(fields) {
|
|
318
|
+
function toQuery$1(fields) {
|
|
319
319
|
const query = new URLSearchParams();
|
|
320
320
|
for (const field of Array.isArray(fields) ? fields : []) {
|
|
321
321
|
const split = field.indexOf("=");
|
|
@@ -324,6 +324,100 @@ function toQuery(fields) {
|
|
|
324
324
|
}
|
|
325
325
|
return query;
|
|
326
326
|
}
|
|
327
|
+
function toBody$1(source) {
|
|
328
|
+
const text = source === "-" ? readFileSync(0, "utf8") : readFileSync(source, "utf8");
|
|
329
|
+
try {
|
|
330
|
+
return JSON.parse(text);
|
|
331
|
+
} catch {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/command/operation.ts
|
|
337
|
+
const INPUT_FLAG = {
|
|
338
|
+
name: "input",
|
|
339
|
+
description: "A file holding the JSON request body, or - for stdin",
|
|
340
|
+
valueName: "file",
|
|
341
|
+
schema: z.string()
|
|
342
|
+
};
|
|
343
|
+
const JSON_FLAG = {
|
|
344
|
+
name: "json",
|
|
345
|
+
description: "Print machine-readable output, which is the default when stdout is not a terminal",
|
|
346
|
+
schema: z.boolean()
|
|
347
|
+
};
|
|
348
|
+
/** defineOperation turns one endpoint into the command that calls it. */
|
|
349
|
+
function defineOperation(operation) {
|
|
350
|
+
const flags = [...operation.queryFlags, JSON_FLAG];
|
|
351
|
+
if (operation.takesBody) flags.splice(flags.length - 1, 0, INPUT_FLAG);
|
|
352
|
+
return {
|
|
353
|
+
name: operation.name,
|
|
354
|
+
summary: operation.summary,
|
|
355
|
+
description: operation.description ?? operation.summary,
|
|
356
|
+
arguments: operation.pathParameters,
|
|
357
|
+
flags,
|
|
358
|
+
examples: [],
|
|
359
|
+
run: (input) => runOperation(operation, input)
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
async function runOperation(operation, input) {
|
|
363
|
+
const apiKey = toApiKey();
|
|
364
|
+
if (!apiKey) {
|
|
365
|
+
writeFailure("not authenticated. Set HARDFIN_API_KEY to an API key for your organization", input.isJSON);
|
|
366
|
+
return ExitCode.NOT_AUTHENTICATED;
|
|
367
|
+
}
|
|
368
|
+
const path = toPath(operation, input.args);
|
|
369
|
+
if (path === void 0) {
|
|
370
|
+
writeFailure(`this command takes ${operation.pathParameters.length} argument(s)`, input.isJSON);
|
|
371
|
+
return ExitCode.USAGE;
|
|
372
|
+
}
|
|
373
|
+
let body;
|
|
374
|
+
if (typeof input.flags["input"] === "string") {
|
|
375
|
+
body = toBody(input.flags["input"]);
|
|
376
|
+
if (body === void 0) {
|
|
377
|
+
writeFailure(`${input.flags["input"]} does not hold JSON`, input.isJSON);
|
|
378
|
+
return ExitCode.USAGE;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
try {
|
|
382
|
+
writeData((await request({
|
|
383
|
+
apiUrl: toApiUrl(),
|
|
384
|
+
apiKey,
|
|
385
|
+
method: operation.method,
|
|
386
|
+
path,
|
|
387
|
+
query: toQuery(operation, input.flags),
|
|
388
|
+
body
|
|
389
|
+
})).data);
|
|
390
|
+
return ExitCode.OK;
|
|
391
|
+
} catch (error) {
|
|
392
|
+
if (error instanceof RequestFailure) {
|
|
393
|
+
writeFailure(error.message, input.isJSON, error.errors, error.requestId);
|
|
394
|
+
return error.status === 401 ? ExitCode.NOT_AUTHENTICATED : ExitCode.ERROR;
|
|
395
|
+
}
|
|
396
|
+
writeFailure(error instanceof Error ? error.message : String(error), input.isJSON);
|
|
397
|
+
return ExitCode.ERROR;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/** toPath fills the path template from the positional arguments, in order. */
|
|
401
|
+
function toPath(operation, args) {
|
|
402
|
+
if (args.length !== operation.pathParameters.length) return;
|
|
403
|
+
let path = operation.path;
|
|
404
|
+
for (const [index, parameter] of operation.pathParameters.entries()) path = path.replace(`{${parameter.name}}`, encodeURIComponent(args[index] ?? ""));
|
|
405
|
+
return path;
|
|
406
|
+
}
|
|
407
|
+
/** toQuery carries only the flags this invocation actually set. */
|
|
408
|
+
function toQuery(operation, flags) {
|
|
409
|
+
const query = new URLSearchParams();
|
|
410
|
+
for (const flag of operation.queryFlags) {
|
|
411
|
+
const value = flags[toOptionKey(flag.name)];
|
|
412
|
+
if (value === void 0) continue;
|
|
413
|
+
for (const entry of Array.isArray(value) ? value : [value]) query.append(flag.queryName, String(entry));
|
|
414
|
+
}
|
|
415
|
+
return query;
|
|
416
|
+
}
|
|
417
|
+
/** toOptionKey names the parsed flag, which the parser reports in camel case. */
|
|
418
|
+
function toOptionKey(name) {
|
|
419
|
+
return name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
420
|
+
}
|
|
327
421
|
function toBody(source) {
|
|
328
422
|
const text = source === "-" ? readFileSync(0, "utf8") : readFileSync(source, "utf8");
|
|
329
423
|
try {
|
|
@@ -335,7 +429,1068 @@ function toBody(source) {
|
|
|
335
429
|
//#endregion
|
|
336
430
|
//#region src/command/commands.ts
|
|
337
431
|
/** commands is every command the CLI offers, and drives help and the agent guide. */
|
|
338
|
-
const commands = [
|
|
432
|
+
const commands = [
|
|
433
|
+
...[
|
|
434
|
+
{
|
|
435
|
+
name: "asset",
|
|
436
|
+
summary: "Asset commands",
|
|
437
|
+
arguments: [],
|
|
438
|
+
flags: [],
|
|
439
|
+
examples: [],
|
|
440
|
+
subcommands: [
|
|
441
|
+
defineOperation({
|
|
442
|
+
name: "list",
|
|
443
|
+
summary: "Get asset listing",
|
|
444
|
+
method: "GET",
|
|
445
|
+
path: "/asset",
|
|
446
|
+
pathParameters: [],
|
|
447
|
+
queryFlags: [
|
|
448
|
+
{
|
|
449
|
+
name: "page",
|
|
450
|
+
queryName: "page",
|
|
451
|
+
description: "The page to return, starting at 1",
|
|
452
|
+
valueName: "number",
|
|
453
|
+
schema: z.coerce.number()
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
name: "limit",
|
|
457
|
+
queryName: "limit",
|
|
458
|
+
description: "The number of records per page, from 1 to 100",
|
|
459
|
+
valueName: "number",
|
|
460
|
+
schema: z.coerce.number()
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
name: "sort-by",
|
|
464
|
+
queryName: "sortBy",
|
|
465
|
+
description: "The field to sort by",
|
|
466
|
+
valueName: "value",
|
|
467
|
+
schema: z.enum([
|
|
468
|
+
"serial",
|
|
469
|
+
"project",
|
|
470
|
+
"item",
|
|
471
|
+
"location",
|
|
472
|
+
"owner",
|
|
473
|
+
"activity"
|
|
474
|
+
])
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
name: "sort-order",
|
|
478
|
+
queryName: "sortOrder",
|
|
479
|
+
description: "The sort direction",
|
|
480
|
+
valueName: "value",
|
|
481
|
+
schema: z.enum(["ASC", "DESC"])
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
name: "archived",
|
|
485
|
+
queryName: "archived",
|
|
486
|
+
description: "Whether to return unarchived records, archived records, or all of them",
|
|
487
|
+
valueName: "value",
|
|
488
|
+
schema: z.enum([
|
|
489
|
+
"all",
|
|
490
|
+
"false",
|
|
491
|
+
"true"
|
|
492
|
+
])
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
name: "search-query",
|
|
496
|
+
queryName: "searchQuery",
|
|
497
|
+
description: "Text to match against the serial, description, item, project, owner, and location, ignored when shorter than three characters",
|
|
498
|
+
valueName: "value",
|
|
499
|
+
schema: z.string()
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
name: "for-asset-id",
|
|
503
|
+
queryName: "forAssetId",
|
|
504
|
+
description: "The IDs of the assets to list",
|
|
505
|
+
valueName: "value",
|
|
506
|
+
repeatable: true,
|
|
507
|
+
schema: z.array(z.string())
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
name: "for-asset-key",
|
|
511
|
+
queryName: "forAssetKey",
|
|
512
|
+
description: "The keys of the assets to list",
|
|
513
|
+
valueName: "value",
|
|
514
|
+
repeatable: true,
|
|
515
|
+
schema: z.array(z.string())
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
name: "for-customer",
|
|
519
|
+
queryName: "forCustomer",
|
|
520
|
+
description: "The IDs of the customers whose assets to list",
|
|
521
|
+
valueName: "value",
|
|
522
|
+
repeatable: true,
|
|
523
|
+
schema: z.array(z.string())
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
name: "for-item",
|
|
527
|
+
queryName: "forItem",
|
|
528
|
+
description: "The IDs of the items whose assets to list",
|
|
529
|
+
valueName: "value",
|
|
530
|
+
repeatable: true,
|
|
531
|
+
schema: z.array(z.string())
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: "at-site",
|
|
535
|
+
queryName: "atSite",
|
|
536
|
+
description: "The IDs of the locations whose assets to list",
|
|
537
|
+
valueName: "value",
|
|
538
|
+
repeatable: true,
|
|
539
|
+
schema: z.array(z.string())
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
name: "at-customer-sites",
|
|
543
|
+
queryName: "atCustomerSites",
|
|
544
|
+
description: "The IDs of the customers whose sites to list assets at",
|
|
545
|
+
valueName: "value",
|
|
546
|
+
repeatable: true,
|
|
547
|
+
schema: z.array(z.string())
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
name: "with-functional-statuses",
|
|
551
|
+
queryName: "withFunctionalStatuses",
|
|
552
|
+
description: "The functional statuses to list, where SCRAPPED lists scrapped assets",
|
|
553
|
+
valueName: "value",
|
|
554
|
+
repeatable: true,
|
|
555
|
+
schema: z.array(z.enum([
|
|
556
|
+
"FUNCTIONAL",
|
|
557
|
+
"NEEDS_REVIEW",
|
|
558
|
+
"NON-FUNCTIONAL",
|
|
559
|
+
"SCRAPPED"
|
|
560
|
+
]))
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
name: "with-transit-statuses",
|
|
564
|
+
queryName: "withTransitStatuses",
|
|
565
|
+
description: "The transit statuses to list",
|
|
566
|
+
valueName: "value",
|
|
567
|
+
repeatable: true,
|
|
568
|
+
schema: z.array(z.enum([
|
|
569
|
+
"IN_TRANSIT",
|
|
570
|
+
"IN_TRANSIT_TO_FIELD",
|
|
571
|
+
"IN_TRANSIT_TO_INVENTORY",
|
|
572
|
+
"NOT_IN_TRANSIT"
|
|
573
|
+
]))
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
name: "with-inventory-statuses",
|
|
577
|
+
queryName: "withInventoryStatuses",
|
|
578
|
+
description: "In_inventory, not_in_inventory, or both, which filters only when one is sent",
|
|
579
|
+
valueName: "value",
|
|
580
|
+
repeatable: true,
|
|
581
|
+
schema: z.array(z.string())
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
name: "with-location-company-type",
|
|
585
|
+
queryName: "withLocationCompanyType",
|
|
586
|
+
description: "Customer, manufacturer, or both, for assets at customer sites or your own, which filters only when one is sent",
|
|
587
|
+
valueName: "value",
|
|
588
|
+
repeatable: true,
|
|
589
|
+
schema: z.array(z.string())
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
name: "with-project-status",
|
|
593
|
+
queryName: "withProjectStatus",
|
|
594
|
+
description: "The project assignments to list: upcoming, active, past, or none",
|
|
595
|
+
valueName: "value",
|
|
596
|
+
repeatable: true,
|
|
597
|
+
schema: z.array(z.string())
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
name: "with-owner",
|
|
601
|
+
queryName: "withOwner",
|
|
602
|
+
description: "Manufacturer for assets your organization owns, customer for assets customers own, or both",
|
|
603
|
+
valueName: "value",
|
|
604
|
+
repeatable: true,
|
|
605
|
+
schema: z.array(z.string())
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
name: "for-project",
|
|
609
|
+
queryName: "forProject",
|
|
610
|
+
description: "The IDs of the projects whose assets to list",
|
|
611
|
+
valueName: "value",
|
|
612
|
+
repeatable: true,
|
|
613
|
+
schema: z.array(z.string())
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
name: "scrapped",
|
|
617
|
+
queryName: "scrapped",
|
|
618
|
+
description: "Whether to list unscrapped assets, scrapped assets, or all of them",
|
|
619
|
+
valueName: "value",
|
|
620
|
+
schema: z.enum([
|
|
621
|
+
"all",
|
|
622
|
+
"false",
|
|
623
|
+
"true"
|
|
624
|
+
])
|
|
625
|
+
}
|
|
626
|
+
],
|
|
627
|
+
takesBody: false
|
|
628
|
+
}),
|
|
629
|
+
defineOperation({
|
|
630
|
+
name: "create",
|
|
631
|
+
summary: "Create asset",
|
|
632
|
+
method: "POST",
|
|
633
|
+
path: "/asset",
|
|
634
|
+
pathParameters: [],
|
|
635
|
+
queryFlags: [],
|
|
636
|
+
takesBody: true
|
|
637
|
+
}),
|
|
638
|
+
defineOperation({
|
|
639
|
+
name: "get",
|
|
640
|
+
summary: "Get asset",
|
|
641
|
+
method: "GET",
|
|
642
|
+
path: "/asset/{assetKey}",
|
|
643
|
+
pathParameters: [{
|
|
644
|
+
name: "assetKey",
|
|
645
|
+
description: "The asset's key",
|
|
646
|
+
required: true
|
|
647
|
+
}],
|
|
648
|
+
queryFlags: [],
|
|
649
|
+
takesBody: false
|
|
650
|
+
}),
|
|
651
|
+
defineOperation({
|
|
652
|
+
name: "update",
|
|
653
|
+
summary: "Patch asset",
|
|
654
|
+
method: "PATCH",
|
|
655
|
+
path: "/asset/{assetKey}",
|
|
656
|
+
pathParameters: [{
|
|
657
|
+
name: "assetKey",
|
|
658
|
+
description: "The asset's key",
|
|
659
|
+
required: true
|
|
660
|
+
}],
|
|
661
|
+
queryFlags: [],
|
|
662
|
+
takesBody: true
|
|
663
|
+
}),
|
|
664
|
+
{
|
|
665
|
+
name: "accounting",
|
|
666
|
+
summary: "Accounting commands",
|
|
667
|
+
arguments: [],
|
|
668
|
+
flags: [],
|
|
669
|
+
examples: [],
|
|
670
|
+
subcommands: [defineOperation({
|
|
671
|
+
name: "update",
|
|
672
|
+
summary: "Update asset accounting",
|
|
673
|
+
method: "PATCH",
|
|
674
|
+
path: "/asset/{assetKey}/accounting",
|
|
675
|
+
pathParameters: [{
|
|
676
|
+
name: "assetKey",
|
|
677
|
+
description: "The asset's key",
|
|
678
|
+
required: true
|
|
679
|
+
}],
|
|
680
|
+
queryFlags: [],
|
|
681
|
+
takesBody: true
|
|
682
|
+
}), {
|
|
683
|
+
name: "in-service-management",
|
|
684
|
+
summary: "In service management commands",
|
|
685
|
+
arguments: [],
|
|
686
|
+
flags: [],
|
|
687
|
+
examples: [],
|
|
688
|
+
subcommands: [defineOperation({
|
|
689
|
+
name: "update",
|
|
690
|
+
summary: "Toggle in service date management",
|
|
691
|
+
method: "PATCH",
|
|
692
|
+
path: "/asset/{assetKey}/accounting/in-service-management",
|
|
693
|
+
pathParameters: [{
|
|
694
|
+
name: "assetKey",
|
|
695
|
+
description: "The asset's key",
|
|
696
|
+
required: true
|
|
697
|
+
}],
|
|
698
|
+
queryFlags: [],
|
|
699
|
+
takesBody: true
|
|
700
|
+
})]
|
|
701
|
+
}]
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
name: "cost-adjustment",
|
|
705
|
+
summary: "Cost adjustment commands",
|
|
706
|
+
arguments: [],
|
|
707
|
+
flags: [],
|
|
708
|
+
examples: [],
|
|
709
|
+
subcommands: [defineOperation({
|
|
710
|
+
name: "create",
|
|
711
|
+
summary: "Create asset cost adjustment",
|
|
712
|
+
method: "POST",
|
|
713
|
+
path: "/asset/{assetKey}/cost-adjustment",
|
|
714
|
+
pathParameters: [{
|
|
715
|
+
name: "assetKey",
|
|
716
|
+
description: "The asset's key",
|
|
717
|
+
required: true
|
|
718
|
+
}],
|
|
719
|
+
queryFlags: [],
|
|
720
|
+
takesBody: true
|
|
721
|
+
})]
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
name: "file",
|
|
725
|
+
summary: "File commands",
|
|
726
|
+
arguments: [],
|
|
727
|
+
flags: [],
|
|
728
|
+
examples: [],
|
|
729
|
+
subcommands: [defineOperation({
|
|
730
|
+
name: "list",
|
|
731
|
+
summary: "Get asset files",
|
|
732
|
+
method: "GET",
|
|
733
|
+
path: "/asset/{assetKey}/file",
|
|
734
|
+
pathParameters: [{
|
|
735
|
+
name: "assetKey",
|
|
736
|
+
description: "The asset's key",
|
|
737
|
+
required: true
|
|
738
|
+
}],
|
|
739
|
+
queryFlags: [],
|
|
740
|
+
takesBody: false
|
|
741
|
+
}), defineOperation({
|
|
742
|
+
name: "delete",
|
|
743
|
+
summary: "Delete asset file",
|
|
744
|
+
method: "DELETE",
|
|
745
|
+
path: "/asset/{assetKey}/file/{fileKey}",
|
|
746
|
+
pathParameters: [{
|
|
747
|
+
name: "assetKey",
|
|
748
|
+
description: "The asset's key",
|
|
749
|
+
required: true
|
|
750
|
+
}, {
|
|
751
|
+
name: "fileKey",
|
|
752
|
+
description: "The file's key",
|
|
753
|
+
required: true
|
|
754
|
+
}],
|
|
755
|
+
queryFlags: [],
|
|
756
|
+
takesBody: false
|
|
757
|
+
})]
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
name: "ownership",
|
|
761
|
+
summary: "Ownership commands",
|
|
762
|
+
arguments: [],
|
|
763
|
+
flags: [],
|
|
764
|
+
examples: [],
|
|
765
|
+
subcommands: [
|
|
766
|
+
defineOperation({
|
|
767
|
+
name: "list",
|
|
768
|
+
summary: "Get asset ownership history",
|
|
769
|
+
method: "GET",
|
|
770
|
+
path: "/asset/{assetKey}/ownership",
|
|
771
|
+
pathParameters: [{
|
|
772
|
+
name: "assetKey",
|
|
773
|
+
description: "The asset's key",
|
|
774
|
+
required: true
|
|
775
|
+
}],
|
|
776
|
+
queryFlags: [],
|
|
777
|
+
takesBody: false
|
|
778
|
+
}),
|
|
779
|
+
defineOperation({
|
|
780
|
+
name: "create",
|
|
781
|
+
summary: "Create asset ownership",
|
|
782
|
+
method: "POST",
|
|
783
|
+
path: "/asset/{assetKey}/ownership",
|
|
784
|
+
pathParameters: [{
|
|
785
|
+
name: "assetKey",
|
|
786
|
+
description: "The asset's key",
|
|
787
|
+
required: true
|
|
788
|
+
}],
|
|
789
|
+
queryFlags: [],
|
|
790
|
+
takesBody: true
|
|
791
|
+
}),
|
|
792
|
+
defineOperation({
|
|
793
|
+
name: "clear",
|
|
794
|
+
summary: "Clear the ownership an asset holds today",
|
|
795
|
+
method: "DELETE",
|
|
796
|
+
path: "/asset/{assetKey}/ownership",
|
|
797
|
+
pathParameters: [{
|
|
798
|
+
name: "assetKey",
|
|
799
|
+
description: "The asset's key",
|
|
800
|
+
required: true
|
|
801
|
+
}],
|
|
802
|
+
queryFlags: [],
|
|
803
|
+
takesBody: true
|
|
804
|
+
}),
|
|
805
|
+
defineOperation({
|
|
806
|
+
name: "get",
|
|
807
|
+
summary: "Get asset ownership segment",
|
|
808
|
+
method: "GET",
|
|
809
|
+
path: "/asset/{assetKey}/ownership/{segmentKey}",
|
|
810
|
+
pathParameters: [{
|
|
811
|
+
name: "assetKey",
|
|
812
|
+
description: "The asset's key",
|
|
813
|
+
required: true
|
|
814
|
+
}, {
|
|
815
|
+
name: "segmentKey",
|
|
816
|
+
description: "The ownership segment's key",
|
|
817
|
+
required: true
|
|
818
|
+
}],
|
|
819
|
+
queryFlags: [],
|
|
820
|
+
takesBody: false
|
|
821
|
+
}),
|
|
822
|
+
defineOperation({
|
|
823
|
+
name: "update",
|
|
824
|
+
summary: "Patch asset ownership segment",
|
|
825
|
+
method: "PATCH",
|
|
826
|
+
path: "/asset/{assetKey}/ownership/{segmentKey}",
|
|
827
|
+
pathParameters: [{
|
|
828
|
+
name: "assetKey",
|
|
829
|
+
description: "The asset's key",
|
|
830
|
+
required: true
|
|
831
|
+
}, {
|
|
832
|
+
name: "segmentKey",
|
|
833
|
+
description: "The ownership segment's key",
|
|
834
|
+
required: true
|
|
835
|
+
}],
|
|
836
|
+
queryFlags: [],
|
|
837
|
+
takesBody: true
|
|
838
|
+
}),
|
|
839
|
+
defineOperation({
|
|
840
|
+
name: "delete",
|
|
841
|
+
summary: "Delete asset ownership segment",
|
|
842
|
+
method: "DELETE",
|
|
843
|
+
path: "/asset/{assetKey}/ownership/{segmentKey}",
|
|
844
|
+
pathParameters: [{
|
|
845
|
+
name: "assetKey",
|
|
846
|
+
description: "The asset's key",
|
|
847
|
+
required: true
|
|
848
|
+
}, {
|
|
849
|
+
name: "segmentKey",
|
|
850
|
+
description: "The ownership segment's key",
|
|
851
|
+
required: true
|
|
852
|
+
}],
|
|
853
|
+
queryFlags: [],
|
|
854
|
+
takesBody: false
|
|
855
|
+
})
|
|
856
|
+
]
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
name: "url-link",
|
|
860
|
+
summary: "URL link commands",
|
|
861
|
+
arguments: [],
|
|
862
|
+
flags: [],
|
|
863
|
+
examples: [],
|
|
864
|
+
subcommands: [defineOperation({
|
|
865
|
+
name: "list",
|
|
866
|
+
summary: "Get asset URL links",
|
|
867
|
+
method: "GET",
|
|
868
|
+
path: "/asset/{assetKey}/url-link",
|
|
869
|
+
pathParameters: [{
|
|
870
|
+
name: "assetKey",
|
|
871
|
+
description: "The asset's key",
|
|
872
|
+
required: true
|
|
873
|
+
}],
|
|
874
|
+
queryFlags: [],
|
|
875
|
+
takesBody: false
|
|
876
|
+
}), defineOperation({
|
|
877
|
+
name: "create",
|
|
878
|
+
summary: "Create asset URL link",
|
|
879
|
+
method: "POST",
|
|
880
|
+
path: "/asset/{assetKey}/url-link",
|
|
881
|
+
pathParameters: [{
|
|
882
|
+
name: "assetKey",
|
|
883
|
+
description: "The asset's key",
|
|
884
|
+
required: true
|
|
885
|
+
}],
|
|
886
|
+
queryFlags: [],
|
|
887
|
+
takesBody: true
|
|
888
|
+
})]
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
name: "useful-life-revision",
|
|
892
|
+
summary: "Useful life revision commands",
|
|
893
|
+
arguments: [],
|
|
894
|
+
flags: [],
|
|
895
|
+
examples: [],
|
|
896
|
+
subcommands: [defineOperation({
|
|
897
|
+
name: "create",
|
|
898
|
+
summary: "Create asset useful life revision",
|
|
899
|
+
method: "POST",
|
|
900
|
+
path: "/asset/{assetKey}/useful-life-revision",
|
|
901
|
+
pathParameters: [{
|
|
902
|
+
name: "assetKey",
|
|
903
|
+
description: "The asset's key",
|
|
904
|
+
required: true
|
|
905
|
+
}],
|
|
906
|
+
queryFlags: [],
|
|
907
|
+
takesBody: true
|
|
908
|
+
})]
|
|
909
|
+
}
|
|
910
|
+
]
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
name: "customer",
|
|
914
|
+
summary: "Customer commands",
|
|
915
|
+
arguments: [],
|
|
916
|
+
flags: [],
|
|
917
|
+
examples: [],
|
|
918
|
+
subcommands: [
|
|
919
|
+
defineOperation({
|
|
920
|
+
name: "list",
|
|
921
|
+
summary: "Get customers",
|
|
922
|
+
method: "GET",
|
|
923
|
+
path: "/customer",
|
|
924
|
+
pathParameters: [],
|
|
925
|
+
queryFlags: [
|
|
926
|
+
{
|
|
927
|
+
name: "page",
|
|
928
|
+
queryName: "page",
|
|
929
|
+
description: "The page to return, starting at 1",
|
|
930
|
+
valueName: "number",
|
|
931
|
+
schema: z.coerce.number()
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
name: "limit",
|
|
935
|
+
queryName: "limit",
|
|
936
|
+
description: "The number of records per page, from 1 to 100",
|
|
937
|
+
valueName: "number",
|
|
938
|
+
schema: z.coerce.number()
|
|
939
|
+
},
|
|
940
|
+
{
|
|
941
|
+
name: "sort-by",
|
|
942
|
+
queryName: "sortBy",
|
|
943
|
+
description: "The field to sort by",
|
|
944
|
+
valueName: "value",
|
|
945
|
+
schema: z.string()
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
name: "sort-order",
|
|
949
|
+
queryName: "sortOrder",
|
|
950
|
+
description: "The sort direction",
|
|
951
|
+
valueName: "value",
|
|
952
|
+
schema: z.enum(["ASC", "DESC"])
|
|
953
|
+
},
|
|
954
|
+
{
|
|
955
|
+
name: "archived",
|
|
956
|
+
queryName: "archived",
|
|
957
|
+
description: "Whether to return unarchived records, archived records, or all of them",
|
|
958
|
+
valueName: "value",
|
|
959
|
+
schema: z.enum([
|
|
960
|
+
"all",
|
|
961
|
+
"false",
|
|
962
|
+
"true"
|
|
963
|
+
])
|
|
964
|
+
},
|
|
965
|
+
{
|
|
966
|
+
name: "is-customer",
|
|
967
|
+
queryName: "isCustomer",
|
|
968
|
+
description: "True to return only customers, or false to return only non-customers",
|
|
969
|
+
schema: z.boolean()
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
name: "is-supplier",
|
|
973
|
+
queryName: "isSupplier",
|
|
974
|
+
description: "True to return only suppliers, or false to return only non-suppliers",
|
|
975
|
+
schema: z.boolean()
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
name: "sync-statuses",
|
|
979
|
+
queryName: "syncStatuses",
|
|
980
|
+
description: "The CRM sync statuses to return: in-sync, out-of-sync, or not-linked",
|
|
981
|
+
valueName: "value",
|
|
982
|
+
repeatable: true,
|
|
983
|
+
schema: z.array(z.string())
|
|
984
|
+
},
|
|
985
|
+
{
|
|
986
|
+
name: "search",
|
|
987
|
+
queryName: "search",
|
|
988
|
+
description: "Text to match against customer names",
|
|
989
|
+
valueName: "value",
|
|
990
|
+
schema: z.string()
|
|
991
|
+
}
|
|
992
|
+
],
|
|
993
|
+
takesBody: false
|
|
994
|
+
}),
|
|
995
|
+
defineOperation({
|
|
996
|
+
name: "create",
|
|
997
|
+
summary: "Create customer",
|
|
998
|
+
method: "POST",
|
|
999
|
+
path: "/customer",
|
|
1000
|
+
pathParameters: [],
|
|
1001
|
+
queryFlags: [],
|
|
1002
|
+
takesBody: true
|
|
1003
|
+
}),
|
|
1004
|
+
defineOperation({
|
|
1005
|
+
name: "get",
|
|
1006
|
+
summary: "Get customer",
|
|
1007
|
+
method: "GET",
|
|
1008
|
+
path: "/customer/{customerKey}",
|
|
1009
|
+
pathParameters: [{
|
|
1010
|
+
name: "customerKey",
|
|
1011
|
+
description: "The customer's key",
|
|
1012
|
+
required: true
|
|
1013
|
+
}],
|
|
1014
|
+
queryFlags: [],
|
|
1015
|
+
takesBody: false
|
|
1016
|
+
}),
|
|
1017
|
+
defineOperation({
|
|
1018
|
+
name: "update",
|
|
1019
|
+
summary: "Patch customer",
|
|
1020
|
+
method: "PATCH",
|
|
1021
|
+
path: "/customer/{customerKey}",
|
|
1022
|
+
pathParameters: [{
|
|
1023
|
+
name: "customerKey",
|
|
1024
|
+
description: "The customer's key",
|
|
1025
|
+
required: true
|
|
1026
|
+
}],
|
|
1027
|
+
queryFlags: [],
|
|
1028
|
+
takesBody: true
|
|
1029
|
+
})
|
|
1030
|
+
]
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
name: "file",
|
|
1034
|
+
summary: "File commands",
|
|
1035
|
+
arguments: [],
|
|
1036
|
+
flags: [],
|
|
1037
|
+
examples: [],
|
|
1038
|
+
subcommands: [defineOperation({
|
|
1039
|
+
name: "create",
|
|
1040
|
+
summary: "Upload file",
|
|
1041
|
+
method: "POST",
|
|
1042
|
+
path: "/file",
|
|
1043
|
+
pathParameters: [],
|
|
1044
|
+
queryFlags: [],
|
|
1045
|
+
takesBody: true
|
|
1046
|
+
}), defineOperation({
|
|
1047
|
+
name: "get",
|
|
1048
|
+
summary: "Get file",
|
|
1049
|
+
method: "GET",
|
|
1050
|
+
path: "/file/{fileKey}",
|
|
1051
|
+
pathParameters: [{
|
|
1052
|
+
name: "fileKey",
|
|
1053
|
+
description: "The file's key, and a public file is readable with any organization's API key while any other file is readable only with its own organization's",
|
|
1054
|
+
required: true
|
|
1055
|
+
}],
|
|
1056
|
+
queryFlags: [{
|
|
1057
|
+
name: "attachment",
|
|
1058
|
+
queryName: "attachment",
|
|
1059
|
+
description: "Present, with any value or none, when the file should download as an attachment rather than open inline",
|
|
1060
|
+
valueName: "value",
|
|
1061
|
+
schema: z.string()
|
|
1062
|
+
}],
|
|
1063
|
+
takesBody: false
|
|
1064
|
+
})]
|
|
1065
|
+
},
|
|
1066
|
+
{
|
|
1067
|
+
name: "item",
|
|
1068
|
+
summary: "Item commands",
|
|
1069
|
+
arguments: [],
|
|
1070
|
+
flags: [],
|
|
1071
|
+
examples: [],
|
|
1072
|
+
subcommands: [
|
|
1073
|
+
defineOperation({
|
|
1074
|
+
name: "list",
|
|
1075
|
+
summary: "Get items",
|
|
1076
|
+
method: "GET",
|
|
1077
|
+
path: "/item",
|
|
1078
|
+
pathParameters: [],
|
|
1079
|
+
queryFlags: [
|
|
1080
|
+
{
|
|
1081
|
+
name: "type",
|
|
1082
|
+
queryName: "type",
|
|
1083
|
+
description: "The item type to list, SERVICE, DEVICE, or BULK, or absent for every type",
|
|
1084
|
+
valueName: "value",
|
|
1085
|
+
schema: z.enum([
|
|
1086
|
+
"BULK",
|
|
1087
|
+
"DEVICE",
|
|
1088
|
+
"SERVICE"
|
|
1089
|
+
])
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
name: "search",
|
|
1093
|
+
queryName: "search",
|
|
1094
|
+
description: "Text to match against item names, SKUs, and descriptions, in any case",
|
|
1095
|
+
valueName: "value",
|
|
1096
|
+
schema: z.string()
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
name: "page",
|
|
1100
|
+
queryName: "page",
|
|
1101
|
+
description: "The page to return, starting at 1",
|
|
1102
|
+
valueName: "number",
|
|
1103
|
+
schema: z.coerce.number()
|
|
1104
|
+
},
|
|
1105
|
+
{
|
|
1106
|
+
name: "limit",
|
|
1107
|
+
queryName: "limit",
|
|
1108
|
+
description: "The number of records per page, from 1 to 100",
|
|
1109
|
+
valueName: "number",
|
|
1110
|
+
schema: z.coerce.number()
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
name: "sort-by",
|
|
1114
|
+
queryName: "sortBy",
|
|
1115
|
+
description: "The field to sort by",
|
|
1116
|
+
valueName: "value",
|
|
1117
|
+
schema: z.enum([
|
|
1118
|
+
"name",
|
|
1119
|
+
"sku",
|
|
1120
|
+
"type",
|
|
1121
|
+
"lastUpdatedAt"
|
|
1122
|
+
])
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
name: "sort-order",
|
|
1126
|
+
queryName: "sortOrder",
|
|
1127
|
+
description: "The sort direction",
|
|
1128
|
+
valueName: "value",
|
|
1129
|
+
schema: z.enum(["ASC", "DESC"])
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
name: "archived",
|
|
1133
|
+
queryName: "archived",
|
|
1134
|
+
description: "Whether to return unarchived records, archived records, or all of them",
|
|
1135
|
+
valueName: "value",
|
|
1136
|
+
schema: z.enum([
|
|
1137
|
+
"all",
|
|
1138
|
+
"false",
|
|
1139
|
+
"true"
|
|
1140
|
+
])
|
|
1141
|
+
},
|
|
1142
|
+
{
|
|
1143
|
+
name: "exclude-linked-integration",
|
|
1144
|
+
queryName: "excludeLinkedIntegration",
|
|
1145
|
+
description: "An integration whose already-linked items to leave out",
|
|
1146
|
+
valueName: "value",
|
|
1147
|
+
schema: z.string()
|
|
1148
|
+
}
|
|
1149
|
+
],
|
|
1150
|
+
takesBody: false
|
|
1151
|
+
}),
|
|
1152
|
+
defineOperation({
|
|
1153
|
+
name: "create",
|
|
1154
|
+
summary: "Create item",
|
|
1155
|
+
method: "POST",
|
|
1156
|
+
path: "/item",
|
|
1157
|
+
pathParameters: [],
|
|
1158
|
+
queryFlags: [],
|
|
1159
|
+
takesBody: true
|
|
1160
|
+
}),
|
|
1161
|
+
defineOperation({
|
|
1162
|
+
name: "get",
|
|
1163
|
+
summary: "Get item",
|
|
1164
|
+
method: "GET",
|
|
1165
|
+
path: "/item/{itemKey}",
|
|
1166
|
+
pathParameters: [{
|
|
1167
|
+
name: "itemKey",
|
|
1168
|
+
description: "The item's key",
|
|
1169
|
+
required: true
|
|
1170
|
+
}],
|
|
1171
|
+
queryFlags: [],
|
|
1172
|
+
takesBody: false
|
|
1173
|
+
}),
|
|
1174
|
+
defineOperation({
|
|
1175
|
+
name: "update",
|
|
1176
|
+
summary: "Update item",
|
|
1177
|
+
method: "PATCH",
|
|
1178
|
+
path: "/item/{itemKey}",
|
|
1179
|
+
pathParameters: [{
|
|
1180
|
+
name: "itemKey",
|
|
1181
|
+
description: "The item's key",
|
|
1182
|
+
required: true
|
|
1183
|
+
}],
|
|
1184
|
+
queryFlags: [],
|
|
1185
|
+
takesBody: true
|
|
1186
|
+
}),
|
|
1187
|
+
{
|
|
1188
|
+
name: "accounting",
|
|
1189
|
+
summary: "Accounting commands",
|
|
1190
|
+
arguments: [],
|
|
1191
|
+
flags: [],
|
|
1192
|
+
examples: [],
|
|
1193
|
+
subcommands: [defineOperation({
|
|
1194
|
+
name: "update",
|
|
1195
|
+
summary: "Update item accounting",
|
|
1196
|
+
method: "PATCH",
|
|
1197
|
+
path: "/item/{itemKey}/accounting",
|
|
1198
|
+
pathParameters: [{
|
|
1199
|
+
name: "itemKey",
|
|
1200
|
+
description: "The item's key",
|
|
1201
|
+
required: true
|
|
1202
|
+
}],
|
|
1203
|
+
queryFlags: [],
|
|
1204
|
+
takesBody: true
|
|
1205
|
+
})]
|
|
1206
|
+
},
|
|
1207
|
+
{
|
|
1208
|
+
name: "field",
|
|
1209
|
+
summary: "Field commands",
|
|
1210
|
+
arguments: [],
|
|
1211
|
+
flags: [],
|
|
1212
|
+
examples: [],
|
|
1213
|
+
subcommands: [
|
|
1214
|
+
defineOperation({
|
|
1215
|
+
name: "create",
|
|
1216
|
+
summary: "Create item field",
|
|
1217
|
+
method: "POST",
|
|
1218
|
+
path: "/item/{itemKey}/field",
|
|
1219
|
+
pathParameters: [{
|
|
1220
|
+
name: "itemKey",
|
|
1221
|
+
description: "The item's key",
|
|
1222
|
+
required: true
|
|
1223
|
+
}],
|
|
1224
|
+
queryFlags: [],
|
|
1225
|
+
takesBody: true
|
|
1226
|
+
}),
|
|
1227
|
+
defineOperation({
|
|
1228
|
+
name: "update",
|
|
1229
|
+
summary: "Update item field",
|
|
1230
|
+
method: "PATCH",
|
|
1231
|
+
path: "/item/{itemKey}/field/{fieldKey}",
|
|
1232
|
+
pathParameters: [{
|
|
1233
|
+
name: "itemKey",
|
|
1234
|
+
description: "The item's key",
|
|
1235
|
+
required: true
|
|
1236
|
+
}, {
|
|
1237
|
+
name: "fieldKey",
|
|
1238
|
+
description: "The field's key",
|
|
1239
|
+
required: true
|
|
1240
|
+
}],
|
|
1241
|
+
queryFlags: [],
|
|
1242
|
+
takesBody: true
|
|
1243
|
+
}),
|
|
1244
|
+
defineOperation({
|
|
1245
|
+
name: "delete",
|
|
1246
|
+
summary: "Delete item field",
|
|
1247
|
+
method: "DELETE",
|
|
1248
|
+
path: "/item/{itemKey}/field/{fieldKey}",
|
|
1249
|
+
pathParameters: [{
|
|
1250
|
+
name: "itemKey",
|
|
1251
|
+
description: "The item's key",
|
|
1252
|
+
required: true
|
|
1253
|
+
}, {
|
|
1254
|
+
name: "fieldKey",
|
|
1255
|
+
description: "The field's key",
|
|
1256
|
+
required: true
|
|
1257
|
+
}],
|
|
1258
|
+
queryFlags: [],
|
|
1259
|
+
takesBody: false
|
|
1260
|
+
})
|
|
1261
|
+
]
|
|
1262
|
+
}
|
|
1263
|
+
]
|
|
1264
|
+
},
|
|
1265
|
+
{
|
|
1266
|
+
name: "location",
|
|
1267
|
+
summary: "Location commands",
|
|
1268
|
+
arguments: [],
|
|
1269
|
+
flags: [],
|
|
1270
|
+
examples: [],
|
|
1271
|
+
subcommands: [
|
|
1272
|
+
defineOperation({
|
|
1273
|
+
name: "list",
|
|
1274
|
+
summary: "Get location listing",
|
|
1275
|
+
method: "GET",
|
|
1276
|
+
path: "/location",
|
|
1277
|
+
pathParameters: [],
|
|
1278
|
+
queryFlags: [
|
|
1279
|
+
{
|
|
1280
|
+
name: "page",
|
|
1281
|
+
queryName: "page",
|
|
1282
|
+
description: "The page to return, starting at 1",
|
|
1283
|
+
valueName: "number",
|
|
1284
|
+
schema: z.coerce.number()
|
|
1285
|
+
},
|
|
1286
|
+
{
|
|
1287
|
+
name: "limit",
|
|
1288
|
+
queryName: "limit",
|
|
1289
|
+
description: "The number of records per page, from 1 to 100",
|
|
1290
|
+
valueName: "number",
|
|
1291
|
+
schema: z.coerce.number()
|
|
1292
|
+
},
|
|
1293
|
+
{
|
|
1294
|
+
name: "sort-by",
|
|
1295
|
+
queryName: "sortBy",
|
|
1296
|
+
description: "The field to sort by",
|
|
1297
|
+
valueName: "value",
|
|
1298
|
+
schema: z.enum([
|
|
1299
|
+
"name",
|
|
1300
|
+
"company",
|
|
1301
|
+
"assetCount"
|
|
1302
|
+
])
|
|
1303
|
+
},
|
|
1304
|
+
{
|
|
1305
|
+
name: "sort-order",
|
|
1306
|
+
queryName: "sortOrder",
|
|
1307
|
+
description: "The sort direction",
|
|
1308
|
+
valueName: "value",
|
|
1309
|
+
schema: z.enum(["ASC", "DESC"])
|
|
1310
|
+
},
|
|
1311
|
+
{
|
|
1312
|
+
name: "archived",
|
|
1313
|
+
queryName: "archived",
|
|
1314
|
+
description: "Whether to return unarchived records, archived records, or all of them",
|
|
1315
|
+
valueName: "value",
|
|
1316
|
+
schema: z.enum([
|
|
1317
|
+
"all",
|
|
1318
|
+
"false",
|
|
1319
|
+
"true"
|
|
1320
|
+
])
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
name: "is-transient",
|
|
1324
|
+
queryName: "isTransient",
|
|
1325
|
+
description: "Whether to return permanent locations (false), transient locations (true), or both (all)",
|
|
1326
|
+
valueName: "value",
|
|
1327
|
+
schema: z.enum([
|
|
1328
|
+
"all",
|
|
1329
|
+
"false",
|
|
1330
|
+
"true"
|
|
1331
|
+
])
|
|
1332
|
+
},
|
|
1333
|
+
{
|
|
1334
|
+
name: "search",
|
|
1335
|
+
queryName: "search",
|
|
1336
|
+
description: "Text to match against location and customer names",
|
|
1337
|
+
valueName: "value",
|
|
1338
|
+
schema: z.string()
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
name: "for-customer-ids",
|
|
1342
|
+
queryName: "forCustomerIds",
|
|
1343
|
+
description: "The IDs of the customers whose locations to return",
|
|
1344
|
+
valueName: "value",
|
|
1345
|
+
repeatable: true,
|
|
1346
|
+
schema: z.array(z.string())
|
|
1347
|
+
},
|
|
1348
|
+
{
|
|
1349
|
+
name: "include-organization",
|
|
1350
|
+
queryName: "includeOrganization",
|
|
1351
|
+
description: "Whether the customer filter also matches your organization's own locations, which on its own returns only those",
|
|
1352
|
+
schema: z.boolean()
|
|
1353
|
+
},
|
|
1354
|
+
{
|
|
1355
|
+
name: "sync-statuses",
|
|
1356
|
+
queryName: "syncStatuses",
|
|
1357
|
+
description: "The CRM sync statuses to return: in-sync, out-of-sync, or not-linked",
|
|
1358
|
+
valueName: "value",
|
|
1359
|
+
repeatable: true,
|
|
1360
|
+
schema: z.array(z.string())
|
|
1361
|
+
}
|
|
1362
|
+
],
|
|
1363
|
+
takesBody: false
|
|
1364
|
+
}),
|
|
1365
|
+
defineOperation({
|
|
1366
|
+
name: "create",
|
|
1367
|
+
summary: "Create location",
|
|
1368
|
+
method: "POST",
|
|
1369
|
+
path: "/location",
|
|
1370
|
+
pathParameters: [],
|
|
1371
|
+
queryFlags: [],
|
|
1372
|
+
takesBody: true
|
|
1373
|
+
}),
|
|
1374
|
+
defineOperation({
|
|
1375
|
+
name: "get",
|
|
1376
|
+
summary: "Get location",
|
|
1377
|
+
method: "GET",
|
|
1378
|
+
path: "/location/{locationKey}",
|
|
1379
|
+
pathParameters: [{
|
|
1380
|
+
name: "locationKey",
|
|
1381
|
+
description: "The location's key",
|
|
1382
|
+
required: true
|
|
1383
|
+
}],
|
|
1384
|
+
queryFlags: [],
|
|
1385
|
+
takesBody: false
|
|
1386
|
+
}),
|
|
1387
|
+
defineOperation({
|
|
1388
|
+
name: "update",
|
|
1389
|
+
summary: "Patch location",
|
|
1390
|
+
method: "PATCH",
|
|
1391
|
+
path: "/location/{locationKey}",
|
|
1392
|
+
pathParameters: [{
|
|
1393
|
+
name: "locationKey",
|
|
1394
|
+
description: "The location's key",
|
|
1395
|
+
required: true
|
|
1396
|
+
}],
|
|
1397
|
+
queryFlags: [],
|
|
1398
|
+
takesBody: true
|
|
1399
|
+
}),
|
|
1400
|
+
{
|
|
1401
|
+
name: "zones",
|
|
1402
|
+
summary: "Zones commands",
|
|
1403
|
+
arguments: [],
|
|
1404
|
+
flags: [],
|
|
1405
|
+
examples: [],
|
|
1406
|
+
subcommands: [defineOperation({
|
|
1407
|
+
name: "list",
|
|
1408
|
+
summary: "Get zones",
|
|
1409
|
+
method: "GET",
|
|
1410
|
+
path: "/location/{locationKey}/zones",
|
|
1411
|
+
pathParameters: [{
|
|
1412
|
+
name: "locationKey",
|
|
1413
|
+
description: "The key of the site whose zones to list",
|
|
1414
|
+
required: true
|
|
1415
|
+
}],
|
|
1416
|
+
queryFlags: [{
|
|
1417
|
+
name: "archived",
|
|
1418
|
+
queryName: "archived",
|
|
1419
|
+
description: "Whether to return unarchived zones, archived zones, or all of them",
|
|
1420
|
+
valueName: "value",
|
|
1421
|
+
schema: z.enum([
|
|
1422
|
+
"all",
|
|
1423
|
+
"false",
|
|
1424
|
+
"true"
|
|
1425
|
+
])
|
|
1426
|
+
}],
|
|
1427
|
+
takesBody: false
|
|
1428
|
+
})]
|
|
1429
|
+
}
|
|
1430
|
+
]
|
|
1431
|
+
},
|
|
1432
|
+
{
|
|
1433
|
+
name: "url-link",
|
|
1434
|
+
summary: "URL link commands",
|
|
1435
|
+
arguments: [],
|
|
1436
|
+
flags: [],
|
|
1437
|
+
examples: [],
|
|
1438
|
+
subcommands: [
|
|
1439
|
+
defineOperation({
|
|
1440
|
+
name: "get",
|
|
1441
|
+
summary: "Get URL link by key",
|
|
1442
|
+
method: "GET",
|
|
1443
|
+
path: "/url-link/{linkKey}",
|
|
1444
|
+
pathParameters: [{
|
|
1445
|
+
name: "linkKey",
|
|
1446
|
+
description: "The URL link's key",
|
|
1447
|
+
required: true
|
|
1448
|
+
}],
|
|
1449
|
+
queryFlags: [],
|
|
1450
|
+
takesBody: false
|
|
1451
|
+
}),
|
|
1452
|
+
defineOperation({
|
|
1453
|
+
name: "update",
|
|
1454
|
+
summary: "Update URL link",
|
|
1455
|
+
method: "PATCH",
|
|
1456
|
+
path: "/url-link/{linkKey}",
|
|
1457
|
+
pathParameters: [{
|
|
1458
|
+
name: "linkKey",
|
|
1459
|
+
description: "The URL link's key",
|
|
1460
|
+
required: true
|
|
1461
|
+
}],
|
|
1462
|
+
queryFlags: [],
|
|
1463
|
+
takesBody: true
|
|
1464
|
+
}),
|
|
1465
|
+
defineOperation({
|
|
1466
|
+
name: "delete",
|
|
1467
|
+
summary: "Delete URL link",
|
|
1468
|
+
method: "DELETE",
|
|
1469
|
+
path: "/url-link/{linkKey}",
|
|
1470
|
+
pathParameters: [{
|
|
1471
|
+
name: "linkKey",
|
|
1472
|
+
description: "The URL link's key",
|
|
1473
|
+
required: true
|
|
1474
|
+
}],
|
|
1475
|
+
queryFlags: [],
|
|
1476
|
+
takesBody: false
|
|
1477
|
+
})
|
|
1478
|
+
]
|
|
1479
|
+
}
|
|
1480
|
+
],
|
|
1481
|
+
apiCommand,
|
|
1482
|
+
agentGuideCommand
|
|
1483
|
+
];
|
|
1484
|
+
//#endregion
|
|
1485
|
+
//#region src/command/validate.ts
|
|
1486
|
+
/** toRejectedFlag names the first flag whose value its schema refuses. */
|
|
1487
|
+
function toRejectedFlag(command, flags) {
|
|
1488
|
+
for (const flag of command.flags) {
|
|
1489
|
+
const value = flags[toOptionKey(flag.name)];
|
|
1490
|
+
if (value === void 0) continue;
|
|
1491
|
+
if (!flag.schema.safeParse(value).success) return `--${flag.name} does not accept ${JSON.stringify(value)}`;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
339
1494
|
//#endregion
|
|
340
1495
|
//#region src/cli.ts
|
|
341
1496
|
const program = new Command();
|
|
@@ -358,6 +1513,8 @@ function toProgram(command) {
|
|
|
358
1513
|
program.addOption(option);
|
|
359
1514
|
}
|
|
360
1515
|
for (const example of command.examples) program.addHelpText("after", `\n${example.description}:\n $ ${example.command}`);
|
|
1516
|
+
for (const subcommand of command.subcommands ?? []) program.addCommand(toProgram(subcommand));
|
|
1517
|
+
if (!command.run) return program;
|
|
361
1518
|
program.action(async (...parsed) => {
|
|
362
1519
|
const flags = parsed[parsed.length - 2] ?? {};
|
|
363
1520
|
const args = parsed.slice(0, parsed.length - 2).flatMap(toArgumentList);
|
|
@@ -367,13 +1524,18 @@ function toProgram(command) {
|
|
|
367
1524
|
}
|
|
368
1525
|
async function toExitCode(command, args, flags) {
|
|
369
1526
|
const isJSON = isJSONOutput(flags);
|
|
1527
|
+
const rejected = toRejectedFlag(command, flags);
|
|
1528
|
+
if (rejected) {
|
|
1529
|
+
writeFailure(rejected, isJSON);
|
|
1530
|
+
return ExitCode.USAGE;
|
|
1531
|
+
}
|
|
370
1532
|
try {
|
|
371
|
-
return await command.run({
|
|
1533
|
+
return await command.run?.({
|
|
372
1534
|
args,
|
|
373
1535
|
flags,
|
|
374
1536
|
isJSON,
|
|
375
1537
|
commands
|
|
376
|
-
});
|
|
1538
|
+
}) ?? ExitCode.OK;
|
|
377
1539
|
} catch (error) {
|
|
378
1540
|
writeFailure(error instanceof Error ? error.message : String(error), isJSON);
|
|
379
1541
|
return ExitCode.ERROR;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hardfin/cli",
|
|
3
|
-
"version": "0.0.2-dev.
|
|
3
|
+
"version": "0.0.2-dev.6",
|
|
4
4
|
"description": "Command line interface for the Hardfin API",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Hardfin, Inc.",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "tsdown",
|
|
36
|
+
"generate-surface": "node scripts/generate-surface.mjs",
|
|
36
37
|
"check-types": "tsc --noEmit",
|
|
37
38
|
"test": "vitest run",
|
|
38
39
|
"test:watch": "vitest",
|
|
@@ -47,6 +48,7 @@
|
|
|
47
48
|
"tsdown": "^0.23.0",
|
|
48
49
|
"typescript": "^5.9.0",
|
|
49
50
|
"unrun": "^0.3.1",
|
|
50
|
-
"vitest": "^5.0.1"
|
|
51
|
+
"vitest": "^5.0.1",
|
|
52
|
+
"yaml": "^2.9.1"
|
|
51
53
|
}
|
|
52
54
|
}
|