Skip to content

Commit 5f991ee

Browse files
feat: add positional arguments support to get commands (#489)
* feat: add positional arguments support to get commands * fix: dedupe environments get by using shared parseKeysFromArgs * test: remove unused mocks from get command tests
1 parent 99ff980 commit 5f991ee

File tree

9 files changed

+267
-19
lines changed

9 files changed

+267
-19
lines changed

oclif.manifest.json

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "5.21.2",
2+
"version": "6.0.0",
33
"commands": {
44
"authCommand": {
55
"id": "authCommand",
@@ -1194,7 +1194,7 @@
11941194
"environments:get": {
11951195
"id": "environments:get",
11961196
"description": "Retrieve Environments from the management API",
1197-
"strict": true,
1197+
"strict": false,
11981198
"pluginName": "@devcycle/cli",
11991199
"pluginAlias": "@devcycle/cli",
12001200
"pluginType": "core",
@@ -1203,6 +1203,9 @@
12031203
"hiddenAliases": [],
12041204
"examples": [
12051205
"<%= config.bin %> <%= command.id %>",
1206+
"<%= config.bin %> <%= command.id %> environment-one",
1207+
"<%= config.bin %> <%= command.id %> environment-one environment-two",
1208+
"<%= config.bin %> <%= command.id %> environment-one,environment-two",
12061209
"<%= config.bin %> <%= command.id %> --keys=environment-one,environment-two"
12071210
],
12081211
"flags": {
@@ -1295,7 +1298,13 @@
12951298
"multiple": false
12961299
}
12971300
},
1298-
"args": {}
1301+
"args": {
1302+
"keys": {
1303+
"name": "keys",
1304+
"description": "Environment keys to fetch (space-separated or comma-separated)",
1305+
"required": false
1306+
}
1307+
}
12991308
},
13001309
"environments:list": {
13011310
"id": "environments:list",
@@ -1761,7 +1770,7 @@
17611770
"features:get": {
17621771
"id": "features:get",
17631772
"description": "Retrieve Features from the Management API",
1764-
"strict": true,
1773+
"strict": false,
17651774
"pluginName": "@devcycle/cli",
17661775
"pluginAlias": "@devcycle/cli",
17671776
"pluginType": "core",
@@ -1770,6 +1779,9 @@
17701779
"hiddenAliases": [],
17711780
"examples": [
17721781
"<%= config.bin %> <%= command.id %>",
1782+
"<%= config.bin %> <%= command.id %> feature-one",
1783+
"<%= config.bin %> <%= command.id %> feature-one feature-two",
1784+
"<%= config.bin %> <%= command.id %> feature-one,feature-two",
17731785
"<%= config.bin %> <%= command.id %> --keys=feature-one,feature-two"
17741786
],
17751787
"flags": {
@@ -1880,7 +1892,13 @@
18801892
"multiple": false
18811893
}
18821894
},
1883-
"args": {}
1895+
"args": {
1896+
"keys": {
1897+
"name": "keys",
1898+
"description": "Feature keys to fetch (space-separated or comma-separated)",
1899+
"required": false
1900+
}
1901+
}
18841902
},
18851903
"features:list": {
18861904
"id": "features:list",
@@ -5198,13 +5216,21 @@
51985216
},
51995217
"variables:get": {
52005218
"id": "variables:get",
5201-
"strict": true,
5219+
"description": "Retrieve Variables from the Management API",
5220+
"strict": false,
52025221
"pluginName": "@devcycle/cli",
52035222
"pluginAlias": "@devcycle/cli",
52045223
"pluginType": "core",
52055224
"hidden": false,
52065225
"aliases": [],
52075226
"hiddenAliases": [],
5227+
"examples": [
5228+
"<%= config.bin %> <%= command.id %>",
5229+
"<%= config.bin %> <%= command.id %> var-one",
5230+
"<%= config.bin %> <%= command.id %> var-one var-two",
5231+
"<%= config.bin %> <%= command.id %> var-one,var-two",
5232+
"<%= config.bin %> <%= command.id %> --keys=var-one,var-two"
5233+
],
52085234
"flags": {
52095235
"config-path": {
52105236
"name": "config-path",
@@ -5313,7 +5339,13 @@
53135339
"multiple": false
53145340
}
53155341
},
5316-
"args": {}
5342+
"args": {
5343+
"keys": {
5344+
"name": "keys",
5345+
"description": "Variable keys to fetch (space-separated or comma-separated)",
5346+
"required": false
5347+
}
5348+
}
53175349
},
53185350
"variables:list": {
53195351
"id": "variables:list",
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { expect } from '@oclif/test'
2+
import { dvcTest } from '../../../test-utils'
3+
import { BASE_URL } from '../../api/common'
4+
5+
describe('environments get', () => {
6+
const projectKey = 'test-project'
7+
const authFlags = [
8+
'--client-id',
9+
'test-client-id',
10+
'--client-secret',
11+
'test-client-secret',
12+
]
13+
14+
const mockEnvironments = [
15+
{
16+
key: 'development',
17+
name: 'Development',
18+
_id: '61450f3daec96f5cf4a49960',
19+
},
20+
{
21+
key: 'production',
22+
name: 'Production',
23+
_id: '61450f3daec96f5cf4a49961',
24+
},
25+
]
26+
27+
dvcTest()
28+
.nock(BASE_URL, (api) =>
29+
api
30+
.get(`/v1/projects/${projectKey}/environments`)
31+
.reply(200, mockEnvironments),
32+
)
33+
.stdout()
34+
.command([
35+
'environments get',
36+
'--project',
37+
projectKey,
38+
'--headless',
39+
...authFlags,
40+
])
41+
.it('returns a list of environment objects in headless mode', (ctx) => {
42+
expect(ctx.stdout).to.contain(JSON.stringify(mockEnvironments))
43+
})
44+
45+
// Test positional arguments functionality
46+
dvcTest()
47+
.nock(BASE_URL, (api) =>
48+
api
49+
.get(`/v1/projects/${projectKey}/environments/development`)
50+
.reply(200, mockEnvironments[0])
51+
.get(`/v1/projects/${projectKey}/environments/production`)
52+
.reply(200, mockEnvironments[1]),
53+
)
54+
.stdout()
55+
.command([
56+
'environments get',
57+
'development',
58+
'production',
59+
'--project',
60+
projectKey,
61+
...authFlags,
62+
])
63+
.it(
64+
'fetches multiple environments by space-separated positional arguments',
65+
(ctx) => {
66+
expect(ctx.stdout).to.contain(
67+
JSON.stringify(mockEnvironments, null, 2),
68+
)
69+
},
70+
)
71+
})

src/commands/environments/get.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Flags } from '@oclif/core'
1+
import { Args, Flags } from '@oclif/core'
22
import inquirer from '../../ui/autocomplete'
33
import {
44
fetchEnvironments,
@@ -7,14 +7,26 @@ import {
77
import { EnvironmentPromptResult, environmentPrompt } from '../../ui/prompts'
88
import Base from '../base'
99
import { batchRequests } from '../../utils/batchRequests'
10+
import { parseKeysFromArgs } from '../../utils/parseKeysFromArgs'
1011

1112
export default class DetailedEnvironments extends Base {
1213
static hidden = false
1314
static description = 'Retrieve Environments from the management API'
1415
static examples = [
1516
'<%= config.bin %> <%= command.id %>',
17+
'<%= config.bin %> <%= command.id %> environment-one',
18+
'<%= config.bin %> <%= command.id %> environment-one environment-two',
19+
'<%= config.bin %> <%= command.id %> environment-one,environment-two',
1620
'<%= config.bin %> <%= command.id %> --keys=environment-one,environment-two',
1721
]
22+
static args = {
23+
keys: Args.string({
24+
description:
25+
'Environment keys to fetch (space-separated or comma-separated)',
26+
required: false,
27+
}),
28+
}
29+
static strict = false
1830
static flags = {
1931
...Base.flags,
2032
keys: Flags.string({
@@ -25,12 +37,13 @@ export default class DetailedEnvironments extends Base {
2537
authRequired = true
2638

2739
public async run(): Promise<void> {
28-
const { flags } = await this.parse(DetailedEnvironments)
29-
const keys = flags['keys']?.split(',')
40+
const { args, argv, flags } = await this.parse(DetailedEnvironments)
3041
const { headless, project } = flags
3142
await this.requireProject(project, headless)
3243

33-
if (keys) {
44+
const keys = parseKeysFromArgs(args, argv, flags)
45+
46+
if (keys && keys.length > 0) {
3447
const environments = await batchRequests(keys, (key) =>
3548
fetchEnvironmentByKey(this.authToken, this.projectKey, key),
3649
)

src/commands/features/get.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,32 @@ describe('features get', () => {
7070
.it('passes search param to api', (ctx) => {
7171
verifyOutput(JSON.parse(ctx.stdout))
7272
})
73+
74+
// Test positional arguments functionality
75+
dvcTest()
76+
.nock(BASE_URL, (api) =>
77+
api
78+
.get(`/v2/projects/${projectKey}/features/feature-1`)
79+
.reply(200, mockFeatures[0])
80+
.get(`/v2/projects/${projectKey}/features/feature-2`)
81+
.reply(200, mockFeatures[1]),
82+
)
83+
.stdout()
84+
.command([
85+
'features get',
86+
'feature-1',
87+
'feature-2',
88+
'--project',
89+
projectKey,
90+
...authFlags,
91+
])
92+
.it(
93+
'fetches multiple features by space-separated positional arguments',
94+
(ctx) => {
95+
expect(JSON.parse(ctx.stdout)).to.deep.equal([
96+
mockFeatures[0],
97+
mockFeatures[1],
98+
])
99+
},
100+
)
73101
})

src/commands/features/get.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
1-
import { Flags } from '@oclif/core'
1+
import { Args, Flags } from '@oclif/core'
22
import { fetchFeatures, fetchFeatureByKey } from '../../api/features'
33
import Base from '../base'
44
import { batchRequests } from '../../utils/batchRequests'
5+
import { parseKeysFromArgs } from '../../utils/parseKeysFromArgs'
56

67
export default class DetailedFeatures extends Base {
78
static hidden = false
89
static description = 'Retrieve Features from the Management API'
910
static examples = [
1011
'<%= config.bin %> <%= command.id %>',
12+
'<%= config.bin %> <%= command.id %> feature-one',
13+
'<%= config.bin %> <%= command.id %> feature-one feature-two',
14+
'<%= config.bin %> <%= command.id %> feature-one,feature-two',
1115
'<%= config.bin %> <%= command.id %> --keys=feature-one,feature-two',
1216
]
17+
static args = {
18+
keys: Args.string({
19+
description:
20+
'Feature keys to fetch (space-separated or comma-separated)',
21+
required: false,
22+
}),
23+
}
24+
static strict = false
1325
static flags = {
1426
...Base.flags,
1527
keys: Flags.string({
@@ -29,12 +41,14 @@ export default class DetailedFeatures extends Base {
2941
authRequired = true
3042

3143
public async run(): Promise<void> {
32-
const { flags } = await this.parse(DetailedFeatures)
33-
const keys = flags['keys']?.split(',')
44+
const { args, argv, flags } = await this.parse(DetailedFeatures)
3445
const { project, headless } = flags
3546
await this.requireProject(project, headless)
47+
48+
const keys = parseKeysFromArgs(args, argv, flags)
49+
3650
let features
37-
if (keys) {
51+
if (keys && keys.length > 0) {
3852
features = await batchRequests(keys, (key) =>
3953
fetchFeatureByKey(this.authToken, this.projectKey, key),
4054
)

src/commands/variables/get.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,31 @@ describe('variables get', () => {
8383
JSON.stringify(mockVariables, null, 2),
8484
)
8585
})
86+
87+
// Test positional arguments functionality
88+
dvcTest()
89+
.nock(BASE_URL, (api) =>
90+
api
91+
.get(`/v1/projects/${projectKey}/variables/variable-1`)
92+
.reply(200, mockVariables[0])
93+
.get(`/v1/projects/${projectKey}/variables/variable-2`)
94+
.reply(200, mockVariables[1]),
95+
)
96+
.stdout()
97+
.command([
98+
'variables get',
99+
'variable-1',
100+
'variable-2',
101+
'--project',
102+
projectKey,
103+
...authFlags,
104+
])
105+
.it(
106+
'fetches multiple variables by space-separated positional arguments',
107+
(ctx) => {
108+
expect(ctx.stdout).to.contain(
109+
JSON.stringify(mockVariables, null, 2),
110+
)
111+
},
112+
)
86113
})

src/commands/variables/get.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,27 @@
1-
import { Flags } from '@oclif/core'
1+
import { Args, Flags } from '@oclif/core'
22
import { fetchVariables, fetchVariableByKey } from '../../api/variables'
33
import Base from '../base'
44
import { batchRequests } from '../../utils/batchRequests'
5+
import { parseKeysFromArgs } from '../../utils/parseKeysFromArgs'
56

67
export default class DetailedVariables extends Base {
78
static hidden = false
9+
static description = 'Retrieve Variables from the Management API'
10+
static examples = [
11+
'<%= config.bin %> <%= command.id %>',
12+
'<%= config.bin %> <%= command.id %> var-one',
13+
'<%= config.bin %> <%= command.id %> var-one var-two',
14+
'<%= config.bin %> <%= command.id %> var-one,var-two',
15+
'<%= config.bin %> <%= command.id %> --keys=var-one,var-two',
16+
]
17+
static args = {
18+
keys: Args.string({
19+
description:
20+
'Variable keys to fetch (space-separated or comma-separated)',
21+
required: false,
22+
}),
23+
}
24+
static strict = false
825
static flags = {
926
...Base.flags,
1027
keys: Flags.string({
@@ -24,13 +41,14 @@ export default class DetailedVariables extends Base {
2441
authRequired = true
2542

2643
public async run(): Promise<void> {
27-
const { flags } = await this.parse(DetailedVariables)
28-
const keys = flags['keys']?.split(',')
44+
const { args, argv, flags } = await this.parse(DetailedVariables)
2945
const { project, headless } = flags
3046
await this.requireProject(project, headless)
3147

48+
const keys = parseKeysFromArgs(args, argv, flags)
49+
3250
let variables
33-
if (keys) {
51+
if (keys && keys.length > 0) {
3452
variables = await batchRequests(keys, (key) =>
3553
fetchVariableByKey(this.authToken, this.projectKey, key),
3654
)

0 commit comments

Comments
 (0)