Skip to content

Commit

Permalink
Merge pull request 99designs#298 from 99designs/handle-response-nulls
Browse files Browse the repository at this point in the history
Nulls in required fields should cause errors and bubble
  • Loading branch information
vektah authored Aug 22, 2018
2 parents ebaab0f + 6b895ab commit e8eab66
Show file tree
Hide file tree
Showing 24 changed files with 2,235 additions and 22 deletions.
8 changes: 5 additions & 3 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (p *Client) mkRequest(query string, options ...Option) Request {
return r
}

func (p *Client) Post(query string, response interface{}, options ...Option) error {
func (p *Client) Post(query string, response interface{}, options ...Option) (resperr error) {
r := p.mkRequest(query, options...)
requestBody, err := json.Marshal(r)
if err != nil {
Expand Down Expand Up @@ -109,11 +109,13 @@ func (p *Client) Post(query string, response interface{}, options ...Option) err
return fmt.Errorf("decode: %s", err.Error())
}

// we want to unpack even if there is an error, so we can see partial responses
unpackErr := unpack(respDataRaw.Data, response)

if respDataRaw.Errors != nil {
return RawJsonError{respDataRaw.Errors}
}

return unpack(respDataRaw.Data, response)
return unpackErr
}

type RawJsonError struct {
Expand Down
23 changes: 19 additions & 4 deletions codegen/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"strings"
"text/template"
"unicode"

"github.com/vektah/gqlparser/ast"
)

type GoFieldType int
Expand Down Expand Up @@ -183,13 +185,26 @@ func (f *Field) CallArgs() string {

// should be in the template, but its recursive and has a bunch of args
func (f *Field) WriteJson() string {
return f.doWriteJson("res", f.Type.Modifiers, false, 1)
return f.doWriteJson("res", f.Type.Modifiers, f.ASTType, false, 1)
}

func (f *Field) doWriteJson(val string, remainingMods []string, isPtr bool, depth int) string {
func (f *Field) doWriteJson(val string, remainingMods []string, astType *ast.Type, isPtr bool, depth int) string {
switch {
case len(remainingMods) > 0 && remainingMods[0] == modPtr:
return fmt.Sprintf("if %s == nil { return graphql.Null }\n%s", val, f.doWriteJson(val, remainingMods[1:], true, depth+1))
return tpl(`
if {{.val}} == nil {
{{- if .nonNull }}
if !ec.HasError(rctx) {
ec.Errorf(ctx, "must not be null")
}
{{- end }}
return graphql.Null
}
{{.next }}`, map[string]interface{}{
"val": val,
"nonNull": astType.NonNull,
"next": f.doWriteJson(val, remainingMods[1:], astType, true, depth+1),
})

case len(remainingMods) > 0 && remainingMods[0] == modList:
if isPtr {
Expand All @@ -211,7 +226,7 @@ func (f *Field) doWriteJson(val string, remainingMods []string, isPtr bool, dept
"val": val,
"arr": arr,
"index": index,
"next": f.doWriteJson(val+"["+index+"]", remainingMods[1:], false, depth+1),
"next": f.doWriteJson(val+"["+index+"]", remainingMods[1:], astType.Elem, false, depth+1),
})

case f.IsScalar:
Expand Down
6 changes: 3 additions & 3 deletions codegen/object_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (cfg *Config) buildObject(types NamedTypes, typ *ast.Definition, imports *I
for _, field := range typ.Fields {
if typ == cfg.schema.Query && field.Name == "__type" {
obj.Fields = append(obj.Fields, Field{
Type: &Type{types["__Schema"], []string{modPtr}, nil},
Type: &Type{types["__Schema"], []string{modPtr}, ast.NamedType("__Schema", nil), nil},
GQLName: "__schema",
NoErr: true,
GoFieldType: GoFieldMethod,
Expand All @@ -120,14 +120,14 @@ func (cfg *Config) buildObject(types NamedTypes, typ *ast.Definition, imports *I
}
if typ == cfg.schema.Query && field.Name == "__schema" {
obj.Fields = append(obj.Fields, Field{
Type: &Type{types["__Type"], []string{modPtr}, nil},
Type: &Type{types["__Type"], []string{modPtr}, ast.NamedType("__Schema", nil), nil},
GQLName: "__type",
NoErr: true,
GoFieldType: GoFieldMethod,
GoReceiverName: "ec",
GoFieldName: "introspectType",
Args: []FieldArgument{
{GQLName: "name", Type: &Type{types["String"], []string{}, nil}, Object: &Object{}},
{GQLName: "name", Type: &Type{types["String"], []string{}, ast.NamedType("String", nil), nil}, Object: &Object{}},
},
Object: obj,
})
Expand Down
4 changes: 2 additions & 2 deletions codegen/templates/data.go

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions codegen/templates/field.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
{{- end }}
})
if resTmp == nil {
{{- if $field.ASTType.NonNull }}
if !ec.HasError(rctx) {
ec.Errorf(ctx, "must not be null")
}
{{- end }}
return graphql.Null
}
res := resTmp.({{$field.Signature}})
Expand Down
7 changes: 7 additions & 0 deletions codegen/templates/object.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel ast.Se

{{if $object.IsConcurrent}} var wg sync.WaitGroup {{end}}
out := graphql.NewOrderedMap(len(fields))
invalid := false
for i, field := range fields {
out.Keys[i] = field.Alias

Expand All @@ -47,6 +48,11 @@ func (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel ast.Se
go func(i int, field graphql.CollectedField) {
{{- end }}
out.Values[i] = ec._{{$object.GQLType}}_{{$field.GQLName}}(ctx, field{{if not $object.Root}}, obj{{end}})
{{- if $field.ASTType.NonNull }}
if out.Values[i] == graphql.Null {
invalid = true
}
{{- end }}
{{- if $field.IsConcurrent }}
wg.Done()
}(i, field)
Expand All @@ -57,6 +63,7 @@ func (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel ast.Se
}
}
{{if $object.IsConcurrent}} wg.Wait() {{end}}
if invalid { return graphql.Null }
return out
}
{{- end }}
Loading

0 comments on commit e8eab66

Please sign in to comment.