diff options
Diffstat (limited to 'internal/api/handler.go')
-rw-r--r-- | internal/api/handler.go | 155 |
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 +} |