Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

graphql: Fix 500 errors on invalid JSON #3499

Merged
merged 3 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions graphql2/graphqlapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
"strconv"
"strings"
"time"

"github.com/99designs/gqlgen/graphql"
Expand Down Expand Up @@ -132,6 +133,17 @@ func isGQLValidation(gqlErr *gqlerror.Error) bool {
return true
}

if strings.HasPrefix(gqlErr.Message, "json request body") {
var body string
gqlErr.Message, body, _ = strings.Cut(gqlErr.Message, " body:") // remove body
if !strings.HasPrefix(strings.TrimSpace(body), "{") {
// Make the error more readable for common JSON errors.
gqlErr.Message = "json request body could not be decoded: body must be an object, missing '{'"
}

return true
}

if gqlErr.Extensions == nil {
return false
}
Expand Down
37 changes: 37 additions & 0 deletions test/smoke/graphqlerror_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package smoke

import (
"encoding/json"
"net/http"
"strings"
"testing"

"github.com/stretchr/testify/require"
"github.com/target/goalert/test/smoke/harness"
)

func TestGraphQLError(t *testing.T) {
h := harness.NewHarness(t, "", "ids-to-uuids")
defer h.Close()

req, err := http.NewRequest("POST", h.URL()+"/api/graphql", strings.NewReader(`"test"`))
require.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+h.GraphQLToken(harness.DefaultGraphQLAdminUserID))
resp, err := http.DefaultClient.Do(req)
require.NoError(t, err)
defer resp.Body.Close()

require.Equal(t, 400, resp.StatusCode, "expected 400 error")

var r struct {
Errors []struct {
Message string
}
}
err = json.NewDecoder(resp.Body).Decode(&r)
require.NoError(t, err)
require.Len(t, r.Errors, 1)

require.Equal(t, "json request body could not be decoded: body must be an object, missing '{'", r.Errors[0].Message)
}
24 changes: 14 additions & 10 deletions test/smoke/harness/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,24 +88,28 @@ func (h *Harness) GraphQLQueryT(t *testing.T, query string) *QLResponse {
return h.GraphQLQueryUserT(t, DefaultGraphQLAdminUserID, query)
}

// GraphQLQueryUserT will perform a GraphQL query against the backend, internally
// handling authentication. Queries are performed with the provided UserID.
func (h *Harness) GraphQLQueryUserT(t *testing.T, userID, query string) *QLResponse {
t.Helper()
retry := 1
var err error
var resp *http.Response
var tok string

func (h *Harness) GraphQLToken(userID string) string {
h.mx.Lock()
tok = h.gqlSessions[userID]
tok := h.gqlSessions[userID]
if tok == "" {
if userID == DefaultGraphQLAdminUserID {
h.createGraphQLUser(userID)
}
tok = h.createGraphQLSession(userID)
}
h.mx.Unlock()
return tok
}

// GraphQLQueryUserT will perform a GraphQL query against the backend, internally
// handling authentication. Queries are performed with the provided UserID.
func (h *Harness) GraphQLQueryUserT(t *testing.T, userID, query string) *QLResponse {
t.Helper()
retry := 1
var err error
var resp *http.Response

tok := h.GraphQLToken(userID)

for {
query = strings.Replace(query, "\t", "", -1)
Expand Down