Skip to content

Commit a759da8

Browse files
committed
Strip heavy fields (e.g., versions array from service list)
1 parent 2f8f130 commit a759da8

3 files changed

Lines changed: 134 additions & 0 deletions

File tree

internal/fastly/executor.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ func ExecuteCommand(req types.CommandRequest) types.CommandResponse {
199199
cleanedOutput = SanitizeOutput(cleanedOutput, globalSanitizeOpts)
200200
}
201201

202+
// Strip heavy fields (e.g., versions array from service list) before
203+
// caching or truncation so the output stays manageable.
204+
cleanedOutput = StripHeavyFields(cleanedOutput, req.Command, req.Args)
205+
202206
response := types.CommandResponse{
203207
Command: cmdStr,
204208
CommandLine: fullCmdLine,

internal/fastly/helper.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,46 @@ func CleanANSI(text string) string {
109109
return text
110110
}
111111

112+
// StripHeavyFields removes excessively large fields from JSON command output.
113+
// Service list responses include a "versions" array per service that can reach
114+
// megabytes for accounts with many services. Use "service describe" for full
115+
// version details on a specific service.
116+
func StripHeavyFields(output string, command string, args []string) string {
117+
if command != "service" || len(args) == 0 || args[0] != "list" {
118+
return output
119+
}
120+
121+
trimmedBytes := []byte(strings.TrimSpace(output))
122+
if len(trimmedBytes) == 0 {
123+
return output
124+
}
125+
126+
var jsonData interface{}
127+
if err := json.Unmarshal(trimmedBytes, &jsonData); err != nil {
128+
return output
129+
}
130+
131+
arr, ok := jsonData.([]interface{})
132+
if !ok {
133+
return output
134+
}
135+
136+
for _, item := range arr {
137+
obj, ok := item.(map[string]interface{})
138+
if !ok {
139+
continue
140+
}
141+
delete(obj, "versions")
142+
}
143+
144+
result, err := json.Marshal(jsonData)
145+
if err != nil {
146+
return output
147+
}
148+
149+
return string(result)
150+
}
151+
112152
// TruncateOutput truncates text output to a maximum size while preserving readability.
113153
// It attempts to truncate at line boundaries within the last 1000 bytes to avoid
114154
// cutting off mid-line. Returns pagination information when truncation occurs,

internal/fastly/helper_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,96 @@ func TestIsDangerousOperation(t *testing.T) {
141141
}
142142
}
143143

144+
func TestStripHeavyFields(t *testing.T) {
145+
tests := []struct {
146+
name string
147+
output string
148+
command string
149+
args []string
150+
check func(t *testing.T, result string)
151+
}{
152+
{
153+
name: "strips versions from service list JSON",
154+
command: "service",
155+
args: []string{"list"},
156+
output: `[{"id":"svc1","name":"my-service","versions":[{"number":1},{"number":2},{"number":3}]},{"id":"svc2","name":"other","versions":[{"number":1}]}]`,
157+
check: func(t *testing.T, result string) {
158+
if strings.Contains(result, "versions") {
159+
t.Error("Expected versions to be stripped")
160+
}
161+
if !strings.Contains(result, "svc1") || !strings.Contains(result, "svc2") {
162+
t.Error("Expected service IDs to be preserved")
163+
}
164+
if !strings.Contains(result, "my-service") {
165+
t.Error("Expected service names to be preserved")
166+
}
167+
},
168+
},
169+
{
170+
name: "does not strip for non-service commands",
171+
command: "backend",
172+
args: []string{"list"},
173+
output: `[{"id":"b1","versions":[1,2,3]}]`,
174+
check: func(t *testing.T, result string) {
175+
if !strings.Contains(result, "versions") {
176+
t.Error("Expected versions to remain for non-service commands")
177+
}
178+
},
179+
},
180+
{
181+
name: "does not strip for service describe",
182+
command: "service",
183+
args: []string{"describe"},
184+
output: `{"id":"svc1","versions":[{"number":1}]}`,
185+
check: func(t *testing.T, result string) {
186+
if !strings.Contains(result, "versions") {
187+
t.Error("Expected versions to remain for service describe")
188+
}
189+
},
190+
},
191+
{
192+
name: "handles non-JSON output",
193+
command: "service",
194+
args: []string{"list"},
195+
output: "NAME ID\nmy-service svc1\n",
196+
check: func(t *testing.T, result string) {
197+
if result != "NAME ID\nmy-service svc1\n" {
198+
t.Error("Expected non-JSON output to be unchanged")
199+
}
200+
},
201+
},
202+
{
203+
name: "handles empty output",
204+
command: "service",
205+
args: []string{"list"},
206+
output: "",
207+
check: func(t *testing.T, result string) {
208+
if result != "" {
209+
t.Error("Expected empty output to be unchanged")
210+
}
211+
},
212+
},
213+
{
214+
name: "handles empty args",
215+
command: "service",
216+
args: []string{},
217+
output: `[{"id":"svc1","versions":[1]}]`,
218+
check: func(t *testing.T, result string) {
219+
if !strings.Contains(result, "versions") {
220+
t.Error("Expected versions to remain when args are empty")
221+
}
222+
},
223+
},
224+
}
225+
226+
for _, tt := range tests {
227+
t.Run(tt.name, func(t *testing.T) {
228+
result := StripHeavyFields(tt.output, tt.command, tt.args)
229+
tt.check(t, result)
230+
})
231+
}
232+
}
233+
144234
func TestTruncateOutput(t *testing.T) {
145235
tests := []struct {
146236
name string

0 commit comments

Comments
 (0)