Clean up JSON responses with responder library
This commit is contained in:
parent
cf23018d70
commit
e5018c38c2
|
@ -1,17 +1,12 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
"github.com/tomwright/queryparam/v4"
|
"github.com/tomwright/queryparam/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func httpErr(w http.ResponseWriter, code int) {
|
|
||||||
http.Error(w, http.StatusText(code), code)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringPtr(s string) *string {
|
func stringPtr(s string) *string {
|
||||||
return &s
|
return &s
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
package responder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type defaultResponse struct {
|
||||||
|
Status int `json:"status"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type response struct {
|
||||||
|
value *interface{}
|
||||||
|
status int
|
||||||
|
pretty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(r *response)
|
||||||
|
|
||||||
|
func Pretty(pretty bool) Option {
|
||||||
|
return func(r *response) {
|
||||||
|
r.pretty = pretty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Status(status int) Option {
|
||||||
|
return func(r *response) {
|
||||||
|
r.status = status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Body(v interface{}) Option {
|
||||||
|
return func(r *response) {
|
||||||
|
r.value = &v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Respond(w http.ResponseWriter, opts ...Option) {
|
||||||
|
r := &response{
|
||||||
|
status: http.StatusOK,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
|
||||||
|
if r.pretty {
|
||||||
|
enc.SetIndent(" ", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(r.status)
|
||||||
|
|
||||||
|
if v := r.value; v != nil {
|
||||||
|
_ = enc.Encode(*v)
|
||||||
|
} else {
|
||||||
|
_ = enc.Encode(&defaultResponse{
|
||||||
|
Status: r.status,
|
||||||
|
Message: http.StatusText(r.status),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
101
main.go
101
main.go
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"github.com/tomwright/queryparam/v4"
|
"github.com/tomwright/queryparam/v4"
|
||||||
"github.com/zikaeroh/codies/internal/protocol"
|
"github.com/zikaeroh/codies/internal/protocol"
|
||||||
|
"github.com/zikaeroh/codies/internal/responder"
|
||||||
"github.com/zikaeroh/codies/internal/server"
|
"github.com/zikaeroh/codies/internal/server"
|
||||||
"github.com/zikaeroh/codies/internal/version"
|
"github.com/zikaeroh/codies/internal/version"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
@ -83,19 +84,18 @@ func main() {
|
||||||
r.Use(middleware.NoCache)
|
r.Use(middleware.NoCache)
|
||||||
|
|
||||||
r.Get("/api/time", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/api/time", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Add("Content-Type", "application/json")
|
responder.Respond(w, responder.Body(&protocol.TimeResponse{Time: time.Now()}))
|
||||||
_ = json.NewEncoder(w).Encode(&protocol.TimeResponse{Time: time.Now()})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Get("/api/stats", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/api/stats", func(w http.ResponseWriter, r *http.Request) {
|
||||||
rooms, clients := srv.Stats()
|
rooms, clients := srv.Stats()
|
||||||
|
responder.Respond(w,
|
||||||
enc := json.NewEncoder(w)
|
responder.Body(&protocol.StatsResponse{
|
||||||
enc.SetIndent("", " ")
|
Rooms: rooms,
|
||||||
_ = enc.Encode(&protocol.StatsResponse{
|
Clients: clients,
|
||||||
Rooms: rooms,
|
}),
|
||||||
Clients: clients,
|
responder.Pretty(true),
|
||||||
})
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
|
@ -106,18 +106,16 @@ func main() {
|
||||||
r.Get("/api/exists", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/api/exists", func(w http.ResponseWriter, r *http.Request) {
|
||||||
query := &protocol.ExistsQuery{}
|
query := &protocol.ExistsQuery{}
|
||||||
if err := queryparam.Parse(r.URL.Query(), query); err != nil {
|
if err := queryparam.Parse(r.URL.Query(), query); err != nil {
|
||||||
httpErr(w, http.StatusBadRequest)
|
responder.Respond(w, responder.Status(http.StatusBadRequest))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
room := srv.FindRoomByID(query.RoomID)
|
room := srv.FindRoomByID(query.RoomID)
|
||||||
if room == nil {
|
if room == nil {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
responder.Respond(w, responder.Status(http.StatusNotFound))
|
||||||
} else {
|
} else {
|
||||||
w.WriteHeader(http.StatusOK)
|
responder.Respond(w, responder.Status(http.StatusOK))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = w.Write([]byte("."))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Post("/api/room", func(w http.ResponseWriter, r *http.Request) {
|
r.Post("/api/room", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -125,76 +123,88 @@ func main() {
|
||||||
|
|
||||||
req := &protocol.RoomRequest{}
|
req := &protocol.RoomRequest{}
|
||||||
if err := json.NewDecoder(r.Body).Decode(req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(req); err != nil {
|
||||||
httpErr(w, http.StatusBadRequest)
|
responder.Respond(w, responder.Status(http.StatusBadRequest))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
|
|
||||||
if msg, valid := req.Valid(); !valid {
|
if msg, valid := req.Valid(); !valid {
|
||||||
resp := &protocol.RoomResponse{
|
responder.Respond(w,
|
||||||
Error: stringPtr(msg),
|
responder.Status(http.StatusBadRequest),
|
||||||
}
|
responder.Body(&protocol.RoomResponse{
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
Error: stringPtr(msg),
|
||||||
_ = json.NewEncoder(w).Encode(resp)
|
}),
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &protocol.RoomResponse{}
|
var room *server.Room
|
||||||
|
|
||||||
if req.Create {
|
if req.Create {
|
||||||
room, err := srv.CreateRoom(req.RoomName, req.RoomPass)
|
var err error
|
||||||
|
room, err = srv.CreateRoom(req.RoomName, req.RoomPass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err {
|
switch err {
|
||||||
case server.ErrRoomExists:
|
case server.ErrRoomExists:
|
||||||
resp.Error = stringPtr("Room already exists.")
|
responder.Respond(w,
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
responder.Status(http.StatusBadRequest),
|
||||||
|
responder.Body(&protocol.RoomResponse{
|
||||||
|
Error: stringPtr("Room already exists."),
|
||||||
|
}),
|
||||||
|
)
|
||||||
case server.ErrTooManyRooms:
|
case server.ErrTooManyRooms:
|
||||||
resp.Error = stringPtr("Too many rooms.")
|
responder.Respond(w,
|
||||||
w.WriteHeader(http.StatusServiceUnavailable)
|
responder.Status(http.StatusServiceUnavailable),
|
||||||
|
responder.Body(&protocol.RoomResponse{
|
||||||
|
Error: stringPtr("Too many rooms."),
|
||||||
|
}),
|
||||||
|
)
|
||||||
default:
|
default:
|
||||||
resp.Error = stringPtr("An unknown error occurred.")
|
responder.Respond(w,
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
responder.Status(http.StatusInternalServerError),
|
||||||
|
responder.Body(&protocol.RoomResponse{
|
||||||
|
Error: stringPtr("An unknown error occurred."),
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
return
|
||||||
resp.ID = &room.ID
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
room := srv.FindRoom(req.RoomName)
|
room = srv.FindRoom(req.RoomName)
|
||||||
if room == nil || room.Password != req.RoomPass {
|
if room == nil || room.Password != req.RoomPass {
|
||||||
resp.Error = stringPtr("Room not found or password does not match.")
|
responder.Respond(w,
|
||||||
w.WriteHeader(http.StatusNotFound)
|
responder.Status(http.StatusNotFound),
|
||||||
} else {
|
responder.Body(&protocol.RoomResponse{
|
||||||
resp.ID = &room.ID
|
Error: stringPtr("Room not found or password does not match."),
|
||||||
w.WriteHeader(http.StatusOK)
|
}),
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = json.NewEncoder(w).Encode(resp)
|
responder.Respond(w, responder.Body(&protocol.RoomResponse{
|
||||||
|
ID: &room.ID,
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Get("/api/ws", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/api/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||||
query := &protocol.WSQuery{}
|
query := &protocol.WSQuery{}
|
||||||
if err := queryparam.Parse(r.URL.Query(), query); err != nil {
|
if err := queryparam.Parse(r.URL.Query(), query); err != nil {
|
||||||
httpErr(w, http.StatusBadRequest)
|
responder.Respond(w, responder.Status(http.StatusBadRequest))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, valid := query.Valid(); !valid {
|
if _, valid := query.Valid(); !valid {
|
||||||
httpErr(w, http.StatusBadRequest)
|
responder.Respond(w, responder.Status(http.StatusBadRequest))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
room := srv.FindRoomByID(query.RoomID)
|
room := srv.FindRoomByID(query.RoomID)
|
||||||
if room == nil {
|
if room == nil {
|
||||||
httpErr(w, http.StatusNotFound)
|
responder.Respond(w, responder.Status(http.StatusBadRequest))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := websocket.Accept(w, r, wsOpts)
|
c, err := websocket.Accept(w, r, wsOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +268,6 @@ func checkVersion(next http.Handler) http.Handler {
|
||||||
if r.Header.Get("Upgrade") == "websocket" {
|
if r.Header.Get("Upgrade") == "websocket" {
|
||||||
c, err := websocket.Accept(w, r, wsOpts)
|
c, err := websocket.Accept(w, r, wsOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Close(4418, reason)
|
c.Close(4418, reason)
|
||||||
|
|
Loading…
Reference in New Issue