-
Notifications
You must be signed in to change notification settings - Fork 3
/
api.go
152 lines (122 loc) · 4.14 KB
/
api.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package torque
import (
"fmt"
"log"
"net/http"
"reflect"
"github.com/tylermmorton/tmpl"
)
// ViewModel is a type that both provides a view and represents the
// data model for the view. This is a conceptual type
type ViewModel interface{}
// HandlerModule is a conceptual type
type Controller interface{}
// Action is executed during an http POST request. Actions perform
// data mutations such as creating or updating resources and are
// usually triggered by a form submission in the browser.
type Action interface {
Action(wr http.ResponseWriter, req *http.Request) error
}
// Loader is executed during an http GET request and provides
// data to the Renderer
// It can parse URL values, attach session data, etc.
type Loader[T ViewModel] interface {
Load(req *http.Request) (T, error)
}
// Renderer is a response to an http GET that renders a template
type Renderer[T ViewModel] interface {
Render(wr http.ResponseWriter, req *http.Request, vm T) error
}
type LoaderRenderer[T ViewModel] interface {
Loader[T]
Renderer[T]
}
// DynamicRenderer is a Renderer that is not constrained by a generic type.
type DynamicRenderer interface {
Render(wr http.ResponseWriter, req *http.Request, vm ViewModel) error
}
// EventSource is a server-sent event stream. It is used to stream data to the
// client in real-time.
type EventSource interface {
Subscribe(wr http.ResponseWriter, req *http.Request) error
}
// ErrorBoundary handles all errors returned by read and write operations in a .
type ErrorBoundary interface {
ErrorBoundary(wr http.ResponseWriter, req *http.Request, err error) http.HandlerFunc
}
// PanicBoundary is a panic recovery handler. It catches any unhandled errors.
//
// If a handler is not returned to redirect the request, a stack trace is printed
// to the server logs.
type PanicBoundary interface {
PanicBoundary(wr http.ResponseWriter, req *http.Request, err error) http.HandlerFunc
}
// TODO(v2.1) Context driven boundaries may be useful in some scenarios
//type CancelBoundary interface {
// CancelBoundary(wr http.ResponseWriter, req *http.Request) http.HandlerFunc
//}
// TODO(v2.1) Each controller can specify a CORS configuration that applies to its subtree
//type CORSProvider interface {
// CORS() []string
//}
// TODO(v2.1) Each controller can supply a filesystem for serving static files
//type FileSystemProvider interface {
// FileSystem() embed.FS
//}
// RouterProvider is executed when the torque TestTemplateModule is initialized. It can
// return a list of components to be nested in the current route. The parent
// route path will be prefixed to any provided paths in the SubRouter.
type RouterProvider interface {
Router(r Router)
}
type GuardProvider interface {
Guards() []Guard
}
type PluginProvider interface {
Plugins() []Plugin
}
func assertImplementations[T ViewModel](h *handlerImpl[T], ctl Controller, vm ViewModel) error {
var err error
// check if the controller is a pointer before asserting any types.
if reflect.ValueOf(ctl).Kind() != reflect.Ptr {
return fmt.Errorf("controller type %T is not a pointer", ctl)
}
if loader, ok := ctl.(Loader[T]); ok {
h.loader = loader
}
// explicit Renderer implementations take precedence
if renderer, ok := ctl.(Renderer[T]); ok {
h.rendererT = renderer
} else if tp, ok := vm.(tmpl.TemplateProvider); ok {
h.rendererT, err = createTemplateRenderer[T](tp)
if err != nil {
return err
}
}
if action, ok := ctl.(Action); ok {
h.action = action
}
if eventSource, ok := ctl.(EventSource); ok {
h.eventSource = eventSource
}
if errorBoundary, ok := ctl.(ErrorBoundary); ok {
h.errorBoundary = errorBoundary
}
if panicBoundary, ok := ctl.(PanicBoundary); ok {
h.panicBoundary = panicBoundary
}
if _, ok := ctl.(RouterProvider); ok {
h.router = createRouterProvider[T](h, ctl)
if h.router != nil && h.mode == ModeDevelopment {
log.Printf("-- RouterProvider(%s) --", h.path)
logRoutes("/", h.router.Routes())
}
}
if guardProvider, ok := ctl.(GuardProvider); ok {
h.guards = append(h.guards, guardProvider.Guards()...)
}
if pluginProvider, ok := ctl.(PluginProvider); ok {
h.plugins = append(h.plugins, pluginProvider.Plugins()...)
}
return nil
}