-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
router.go
136 lines (111 loc) · 4.04 KB
/
router.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
package router
import (
"errors"
"net/http"
"strings"
"github.com/awslabs/goformation/cloudformation"
"github.com/gorilla/mux"
//"github.com/docker/docker/api/types/mount"
)
// ErrNoEventsFound is thrown if a AWS::Serverless::Function is added to this
// router, but no API event sources exist for it.
var ErrNoEventsFound = errors.New("no events with type 'Api' were found")
// ServerlessRouter takes AWS::Serverless::Function and AWS::Serverless::API objects
// and creates a Go http.Handler with the correct paths/methods mounted
type ServerlessRouter struct {
mux *mux.Router
mounts []*ServerlessRouterMount
usePrefix bool
}
// NewServerlessRouter creates a new instance of ServerlessRouter.
// If usePrefix is true then route matching is done using prefix instead of exact match
func NewServerlessRouter(usePrefix bool) *ServerlessRouter {
return &ServerlessRouter{
mux: mux.NewRouter(),
mounts: []*ServerlessRouterMount{},
usePrefix: usePrefix,
}
}
// AddFunction adds a AWS::Serverless::Function to the router and mounts all of it's
// event sources that have type 'Api'
func (r *ServerlessRouter) AddFunction(f *cloudformation.AWSServerlessFunction, handler EventHandlerFunc) error {
// Wrap GoFormation's AWS::Serverless::Function definition in our own, which provides
// convenience methods for extracting the ServerlessRouterMount(s) from it.
function := &AWSServerlessFunction{f, handler}
mounts, err := function.Mounts()
if err != nil {
return err
}
if len(mounts) < 1 {
return ErrNoEventsFound
}
//r.mounts = append(r.mounts, mounts...)
err = r.mergeMounts(mounts)
if err != nil {
return err
}
return nil
}
// AddAPI adds a AWS::Serverless::Api resource to the router, and mounts all of it's API definition
func (r *ServerlessRouter) AddAPI(a *cloudformation.AWSServerlessApi) error {
// Wrap GoFormation's AWS::Serverless::Api definition in our own, which provides
// convenience methods for extracting the ServerlessRouterMount(s) from it.
api := &AWSServerlessApi{a}
mounts, err := api.Mounts()
if err != nil {
return err
}
//r.mounts = append(r.mounts, mounts...)
err = r.mergeMounts(mounts)
if err != nil {
return err
}
return nil
}
// merges the various mount paths. mounts could be coming from a function as well as API
// definition. Mounts defined by an API do not have a handler, only a function ARN.
func (r *ServerlessRouter) mergeMounts(newMounts []*ServerlessRouterMount) error {
for _, newMount := range newMounts {
newMountExists := false
for _, existingMount := range r.mounts {
if newMount.Path == existingMount.Path && strings.ToLower(newMount.Method) == strings.ToLower(existingMount.Method) {
newMountExists = true
// if the new mount has a valid handler I override the existing one anyway
if newMount.Handler != nil {
existingMount.Handler = newMount.Handler
existingMount.Function = newMount.Function
}
}
}
if !newMountExists {
if newMount.Handler == nil {
newMount.Handler = r.missingFunctionHandler()
}
r.mounts = append(r.mounts, newMount)
}
}
return nil
}
// AddStaticDir mounts a static directory provided, at the mount point also provided
func (r *ServerlessRouter) AddStaticDir(dirname string) {
r.mux.NotFoundHandler = http.FileServer(http.Dir(dirname))
}
// Router returns the Go http.Handler for the router, to be passed to http.ListenAndServe()
func (r *ServerlessRouter) Router() http.Handler {
// Mount all of the things!
for _, mount := range r.Mounts() {
r.mux.Handle(mount.GetMuxPath(), mount.WrappedHandler()).Methods(mount.Methods()...)
}
return r.mux
}
// Mounts returns a list of the mounts associated with this router
func (r *ServerlessRouter) Mounts() []*ServerlessRouterMount {
return r.mounts
}
func (r *ServerlessRouter) missingFunctionHandler() func(http.ResponseWriter, *Event) {
return func(w http.ResponseWriter, event *Event) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadGateway)
w.Write([]byte(`{ "message": "No function defined for resource method" }`))
}
}