-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathendpoint.go
More file actions
212 lines (178 loc) · 6.27 KB
/
endpoint.go
File metadata and controls
212 lines (178 loc) · 6.27 KB
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
package hyperdrive
import (
"fmt"
"net/http"
"strings"
"github.com/Masterminds/semver"
"github.com/gorilla/handlers"
)
// GetHandler interface is satisfied if the endpoint has implemented
// a http.Handler method called Get(). If this is not implemented,
// GET requests will be responded to with a `405 Method Not Allowed`
// error.
type GetHandler interface {
Get(http.ResponseWriter, *http.Request)
}
// PostHandler interface is satisfied if the endpoint has implemented
// a http.Handler method called Post(). If this is not implemented,
// POST requests will be responded to with a `405 Method Not Allowed`
// error.
type PostHandler interface {
Post(http.ResponseWriter, *http.Request)
}
// PutHandler interface is satisfied if the endpoint has implemented
// a http.Handler method called Put(). If this is not implemented,
// PUT requests will be responded to with a `405 Method Not Allowed`
// error.
type PutHandler interface {
Put(http.ResponseWriter, *http.Request)
}
// PatchHandler interface is satisfied if the endpoint has implemented
// a http.Handler method called Patch(). If this is not implemented,
// PATCH requests will be responded to with a `405 Method Not Allowed`
// error.
type PatchHandler interface {
Patch(http.ResponseWriter, *http.Request)
}
// DeleteHandler interface is satisfied if the endpoint has implemented
// a http.Handler method called Delete(). If this is not implemented,
// DELETE requests will be responded to with a `405 Method Not Allowed`
// error.
type DeleteHandler interface {
Delete(http.ResponseWriter, *http.Request)
}
// OptionsHandler interface is satisfied if the endpoint has implemented
// a http.Handler method called Options(). If this is not implemented,
// OPTIONS requests will be responded to with a `200 OK` and the `Allow`
// header will be set with a list of all the methods your endpoint does
// support.
type OptionsHandler interface {
Options(http.ResponseWriter, *http.Request)
}
// Endpointer interface provides flexibility in how endpoints are created
// allowing for expressiveness in how developers make use of the hyperdrive
// package.
type Endpointer interface {
GetName() string
GetDesc() string
GetPath() string
GetVersion() string
}
// Endpoint is a basic implementation of the Endpointer interface and
// can be used directly if desired.
type Endpoint struct {
EndpointName string
EndpointDesc string
EndpointPath string
EndpointVersion *semver.Version
}
// GetName satisfies part of the Endpointer interface and returns a
// string containing the name of the endpoint.
func (e *Endpoint) GetName() string {
return e.EndpointName
}
// GetDesc satisfies part of the Endpointer interface and returns a
// string containing the description of the endpoint.
func (e *Endpoint) GetDesc() string {
return e.EndpointDesc
}
// GetPath satisfies part of the Endpointer interface and returns a
// string containing the path of the endpoint, used by the Router.
//
// This string can contain named segmets, regex, and other features as
// described here: http://www.gorillatoolkit.org/pkg/mux
func (e *Endpoint) GetPath() string {
return e.EndpointPath
}
// GetVersion returns a string representing the version.
func (e *Endpoint) GetVersion() string {
var v = fmt.Sprintf("v%d", e.EndpointVersion.Major())
if (e.EndpointVersion.Major() >= 0 && e.EndpointVersion.Minor() != 0) || (e.EndpointVersion.Major() >= 0 && e.EndpointVersion.Patch() > 0) {
v = fmt.Sprintf("%s%s%d", v, ".", e.EndpointVersion.Minor())
}
if e.EndpointVersion.Patch() != 0 {
v = fmt.Sprintf("%s%s%d", v, ".", e.EndpointVersion.Patch())
}
if e.EndpointVersion.Prerelease() != "" {
v = fmt.Sprintf("%s%s%s", v, "-", e.EndpointVersion.Prerelease())
}
return v
}
// NewEndpoint creates an instance of Endpoint.
func NewEndpoint(name string, desc string, path string, version string) *Endpoint {
var (
v *semver.Version
err error
)
v, err = semver.NewVersion(version)
if err != nil {
v, _ = semver.NewVersion("1")
}
return &Endpoint{EndpointName: name, EndpointDesc: desc, EndpointPath: path, EndpointVersion: v}
}
// GetMethods returns a slice of the methods an Endpoint supports.
func GetMethods(e Endpointer) []string {
var methods = []string{"OPTIONS"}
if _, ok := interface{}(e).(GetHandler); ok {
methods = append(methods, "GET")
}
if _, ok := interface{}(e).(PostHandler); ok {
methods = append(methods, "POST")
}
if _, ok := interface{}(e).(PutHandler); ok {
methods = append(methods, "PUT")
}
if _, ok := interface{}(e).(PatchHandler); ok {
methods = append(methods, "PATCH")
}
if _, ok := interface{}(e).(DeleteHandler); ok {
methods = append(methods, "DELETE")
}
return methods
}
// GetMethodsList returns a list of the methods an Endpoint supports.
func GetMethodsList(e Endpointer) string {
return strings.Join(GetMethods(e), ", ")
}
// NewMethodHandler sets the correct http.Handler for each method, depending on
// the interfaces the Endpointer supports. It returns an http.Handler, ready
// to be served directly, wrapped in other middleware, etc.
func NewMethodHandler(e Endpointer) http.Handler {
handler := make(handlers.MethodHandler)
if h, ok := interface{}(e).(GetHandler); ok {
handler["GET"] = http.HandlerFunc(h.Get)
}
if h, ok := interface{}(e).(PostHandler); ok {
handler["POST"] = http.HandlerFunc(h.Post)
}
if h, ok := interface{}(e).(PutHandler); ok {
handler["PUT"] = http.HandlerFunc(h.Put)
}
if h, ok := interface{}(e).(PatchHandler); ok {
handler["PATCH"] = http.HandlerFunc(h.Patch)
}
if h, ok := interface{}(e).(DeleteHandler); ok {
handler["DELETE"] = http.HandlerFunc(h.Delete)
}
if h, ok := interface{}(e).(OptionsHandler); ok {
handler["OPTIONS"] = http.HandlerFunc(h.Options)
}
return handler
}
// Respond is a helper function to make it easy for an Endpointer's method
// handler (e.g. GetHandler) to respond with the appropriate Content-Type.
func Respond(rw http.ResponseWriter, r *http.Request, status int, body interface{}, headers ...http.Header) (http.ResponseWriter, *http.Request) {
var enc ContentEncoder
enc, rw = GetEncoder(rw, r.Header.Get("Accept"))
err := enc.Encode(body)
if err != nil {
http.Error(rw, err.Error(), http.StatusNotAcceptable)
// TODO: Add LOGGING
return rw, r
}
rw.WriteHeader(status)
for _, header := range headers {
header.Write(rw)
}
return rw, r
}