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

Special handling for pointers to slices #1363

Merged
merged 3 commits into from
Apr 12, 2021
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
4 changes: 2 additions & 2 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Testing generated code is a little tricky, heres how its currently set up.

### Testing responses from a server

There is a server in `codegen/testserver` that is generated as part
There is a server in `codegen/testserver` that is generated as part
of `go generate ./...`, and tests written against it.

There are also a bunch of tests in against the examples, feel free to take examples from there.
Expand All @@ -15,7 +15,7 @@ There are also a bunch of tests in against the examples, feel free to take examp

These tests are **really** slow, because they need to run the whole codegen step. Use them very sparingly. If you can, find a way to unit test it instead.

Take a look at `codegen/input_test.go` for an example.
Take a look at `codegen/testserver/input_test.go` for an example.

### Testing introspection

Expand Down
8 changes: 8 additions & 0 deletions codegen/config/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,14 @@ func (t *TypeReference) IsSlice() bool {
return t.GQL.Elem != nil && isSlice
}

func (t *TypeReference) IsPtrToSlice() bool {
if t.IsPtr() {
_, isPointerToSlice := t.GO.(*types.Pointer).Elem().(*types.Slice)
return isPointerToSlice
}
return false
}

func (t *TypeReference) IsNamed() bool {
_, isSlice := t.GO.(*types.Named)
return isSlice
Expand Down
153 changes: 153 additions & 0 deletions codegen/testserver/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions codegen/testserver/ptr_to_slice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package testserver

type PtrToSliceContainer struct {
PtrToSlice *[]string
}
7 changes: 7 additions & 0 deletions codegen/testserver/ptr_to_slice.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type PtrToSliceContainer {
ptrToSlice: [String!]
}

extend type Query {
ptrToSliceContainer: PtrToSliceContainer!
}
37 changes: 37 additions & 0 deletions codegen/testserver/ptr_to_slice_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package testserver

import (
"context"
"testing"

"github.com/99designs/gqlgen/client"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/stretchr/testify/require"
)

func TestPtrToSlice(t *testing.T) {
resolvers := &Stub{}

c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers})))

resolvers.QueryResolver.PtrToSliceContainer = func(ctx context.Context) (wrappedStruct *PtrToSliceContainer, e error) {
ptrToSliceContainer := PtrToSliceContainer{
PtrToSlice: &[]string{"hello"},
}
return &ptrToSliceContainer, nil
}

t.Run("pointer to slice", func(t *testing.T) {
var resp struct {
PtrToSliceContainer struct {
PtrToSlice []string
}
}

err := c.Post(`query { ptrToSliceContainer { ptrToSlice }}`, &resp)
require.NoError(t, err)

require.Equal(t, []string{"hello"}, resp.PtrToSliceContainer.PtrToSlice)

})
}
4 changes: 4 additions & 0 deletions codegen/testserver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ func (r *queryResolver) PrimitiveStringObject(ctx context.Context) ([]PrimitiveS
panic("not implemented")
}

func (r *queryResolver) PtrToSliceContainer(ctx context.Context) (*PtrToSliceContainer, error) {
panic("not implemented")
}

func (r *queryResolver) DefaultScalar(ctx context.Context, arg string) (string, error) {
panic("not implemented")
}
Expand Down
4 changes: 4 additions & 0 deletions codegen/testserver/stub.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion codegen/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func processType(ret map[string]*config.TypeReference, ref *config.TypeReference
}
ret[key] = ref

if ref.IsSlice() {
if ref.IsSlice() || ref.IsPtrToSlice() {
processType(ret, ref.Elem())
}
}
9 changes: 7 additions & 2 deletions codegen/type.gotpl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
{{- if and $type.IsNilable (not $type.GQL.NonNull) }}
if v == nil { return nil, nil }
{{- end }}
{{- if $type.IsSlice }}
{{- if $type.IsPtrToSlice }}
res, err := ec.{{ $type.Elem.UnmarshalFunc }}(ctx, v)
return &res, graphql.ErrorOnPath(ctx, err)
{{- else if $type.IsSlice }}
var vSlice []interface{}
if v != nil {
if tmp1, ok := v.([]interface{}); ok {
Expand Down Expand Up @@ -66,7 +69,9 @@

{{ with $type.MarshalFunc }}
func (ec *executionContext) {{ . }}(ctx context.Context, sel ast.SelectionSet, v {{ $type.GO | ref }}) graphql.Marshaler {
{{- if $type.IsSlice }}
{{- if $type.IsPtrToSlice }}
return ec.{{ $type.Elem.MarshalFunc }}(ctx, sel, *v)
{{- else if $type.IsSlice }}
{{- if not $type.GQL.NonNull }}
if v == nil {
return graphql.Null
Expand Down