From d4a82f610cd226f55bf98201dbd06aa05c2cf33c Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Tue, 1 Oct 2024 17:32:14 +0200 Subject: feat(tfstated): add json encoding and decoding helpers --- cmd/tfstated/get.go | 11 +++++------ cmd/tfstated/helpers.go | 36 ++++++++++++++++++++++++++++++++++++ cmd/tfstated/post.go | 12 +++++------- 3 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 cmd/tfstated/helpers.go diff --git a/cmd/tfstated/get.go b/cmd/tfstated/get.go index 6de78a5..7d54970 100644 --- a/cmd/tfstated/get.go +++ b/cmd/tfstated/get.go @@ -12,21 +12,20 @@ import ( func handleGet(db *database.DB) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Cache-Control", "no-store, no-cache") - w.Header().Set("Content-Type", "application/json") if r.URL.Path == "/" { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte("{\"msg\": \"No state path provided, cannot GET /\"}")) + _ = errorResponse(w, http.StatusBadRequest, + fmt.Errorf("no state path provided, cannot GET /")) return } if data, err := db.GetState(r.URL.Path); err != nil { if errors.Is(err, sql.ErrNoRows) { - w.WriteHeader(http.StatusNotFound) + _ = errorResponse(w, http.StatusNotFound, + fmt.Errorf("state path not found: %s", r.URL.Path)) } else { - w.WriteHeader(http.StatusInternalServerError) + _ = errorResponse(w, http.StatusInternalServerError, err) } - _, _ = w.Write([]byte(fmt.Sprintf("{\"msg\": \"%+v\"}", err))) } else { w.WriteHeader(http.StatusOK) _, _ = w.Write(data) diff --git a/cmd/tfstated/helpers.go b/cmd/tfstated/helpers.go new file mode 100644 index 0000000..33e14a4 --- /dev/null +++ b/cmd/tfstated/helpers.go @@ -0,0 +1,36 @@ +package main + +import ( + "encoding/json" + "fmt" + "log/slog" + "net/http" +) + +func decode(r *http.Request, data any) error { + if err := json.NewDecoder(r.Body).Decode(&data); err != nil { + return fmt.Errorf("failed to decode json: %w", err) + } + return nil +} + +func encode(w http.ResponseWriter, status int, data any) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + if err := json.NewEncoder(w).Encode(data); err != nil { + slog.Error("failed to encode json", "err", err) + return fmt.Errorf("failed to encode json: %w", err) + } + return nil +} + +func errorResponse(w http.ResponseWriter, status int, err error) error { + type errorResponse struct { + Msg string `json:"msg"` + Status int `json:"status"` + } + return encode(w, status, &errorResponse{ + Msg: fmt.Sprintf("%+v", err), + Status: status, + }) +} diff --git a/cmd/tfstated/post.go b/cmd/tfstated/post.go index e47f5ad..b88109e 100644 --- a/cmd/tfstated/post.go +++ b/cmd/tfstated/post.go @@ -10,21 +10,19 @@ import ( func handlePost(db *database.DB) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") if r.URL.Path == "/" { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte("{\"msg\": \"No state path provided, cannot POST /\"}")) + _ = errorResponse(w, http.StatusBadRequest, + fmt.Errorf("no state path provided, cannot POST /"), + ) return } data, err := io.ReadAll(r.Body) if err != nil { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(fmt.Sprintf("{\"msg\": \"failed to read request body: %+v\"}", err))) + _ = errorResponse(w, http.StatusBadRequest, err) return } if err := db.SetState(r.URL.Path, data); err != nil { - w.WriteHeader(http.StatusInternalServerError) - _, _ = w.Write([]byte(fmt.Sprintf("{\"msg\": \"%+v\"}", err))) + _ = errorResponse(w, http.StatusInternalServerError, err) } else { w.WriteHeader(http.StatusOK) } -- cgit v1.2.3