Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 968abbb

Browse files
committed
chore(codersdk/toolsdk): add panic recovery tool middleware
1 parent c2db133 commit 968abbb

File tree

2 files changed

+83
-2
lines changed

2 files changed

+83
-2
lines changed

codersdk/toolsdk/toolsdk.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,31 @@ type UploadTarFileArgs struct {
150150
Files map[string]string `json:"files"`
151151
}
152152

153+
// WithRecover wraps a HandlerFunc to recover from panics and return an error.
154+
func WithRecover[Arg, Ret any](h HandlerFunc[Arg, Ret]) HandlerFunc[Arg, Ret] {
155+
return func(tb Toolbox, args Arg) (ret Ret, err error) {
156+
defer func() {
157+
if r := recover(); r != nil {
158+
err = xerrors.Errorf("tool handler panic: %v", r)
159+
}
160+
}()
161+
return h(tb, args)
162+
}
163+
}
164+
165+
// wrapAll wraps all provided tools with the given middleware function.
166+
func wrapAll(mw func(HandlerFunc[any, any]) HandlerFunc[any, any], tools ...Tool[any, any]) []Tool[any, any] {
167+
for i, t := range tools {
168+
t.Handler = mw(t.Handler)
169+
tools[i] = t
170+
}
171+
return tools
172+
}
173+
153174
var (
154175
// All is a list of all tools that can be used in the Coder CLI.
155176
// When you add a new tool, be sure to include it here!
156-
All = []Tool[any, any]{
177+
All = wrapAll(WithRecover,
157178
CreateTemplate.Generic(),
158179
CreateTemplateVersion.Generic(),
159180
CreateWorkspace.Generic(),
@@ -170,7 +191,7 @@ var (
170191
ReportTask.Generic(),
171192
UploadTarFile.Generic(),
172193
UpdateTemplateActiveVersion.Generic(),
173-
}
194+
)
174195

175196
ReportTask = Tool[ReportTaskArgs, string]{
176197
Tool: aisdk.Tool{

codersdk/toolsdk/toolsdk_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"time"
1010

1111
"github.com/google/uuid"
12+
"github.com/kylecarbs/aisdk-go"
13+
"github.com/stretchr/testify/assert"
1214
"github.com/stretchr/testify/require"
1315

1416
"github.com/coder/coder/v2/coderd/coderdtest"
@@ -379,3 +381,61 @@ func TestMain(m *testing.M) {
379381

380382
os.Exit(code)
381383
}
384+
385+
func TestWithRecovery(t *testing.T) {
386+
t.Parallel()
387+
t.Run("OK", func(t *testing.T) {
388+
t.Parallel()
389+
fakeTool := toolsdk.Tool[string, string]{
390+
Tool: aisdk.Tool{
391+
Name: "fake_tool",
392+
Description: "Returns a string for testing.",
393+
},
394+
Handler: func(tb toolsdk.Toolbox, args string) (string, error) {
395+
require.Equal(t, "test", args)
396+
return "ok", nil
397+
},
398+
}
399+
400+
wrapped := toolsdk.WithRecover(fakeTool.Handler)
401+
v, err := wrapped(nil, "test")
402+
require.NoError(t, err)
403+
require.Equal(t, "ok", v)
404+
})
405+
406+
t.Run("Error", func(t *testing.T) {
407+
t.Parallel()
408+
fakeTool := toolsdk.Tool[string, string]{
409+
Tool: aisdk.Tool{
410+
Name: "fake_tool",
411+
Description: "Returns an error for testing.",
412+
},
413+
Handler: func(tb toolsdk.Toolbox, args string) (string, error) {
414+
require.Equal(t, "test", args)
415+
return "", assert.AnError
416+
},
417+
}
418+
wrapped := toolsdk.WithRecover(fakeTool.Handler)
419+
v, err := wrapped(nil, "test")
420+
require.Empty(t, v)
421+
require.ErrorIs(t, err, assert.AnError)
422+
})
423+
424+
t.Run("Panic", func(t *testing.T) {
425+
t.Parallel()
426+
panicTool := toolsdk.Tool[string, string]{
427+
Tool: aisdk.Tool{
428+
Name: "panic_tool",
429+
Description: "Panics for testing.",
430+
},
431+
Handler: func(tb toolsdk.Toolbox, args string) (string, error) {
432+
panic("you can't sweat this fever out")
433+
},
434+
}
435+
436+
wrapped := toolsdk.WithRecover(panicTool.Handler)
437+
v, err := wrapped(nil, "disco")
438+
require.Empty(t, v)
439+
require.ErrorContains(t, err, "you can't sweat this fever out")
440+
})
441+
}

0 commit comments

Comments
 (0)