// getResource is the function that handles the GET /resources/:resourceID route. func getResource(c siesta.Context, w http.ResponseWriter, r *http.Request) { // Context variables requestID := c.Get("request-id").(string) db := c.Get("db").(*DB) user := c.Get("user").(string) // Check parameters var params siesta.Params resourceID := params.Int("resourceID", -1, "Resource identifier") err := params.Parse(r.Form) if err != nil { log.Printf("[Req %s] %v", requestID, err) c.Set("error", err.Error()) c.Set("status-code", http.StatusBadRequest) return } // Make sure we have a valid resource ID. if *resourceID == -1 { c.Set("error", "invalid or missing resource ID") c.Set("status-code", http.StatusBadRequest) return } resource, err := db.resource(user, *resourceID) if err != nil { c.Set("status-code", http.StatusNotFound) c.Set("error", "not found") return } c.Set("data", resource) }
func initSiesta() { h := siesta.NewService("/") h.Route("GET", "/", "", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") fmt.Fprintf(w, "Hello, World") }) h.Route("GET", "/:name", "", func(w http.ResponseWriter, r *http.Request) { var params siesta.Params name := params.String("name", "", "") params.Parse(r.Form) w.Header().Set("Content-Type", "text/plain; charset=utf-8") fmt.Fprintf(w, "Hello, %s", *name) }) registerHandler("siesta", h) }
func main() { // Create a new Service rooted at "/" service := siesta.NewService("/") // Here's a handler that uses a URL parameter. // Example: GET /greet/Bob service.Route("GET", "/greet/:name", "Greets with a name.", func(w http.ResponseWriter, r *http.Request) { var params siesta.Params name := params.String("name", "", "Person's name") err := params.Parse(r.Form) if err != nil { log.Println("Error parsing parameters!", err) return } fmt.Fprintf(w, "Hello, %s!", *name) }, ) // Here's a handler that uses a query string parameter. // Example: GET /square?number=10 service.Route("GET", "/square", "Prints the square of a number.", func(w http.ResponseWriter, r *http.Request) { var params siesta.Params number := params.Int("number", 0, "A number to square") err := params.Parse(r.Form) if err != nil { log.Println("Error parsing parameters!", err) return } fmt.Fprintf(w, "%d * %d = %d.", *number, *number, (*number)*(*number)) }, ) // We can also use both URL and query string parameters. // Example: GET /exponentiate/10?power=10 service.Route("GET", "/exponentiate/:number", "Exponentiates a number.", func(w http.ResponseWriter, r *http.Request) { var params siesta.Params number := params.Float64("number", 0, "A number to exponentiate") power := params.Float64("power", 1, "Power") err := params.Parse(r.Form) if err != nil { log.Println("Error parsing parameters!", err) return } fmt.Fprintf(w, "%g ^ %g = %g.", *number, *power, math.Pow(*number, *power)) }, ) // service is an http.Handler, so we can pass it directly to ListenAndServe. log.Fatal(http.ListenAndServe(":8080", service)) }
func (s *APIServer) Run() { service := siesta.NewService("/") service.AddPre(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers")) }) service.AddPost(func(c siesta.Context, w http.ResponseWriter, r *http.Request, q func()) { resp := c.Get(responseKey) err, _ := c.Get(errorKey).(string) if resp == nil && err == "" { return } enc := json.NewEncoder(w) enc.Encode(APIResponse{ Data: resp, Error: err, }) }) service.Route("GET", "/", "Default page", func(c siesta.Context, w http.ResponseWriter, r *http.Request) { c.Set(responseKey, "Welcome to the Cistern API!") }) service.Route("GET", "/devices", "Lists sources", func(c siesta.Context, w http.ResponseWriter, r *http.Request) { type ipHostname struct { IP string `json:"ip"` Hostname string `json:"hostname,omitempty"` } devices := []ipHostname{} for _, dev := range s.deviceRegistry.Devices() { devices = append(devices, ipHostname{ IP: dev.IP().String(), Hostname: dev.Hostname(), }) } c.Set(responseKey, devices) }) service.Route("GET", "/devices/:device/metrics", "Lists metrics for a device", func(c siesta.Context, w http.ResponseWriter, r *http.Request) { var params siesta.Params device := params.String("device", "", "Device name") err := params.Parse(r.Form) if err != nil { c.Set(errorKey, err.Error()) return } address := net.ParseIP(*device) dev, present := s.deviceRegistry.Lookup(address) if !present { c.Set(errorKey, "device not found") return } c.Set(responseKey, dev.Metrics()) }) service.Route("GET", "/devices/:device/flows", "Lists top flows for a device", func(c siesta.Context, w http.ResponseWriter, r *http.Request) { var params siesta.Params device := params.String("device", "", "Device name") err := params.Parse(r.Form) if err != nil { c.Set(errorKey, err.Error()) return } address := net.ParseIP(*device) dev, present := s.deviceRegistry.Lookup(address) if !present { c.Set(errorKey, "device not found") return } type flowsResponse struct { ByBytes []flows.Flow `json:"byBytes"` ByPackets []flows.Flow `json:"byPackets"` } topTalkers := dev.TopTalkers() if topTalkers == nil { c.Set(errorKey, "No active flows") return } resp := flowsResponse{ ByBytes: topTalkers.ByBytes(), ByPackets: topTalkers.ByPackets(), } c.Set(responseKey, resp) }) service.Route("OPTIONS", "/series/query", "Accepts an OPTIONS request", func(w http.ResponseWriter, r *http.Request) { // Doesn't do anything }) service.Route("POST", "/series/query", "Lists metrics for a device", s.querySeriesRoute()) http.Handle("/", service) go http.ListenAndServe(s.addr, nil) }
func (s *APIServer) querySeriesRoute() func(siesta.Context, http.ResponseWriter, *http.Request) { return func(c siesta.Context, w http.ResponseWriter, r *http.Request) { var params siesta.Params pointWidth := params.Int64("pointWidth", 1, "Number of points to average together") err := params.Parse(r.Form) if err != nil { c.Set(errorKey, err.Error()) return } var descs []queryDesc dec := json.NewDecoder(r.Body) err = dec.Decode(&descs) if err != nil { c.Set(errorKey, err.Error()) return } now := time.Now().Unix() for i, desc := range descs { if desc.Start <= 0 { desc.Start += now } if desc.End <= 0 { desc.End += now } descs[i] = desc } resp := queryResponse{} for _, desc := range descs { log.Println(desc) i, err := s.seriesEngine.NewIterator(desc.Source, desc.Metric) if err != nil { log.Println(err) continue } err = i.Seek(desc.Start) if err != nil { log.Println(err) continue } s := querySeries{ Source: desc.Source, Metric: desc.Metric, Start: i.Point().Timestamp, End: i.Point().Timestamp, } pointsSeen := 0 currentInterval := i.Point().Timestamp / *pointWidth currentPoint := catena.Point{ Timestamp: currentInterval * *pointWidth, } for { p := i.Point() if p.Timestamp > desc.End { break } if p.Timestamp / *pointWidth != currentInterval { currentPoint.Value /= float64(pointsSeen) s.Points = append(s.Points, currentPoint) currentInterval = i.Point().Timestamp / *pointWidth currentPoint = catena.Point{ Timestamp: currentInterval * *pointWidth, Value: p.Value, } pointsSeen = 1 continue } currentPoint.Value += p.Value pointsSeen++ err := i.Next() if err != nil { log.Println(err) break } } if pointsSeen > 0 { currentPoint.Value /= float64(pointsSeen) s.Points = append(s.Points, currentPoint) } i.Close() resp.Series = append(resp.Series, s) } c.Set(responseKey, resp) } }