@contrail/cli 3.6.0-alpha-8-alpha.e4ae6bd → 3.6.0-alpha-8-alpha.7d24daa
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 +22 -12
- package/dist/commands/entities/create.d.ts +1 -0
- package/dist/commands/entities/create.js +17 -9
- package/dist/commands/entities/create.spec.js +2 -2
- package/dist/commands/entities/delete.d.ts +1 -0
- package/dist/commands/entities/delete.js +12 -9
- package/dist/commands/entities/get.d.ts +1 -0
- package/dist/commands/entities/get.js +14 -6
- package/dist/commands/entities/grammar.spec.js +12 -6
- package/dist/commands/entities/update.d.ts +1 -0
- package/dist/commands/entities/update.js +13 -6
- package/dist/helpers/propertySpecifications/propertySpecifications.d.ts +1 -2
- package/dist/helpers/propertySpecifications/propertySpecifications.js +1 -4
- package/dist/logger.spec.js +1 -1
- package/oclif.manifest.json +33 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ $ npm install -g @contrail/cli
|
|
|
17
17
|
$ contrail COMMAND
|
|
18
18
|
running command...
|
|
19
19
|
$ contrail (--version)
|
|
20
|
-
@contrail/cli/3.6.0-alpha-8-alpha.
|
|
20
|
+
@contrail/cli/3.6.0-alpha-8-alpha.7d24daa linux-x64 node-v24.21.0
|
|
21
21
|
$ contrail --help [COMMAND]
|
|
22
22
|
USAGE
|
|
23
23
|
$ contrail COMMAND
|
|
@@ -543,12 +543,12 @@ EXAMPLES
|
|
|
543
543
|
|
|
544
544
|
## `contrail entities create PATH`
|
|
545
545
|
|
|
546
|
-
|
|
546
|
+
Create entities
|
|
547
547
|
|
|
548
548
|
```
|
|
549
549
|
USAGE
|
|
550
550
|
$ contrail entities create PATH [-l error|warn|info|debug] [-e true|false] [-n <value>] [-d] [-i <value>] [-o
|
|
551
|
-
<value>] [-g] [-r] [-S]
|
|
551
|
+
<value>] [-g] [-r] [-S] [--suffix <value>]
|
|
552
552
|
|
|
553
553
|
ARGUMENTS
|
|
554
554
|
PATH Path of the entity type to create
|
|
@@ -568,9 +568,11 @@ FLAGS
|
|
|
568
568
|
-n, --count=<value> Number of random entities to create
|
|
569
569
|
-o, --outputFile=<value> File to save the output (optional)
|
|
570
570
|
-r, --readableIds If true, uses readable IDs for the created entities
|
|
571
|
+
--suffix=<value> Append a sub-route to the entity endpoint (e.g. --suffix=synchronous targets
|
|
572
|
+
/<entities>/synchronous)
|
|
571
573
|
|
|
572
574
|
DESCRIPTION
|
|
573
|
-
|
|
575
|
+
Create entities
|
|
574
576
|
|
|
575
577
|
EXAMPLES
|
|
576
578
|
$ contrail entities create color -g -n=10 # creates 10 random colors
|
|
@@ -594,11 +596,12 @@ EXAMPLES
|
|
|
594
596
|
|
|
595
597
|
## `contrail entities delete ENTITYNAME [IDS]`
|
|
596
598
|
|
|
597
|
-
|
|
599
|
+
Delete entities by id
|
|
598
600
|
|
|
599
601
|
```
|
|
600
602
|
USAGE
|
|
601
|
-
$ contrail entities delete ENTITYNAME [IDS] [-l error|warn|info|debug] [-e true|false] [-d] [-i <value>]
|
|
603
|
+
$ contrail entities delete ENTITYNAME [IDS] [-l error|warn|info|debug] [-e true|false] [-d] [-i <value>] [--suffix
|
|
604
|
+
<value>]
|
|
602
605
|
|
|
603
606
|
ARGUMENTS
|
|
604
607
|
ENTITYNAME The name of the entity type to delete
|
|
@@ -612,9 +615,11 @@ FLAGS
|
|
|
612
615
|
entity in the file.
|
|
613
616
|
-l, --logLevel=<option> The Log Level
|
|
614
617
|
<options: error|warn|info|debug>
|
|
618
|
+
--suffix=<value> Append a sub-route to the entity endpoint (e.g. --suffix=synchronous targets
|
|
619
|
+
/<entities>/synchronous)
|
|
615
620
|
|
|
616
621
|
DESCRIPTION
|
|
617
|
-
|
|
622
|
+
Delete entities by id
|
|
618
623
|
|
|
619
624
|
EXAMPLES
|
|
620
625
|
$ contrail entities delete color 123
|
|
@@ -626,12 +631,12 @@ EXAMPLES
|
|
|
626
631
|
|
|
627
632
|
## `contrail entities get TYPEPATH [IDS]`
|
|
628
633
|
|
|
629
|
-
|
|
634
|
+
Get an entity
|
|
630
635
|
|
|
631
636
|
```
|
|
632
637
|
USAGE
|
|
633
638
|
$ contrail entities get TYPEPATH [IDS] [-l error|warn|info|debug] [-e true|false] [-o <value>] [-p <value>] [-P]
|
|
634
|
-
[-s <value>] [-S] [-t <value>] [-r <value>]
|
|
639
|
+
[-s <value>] [-S] [-t <value>] [-r <value>] [--suffix <value>]
|
|
635
640
|
|
|
636
641
|
ARGUMENTS
|
|
637
642
|
TYPEPATH The name of the entity type to fetch
|
|
@@ -649,9 +654,11 @@ FLAGS
|
|
|
649
654
|
-r, --relations=<value> Comma-separated list of relations to include in the output
|
|
650
655
|
-s, --skip=<value> Number of entities to skip (used with paginated fetch)
|
|
651
656
|
-t, --take=<value> Number of entities to fetch in a single request (default is 100)
|
|
657
|
+
--suffix=<value> Append a sub-route to the entity endpoint (e.g. --suffix=synchronous targets
|
|
658
|
+
/<entities>/synchronous)
|
|
652
659
|
|
|
653
660
|
DESCRIPTION
|
|
654
|
-
|
|
661
|
+
Get an entity
|
|
655
662
|
|
|
656
663
|
EXAMPLES
|
|
657
664
|
$ contrail entities get color 123
|
|
@@ -669,11 +676,12 @@ EXAMPLES
|
|
|
669
676
|
|
|
670
677
|
## `contrail entities update ENTITYNAME [IDS]`
|
|
671
678
|
|
|
672
|
-
|
|
679
|
+
Update an entity
|
|
673
680
|
|
|
674
681
|
```
|
|
675
682
|
USAGE
|
|
676
683
|
$ contrail entities update ENTITYNAME [IDS] [-l error|warn|info|debug] [-e true|false] [-S] [-d] [-i <value>] [-F]
|
|
684
|
+
[--suffix <value>]
|
|
677
685
|
|
|
678
686
|
ARGUMENTS
|
|
679
687
|
ENTITYNAME The name of the entity type to update
|
|
@@ -688,9 +696,11 @@ FLAGS
|
|
|
688
696
|
-i, --inputFile=<value> File containing properties to update the entity (optional)
|
|
689
697
|
-l, --logLevel=<option> The Log Level
|
|
690
698
|
<options: error|warn|info|debug>
|
|
699
|
+
--suffix=<value> Append a sub-route to the entity endpoint (e.g. --suffix=synchronous targets
|
|
700
|
+
/<entities>/synchronous)
|
|
691
701
|
|
|
692
702
|
DESCRIPTION
|
|
693
|
-
|
|
703
|
+
Update an entity
|
|
694
704
|
|
|
695
705
|
EXAMPLES
|
|
696
706
|
$ contrail entities update color 123 -- name=obsidian hex=000000
|
|
@@ -14,6 +14,7 @@ export default class CreateEntity extends BaseCommand {
|
|
|
14
14
|
generateValues: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
15
15
|
readableIds: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
16
16
|
skipHydrateRelationships: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
suffix: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
17
18
|
alwaysConfirmEnv: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
18
19
|
logLevel: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
19
20
|
};
|
|
@@ -145,7 +145,7 @@ const FIND_VALID_VALUE_MAP = {
|
|
|
145
145
|
],
|
|
146
146
|
};
|
|
147
147
|
class CreateEntity extends basecommand_1.BaseCommand {
|
|
148
|
-
static description = '
|
|
148
|
+
static description = 'Create entities';
|
|
149
149
|
static examples = [
|
|
150
150
|
'contrail entities create color -g -n=10 # creates 10 random colors',
|
|
151
151
|
'contrail entities create color -- name="Electric Blue" green=1 red=2 blue=3 # creates one color with specified properties only',
|
|
@@ -200,6 +200,10 @@ class CreateEntity extends basecommand_1.BaseCommand {
|
|
|
200
200
|
required: false,
|
|
201
201
|
default: false,
|
|
202
202
|
}),
|
|
203
|
+
suffix: core_1.Flags.string({
|
|
204
|
+
description: 'Append a sub-route to the entity endpoint (e.g. --suffix=synchronous targets /<entities>/synchronous)',
|
|
205
|
+
required: false,
|
|
206
|
+
}),
|
|
203
207
|
};
|
|
204
208
|
static args = {
|
|
205
209
|
path: core_1.Args.string({
|
|
@@ -211,10 +215,9 @@ class CreateEntity extends basecommand_1.BaseCommand {
|
|
|
211
215
|
static strict = false;
|
|
212
216
|
async run() {
|
|
213
217
|
(0, common_1.routeConsoleLogToStderr)();
|
|
214
|
-
console.error('🚨 This command is in BETA and may change or be removed at any time.');
|
|
215
218
|
await (0, login_1.authUser)();
|
|
216
219
|
const { flags, argv, args } = await this.parse(CreateEntity);
|
|
217
|
-
const { count, dryRun: isDryRun, inputFile: inputFilename, outputFile: outputFilename, generateValues: shouldGenerateMissingValues, readableIds: shouldUseReadableIds, skipHydrateRelationships, } = flags;
|
|
220
|
+
const { count, dryRun: isDryRun, inputFile: inputFilename, outputFile: outputFilename, generateValues: shouldGenerateMissingValues, readableIds: shouldUseReadableIds, skipHydrateRelationships, suffix, } = flags;
|
|
218
221
|
const { path: entityTypePath } = args;
|
|
219
222
|
// Global flags is a little hacky, but it was easier
|
|
220
223
|
// and it doesn't see *that* bad because they are only valid
|
|
@@ -260,7 +263,7 @@ class CreateEntity extends basecommand_1.BaseCommand {
|
|
|
260
263
|
}
|
|
261
264
|
try {
|
|
262
265
|
const entitiesToCreate = await buildEntitiesToCreate(entityTypePath, partialEntities, propertySpecBySlug);
|
|
263
|
-
const summary = await createEntities(entityName, entitiesToCreate);
|
|
266
|
+
const summary = await createEntities(entityName, entitiesToCreate, suffix);
|
|
264
267
|
if (outputFilename) {
|
|
265
268
|
fs_1.default.writeFileSync(outputFilename, JSON.stringify(summary, null, 2));
|
|
266
269
|
console.error(`Output saved to ${outputFilename}`);
|
|
@@ -295,13 +298,13 @@ function summarizeSettledCreates(inputs, settled) {
|
|
|
295
298
|
return { created, failed };
|
|
296
299
|
}
|
|
297
300
|
exports.summarizeSettledCreates = summarizeSettledCreates;
|
|
298
|
-
async function createEntities(entityName, entities) {
|
|
301
|
+
async function createEntities(entityName, entities, suffix) {
|
|
299
302
|
if (IS_DRY_RUN) {
|
|
300
303
|
console.error(`Dry run mode: not creating entities, but simulating creation for ${entityName}`);
|
|
301
304
|
return { created: entities, failed: [] };
|
|
302
305
|
}
|
|
303
306
|
const limit = (0, p_limit_1.default)(20);
|
|
304
|
-
const settled = await Promise.allSettled(entities.map(async (e) => limit(() => createEntity(entityName, e))));
|
|
307
|
+
const settled = await Promise.allSettled(entities.map(async (e) => limit(() => createEntity(entityName, e, suffix))));
|
|
305
308
|
return summarizeSettledCreates(entities, settled);
|
|
306
309
|
}
|
|
307
310
|
async function buildEntitiesToCreate(entityTypePath, entities, propertySpecificationBySlug) {
|
|
@@ -541,12 +544,17 @@ function castValueToPropertyType(value, propertyType) {
|
|
|
541
544
|
}
|
|
542
545
|
}
|
|
543
546
|
exports.castValueToPropertyType = castValueToPropertyType;
|
|
544
|
-
async function createEntity(entityName, entity) {
|
|
547
|
+
async function createEntity(entityName, entity, suffix) {
|
|
545
548
|
const client = new sdk_1.Entities();
|
|
546
549
|
const skipHydrateRelationships = SHOULD_SKIP_HYDRATE_RELATIONSHIPS;
|
|
547
|
-
if (entityName === 'assortment-item')
|
|
550
|
+
if (entityName === 'assortment-item' && !suffix)
|
|
548
551
|
return (0, entities_1.createAssortmentItem)(entity, { skipHydrateRelationships }); // special case
|
|
549
|
-
const response = await client.create({
|
|
552
|
+
const response = await client.create({
|
|
553
|
+
entityName,
|
|
554
|
+
object: entity,
|
|
555
|
+
skipHydrateRelationships,
|
|
556
|
+
suffix,
|
|
557
|
+
});
|
|
550
558
|
return response;
|
|
551
559
|
}
|
|
552
560
|
function getReferencedObjectPaths(type) {
|
|
@@ -527,7 +527,7 @@ describe('castValueToPropertyType array coercion', () => {
|
|
|
527
527
|
]);
|
|
528
528
|
});
|
|
529
529
|
it('should keep a comma value literal on a multi-value property and warn with the repeated-key form', () => {
|
|
530
|
-
const consoleError = jest.spyOn(console, 'error').mockImplementation(() =>
|
|
530
|
+
const consoleError = jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
531
531
|
expect((0, create_1.castValueToPropertyType)('option,family', types_1.PropertyType.MultiSelect)).toEqual([
|
|
532
532
|
'option,family',
|
|
533
533
|
]);
|
|
@@ -535,7 +535,7 @@ describe('castValueToPropertyType array coercion', () => {
|
|
|
535
535
|
consoleError.mockRestore();
|
|
536
536
|
});
|
|
537
537
|
it('should keep commas literal on string properties without warning', () => {
|
|
538
|
-
const consoleError = jest.spyOn(console, 'error').mockImplementation(() =>
|
|
538
|
+
const consoleError = jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
539
539
|
expect((0, create_1.castValueToPropertyType)('my,name', types_1.PropertyType.String)).toBe('my,name');
|
|
540
540
|
expect(consoleError).not.toHaveBeenCalled();
|
|
541
541
|
consoleError.mockRestore();
|
|
@@ -5,6 +5,7 @@ export default class DeleteEntity extends BaseCommand {
|
|
|
5
5
|
static flags: {
|
|
6
6
|
dryRun: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
7
|
inputFile: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
|
+
suffix: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
9
|
alwaysConfirmEnv: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
9
10
|
logLevel: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
10
11
|
};
|
|
@@ -10,7 +10,7 @@ const common_1 = require("../../common");
|
|
|
10
10
|
const file_helpers_1 = require("../../helpers/file-helpers");
|
|
11
11
|
let IS_DRY_RUN = false;
|
|
12
12
|
class DeleteEntity extends basecommand_1.BaseCommand {
|
|
13
|
-
static description = '
|
|
13
|
+
static description = 'Delete entities by id';
|
|
14
14
|
static examples = [
|
|
15
15
|
'contrail entities delete color 123',
|
|
16
16
|
'contrail entities delete color id1 id2 id3',
|
|
@@ -26,10 +26,14 @@ class DeleteEntity extends basecommand_1.BaseCommand {
|
|
|
26
26
|
}),
|
|
27
27
|
inputFile: core_1.Flags.string({
|
|
28
28
|
char: 'i',
|
|
29
|
-
description:
|
|
29
|
+
description: "A file with a JSON array of objects; only each object's id is used. Deletes every entity in the file.",
|
|
30
30
|
required: false,
|
|
31
31
|
multiple: false,
|
|
32
32
|
}),
|
|
33
|
+
suffix: core_1.Flags.string({
|
|
34
|
+
description: 'Append a sub-route to the entity endpoint (e.g. --suffix=synchronous targets /<entities>/synchronous)',
|
|
35
|
+
required: false,
|
|
36
|
+
}),
|
|
33
37
|
};
|
|
34
38
|
static args = {
|
|
35
39
|
entityName: core_1.Args.string({
|
|
@@ -44,11 +48,10 @@ class DeleteEntity extends basecommand_1.BaseCommand {
|
|
|
44
48
|
static strict = false;
|
|
45
49
|
async run() {
|
|
46
50
|
(0, common_1.routeConsoleLogToStderr)();
|
|
47
|
-
console.error('🚨 This command is in BETA and may change or be removed at any time.');
|
|
48
51
|
await (0, login_1.authUser)();
|
|
49
52
|
const { args, flags, argv } = await this.parse(DeleteEntity);
|
|
50
53
|
const { entityName } = args;
|
|
51
|
-
const { inputFile: inputFilename } = flags;
|
|
54
|
+
const { inputFile: inputFilename, suffix } = flags;
|
|
52
55
|
if (flags.dryRun) {
|
|
53
56
|
IS_DRY_RUN = true;
|
|
54
57
|
console.error('Running in dry run mode. No entities will be deleted.');
|
|
@@ -72,7 +75,7 @@ class DeleteEntity extends basecommand_1.BaseCommand {
|
|
|
72
75
|
}
|
|
73
76
|
const ids = rowsWithId ? rowsWithId.map((item) => item.id) : positionalIds;
|
|
74
77
|
try {
|
|
75
|
-
const response = await deleteEntityByIds(entityName, ids);
|
|
78
|
+
const response = await deleteEntityByIds(entityName, ids, suffix);
|
|
76
79
|
if (IS_DRY_RUN) {
|
|
77
80
|
console.error('simulated deletion of entities:');
|
|
78
81
|
}
|
|
@@ -91,12 +94,12 @@ class DeleteEntity extends basecommand_1.BaseCommand {
|
|
|
91
94
|
}
|
|
92
95
|
}
|
|
93
96
|
exports.default = DeleteEntity;
|
|
94
|
-
async function deleteEntityByIds(entityName, ids) {
|
|
97
|
+
async function deleteEntityByIds(entityName, ids, suffix) {
|
|
95
98
|
const idsDeleted = new Set();
|
|
96
99
|
const idsNotFound = new Set();
|
|
97
100
|
const idsErrored = new Set();
|
|
98
101
|
const limit = (0, p_limit_1.default)(10);
|
|
99
|
-
const deletePromises = ids.map((id) => limit(() => deleteEntityById(entityName, id).then((entity) => {
|
|
102
|
+
const deletePromises = ids.map((id) => limit(() => deleteEntityById(entityName, id, suffix).then((entity) => {
|
|
100
103
|
if (!entity) {
|
|
101
104
|
idsNotFound.add(id);
|
|
102
105
|
return null;
|
|
@@ -119,12 +122,12 @@ async function deleteEntityByIds(entityName, ids) {
|
|
|
119
122
|
idsErrored: Array.from(idsErrored),
|
|
120
123
|
};
|
|
121
124
|
}
|
|
122
|
-
async function deleteEntityById(entityName, id) {
|
|
125
|
+
async function deleteEntityById(entityName, id, suffix) {
|
|
123
126
|
const client = new sdk_1.Entities();
|
|
124
127
|
if (IS_DRY_RUN) {
|
|
125
128
|
return client.get({ entityName, id, ignore404: true }).catch(throwIfNot404);
|
|
126
129
|
}
|
|
127
|
-
return client.delete({ entityName, id, ignore404: true }).catch(throwIfNot404);
|
|
130
|
+
return client.delete({ entityName, id, ignore404: true, suffix }).catch(throwIfNot404);
|
|
128
131
|
}
|
|
129
132
|
function throwIfNot404(e) {
|
|
130
133
|
const { statusCode } = parseSDKErrorMessage(e);
|
|
@@ -10,6 +10,7 @@ export default class GetEntity extends BaseCommand {
|
|
|
10
10
|
skipHydrateRelationships: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
11
|
take: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
12
12
|
relations: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
13
|
+
suffix: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
13
14
|
alwaysConfirmEnv: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
14
15
|
logLevel: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
15
16
|
};
|
|
@@ -13,7 +13,7 @@ const batchFetch_1 = require("../../helpers/entities/batchFetch");
|
|
|
13
13
|
let SHOULD_USE_PAGINATED_FIND = false;
|
|
14
14
|
let MAX_NUMBER_OF_ENTITIES_TO_FETCH = undefined;
|
|
15
15
|
class GetEntity extends basecommand_1.BaseCommand {
|
|
16
|
-
static description = '
|
|
16
|
+
static description = 'Get an entity';
|
|
17
17
|
static examples = [
|
|
18
18
|
'contrail entities get color 123',
|
|
19
19
|
'contrail entities get color id1 id2 id3',
|
|
@@ -62,6 +62,10 @@ class GetEntity extends basecommand_1.BaseCommand {
|
|
|
62
62
|
description: 'Comma-separated list of relations to include in the output',
|
|
63
63
|
required: false,
|
|
64
64
|
}),
|
|
65
|
+
suffix: core_1.Flags.string({
|
|
66
|
+
description: 'Append a sub-route to the entity endpoint (e.g. --suffix=synchronous targets /<entities>/synchronous)',
|
|
67
|
+
required: false,
|
|
68
|
+
}),
|
|
65
69
|
};
|
|
66
70
|
static args = {
|
|
67
71
|
typePath: core_1.Args.string({
|
|
@@ -76,11 +80,10 @@ class GetEntity extends basecommand_1.BaseCommand {
|
|
|
76
80
|
static strict = false;
|
|
77
81
|
async run() {
|
|
78
82
|
(0, common_1.routeConsoleLogToStderr)();
|
|
79
|
-
console.error('🚨 This command is in BETA and may change or be removed at any time.');
|
|
80
83
|
await (0, login_1.authUser)();
|
|
81
84
|
const { args, flags, argv } = await this.parse(GetEntity);
|
|
82
85
|
const { typePath } = args;
|
|
83
|
-
const { outputFile, paginated: shouldUsePaginatedFind, propertySlugs, relations: relationString, skip, skipHydrateRelationships, take, } = flags;
|
|
86
|
+
const { outputFile, paginated: shouldUsePaginatedFind, propertySlugs, relations: relationString, skip, skipHydrateRelationships, suffix, take, } = flags;
|
|
84
87
|
SHOULD_USE_PAGINATED_FIND = shouldUsePaginatedFind;
|
|
85
88
|
MAX_NUMBER_OF_ENTITIES_TO_FETCH = take;
|
|
86
89
|
if (MAX_NUMBER_OF_ENTITIES_TO_FETCH)
|
|
@@ -121,6 +124,7 @@ class GetEntity extends basecommand_1.BaseCommand {
|
|
|
121
124
|
criteria: convertPropertySpecIntoCriteria(propertySpecBySlug),
|
|
122
125
|
skipHydrateRelationships,
|
|
123
126
|
skip,
|
|
127
|
+
suffix,
|
|
124
128
|
relations,
|
|
125
129
|
propertySlugs: propertySlugsArray,
|
|
126
130
|
});
|
|
@@ -143,6 +147,7 @@ class GetEntity extends basecommand_1.BaseCommand {
|
|
|
143
147
|
relations,
|
|
144
148
|
propertySlugs: propertySlugsArray,
|
|
145
149
|
skip,
|
|
150
|
+
suffix,
|
|
146
151
|
take,
|
|
147
152
|
});
|
|
148
153
|
if (outputFile) {
|
|
@@ -161,7 +166,7 @@ class GetEntity extends basecommand_1.BaseCommand {
|
|
|
161
166
|
}
|
|
162
167
|
exports.default = GetEntity;
|
|
163
168
|
async function getEntityByTypePath(typePath, propertySpec, options = {}) {
|
|
164
|
-
const { propertySlugs, relations, skipHydrateRelationships, skip, take } = options;
|
|
169
|
+
const { propertySlugs, relations, skipHydrateRelationships, skip, suffix, take } = options;
|
|
165
170
|
const id = propertySpec?.get('id')?.toString() || undefined;
|
|
166
171
|
const criteria = convertPropertySpecIntoCriteria(propertySpec || new Map());
|
|
167
172
|
const { root: entityName } = (0, types_1.parseTypePath)(typePath);
|
|
@@ -175,6 +180,7 @@ async function getEntityByTypePath(typePath, propertySpec, options = {}) {
|
|
|
175
180
|
propertySlugs,
|
|
176
181
|
criteria,
|
|
177
182
|
skip,
|
|
183
|
+
suffix,
|
|
178
184
|
take,
|
|
179
185
|
});
|
|
180
186
|
}
|
|
@@ -187,19 +193,21 @@ function convertPropertySpecIntoCriteria(propertySpec) {
|
|
|
187
193
|
}
|
|
188
194
|
async function find(entityName, options = {}) {
|
|
189
195
|
const client = new sdk_1.Entities();
|
|
190
|
-
const { criteria, id, propertySlugs, skipHydrateRelationships, relations, skip, take } = options;
|
|
196
|
+
const { criteria, id, propertySlugs, skipHydrateRelationships, relations, skip, suffix, take } = options;
|
|
191
197
|
const queryOptions = {
|
|
192
198
|
entityName,
|
|
193
199
|
skipHydrateRelationships,
|
|
194
200
|
relations,
|
|
195
201
|
skip,
|
|
202
|
+
suffix,
|
|
196
203
|
take,
|
|
197
204
|
};
|
|
198
205
|
if (propertySlugs && propertySlugs.length > 0) {
|
|
199
206
|
queryOptions.customParams = { propertySlugs: propertySlugs.join(',') };
|
|
200
207
|
}
|
|
201
208
|
const payload = id ? { id, ...queryOptions } : { criteria, ...queryOptions };
|
|
202
|
-
|
|
209
|
+
// a suffix names a specific sub-route, so it always goes through the plain client
|
|
210
|
+
if ((0, batchFetch_1.canBatchFetchEntity)(entityName) && !suffix) {
|
|
203
211
|
return (0, batchFetch_1.batchFetch)(entityName, payload);
|
|
204
212
|
}
|
|
205
213
|
else {
|
|
@@ -35,35 +35,41 @@ describe('entities command grammar: entities <verb> <typePath> [ID...]', () => {
|
|
|
35
35
|
});
|
|
36
36
|
});
|
|
37
37
|
describe('positional id arguments', () => {
|
|
38
|
-
it('
|
|
38
|
+
it('should accept variadic positional ids on get', () => {
|
|
39
39
|
expect(get_1.default.strict).toBe(false);
|
|
40
40
|
expect(get_1.default.args.ids).toBeDefined();
|
|
41
41
|
});
|
|
42
|
-
it('
|
|
42
|
+
it('should accept variadic positional ids on update with no single id arg', () => {
|
|
43
43
|
expect(update_1.default.strict).toBe(false);
|
|
44
44
|
expect(update_1.default.args.ids).toBeDefined();
|
|
45
45
|
expect(update_1.default.args.id).toBeUndefined();
|
|
46
46
|
});
|
|
47
|
-
it('
|
|
47
|
+
it('should accept variadic positional ids on delete with no --id flag', () => {
|
|
48
48
|
expect(delete_1.default.strict).toBe(false);
|
|
49
49
|
expect(delete_1.default.args.ids).toBeDefined();
|
|
50
50
|
expect(delete_1.default.flags.id).toBeUndefined();
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
53
|
describe('flag alignment across verbs', () => {
|
|
54
|
-
it('the input file
|
|
54
|
+
it('should name the input file -i, --inputFile on every verb that accepts one', () => {
|
|
55
55
|
expect(create_1.default.flags.inputFile.char).toBe('i');
|
|
56
56
|
expect(update_1.default.flags.inputFile.char).toBe('i');
|
|
57
57
|
expect(delete_1.default.flags.inputFile.char).toBe('i');
|
|
58
58
|
expect(delete_1.default.flags.input).toBeUndefined();
|
|
59
59
|
});
|
|
60
|
-
it('skipHydrateRelationships
|
|
60
|
+
it('should map skipHydrateRelationships to -S on every verb', () => {
|
|
61
61
|
expect(create_1.default.flags.skipHydrateRelationships.char).toBe('S');
|
|
62
62
|
expect(get_1.default.flags.skipHydrateRelationships.char).toBe('S');
|
|
63
63
|
expect(update_1.default.flags.skipHydrateRelationships.char).toBe('S');
|
|
64
64
|
});
|
|
65
|
-
it('
|
|
65
|
+
it('should keep -s for --skip on get', () => {
|
|
66
66
|
expect(get_1.default.flags.skip.char).toBe('s');
|
|
67
67
|
});
|
|
68
|
+
it('should expose --suffix on every verb to reach entity sub-routes', () => {
|
|
69
|
+
expect(create_1.default.flags.suffix).toBeDefined();
|
|
70
|
+
expect(get_1.default.flags.suffix).toBeDefined();
|
|
71
|
+
expect(update_1.default.flags.suffix).toBeDefined();
|
|
72
|
+
expect(delete_1.default.flags.suffix).toBeDefined();
|
|
73
|
+
});
|
|
68
74
|
});
|
|
69
75
|
});
|
|
@@ -7,6 +7,7 @@ export default class UpdateEntity extends BaseCommand {
|
|
|
7
7
|
dryRun: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
8
|
inputFile: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
9
9
|
force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
suffix: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
10
11
|
alwaysConfirmEnv: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
11
12
|
logLevel: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
12
13
|
};
|
|
@@ -11,7 +11,7 @@ const common_1 = require("../../common");
|
|
|
11
11
|
const propertySpecifications_1 = require("../../helpers/propertySpecifications/propertySpecifications");
|
|
12
12
|
const file_helpers_1 = require("../../helpers/file-helpers");
|
|
13
13
|
class UpdateEntity extends basecommand_1.BaseCommand {
|
|
14
|
-
static description = '
|
|
14
|
+
static description = 'Update an entity';
|
|
15
15
|
static examples = [
|
|
16
16
|
'contrail entities update color 123 -- name=obsidian hex=000000',
|
|
17
17
|
'contrail entities update item id1 id2 id3 -- status=archived # apply the same change to several entities',
|
|
@@ -41,6 +41,10 @@ class UpdateEntity extends basecommand_1.BaseCommand {
|
|
|
41
41
|
description: 'Force update without validation. Internal use only',
|
|
42
42
|
required: false,
|
|
43
43
|
}),
|
|
44
|
+
suffix: core_1.Flags.string({
|
|
45
|
+
description: 'Append a sub-route to the entity endpoint (e.g. --suffix=synchronous targets /<entities>/synchronous)',
|
|
46
|
+
required: false,
|
|
47
|
+
}),
|
|
44
48
|
};
|
|
45
49
|
static args = {
|
|
46
50
|
entityName: core_1.Args.string({
|
|
@@ -55,11 +59,10 @@ class UpdateEntity extends basecommand_1.BaseCommand {
|
|
|
55
59
|
static strict = false;
|
|
56
60
|
async run() {
|
|
57
61
|
(0, common_1.routeConsoleLogToStderr)();
|
|
58
|
-
console.error('🚨 This command is in BETA and may change or be removed at any time.');
|
|
59
62
|
await (0, login_1.authUser)();
|
|
60
63
|
const { args, argv } = await this.parse(UpdateEntity);
|
|
61
64
|
const { entityName } = args;
|
|
62
|
-
const { dryRun: isDryRun, inputFile: inputFilename, skipHydrateRelationships, force, } = this.flags;
|
|
65
|
+
const { dryRun: isDryRun, inputFile: inputFilename, skipHydrateRelationships, suffix, force, } = this.flags;
|
|
63
66
|
const { ids: positionalIds, specs } = (0, propertySpecifications_1.partitionIdsAndSpecs)(argv.slice(1));
|
|
64
67
|
const propertySpec = (0, propertySpecifications_1.parsePropertySpecificationsIntoMap)(specs);
|
|
65
68
|
if (propertySpec.has('id') || propertySpec.has('ids')) {
|
|
@@ -89,7 +92,11 @@ class UpdateEntity extends basecommand_1.BaseCommand {
|
|
|
89
92
|
const id = entity.id;
|
|
90
93
|
return isDryRun
|
|
91
94
|
? mockUpdateById(entityName, id, newEntityState, { skipHydrateRelationships })
|
|
92
|
-
: updateEntityById(entityName, id, newEntityState, {
|
|
95
|
+
: updateEntityById(entityName, id, newEntityState, {
|
|
96
|
+
skipHydrateRelationships,
|
|
97
|
+
suffix,
|
|
98
|
+
force,
|
|
99
|
+
});
|
|
93
100
|
}
|
|
94
101
|
try {
|
|
95
102
|
const limit = (0, p_limit_1.default)(20);
|
|
@@ -131,8 +138,8 @@ function summarizeSettledUpdates(inputs, settled, skipped) {
|
|
|
131
138
|
exports.summarizeSettledUpdates = summarizeSettledUpdates;
|
|
132
139
|
async function updateEntityById(entityName, id, changes, options = {}) {
|
|
133
140
|
const client = new sdk_1.Entities();
|
|
134
|
-
const { skipHydrateRelationships, force } = options;
|
|
135
|
-
return client.update({ entityName, id, object: changes, skipHydrateRelationships });
|
|
141
|
+
const { skipHydrateRelationships, suffix, force } = options;
|
|
142
|
+
return client.update({ entityName, id, object: changes, skipHydrateRelationships, suffix });
|
|
136
143
|
}
|
|
137
144
|
async function mockUpdateById(entityName, id, changes, options = {}) {
|
|
138
145
|
const client = new sdk_1.Entities();
|
|
@@ -31,8 +31,7 @@ export declare function parsePropertySpecification(spec: PropertySpecificationSt
|
|
|
31
31
|
export declare function parsePropertySpecificationsIntoMap(propertySpecifications: PropertySpecificationString[]): PropertySpecificationBySlug;
|
|
32
32
|
/**
|
|
33
33
|
* Splits command argv tokens (after the typePath) into positional ids and
|
|
34
|
-
* property specifications.
|
|
35
|
-
* candidate; every other token is an id.
|
|
34
|
+
* property specifications.
|
|
36
35
|
*/
|
|
37
36
|
export declare function partitionIdsAndSpecs(tokens: string[]): {
|
|
38
37
|
ids: string[];
|
|
@@ -71,8 +71,6 @@ function parsePropertySpecificationsIntoMap(propertySpecifications) {
|
|
|
71
71
|
propertySpecBySlug.set(slug, idsArray);
|
|
72
72
|
continue;
|
|
73
73
|
}
|
|
74
|
-
// Repeating a key accumulates values into an array; commas in every other
|
|
75
|
-
// value are literal.
|
|
76
74
|
const existing = propertySpecBySlug.get(slug);
|
|
77
75
|
if (propertySpecBySlug.has(slug) && existing !== undefined && value !== undefined) {
|
|
78
76
|
propertySpecBySlug.set(slug, Array.isArray(existing) ? [...existing, value] : [existing, value]);
|
|
@@ -85,8 +83,7 @@ function parsePropertySpecificationsIntoMap(propertySpecifications) {
|
|
|
85
83
|
exports.parsePropertySpecificationsIntoMap = parsePropertySpecificationsIntoMap;
|
|
86
84
|
/**
|
|
87
85
|
* Splits command argv tokens (after the typePath) into positional ids and
|
|
88
|
-
* property specifications.
|
|
89
|
-
* candidate; every other token is an id.
|
|
86
|
+
* property specifications.
|
|
90
87
|
*/
|
|
91
88
|
function partitionIdsAndSpecs(tokens) {
|
|
92
89
|
return {
|
package/dist/logger.spec.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const logger_1 = tslib_1.__importDefault(require("./logger"));
|
|
5
5
|
describe('logger', () => {
|
|
6
|
-
it('
|
|
6
|
+
it('should write every log level to stderr so stdout carries only command output', () => {
|
|
7
7
|
const consoleTransport = logger_1.default.transports[0];
|
|
8
8
|
for (const level of ['error', 'warn', 'info', 'debug']) {
|
|
9
9
|
expect(consoleTransport.stderrLevels[level]).toBe(true);
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.6.0-alpha-8-alpha.
|
|
2
|
+
"version": "3.6.0-alpha-8-alpha.7d24daa",
|
|
3
3
|
"commands": {
|
|
4
4
|
"app:create": {
|
|
5
5
|
"id": "app:create",
|
|
@@ -675,7 +675,7 @@
|
|
|
675
675
|
},
|
|
676
676
|
"entities:create": {
|
|
677
677
|
"id": "entities:create",
|
|
678
|
-
"description": "
|
|
678
|
+
"description": "Create entities",
|
|
679
679
|
"strict": false,
|
|
680
680
|
"pluginName": "@contrail/cli",
|
|
681
681
|
"pluginAlias": "@contrail/cli",
|
|
@@ -775,6 +775,13 @@
|
|
|
775
775
|
"description": "If true, does not hydrate relationships for the created entities",
|
|
776
776
|
"required": false,
|
|
777
777
|
"allowNo": false
|
|
778
|
+
},
|
|
779
|
+
"suffix": {
|
|
780
|
+
"name": "suffix",
|
|
781
|
+
"type": "option",
|
|
782
|
+
"description": "Append a sub-route to the entity endpoint (e.g. --suffix=synchronous targets /<entities>/synchronous)",
|
|
783
|
+
"required": false,
|
|
784
|
+
"multiple": false
|
|
778
785
|
}
|
|
779
786
|
},
|
|
780
787
|
"args": {
|
|
@@ -787,7 +794,7 @@
|
|
|
787
794
|
},
|
|
788
795
|
"entities:delete": {
|
|
789
796
|
"id": "entities:delete",
|
|
790
|
-
"description": "
|
|
797
|
+
"description": "Delete entities by id",
|
|
791
798
|
"strict": false,
|
|
792
799
|
"pluginName": "@contrail/cli",
|
|
793
800
|
"pluginAlias": "@contrail/cli",
|
|
@@ -841,6 +848,13 @@
|
|
|
841
848
|
"description": "A file with a JSON array of objects; only each object's id is used. Deletes every entity in the file.",
|
|
842
849
|
"required": false,
|
|
843
850
|
"multiple": false
|
|
851
|
+
},
|
|
852
|
+
"suffix": {
|
|
853
|
+
"name": "suffix",
|
|
854
|
+
"type": "option",
|
|
855
|
+
"description": "Append a sub-route to the entity endpoint (e.g. --suffix=synchronous targets /<entities>/synchronous)",
|
|
856
|
+
"required": false,
|
|
857
|
+
"multiple": false
|
|
844
858
|
}
|
|
845
859
|
},
|
|
846
860
|
"args": {
|
|
@@ -858,7 +872,7 @@
|
|
|
858
872
|
},
|
|
859
873
|
"entities:get": {
|
|
860
874
|
"id": "entities:get",
|
|
861
|
-
"description": "
|
|
875
|
+
"description": "Get an entity",
|
|
862
876
|
"strict": false,
|
|
863
877
|
"pluginName": "@contrail/cli",
|
|
864
878
|
"pluginAlias": "@contrail/cli",
|
|
@@ -955,6 +969,13 @@
|
|
|
955
969
|
"description": "Comma-separated list of relations to include in the output",
|
|
956
970
|
"required": false,
|
|
957
971
|
"multiple": false
|
|
972
|
+
},
|
|
973
|
+
"suffix": {
|
|
974
|
+
"name": "suffix",
|
|
975
|
+
"type": "option",
|
|
976
|
+
"description": "Append a sub-route to the entity endpoint (e.g. --suffix=synchronous targets /<entities>/synchronous)",
|
|
977
|
+
"required": false,
|
|
978
|
+
"multiple": false
|
|
958
979
|
}
|
|
959
980
|
},
|
|
960
981
|
"args": {
|
|
@@ -972,7 +993,7 @@
|
|
|
972
993
|
},
|
|
973
994
|
"entities:update": {
|
|
974
995
|
"id": "entities:update",
|
|
975
|
-
"description": "
|
|
996
|
+
"description": "Update an entity",
|
|
976
997
|
"strict": false,
|
|
977
998
|
"pluginName": "@contrail/cli",
|
|
978
999
|
"pluginAlias": "@contrail/cli",
|
|
@@ -1042,6 +1063,13 @@
|
|
|
1042
1063
|
"description": "Force update without validation. Internal use only",
|
|
1043
1064
|
"required": false,
|
|
1044
1065
|
"allowNo": false
|
|
1066
|
+
},
|
|
1067
|
+
"suffix": {
|
|
1068
|
+
"name": "suffix",
|
|
1069
|
+
"type": "option",
|
|
1070
|
+
"description": "Append a sub-route to the entity endpoint (e.g. --suffix=synchronous targets /<entities>/synchronous)",
|
|
1071
|
+
"required": false,
|
|
1072
|
+
"multiple": false
|
|
1045
1073
|
}
|
|
1046
1074
|
},
|
|
1047
1075
|
"args": {
|