Skip to content

Commit

Permalink
standardize errors returned by both Events and Config API (#359)
Browse files Browse the repository at this point in the history
Always return JSON object with `errors` field.
  • Loading branch information
RaeesBhatti authored and mthenw committed Feb 9, 2018
1 parent 0b87dc3 commit 7907ff2
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 21 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea
/event-gateway
vendor
*.swp
Expand Down
10 changes: 5 additions & 5 deletions functions/httpapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (h HTTPAPI) getFunction(w http.ResponseWriter, r *http.Request, params http
w.WriteHeader(http.StatusInternalServerError)
}

encoder.Encode(&httpapi.Error{Error: err.Error()})
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
} else {
encoder.Encode(fn)
}
Expand All @@ -47,7 +47,7 @@ func (h HTTPAPI) getFunctions(w http.ResponseWriter, r *http.Request, params htt
fns, err := h.Functions.GetAllFunctions()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
encoder.Encode(&httpapi.Error{Error: err.Error()})
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
} else {
encoder.Encode(&functions{fns})
}
Expand Down Expand Up @@ -80,7 +80,7 @@ func (h HTTPAPI) registerFunction(w http.ResponseWriter, r *http.Request, params
w.WriteHeader(http.StatusInternalServerError)
}

encoder.Encode(&httpapi.Error{Error: err.Error()})
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
return
}

Expand Down Expand Up @@ -111,7 +111,7 @@ func (h HTTPAPI) updateFunction(w http.ResponseWriter, r *http.Request, params h
w.WriteHeader(http.StatusInternalServerError)
}

encoder.Encode(&httpapi.Error{Error: err.Error()})
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
return
}

Expand All @@ -130,7 +130,7 @@ func (h HTTPAPI) deleteFunction(w http.ResponseWriter, r *http.Request, params h
w.WriteHeader(http.StatusInternalServerError)
}

encoder.Encode(&httpapi.Error{Error: err.Error()})
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
} else {
w.WriteHeader(http.StatusNoContent)
}
Expand Down
6 changes: 5 additions & 1 deletion internal/httpapi/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ package httpapi

import "fmt"

type Response struct {
Errors []Error `json:"errors"`
}

// Error represents generic HTTP error returned by Configuration API.
type Error struct {
Error string `json:"error"`
Message string `json:"message"`
}

// ErrMalformedJSON occurring when it's impossible to decode JSON payload.
Expand Down
48 changes: 36 additions & 12 deletions router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package router
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"regexp"
Expand All @@ -17,6 +16,7 @@ import (
eventpkg "github.com/serverless/event-gateway/event"
"github.com/serverless/event-gateway/functions"
"github.com/serverless/event-gateway/plugin"
"github.com/serverless/event-gateway/internal/httpapi"
)

// Router calls a target function when an endpoint is hit, and handles pubsub message delivery.
Expand Down Expand Up @@ -47,9 +47,12 @@ func New(workersNumber uint, backlogLength uint, targetCache Targeter, plugins *
}

func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
encoder := json.NewEncoder(w)
// if we're draining requests, spit back a 503
if router.isDraining() {
http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
w.WriteHeader(http.StatusServiceUnavailable)
w.Header().Set("content-type", "application/json")
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: http.StatusText(http.StatusServiceUnavailable) }}})
return
}

Expand All @@ -60,7 +63,9 @@ func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {

event, _, err := router.eventFromRequest(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("content-type", "application/json")
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
return
}

Expand All @@ -69,13 +74,16 @@ func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
cors.AllowAll().ServeHTTP(w, r, func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, "custom event can be emitted only with POST method")
w.Header().Set("content-type", "application/json")
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: "custom event can be emitted only with POST method" }}})
return
}

event, path, err := router.eventFromRequest(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("content-type", "application/json")
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
return
}

