-
Notifications
You must be signed in to change notification settings - Fork 1
/
routes.go
275 lines (241 loc) · 10.5 KB
/
routes.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
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
package main
import (
"github.com/ant0ine/go-json-rest/rest"
"strconv"
"time"
)
// API: Shows the partitions managed by this server
func showPartitions(w rest.ResponseWriter, r *rest.Request) {
res := NewHypermediaResource()
res.AddCurie("partitions", "/docs/rels/{rel}", true)
res.Links["self"] = HypermediaLink{
Href: "/partitions",
}
res.Links["partitions:add"] = HypermediaLink{
Href: "/partitions/add",
}
res.Links["partitions:delete"] = HypermediaLink{
Href: "/partitions/delete/{server}/{partition}",
Templated: true,
}
res.Links["partitions:read"] = HypermediaLink{
Href: "/partitions/read/{server}/{partition}",
Templated: true,
}
type partitionInfo struct {
Name string `json:"name"`
Partition Partition `json:"partition"`
Host string `json:"host"`
Database string `json:"database"`
Port string `json:"port"`
}
partitions := []partitionInfo{}
for _, s := range cfg.Servers {
for k, v := range s.Partitions {
partitions = append(partitions, partitionInfo{
Name: k,
Partition: v,
Host: s.Host,
Database: s.Database,
Port: s.Port,
})
}
}
res.Data["totalPartitions"] = len(partitions)
res.Data["partitions"] = partitions
res.Success()
w.WriteJson(res.End("There are " + strconv.Itoa(len(partitions)) + " partitions managed."))
}
// API: Shows the maintenance schedule as currently configured
func showSchedule(w rest.ResponseWriter, r *rest.Request) {
res := NewHypermediaResource()
res.AddCurie("schedule", "/docs/rels/{rel}", true)
res.Links["self"] = HypermediaLink{
Href: "/schedule",
}
res.Links["schedule:add"] = HypermediaLink{
Href: "/schedule/add",
}
res.Links["schedule:delete"] = HypermediaLink{
Href: "/schedule/delete/{id}",
Templated: true,
}
jobs := []map[string]interface{}{}
for _, item := range c.Entries() {
m := make(map[string]interface{})
m["id"] = item.Id
m["name"] = item.Name
m["next"] = item.Next
m["prev"] = item.Prev
m["job"] = getFunctionName(item.Job)
jobs = append(jobs, m)
}
res.Data["totalJobs"] = len(jobs)
res.Data["jobs"] = jobs
res.Success()
w.WriteJson(res.End("There are " + strconv.Itoa(len(jobs)) + " jobs scheduled."))
}
// API: Shows detailed information about a specific partition
func showPartition(w rest.ResponseWriter, r *rest.Request) {
res := NewHypermediaResource()
res.Links["self"] = HypermediaLink{
Href: "/partition/{server}/{partition}",
}
partitionName := r.PathParam("partition")
serverName := r.PathParam("server")
// queryParams := r.URL.Query()
db, partition, err := GetPartition(serverName, partitionName)
if err == nil {
children := db.GetChildPartitions(partition)
res.Data["totalChildren"] = len(children)
res.Data["children"] = children
res.Data["config"] = db.PartitionInfo(partition)
res.Success()
w.WriteJson(res.End("There are " + strconv.Itoa(len(children)) + " children for this partition."))
} else {
l.Error(err)
w.WriteJson(res.End("The partition was not found."))
}
}
// API: Shows just the configuration for a specific partition
func showPartitionConfig(w rest.ResponseWriter, r *rest.Request) {
res := NewHypermediaResource()
res.Links["self"] = HypermediaLink{
Href: "/partition/{server}/{partition}/config",
}
partitionName := r.PathParam("partition")
serverName := r.PathParam("server")
db, partition, err := GetPartition(serverName, partitionName)
if err == nil {
res.Data["config"] = db.PartitionInfo(partition)
res.Data["maintenanceJobId"] = partition.MaintenanceJobId
for _, item := range c.Entries() {
if item.Id == partition.MaintenanceJobId {
res.Data["nextScheduledMaintenance"] = item.Next
}
}
res.Success()
w.WriteJson(res.End("The partition was found."))
} else {
l.Error(err)
w.WriteJson(res.End("The partition was not found."))
}
}
// Inspired by a few hypermedia formats, this is a structure for Social Harvest API responses.
// Storing data into Social Harvest is easy...Getting it back out and having other widgets for the dashboard be able to talk with the API is the hard part.
// So a self documenting API that can be navigated automatically is super handy.
// NOTE: This is going to change a good bit at first.
// A resource is the root level item being returned. It can contain embedded resources if necessary. It's possible to return more than one resource at a time too (though won't be common).
// Within each resource there is "_meta" data
type HypermediaResource struct {
Meta HypermediaMeta `json:"_meta"`
Links map[string]HypermediaLink `json:"_links,omitempty"`
Curies map[string]HypermediaCurie `json:"_curies,omitempty"`
Data map[string]interface{} `json:"_data,omitempty"`
Embedded map[string]HypermediaResource `json:"_embedded,omitempty"`
Forms map[string]HypermediaForm `json:"_forms,omitempty"`
}
// The Meta structure provides some common information helpful to the application and also resource state.
type HypermediaMeta struct {
startTime time.Time
Success bool `json:"success"`
Message string `json:"message"`
ResponseTime float32 `json:"responseTime,omitempty"`
To string `json:"to,omitempty"`
From string `json:"from,omitempty"`
}
// A simple web link structure (somewhat modeled after HAL's links and http://tools.ietf.org/html/rfc5988).
// NOTE: in HAL format, links can be an array with aliases - our format has no such support, but this doens't break HAL compatibility.
// Why not support it? Because that changes from {} to [] and changing data types is a burden for others. Plus we have HTTP 301/302.
// Also, each "_links" key name using this struct should be one of: http://www.iana.org/assignments/link-relations/link-relations.xhtml unless using CURIEs.
type HypermediaLink struct {
Href string `json:"href,omitempty"`
Type string `json:"type,omitempty"`
Deprecation string `json:"deprecation,omitempty"`
Name string `json:"name,omitempty"`
Profile string `json:"profile,omitempty"`
Title string `json:"title,omitempty"`
Hreflang string `json:"hreflang,omitempty"`
Templated bool `json:"templated,omitempty"`
}
// Defines a CURIE
type HypermediaCurie struct {
Name string `json:"name,omitempty"`
Href string `json:"href,omitempty"`
Templated bool `json:"templated,omitempty"`
}
// Form structure defines attributes that match HTML. This tells applications how to work with resources in order to manipulate state.
// Any attribute not found in HTML should be prefixed with an underscore (for example, "_fields").
type HypermediaForm struct {
Name string `json:"name,omitempty"`
Method string `json:"method,omitempty"`
Enctype string `json:"enctype"`
AcceptCharset string `json:"accept-charset,omitempty"`
Target string `json:"target,omitempty"`
Action string `json:"action,omitempty"`
Autocomplete bool `json:"autocomplete,omitempty"`
Fields map[string]HypermediaFormField `json:"_fields,omitempty"`
}
// Defines properties for a field (HTML attributes) as well as holds the "_errors" and validation "_rules" for that field.
// "_rules" have key names that map to HypermediaFormField.Name, like { "fieldName": HypermediaFormFieldRule } and the rules themself are named.
// "_errors" have key names that also map to HypermediaFormField.Name
type HypermediaFormField struct {
Name string `json:"name,omitempty"`
Value string `json:"value,omitempty"`
Type string `json:"type,omitempty"`
Src string `json:"src,omitempty"`
Checked bool `json:"checked,omitempty"`
Disabled bool `json:"disabled,omitempty"`
ReadOnly bool `json:"readonly,omitempty"`
Required bool `json:"required,omitempty"`
Autocomplete bool `json:"autocomplete,omitempty"`
Tabindex int `json:"tabindex,omitempty"`
Multiple bool `json:"multiple,omitempty"`
Accept string `json:"accept,omitempty"`
Errors map[string]HypermediaFormFieldError `json:"_errors,omitempty"`
Rules map[string]HypermediaFormFieldRule `json:"_rules,omitempty"`
}
// Error messages from validation failures (optional) "name" is the HypermediaFormFieldRule.Name in this case and "message" is returned on failure.
type HypermediaFormFieldError struct {
Name string `json:"name"`
Failed bool `json:"name"`
Message string `json:"message,omitempty"`
}
// Simple validation rules. Easily nested into "_rules" on "_fields" (optional). Of course front-end validation is merely convenience and not a trustable process.
// So remember to sanitize and validate any data on the server side of things. However, this does help tremendously in reducing the number of HTTP requests to the API.
type HypermediaFormFieldRule struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Pattern string `json:"pattern"`
Function func(value string) (fail bool, message string) // not for JSON
}
// Conveniently sets a few things up for a resource
func NewHypermediaResource() *HypermediaResource {
r := HypermediaResource{}
r.Meta.startTime = time.Now()
r.Links = make(map[string]HypermediaLink)
r.Data = make(map[string]interface{})
return &r
}
// Not necessary... But there may be some other functions that make sense...
func (h *HypermediaResource) Success() {
h.Meta.Success = true
}
func (h *HypermediaResource) AddCurie(name string, href string, templated bool) {
c := HypermediaCurie{}
c.Name = name
c.Href = href
c.Templated = templated
if len(h.Curies) < 1 {
h.Curies = make(map[string]HypermediaCurie)
}
h.Curies[name] = c
}
// Conveniently sets a few things before returning the resource and optionally allows a passed string to set HypermediaResource.Meta.Message
func (h *HypermediaResource) End(message ...string) *HypermediaResource {
if len(message) > 0 {
h.Meta.Message = message[0]
}
h.Meta.ResponseTime = float32(time.Since(h.Meta.startTime).Seconds())
return h
}