diff --git a/api.go b/api.go index e38d495..48eb8e3 100644 --- a/api.go +++ b/api.go @@ -1,203 +1,78 @@ package main -import "encoding/json" import "fmt" -import "log" import "net/http" -import "time" import "github.com/firestuff/storebus" -import "github.com/google/uuid" -import "github.com/gorilla/mux" type API struct { - router *mux.Router - store *storebus.Store - bus *storebus.Bus + api *storebus.API } -func NewAPI(storePath string) *API { - api := &API{ - router: mux.NewRouter(), - store: storebus.NewStore(storePath), - bus: storebus.NewBus(), +func NewAPI(root string) (*API, error) { + api := &API{} + + var err error + api.api, err = storebus.NewAPI( + root, + &storebus.APIConfig{ + Factory: factory, + Update: update, + MayCreate: mayCreate, + MayUpdate: mayUpdate, + MayRead: mayRead, + }, + ) + + if err != nil { + return nil, err } - api.router.HandleFunc("/template", returnError(jsonOutput(api.createTemplate))).Methods("POST").Headers("Content-Type", "application/json") - api.router.HandleFunc("/template/{id}", returnError(api.streamTemplate)).Methods("GET").Headers("Accept", "text/event-stream") - api.router.HandleFunc("/template/{id}", returnError(jsonOutput(api.getTemplate))).Methods("GET") - api.router.HandleFunc("/template/{id}", returnError(jsonOutput(api.updateTemplate))).Methods("PATCH").Headers("Content-Type", "application/json") - - return api + return api, nil } func (api *API) ServeHTTP(w http.ResponseWriter, r *http.Request) { - api.router.ServeHTTP(w, r) + api.api.ServeHTTP(w, r) } -func (api *API) createTemplate(r *http.Request) (storebus.Object, string, int) { - log.Printf("createTemplate") +func factory(t string) (storebus.Object, error) { + switch t { - template := NewTemplate() - msg, code := readJson(r, template) - if code != 0 { - return nil, msg, code - } + case "template": + return NewTemplate(), nil - template.Id = uuid.NewString() + default: + return nil, fmt.Errorf("Unsupported type: %s", t) - if !template.IsValid() { - return nil, "Invalid template", http.StatusBadRequest - } - - err := api.store.Write(template) - if err != nil { - return nil, "Failed to write template", http.StatusInternalServerError - } - - return template, "", 0 -} - -func (api *API) streamTemplate(w http.ResponseWriter, r *http.Request) (string, int) { - log.Printf("streamTemplate %s", mux.Vars(r)) - - _, ok := w.(http.Flusher) - if !ok { - return "Streaming unsupported", http.StatusBadRequest - } - - w.Header().Set("Content-Type", "text/event-stream") - w.Header().Set("Cache-Control", "no-cache") - w.Header().Set("Connection", "keep-alive") - - template := NewTemplate() - template.Id = mux.Vars(r)["id"] - - err := api.store.Read(template) - if err != nil { - return fmt.Sprintf("Template %s not found", template.Id), http.StatusNotFound - } - - writeEvent(w, template) - - closeChan := w.(http.CloseNotifier).CloseNotify() - msgChan := api.bus.Subscribe(template) - ticker := time.NewTicker(5 * time.Second) - - connected := true - for connected { - select { - case <-closeChan: - connected = false - case msg := <-msgChan: - writeEvent(w, msg) - case <-ticker.C: - writeEvent(w, NewHeartbeat()) - } - } - - log.Printf("streamTemplate %s end", mux.Vars(r)) - - return "", 0 -} - -func (api *API) getTemplate(r *http.Request) (storebus.Object, string, int) { - log.Printf("getTemplate %s", mux.Vars(r)) - - template := NewTemplate() - template.Id = mux.Vars(r)["id"] - - err := api.store.Read(template) - if err != nil { - return nil, fmt.Sprintf("Template %s not found", template.Id), http.StatusNotFound - } - - return template, "", 0 -} - -func (api *API) updateTemplate(r *http.Request) (storebus.Object, string, int) { - log.Printf("updateTemplate %s", mux.Vars(r)) - - patch := NewTemplate() - - msg, code := readJson(r, patch) - if code != 0 { - return nil, msg, code - } - - template := NewTemplate() - template.Id = mux.Vars(r)["id"] - - err := api.store.Read(template) - if err != nil { - return nil, fmt.Sprintf("Template %s not found", template.Id), http.StatusNotFound - } - - if patch.Title != "" { - template.Title = patch.Title - } - - if !template.IsValid() { - return nil, "Invalid template", http.StatusBadRequest - } - - err = api.store.Write(template) - if err != nil { - return nil, "Failed to write template", http.StatusInternalServerError - } - - api.bus.Announce(template) - - return template, "", 0 - -} - -func readJson(r *http.Request, out storebus.Object) (string, int) { - dec := json.NewDecoder(r.Body) - dec.DisallowUnknownFields() - - err := dec.Decode(out) - if err != nil { - return fmt.Sprintf("Invalid JSON: %s", err), http.StatusBadRequest - } - - return "", 0 -} - -func returnError(wrapped func(http.ResponseWriter, *http.Request) (string, int)) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - msg, code := wrapped(w, r) - if code != 0 { - http.Error(w, msg, code) - } } } -func jsonOutput(wrapped func(*http.Request) (storebus.Object, string, int)) func(http.ResponseWriter, *http.Request) (string, int) { - return func(w http.ResponseWriter, r *http.Request) (string, int) { - out, msg, code := wrapped(r) - if code != 0 { - return msg, code +func update(obj storebus.Object, patch storebus.Object) error { + switch o := obj.(type) { + + case *Template: + p := patch.(*Template) + + if p.Title != "" { + o.Title = p.Title } - enc := json.NewEncoder(w) - err := enc.Encode(out) - if err != nil { - return fmt.Sprintf("Failed to encode JSON: %s", err), http.StatusInternalServerError - } + return nil + + default: + return fmt.Errorf("Unsupported type: %s", obj.GetType()) - return "", 0 } } -func writeEvent(w http.ResponseWriter, in storebus.Object) (string, int) { - data, err := json.Marshal(in) - if err != nil { - return fmt.Sprintf("Failed to encode JSON: %s", err), http.StatusInternalServerError - } - - fmt.Fprintf(w, "event: %s\ndata: %s\n\n", in.GetType(), data) - w.(http.Flusher).Flush() - - return "", 0 +func mayCreate(obj storebus.Object, r *http.Request) error { + return nil +} + +func mayUpdate(obj storebus.Object, patch storebus.Object, r *http.Request) error { + return nil +} + +func mayRead(obj storebus.Object, r *http.Request) error { + return nil } diff --git a/go.mod b/go.mod index f86d2fe..acf1ec0 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/firestuff/checky go 1.16 require ( - github.com/firestuff/storebus v0.0.0-20220320234918-5e1588edb2eb + github.com/firestuff/storebus v0.0.0-20220322045810-ba25a8b70634 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 ) diff --git a/go.sum b/go.sum index 908c001..7b89f70 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,15 @@ -github.com/firestuff/storebus v0.0.0-20220320234918-5e1588edb2eb h1:UWxwtE1DbFqGdw6hAZVtBM1cX5HKvRPygwF2MnhEa1g= -github.com/firestuff/storebus v0.0.0-20220320234918-5e1588edb2eb/go.mod h1:GfDVrwTVW/pVlgb7Qg3SJ1hXI4aE3SO/IfYz7btihys= +github.com/firestuff/storebus v0.0.0-20220322045810-ba25a8b70634 h1:cEFhwr+TRDpKro5oZ5oUphmPAEiuxdxCnQu1xwKugpA= +github.com/firestuff/storebus v0.0.0-20220322045810-ba25a8b70634/go.mod h1:Fd962ctR9HIrxz1oHp60xv+AOYGsebQhyqCmF/0mayk= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb h1:pirldcYWx7rx7kE5r+9WsOXPXK0+WH5+uZ7uPmJ44uM= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/heartbeat.go b/heartbeat.go deleted file mode 100644 index 4e4351a..0000000 --- a/heartbeat.go +++ /dev/null @@ -1,16 +0,0 @@ -package main - -type Heartbeat struct { -} - -func NewHeartbeat() *Heartbeat { - return &Heartbeat{} -} - -func (h *Heartbeat) GetType() string { - return "heartbeat" -} - -func (h *Heartbeat) GetId() string { - return "" -} diff --git a/main.go b/main.go index 2ce7cfb..f8b74d2 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,11 @@ func main() { mux := http.NewServeMux() - api := NewAPI(*storeFlag) + api, err := NewAPI(*storeFlag) + if err != nil { + log.Fatal(err) + } + mux.Handle("/api/", http.StripPrefix("/api", api)) srv := &http.Server{ @@ -25,7 +29,7 @@ func main() { log.Printf("listening on %s", *bindFlag) - err := srv.ListenAndServe() + err = srv.ListenAndServe() if err != nil { log.Fatal(err) } diff --git a/template.go b/template.go index a4c80f8..40fe241 100644 --- a/template.go +++ b/template.go @@ -34,6 +34,10 @@ func (t *Template) GetId() string { return t.Id } +func (t *Template) SetId(id string) { + t.Id = id +} + func (t *Template) IsValid() bool { return true }