Expand Down Expand Up @@ -242,6 +250,7 @@ func (router *Router) eventFromRequest(r *http.Request) (*eventpkg.Event, string
return event, path, nil
}
func (router *Router) handleHTTPEvent(event *eventpkg.Event, w http.ResponseWriter, r *http.Request) {
encoder := json.NewEncoder(w)
reqMethod := r.Method

// check if CORS pre-flight request
Expand All @@ -253,7 +262,9 @@ func (router *Router) handleHTTPEvent(event *eventpkg.Event, w http.ResponseWrit
)
if backingFunction == nil {
router.log.Debug("Function not found for HTTP event.", zap.Object("event", event))
http.Error(w, "resource not found", http.StatusNotFound)
w.WriteHeader(http.StatusNotFound)
w.Header().Set("content-type", "application/json")
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: "resource not found" }}})
return
}

Expand All @@ -263,15 +274,19 @@ func (router *Router) handleHTTPEvent(event *eventpkg.Event, w http.ResponseWrit
event.Data = httpdata
resp, err := router.callFunction(*backingFunction, *event)
if err != nil {
http.Error(w, "function call failed", http.StatusInternalServerError)
w.WriteHeader(http.StatusInternalServerError)
w.Header().Set("content-type", "application/json")
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: "function call failed" }}})
return
}

httpResponse := &HTTPResponse{StatusCode: http.StatusOK}
err = json.Unmarshal(resp, httpResponse)
if err != nil {
router.log.Info("HTTP response object malformed.", zap.String("response", string(resp)))
http.Error(w, "HTTP response object malformed", http.StatusInternalServerError)
w.WriteHeader(http.StatusInternalServerError)
w.Header().Set("content-type", "application/json")
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: "HTTP response object malformed" }}})
return
}

Expand All @@ -283,7 +298,9 @@ func (router *Router) handleHTTPEvent(event *eventpkg.Event, w http.ResponseWrit

_, err = w.Write(resp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
w.WriteHeader(http.StatusInternalServerError)
w.Header().Set("content-type", "application/json")
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
return
}

Expand All @@ -306,23 +323,30 @@ func (router *Router) handleHTTPEvent(event *eventpkg.Event, w http.ResponseWrit
}

func (router *Router) handleInvokeEvent(path string, event *eventpkg.Event, w http.ResponseWriter, r *http.Request) {
encoder := json.NewEncoder(w)
routerEventsSyncReceived.Inc()

functionID := functions.FunctionID(r.Header.Get(headerFunctionID))
if !router.targetCache.InvokableFunction(path, functionID) {
http.Error(w, "function or subscription not found", http.StatusNotFound)
w.WriteHeader(http.StatusNotFound)
w.Header().Set("content-type", "application/json")
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: "function or subscription not found" }}})
return
}

resp, err := router.callFunction(functionID, *event)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
w.WriteHeader(http.StatusInternalServerError)
w.Header().Set("content-type", "application/json")
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
return
}

_, err = w.Write(resp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
w.WriteHeader(http.StatusInternalServerError)
w.Header().Set("content-type", "application/json")
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
return
}

Expand Down
6 changes: 3 additions & 3 deletions subscriptions/httpapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (h HTTPAPI) createSubscription(w http.ResponseWriter, r *http.Request, para
w.WriteHeader(http.StatusInternalServerError)
}

encoder.Encode(&httpapi.Error{Error: err.Error()})
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
return
}

Expand All @@ -70,7 +70,7 @@ func (h HTTPAPI) deleteSubscription(w http.ResponseWriter, r *http.Request, para
} else {
w.WriteHeader(http.StatusInternalServerError)
}
encoder.Encode(&httpapi.Error{Error: err.Error()})
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
} else {
w.WriteHeader(http.StatusNoContent)
}
Expand All @@ -83,7 +83,7 @@ func (h HTTPAPI) getSubscriptions(w http.ResponseWriter, r *http.Request, params
subs, err := h.Subscriptions.GetAllSubscriptions()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
encoder.Encode(&httpapi.Error{Error: err.Error()})
encoder.Encode(&httpapi.Response{Errors: []httpapi.Error{{ Message: err.Error() }}})
} else {
encoder.Encode(&subscriptions{subs})
}
Expand Down

0 comments on commit 7907ff2

Please sign in to comment.