Skip to content

Commit

Permalink
add api error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
mpoegel committed May 12, 2024
1 parent c7eae8e commit 209ef89
Showing 1 changed file with 55 additions and 21 deletions.
76 changes: 55 additions & 21 deletions pkg/pizza/server_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package pizza
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"time"
Expand All @@ -15,6 +17,17 @@ import (
zap "go.uber.org/zap"
)

func WriteAPIError(err error, status int, w http.ResponseWriter) {
errObj := &jsonapi.ErrorObject{
Title: http.StatusText(status),
Detail: err.Error(),
Status: strconv.FormatInt(int64(status), 10),
}
allErrs := []*jsonapi.ErrorObject{errObj}
// ignore marshal errors
jsonapi.MarshalErrors(w, allErrs)
}

func (s *Server) HandleAPIAuth(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusNotFound)
Expand Down Expand Up @@ -54,7 +67,12 @@ func (s *Server) HandleAPIAuth(w http.ResponseWriter, r *http.Request) {
func (s *Server) HandleAPIFriday(w http.ResponseWriter, r *http.Request) {
token, claims, ok := s.CheckAuthorization(r)
if !ok {
w.WriteHeader(http.StatusUnauthorized)
WriteAPIError(errors.New("not authorized"), http.StatusUnauthorized, w)
return
}

if r.Header.Get("Accept") != jsonapi.MediaType {
WriteAPIError(fmt.Errorf("must accept %s", jsonapi.MediaType), http.StatusNotAcceptable, w)
return
}

Expand All @@ -78,15 +96,13 @@ func (s *Server) HandleAPIGetFriday(token *jwt.Token, claims *TokenClaims, w htt
if ok {
rawTime, err := strconv.ParseInt(fridayID, 10, 64)
if err != nil {
// TODO create error response
w.WriteHeader(http.StatusBadRequest)
WriteAPIError(err, http.StatusBadRequest, w)
return
}

f, err := s.store.GetFriday(time.Unix(rawTime, 0).In(estZone))
if err != nil {
// TODO create error response
w.WriteHeader(http.StatusNotFound)
WriteAPIError(fmt.Errorf("no matching friday found with ID '%s'", fridayID), http.StatusNotFound, w)
return
}

Expand All @@ -95,8 +111,7 @@ func (s *Server) HandleAPIGetFriday(token *jwt.Token, claims *TokenClaims, w htt
fridays, err = s.store.GetUpcomingFridays(30)
if err != nil {
Log.Error("failed to get fridays", zap.Error(err))
// TODO create error response
w.WriteHeader(http.StatusInternalServerError)
WriteAPIError(errors.New("database error"), http.StatusInternalServerError, w)
return
}
}
Expand Down Expand Up @@ -136,46 +151,42 @@ func (s *Server) HandleAPIGetFriday(token *jwt.Token, claims *TokenClaims, w htt

if err = jsonapi.MarshalPayload(w, res); err != nil {
Log.Warn("api marshal payload", zap.Error(err))
w.WriteHeader(http.StatusInternalServerError)
return
WriteAPIError(errors.New("failed to compose response data"), http.StatusInternalServerError, w)
}
}

func (s *Server) HandleAPIPatchFriday(token *jwt.Token, claims *TokenClaims, w http.ResponseWriter, r *http.Request) {
friday := &api.Friday{}
if err := jsonapi.UnmarshalPayload(r.Body, friday); err != nil {
// TODO create error response
w.WriteHeader(http.StatusBadRequest)
WriteAPIError(err, http.StatusBadRequest, w)
return
}

estZone, _ := time.LoadLocation("America/New_York")
rawTime, err := strconv.ParseInt(friday.ID, 10, 64)
if err != nil {
// TODO create error response
w.WriteHeader(http.StatusBadRequest)
WriteAPIError(err, http.StatusBadRequest, w)
return
}

f, err := s.store.GetFriday(time.Unix(rawTime, 0).In(estZone))
if err != nil {
// TODO create error response
w.WriteHeader(http.StatusNotFound)
WriteAPIError(fmt.Errorf("no matching friday found with ID '%s'", friday.ID), http.StatusNotFound, w)
return
}
friday.Details = *f.Details
friday.StartTime = f.Date

// not part of invited group
if f.Group != nil && !claims.InGroup(*f.Group) {
// TODO create error response
w.WriteHeader(http.StatusUnauthorized)
WriteAPIError(fmt.Errorf("no matching friday found with ID '%s'", friday.ID), http.StatusNotFound, w)
return
}

// check the requested guests
for _, g := range friday.Guests {
if g.ID != claims.Email {
// TODO create error response
w.WriteHeader(http.StatusUnauthorized)
WriteAPIError(fmt.Errorf("not allowed to invite guest '%s'", g.ID), http.StatusUnauthorized, w)
return
}
}
Expand All @@ -184,8 +195,31 @@ func (s *Server) HandleAPIPatchFriday(token *jwt.Token, claims *TokenClaims, w h
Log.Info("rsvp request", zap.String("email", claims.Email))

if err = s.CreateAndInvite(friday.ID, friday.StartTime, claims.Email, claims.Name); err != nil {
// TODO create error response
w.WriteHeader(http.StatusInternalServerError)
WriteAPIError(errors.New("calendar failure"), http.StatusInternalServerError, w)
return
}

if event, err := s.calendar.GetEvent(friday.ID); err != nil && err != ErrEventNotFound {
Log.Warn("failed to get calendar event", zap.Error(err), zap.String("eventID", friday.ID))
} else {
friday.Guests = make([]*api.Guest, len(event.Attendees))
for k, email := range event.Attendees {
g := &api.Guest{
ID: email,
Email: email,
}
if name, err := s.store.GetFriendName(email); err == nil {
g.Name = name
}
friday.Guests[k] = g
}
}

w.Header().Set("Content-Type", jsonapi.MediaType)
w.WriteHeader(http.StatusOK)

if err = jsonapi.MarshalPayload(w, friday); err != nil {
Log.Warn("api marshal payload", zap.Error(err))
WriteAPIError(errors.New("failed to compose response data"), http.StatusInternalServerError, w)
}
}

0 comments on commit 209ef89

Please sign in to comment.