summaryrefslogtreecommitdiff
path: root/internal/api/handler.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/api/handler.go')
-rw-r--r--internal/api/handler.go155
1 files changed, 155 insertions, 0 deletions
diff --git a/internal/api/handler.go b/internal/api/handler.go
new file mode 100644
index 0000000..0e51807
--- /dev/null
+++ b/internal/api/handler.go
@@ -0,0 +1,155 @@
+package api
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "go.neonxp.dev/djson/internal/model"
+ "go.neonxp.dev/djson/internal/tree"
+ "go.neonxp.dev/json"
+)
+
+func NewHandler(tree *tree.Engine) *Handler {
+ return &Handler{tree: tree}
+}
+
+type Handler struct {
+ tree *tree.Engine
+}
+
+func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ switch r.Method {
+ case http.MethodGet:
+ res, err := h.get(r.Context(), r.URL.Path)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ log.Println(err.Error())
+ return
+ }
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ log.Println(err.Error())
+ return
+ }
+ result, err := res.MarshalJSON()
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ log.Println(err.Error())
+ return
+ }
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write(result)
+ case http.MethodPost:
+ jsonBody, err := io.ReadAll(r.Body)
+ r.Body.Close()
+
+ if err := h.post(r.Context(), r.URL.Path, jsonBody); err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ log.Println(err.Error())
+ return
+ }
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ log.Println(err.Error())
+ return
+ }
+ w.WriteHeader(http.StatusCreated)
+ case http.MethodPatch:
+ jsonBody, err := io.ReadAll(r.Body)
+ r.Body.Close()
+
+ if err := h.patch(r.Context(), r.URL.Path, jsonBody); err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ log.Println(err.Error())
+ return
+ }
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ log.Println(err.Error())
+ return
+ }
+ w.WriteHeader(http.StatusOK)
+ case http.MethodDelete:
+ if err := h.remove(r.Context(), r.URL.Path); err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ log.Println(err.Error())
+ return
+ }
+ w.WriteHeader(http.StatusNoContent)
+ default:
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ _, _ = w.Write([]byte(fmt.Sprintf("Unknown method: %s", r.Method)))
+ }
+}
+
+func (h *Handler) get(ctx context.Context, p string) (Marshaller, error) {
+ return h.tree.Get(parsePath(p))
+}
+
+func (h *Handler) post(ctx context.Context, p string, jsonBody []byte) error {
+ node, err := json.Unmarshal(jsonBody)
+ if err != nil {
+ return err
+ }
+
+ return h.tree.Mutation(ctx, &model.Mutation{
+ Date: time.Now(),
+ Type: model.Create,
+ Path: parsePath(p),
+ Body: *node,
+ })
+}
+
+func (h *Handler) patch(ctx context.Context, p string, jsonBody []byte) error {
+ node, err := json.Unmarshal(jsonBody)
+ if err != nil {
+ return err
+ }
+
+ return h.tree.Mutation(ctx, &model.Mutation{
+ Date: time.Now(),
+ Type: model.Merge,
+ Path: parsePath(p),
+ Body: *node,
+ })
+}
+
+func (h *Handler) remove(ctx context.Context, p string) error {
+ return h.tree.Mutation(ctx, &model.Mutation{
+ Date: time.Now(),
+ Type: model.Remove,
+ Path: parsePath(p),
+ })
+}
+
+type ErrorStruct struct {
+ Error string `json:"error"`
+}
+
+func newError(err error) *ErrorStruct {
+ return &ErrorStruct{
+ Error: err.Error(),
+ }
+}
+
+type Marshaller interface {
+ MarshalJSON() ([]byte, error)
+}
+
+func parsePath(nodePath string) []string {
+ nodePath = filepath.Clean(nodePath)
+ nodePath = strings.Trim(nodePath, "/")
+ arr := []string{}
+ for _, v := range strings.Split(nodePath, "/") {
+ if v != "" {
+ arr = append(arr, v)
+ }
+ }
+ return arr
+}