Skip to content

Commit 6e02700

Browse files
authored
feat(REL-12755): adding --fields flag (#662)
* [feat] adding --fields flag * feat(REL-15756): updating agent friendly and improved rich text output (#663) feat(REL-15756) updating agent friendly and improved rich text output
1 parent 733df0d commit 6e02700

File tree

18 files changed

+1946
-61
lines changed

18 files changed

+1946
-61
lines changed

cmd/cliflags/flags.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ func GetOutputKind(cmd *cobra.Command) string {
1313
return viper.GetString(OutputFlag)
1414
}
1515

16+
// GetFields returns the list of fields to include in JSON output, or nil if not specified.
17+
func GetFields(cmd *cobra.Command) []string {
18+
fields, err := cmd.Root().PersistentFlags().GetStringSlice(FieldsFlag)
19+
if err != nil {
20+
return nil
21+
}
22+
return fields
23+
}
24+
1625
const (
1726
BaseURIDefault = "https://app.launchdarkly.com"
1827
DevStreamURIDefault = "https://stream.launchdarkly.com"
@@ -27,6 +36,7 @@ const (
2736
DevStreamURIFlag = "dev-stream-uri"
2837
EmailsFlag = "emails"
2938
EnvironmentFlag = "environment"
39+
FieldsFlag = "fields"
3040
FlagFlag = "flag"
3141
JSONFlag = "json"
3242
OutputFlag = "output"
@@ -42,6 +52,7 @@ const (
4252
CorsOriginFlagDescription = "Allowed CORS origin. Use '*' for all origins (default: '*')"
4353
DevStreamURIDescription = "Streaming service endpoint that the dev server uses to obtain authoritative flag data. This may be a LaunchDarkly or Relay Proxy endpoint"
4454
EnvironmentFlagDescription = "Default environment key"
55+
FieldsFlagDescription = "Comma-separated list of top-level fields to include in JSON output (e.g., --fields key,name,kind)"
4556
FlagFlagDescription = "Default feature flag key"
4657
JSONFlagDescription = "Output JSON format (shorthand for --output json)"
4758
OutputFlagDescription = "Output format: json or plaintext (default: plaintext in a terminal, json otherwise)"

cmd/config/config.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ func outputSetAction(newFields []string) (string, error) {
287287
Items: newFields,
288288
}
289289
fieldsJSON, _ := json.Marshal(fields)
290-
output, err := output.CmdOutput("update", viper.GetString(cliflags.OutputFlag), fieldsJSON)
290+
output, err := output.CmdOutput("update", viper.GetString(cliflags.OutputFlag), fieldsJSON, nil)
291291
if err != nil {
292292
return "", errs.NewError(err.Error())
293293
}
@@ -302,7 +302,7 @@ func outputUnsetAction(newField string) (string, error) {
302302
Key: newField,
303303
}
304304
fieldJSON, _ := json.Marshal(field)
305-
output, err := output.CmdOutput("delete", viper.GetString(cliflags.OutputFlag), fieldJSON)
305+
output, err := output.CmdOutput("delete", viper.GetString(cliflags.OutputFlag), fieldJSON, nil)
306306
if err != nil {
307307
return "", errs.NewError(err.Error())
308308
}

cmd/config/testdata/help.golden

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ Global Flags:
2727
--access-token string LaunchDarkly access token with write-level access
2828
--analytics-opt-out Opt out of analytics tracking
2929
--base-uri string LaunchDarkly base URI (default "https://app.launchdarkly.com")
30+
--fields strings Comma-separated list of top-level fields to include in JSON output (e.g., --fields key,name,kind)
3031
--json Output JSON format (shorthand for --output json)
3132
-o, --output string Output format: json or plaintext (default: plaintext in a terminal, json otherwise) (default "plaintext")

cmd/flags/archive.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ func makeArchiveRequest(client resources.Client) func(*cobra.Command, []string)
5151
return output.NewCmdOutputError(err, cliflags.GetOutputKind(cmd))
5252
}
5353

54-
output, err := output.CmdOutput("update", cliflags.GetOutputKind(cmd), res)
54+
output, err := output.CmdOutput("update", cliflags.GetOutputKind(cmd), res, cliflags.GetFields(cmd), output.CmdOutputOpts{
55+
ResourceName: "flags",
56+
})
5557
if err != nil {
5658
return errors.NewError(err.Error())
5759
}

cmd/flags/archive_test.go

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ func TestArchive(t *testing.T) {
1515
mockClient := &resources.MockClient{
1616
Response: []byte(`{
1717
"key": "test-flag",
18-
"name": "test flag"
18+
"name": "test flag",
19+
"kind": "boolean",
20+
"archived": true
1921
}`),
2022
}
2123

22-
t.Run("succeeds with valid inputs", func(t *testing.T) {
24+
t.Run("succeeds with plaintext output", func(t *testing.T) {
2325
args := []string{
2426
"flags", "archive",
2527
"--access-token", "abcd1234",
@@ -37,8 +39,122 @@ func TestArchive(t *testing.T) {
3739

3840
require.NoError(t, err)
3941
assert.Equal(t, `[{"op": "replace", "path": "/archived", "value": true}]`, string(mockClient.Input))
40-
assert.Equal(t, "Successfully updated test flag (test-flag)\n", string(output))
42+
assert.Contains(t, string(output), "Successfully updated\n\nKey:")
43+
assert.Contains(t, string(output), "test-flag")
44+
assert.Contains(t, string(output), "Name:")
45+
assert.Contains(t, string(output), "test flag")
46+
assert.NotContains(t, string(output), "* ")
4147
})
48+
49+
t.Run("succeeds with JSON output", func(t *testing.T) {
50+
args := []string{
51+
"flags", "archive",
52+
"--access-token", "abcd1234",
53+
"--flag", "test-flag",
54+
"--project", "test-proj",
55+
"--output", "json",
56+
}
57+
output, err := cmd.CallCmd(
58+
t,
59+
cmd.APIClients{
60+
ResourcesClient: mockClient,
61+
},
62+
analytics.NoopClientFn{}.Tracker(),
63+
args,
64+
)
65+
66+
require.NoError(t, err)
67+
assert.JSONEq(t, `{"key":"test-flag","name":"test flag","kind":"boolean","archived":true}`, string(output))
68+
})
69+
70+
t.Run("succeeds with --json shorthand", func(t *testing.T) {
71+
args := []string{
72+
"flags", "archive",
73+
"--access-token", "abcd1234",
74+
"--flag", "test-flag",
75+
"--project", "test-proj",
76+
"--json",
77+
}
78+
output, err := cmd.CallCmd(
79+
t,
80+
cmd.APIClients{
81+
ResourcesClient: mockClient,
82+
},
83+
analytics.NoopClientFn{}.Tracker(),
84+
args,
85+
)
86+
87+
require.NoError(t, err)
88+
assert.JSONEq(t, `{"key":"test-flag","name":"test flag","kind":"boolean","archived":true}`, string(output))
89+
})
90+
91+
t.Run("filters JSON output with --fields", func(t *testing.T) {
92+
args := []string{
93+
"flags", "archive",
94+
"--access-token", "abcd1234",
95+
"--flag", "test-flag",
96+
"--project", "test-proj",
97+
"--output", "json",
98+
"--fields", "key,name",
99+
}
100+
output, err := cmd.CallCmd(
101+
t,
102+
cmd.APIClients{
103+
ResourcesClient: mockClient,
104+
},
105+
analytics.NoopClientFn{}.Tracker(),
106+
args,
107+
)
108+
109+
require.NoError(t, err)
110+
assert.JSONEq(t, `{"key":"test-flag","name":"test flag"}`, string(output))
111+
})
112+
113+
t.Run("filters JSON output with --json and --fields", func(t *testing.T) {
114+
args := []string{
115+
"flags", "archive",
116+
"--access-token", "abcd1234",
117+
"--flag", "test-flag",
118+
"--project", "test-proj",
119+
"--json",
120+
"--fields", "key,name",
121+
}
122+
output, err := cmd.CallCmd(
123+
t,
124+
cmd.APIClients{
125+
ResourcesClient: mockClient,
126+
},
127+
analytics.NoopClientFn{}.Tracker(),
128+
args,
129+
)
130+
131+
require.NoError(t, err)
132+
assert.JSONEq(t, `{"key":"test-flag","name":"test flag"}`, string(output))
133+
})
134+
135+
t.Run("ignores --fields with plaintext output", func(t *testing.T) {
136+
args := []string{
137+
"flags", "archive",
138+
"--access-token", "abcd1234",
139+
"--flag", "test-flag",
140+
"--project", "test-proj",
141+
"--fields", "key",
142+
}
143+
output, err := cmd.CallCmd(
144+
t,
145+
cmd.APIClients{
146+
ResourcesClient: mockClient,
147+
},
148+
analytics.NoopClientFn{}.Tracker(),
149+
args,
150+
)
151+
152+
require.NoError(t, err)
153+
assert.Contains(t, string(output), "Successfully updated")
154+
assert.Contains(t, string(output), "Key:")
155+
assert.Contains(t, string(output), "test-flag")
156+
})
157+
42158
t.Run("returns error with missing flags", func(t *testing.T) {
43159
args := []string{
44160
"flags", "archive",
@@ -55,6 +171,6 @@ func TestArchive(t *testing.T) {
55171
)
56172

57173
assert.Error(t, err)
58-
assert.Equal(t, "required flag(s) \"project\" not set. See `ldcli flags archive --help` for supported flags and usage.", err.Error())
174+
assert.Contains(t, err.Error(), `required flag(s) "project" not set`)
59175
})
60176
}

cmd/flags/toggle.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ func runE(client resources.Client) func(*cobra.Command, []string) error {
7373
return output.NewCmdOutputError(err, cliflags.GetOutputKind(cmd))
7474
}
7575

76-
output, err := output.CmdOutput("update", cliflags.GetOutputKind(cmd), res)
76+
output, err := output.CmdOutput("update", cliflags.GetOutputKind(cmd), res, cliflags.GetFields(cmd), output.CmdOutputOpts{
77+
ResourceName: "flags",
78+
})
7779
if err != nil {
7880
return errors.NewError(err.Error())
7981
}

0 commit comments

Comments
 (0)