-
Notifications
You must be signed in to change notification settings - Fork 3
/
group.go
118 lines (95 loc) · 2.87 KB
/
group.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
package fire
import (
"net/http"
"strings"
"github.com/256dpi/jsonapi"
"github.com/256dpi/stack"
"gopkg.in/mgo.v2/bson"
)
// TODO: Pull in CORS from wood an make it a first class citizen.
// TODO: Add custom resources.
// A Group manages access to multiple controllers and their interconnections.
type Group struct {
prefix string
controllers map[string]*Controller
// The function gets invoked by the controller with occurring fatal errors.
Reporter func(error)
}
// NewGroup creates and returns a new group. The specified prefix is used to
// parse the requests and generate urls for the resources.
func NewGroup(prefix string) *Group {
return &Group{
prefix: prefix,
controllers: make(map[string]*Controller),
}
}
// Add will add a controller to the group.
func (g *Group) Add(controllers ...*Controller) {
for _, controller := range controllers {
// prepare controller
controller.prepare()
// get name
name := controller.Model.Meta().PluralName
// create entry in controller map
g.controllers[name] = controller
}
}
// List will return a list of all added controllers.
func (g *Group) List() []*Controller {
list := make([]*Controller, 0, len(g.controllers))
for _, c := range g.controllers {
list = append(list, c)
}
return list
}
// Find will return the controller with the matching plural name if found.
func (g *Group) Find(pluralName string) *Controller {
return g.controllers[pluralName]
}
// Endpoint will return an http handler that serves requests for this group.
func (g *Group) Endpoint() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// create tracer
tracer := NewTracerFromRequest(r, "fire/Group.Endpoint")
defer tracer.Finish(true)
// continue any previous aborts
defer stack.Resume(func(err error) {
// directly write jsonapi errors
if jsonapiError, ok := err.(*jsonapi.Error); ok {
jsonapi.WriteError(w, jsonapiError)
return
}
// set critical error on last span
tracer.Tag("error", true)
tracer.Log("error", err.Error())
tracer.Log("stack", stack.Trace())
// report critical errors if possible
if g.Reporter != nil {
g.Reporter(err)
}
// ignore errors caused by writing critical errors
jsonapi.WriteError(w, jsonapi.InternalServerError(""))
})
// trim and split path
s := strings.Split(strings.Trim(strings.TrimPrefix(r.URL.Path, g.prefix), "/"), "/")
// check segments
if len(s) == 0 {
stack.Abort(jsonapi.NotFound("resource not found"))
}
// get controller
controller, ok := g.controllers[s[0]]
if !ok {
stack.Abort(jsonapi.NotFound("resource not found"))
}
// call controller with context
controller.generalHandler(&Context{
Selector: bson.M{},
Filter: bson.M{},
HTTPRequest: r,
ResponseWriter: w,
Controller: controller,
Group: g,
Tracer: tracer,
})
})
}