diff options
Diffstat (limited to 'internal/handler/tree.go')
-rw-r--r-- | internal/handler/tree.go | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/internal/handler/tree.go b/internal/handler/tree.go new file mode 100644 index 0000000..e574b2b --- /dev/null +++ b/internal/handler/tree.go @@ -0,0 +1,147 @@ +package handler + +import ( + "io" + "net/http" + "strings" + "time" + + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" + "go.neonxp.dev/json" + jsonModel "go.neonxp.dev/json/model" + + "go.neonxp.dev/djson/internal/model" + "go.neonxp.dev/djson/internal/tree" +) + +func New(core tree.Core) Handler { + return &handler{ + core: core, + } +} + +type handler struct { + core tree.Core +} + +func (h *handler) Handle(r chi.Router) { + r.Use(middleware.CleanPath) + r.Use(middleware.StripSlashes) + + r.Get("/*", func(w http.ResponseWriter, r *http.Request) { + rctx := chi.RouteContext(r.Context()) + res, err := h.core.Get(parsePath(rctx.RoutePath)) + if err != nil { + writeError(http.StatusNotFound, err, w) + return + } + result, err := res.MarshalJSON() + if err != nil { + writeError(http.StatusInternalServerError, err, w) + return + } + w.WriteHeader(http.StatusOK) + _, _ = w.Write(result) + }) + + r.Post("/*", func(w http.ResponseWriter, r *http.Request) { + rctx := chi.RouteContext(r.Context()) + path := parsePath(rctx.RoutePath) + jsonBody, err := io.ReadAll(r.Body) + if err != nil { + writeError(http.StatusBadRequest, err, w) + return + } + r.Body.Close() + node, err := json.Unmarshal(jsonBody) + if err != nil { + writeError(http.StatusBadRequest, err, w) + return + } + mutation := &model.Mutation{ + Date: time.Now(), + Type: model.Create, + Path: path, + Body: node, + } + if err := h.core.Mutation(r.Context(), mutation); err != nil { + writeError(http.StatusInternalServerError, err, w) + return + } + w.WriteHeader(http.StatusCreated) + }) + + r.Patch("/*", func(w http.ResponseWriter, r *http.Request) { + rctx := chi.RouteContext(r.Context()) + path := parsePath(rctx.RoutePath) + jsonBody, err := io.ReadAll(r.Body) + if err != nil { + writeError(http.StatusBadRequest, err, w) + return + } + r.Body.Close() + node, err := json.Unmarshal(jsonBody) + if err != nil { + writeError(http.StatusBadRequest, err, w) + return + } + mutation := &model.Mutation{ + Date: time.Now(), + Type: model.Merge, + Path: path, + Body: node, + } + if err := h.core.Mutation(r.Context(), mutation); err != nil { + writeError(http.StatusInternalServerError, err, w) + return + } + w.WriteHeader(http.StatusOK) + }) + + r.Delete("/*", func(w http.ResponseWriter, r *http.Request) { + rctx := chi.RouteContext(r.Context()) + path := parsePath(rctx.RoutePath) + mutation := &model.Mutation{ + Date: time.Now(), + Type: model.Remove, + Path: path, + Body: nil, + } + if err := h.core.Mutation(r.Context(), mutation); err != nil { + writeError(http.StatusInternalServerError, err, w) + return + } + w.WriteHeader(http.StatusNoContent) + }) +} + +func (h *handler) Hearthbeat(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + switch h.core.State() { + case tree.Ready: + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(".")) + case tree.Failed: + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte("start failed")) + case tree.Running: + w.WriteHeader(http.StatusServiceUnavailable) + _, _ = w.Write([]byte("starting...")) + } +} + +func writeError(code int, err error, w http.ResponseWriter) { + jsonErr, _ := json.Marshal(jsonModel.NewNode(err.Error())) + _, _ = w.Write(jsonErr) +} + +func parsePath(nodePath string) []string { + arr := []string{} + for _, v := range strings.Split(nodePath, "/") { + if v != "" { + arr = append(arr, v) + } + } + return arr +} |