diff --git a/codegen/config/binder.go b/codegen/config/binder.go index bfd12bbce15..cea904ada33 100644 --- a/codegen/config/binder.go +++ b/codegen/config/binder.go @@ -383,7 +383,11 @@ func (b *Binder) TypeReference(schemaType *ast.Type, bindTarget types.Type) (ret func (b *Binder) CopyModifiersFromAst(t *ast.Type, base types.Type) types.Type { if t.Elem != nil { - return types.NewSlice(b.CopyModifiersFromAst(t.Elem, base)) + child := b.CopyModifiersFromAst(t.Elem, base) + if _, isStruct := child.Underlying().(*types.Struct); isStruct { + child = types.NewPointer(child) + } + return types.NewSlice(child) } var isInterface bool diff --git a/codegen/testserver/generated.go b/codegen/testserver/generated.go index 67dc8ce6b5d..d7a074445a8 100644 --- a/codegen/testserver/generated.go +++ b/codegen/testserver/generated.go @@ -341,7 +341,7 @@ type SubscriptionResolver interface { InitPayload(ctx context.Context) (<-chan string, error) } type UserResolver interface { - Friends(ctx context.Context, obj *User) ([]User, error) + Friends(ctx context.Context, obj *User) ([]*User, error) } type executableSchema struct { @@ -4894,10 +4894,10 @@ func (ec *executionContext) _User_friends(ctx context.Context, field graphql.Col } return graphql.Null } - res := resTmp.([]User) + res := resTmp.([]*User) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalNUser2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚐUser(ctx, field.Selections, res) + return ec.marshalNUser2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚐUser(ctx, field.Selections, res) } func (ec *executionContext) _User_created(ctx context.Context, field graphql.CollectedField, obj *User) graphql.Marshaler { @@ -7232,7 +7232,7 @@ func (ec *executionContext) _Primitive(ctx context.Context, sel ast.SelectionSet fields := graphql.CollectFields(ec.RequestContext, sel, primitiveImplementors) out := graphql.NewFieldSet(fields) - invalid := false + var invalids uint32 for i, field := range fields { switch field.Name { case "__typename": @@ -7247,21 +7247,21 @@ func (ec *executionContext) _Primitive(ctx context.Context, sel ast.SelectionSet }() res = ec._Primitive_value(ctx, field, obj) if res == graphql.Null { - invalid = true + atomic.AddUint32(&invalids, 1) } return res }) case "squared": out.Values[i] = ec._Primitive_squared(ctx, field, obj) if out.Values[i] == graphql.Null { - invalid = true + atomic.AddUint32(&invalids, 1) } default: panic("unknown field " + strconv.Quote(field.Name)) } } out.Dispatch() - if invalid { + if invalids > 0 { return graphql.Null } return out @@ -7273,7 +7273,7 @@ func (ec *executionContext) _PrimitiveString(ctx context.Context, sel ast.Select fields := graphql.CollectFields(ec.RequestContext, sel, primitiveStringImplementors) out := graphql.NewFieldSet(fields) - invalid := false + var invalids uint32 for i, field := range fields { switch field.Name { case "__typename": @@ -7288,14 +7288,14 @@ func (ec *executionContext) _PrimitiveString(ctx context.Context, sel ast.Select }() res = ec._PrimitiveString_value(ctx, field, obj) if res == graphql.Null { - invalid = true + atomic.AddUint32(&invalids, 1) } return res }) case "doubled": out.Values[i] = ec._PrimitiveString_doubled(ctx, field, obj) if out.Values[i] == graphql.Null { - invalid = true + atomic.AddUint32(&invalids, 1) } case "len": field := field @@ -7307,7 +7307,7 @@ func (ec *executionContext) _PrimitiveString(ctx context.Context, sel ast.Select }() res = ec._PrimitiveString_len(ctx, field, obj) if res == graphql.Null { - invalid = true + atomic.AddUint32(&invalids, 1) } return res }) @@ -7316,7 +7316,7 @@ func (ec *executionContext) _PrimitiveString(ctx context.Context, sel ast.Select } } out.Dispatch() - if invalid { + if invalids > 0 { return graphql.Null } return out @@ -7637,7 +7637,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr }() res = ec._Query_primitiveObject(ctx, field) if res == graphql.Null { - invalid = true + atomic.AddUint32(&invalids, 1) } return res }) @@ -7651,7 +7651,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr }() res = ec._Query_primitiveStringObject(ctx, field) if res == graphql.Null { - invalid = true + atomic.AddUint32(&invalids, 1) } return res }) @@ -8721,7 +8721,7 @@ func (ec *executionContext) marshalNUser2githubᚗcomᚋ99designsᚋgqlgenᚋcod return ec._User(ctx, sel, &v) } -func (ec *executionContext) marshalNUser2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚐUser(ctx context.Context, sel ast.SelectionSet, v []User) graphql.Marshaler { +func (ec *executionContext) marshalNUser2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚐUser(ctx context.Context, sel ast.SelectionSet, v []*User) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -8745,7 +8745,7 @@ func (ec *executionContext) marshalNUser2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋ if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNUser2githubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚐUser(ctx, sel, v[i]) + ret[i] = ec.marshalNUser2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋcodegenᚋtestserverᚐUser(ctx, sel, v[i]) } if isLen1 { f(i) diff --git a/codegen/testserver/middleware_test.go b/codegen/testserver/middleware_test.go index 2a48ccf5819..92893a172a4 100644 --- a/codegen/testserver/middleware_test.go +++ b/codegen/testserver/middleware_test.go @@ -23,8 +23,8 @@ func TestMiddleware(t *testing.T) { return &User{ID: 1}, nil } - resolvers.UserResolver.Friends = func(ctx context.Context, obj *User) (users []User, e error) { - return []User{{ID: 1}}, nil + resolvers.UserResolver.Friends = func(ctx context.Context, obj *User) (users []*User, e error) { + return []*User{{ID: 1}}, nil } areMethods := []bool{} @@ -57,10 +57,10 @@ func TestMiddleware(t *testing.T) { } called := false - resolvers.UserResolver.Friends = func(ctx context.Context, obj *User) ([]User, error) { + resolvers.UserResolver.Friends = func(ctx context.Context, obj *User) ([]*User, error) { assert.Equal(t, []int{1, 2, 1, 2}, ctx.Value("path")) called = true - return []User{}, nil + return []*User{}, nil } err := c.Post(`query { user(id: 1) { id, friends { id } } }`, &resp) diff --git a/codegen/testserver/models-gen.go b/codegen/testserver/models-gen.go index 3132ae0fc92..5d6ad0e41a6 100644 --- a/codegen/testserver/models-gen.go +++ b/codegen/testserver/models-gen.go @@ -103,7 +103,7 @@ type Slices struct { type User struct { ID int `json:"id"` - Friends []User `json:"friends"` + Friends []*User `json:"friends"` Created time.Time `json:"created"` Updated *time.Time `json:"updated"` } diff --git a/codegen/testserver/resolver.go b/codegen/testserver/resolver.go index eb0e840067c..012b2103850 100644 --- a/codegen/testserver/resolver.go +++ b/codegen/testserver/resolver.go @@ -215,6 +215,6 @@ func (r *subscriptionResolver) InitPayload(ctx context.Context) (<-chan string, type userResolver struct{ *Resolver } -func (r *userResolver) Friends(ctx context.Context, obj *User) ([]User, error) { +func (r *userResolver) Friends(ctx context.Context, obj *User) ([]*User, error) { panic("not implemented") } diff --git a/codegen/testserver/stub.go b/codegen/testserver/stub.go index 776bc189f17..ee69e0c7b37 100644 --- a/codegen/testserver/stub.go +++ b/codegen/testserver/stub.go @@ -77,7 +77,7 @@ type Stub struct { InitPayload func(ctx context.Context) (<-chan string, error) } UserResolver struct { - Friends func(ctx context.Context, obj *User) ([]User, error) + Friends func(ctx context.Context, obj *User) ([]*User, error) } } @@ -285,6 +285,6 @@ func (r *stubSubscription) InitPayload(ctx context.Context) (<-chan string, erro type stubUser struct{ *Stub } -func (r *stubUser) Friends(ctx context.Context, obj *User) ([]User, error) { +func (r *stubUser) Friends(ctx context.Context, obj *User) ([]*User, error) { return r.UserResolver.Friends(ctx, obj) } diff --git a/codegen/testserver/tracer_test.go b/codegen/testserver/tracer_test.go index e2ccc5f95fd..461e6426d76 100644 --- a/codegen/testserver/tracer_test.go +++ b/codegen/testserver/tracer_test.go @@ -64,7 +64,7 @@ func TestTracer(t *testing.T) { } called := false - resolvers.UserResolver.Friends = func(ctx context.Context, obj *User) ([]User, error) { + resolvers.UserResolver.Friends = func(ctx context.Context, obj *User) ([]*User, error) { assert.Equal(t, []string{ "op:p:start:1", "op:p:start:2", "op:v:start:1", "op:v:start:2", @@ -76,7 +76,7 @@ func TestTracer(t *testing.T) { "field'b:e:start:1:[user friends]", "field'b:e:start:2:[user friends]", }, ctx.Value("tracer")) called = true - return []User{}, nil + return []*User{}, nil } err := c.Post(`query { user(id: 1) { id, friends { id } } }`, &resp) @@ -175,9 +175,9 @@ func TestTracer(t *testing.T) { } called := false - resolvers.UserResolver.Friends = func(ctx context.Context, obj *User) ([]User, error) { + resolvers.UserResolver.Friends = func(ctx context.Context, obj *User) ([]*User, error) { called = true - return []User{}, nil + return []*User{}, nil } err := c.Post(`query { user(id: 1) { id, friends { id } } }`, &resp) diff --git a/example/config/generated.go b/example/config/generated.go index 160da9ec0f3..ccf93600be2 100644 --- a/example/config/generated.go +++ b/example/config/generated.go @@ -69,7 +69,7 @@ type MutationResolver interface { CreateTodo(ctx context.Context, input NewTodo) (*Todo, error) } type QueryResolver interface { - Todos(ctx context.Context) ([]Todo, error) + Todos(ctx context.Context) ([]*Todo, error) } type TodoResolver interface { ID(ctx context.Context, obj *Todo) (string, error) @@ -383,10 +383,10 @@ func (ec *executionContext) _Query_todos(ctx context.Context, field graphql.Coll } return graphql.Null } - res := resTmp.([]Todo) + res := resTmp.([]*Todo) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalNTodo2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋconfigᚐTodo(ctx, field.Selections, res) + return ec.marshalNTodo2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋconfigᚐTodo(ctx, field.Selections, res) } func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { @@ -1968,7 +1968,7 @@ func (ec *executionContext) marshalNTodo2githubᚗcomᚋ99designsᚋgqlgenᚋexa return ec._Todo(ctx, sel, &v) } -func (ec *executionContext) marshalNTodo2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋconfigᚐTodo(ctx context.Context, sel ast.SelectionSet, v []Todo) graphql.Marshaler { +func (ec *executionContext) marshalNTodo2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋconfigᚐTodo(ctx context.Context, sel ast.SelectionSet, v []*Todo) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -1992,7 +1992,7 @@ func (ec *executionContext) marshalNTodo2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋ if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNTodo2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋconfigᚐTodo(ctx, sel, v[i]) + ret[i] = ec.marshalNTodo2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋconfigᚐTodo(ctx, sel, v[i]) } if isLen1 { f(i) diff --git a/example/config/resolver.go b/example/config/resolver.go index 85b5eae5bcb..519db1fe290 100644 --- a/example/config/resolver.go +++ b/example/config/resolver.go @@ -10,7 +10,7 @@ import ( func New() Config { c := Config{ Resolvers: &Resolver{ - todos: []Todo{ + todos: []*Todo{ {DatabaseID: 1, Description: "A todo not to forget", Done: false}, {DatabaseID: 2, Description: "This is the most important", Done: false}, {DatabaseID: 3, Description: "Please do this or else", Done: false}, @@ -22,7 +22,7 @@ func New() Config { } type Resolver struct { - todos []Todo + todos []*Todo nextID int } @@ -42,19 +42,19 @@ func (r *mutationResolver) CreateTodo(ctx context.Context, input NewTodo) (*Todo newID := r.nextID r.nextID++ - newTodo := Todo{ + newTodo := &Todo{ DatabaseID: newID, Description: input.Text, } r.todos = append(r.todos, newTodo) - return &newTodo, nil + return newTodo, nil } type queryResolver struct{ *Resolver } -func (r *queryResolver) Todos(ctx context.Context) ([]Todo, error) { +func (r *queryResolver) Todos(ctx context.Context) ([]*Todo, error) { return r.todos, nil } diff --git a/example/dataloader/addressloader_gen.go b/example/dataloader/addressloader_gen.go index 90e1d433fc5..c55a2c87c26 100644 --- a/example/dataloader/addressloader_gen.go +++ b/example/dataloader/addressloader_gen.go @@ -46,13 +46,13 @@ type AddressLoader struct { // the current batch. keys will continue to be collected until timeout is hit, // then everything will be sent to the fetch method and out to the listeners - batch *addressBatch + batch *addressLoaderBatch // mutex to prevent races mu sync.Mutex } -type addressBatch struct { +type addressLoaderBatch struct { keys []int data []*Address error []error @@ -60,12 +60,12 @@ type addressBatch struct { done chan struct{} } -// Load a address by key, batching and caching will be applied automatically +// Load a Address by key, batching and caching will be applied automatically func (l *AddressLoader) Load(key int) (*Address, error) { return l.LoadThunk(key)() } -// LoadThunk returns a function that when called will block waiting for a address. +// LoadThunk returns a function that when called will block waiting for a Address. // This method should be used if you want one goroutine to make requests to many // different data loaders without blocking until the thunk is called. func (l *AddressLoader) LoadThunk(key int) func() (*Address, error) { @@ -77,7 +77,7 @@ func (l *AddressLoader) LoadThunk(key int) func() (*Address, error) { } } if l.batch == nil { - l.batch = &addressBatch{done: make(chan struct{})} + l.batch = &addressLoaderBatch{done: make(chan struct{})} } batch := l.batch pos := batch.keyIndex(l, key) @@ -126,6 +126,24 @@ func (l *AddressLoader) LoadAll(keys []int) ([]*Address, []error) { return addresss, errors } +// LoadAllThunk returns a function that when called will block waiting for a Addresss. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *AddressLoader) LoadAllThunk(keys []int) func() ([]*Address, []error) { + results := make([]func() (*Address, error), len(keys)) + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + return func() ([]*Address, []error) { + addresss := make([]*Address, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + addresss[i], errors[i] = thunk() + } + return addresss, errors + } +} + // Prime the cache with the provided key and value. If the key already exists, no change is made // and false is returned. // (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).) @@ -158,7 +176,7 @@ func (l *AddressLoader) unsafeSet(key int, value *Address) { // keyIndex will return the location of the key in the batch, if its not found // it will add the key to the batch -func (b *addressBatch) keyIndex(l *AddressLoader, key int) int { +func (b *addressLoaderBatch) keyIndex(l *AddressLoader, key int) int { for i, existingKey := range b.keys { if key == existingKey { return i @@ -182,7 +200,7 @@ func (b *addressBatch) keyIndex(l *AddressLoader, key int) int { return pos } -func (b *addressBatch) startTimer(l *AddressLoader) { +func (b *addressLoaderBatch) startTimer(l *AddressLoader) { time.Sleep(l.wait) l.mu.Lock() @@ -198,7 +216,7 @@ func (b *addressBatch) startTimer(l *AddressLoader) { b.end(l) } -func (b *addressBatch) end(l *AddressLoader) { +func (b *addressLoaderBatch) end(l *AddressLoader) { b.data, b.error = l.fetch(b.keys) close(b.done) } diff --git a/example/dataloader/dataloaders.go b/example/dataloader/dataloaders.go index 05944e5d5e0..dcbb2e689f3 100644 --- a/example/dataloader/dataloaders.go +++ b/example/dataloader/dataloaders.go @@ -1,6 +1,6 @@ -//go:generate go run github.com/vektah/dataloaden -keys int github.com/99designs/gqlgen/example/dataloader.Address -//go:generate go run github.com/vektah/dataloaden -keys int -slice github.com/99designs/gqlgen/example/dataloader.Order -//go:generate go run github.com/vektah/dataloaden -keys int -slice github.com/99designs/gqlgen/example/dataloader.Item +//go:generate go run github.com/vektah/dataloaden AddressLoader int *github.com/99designs/gqlgen/example/dataloader.Address +//go:generate go run github.com/vektah/dataloaden OrderSliceLoader int []*github.com/99designs/gqlgen/example/dataloader.Order +//go:generate go run github.com/vektah/dataloaden ItemSliceLoader int []*github.com/99designs/gqlgen/example/dataloader.Item package dataloader @@ -57,7 +57,7 @@ func LoaderMiddleware(next http.Handler) http.Handler { ldrs.ordersByCustomer = &OrderSliceLoader{ wait: wait, maxBatch: 100, - fetch: func(keys []int) ([][]Order, []error) { + fetch: func(keys []int) ([][]*Order, []error) { var keySql []string for _, key := range keys { keySql = append(keySql, strconv.Itoa(key)) @@ -66,11 +66,11 @@ func LoaderMiddleware(next http.Handler) http.Handler { fmt.Printf("SELECT * FROM orders WHERE customer_id IN (%s)\n", strings.Join(keySql, ",")) time.Sleep(5 * time.Millisecond) - orders := make([][]Order, len(keys)) + orders := make([][]*Order, len(keys)) errors := make([]error, len(keys)) for i, key := range keys { id := 10 + rand.Int()%3 - orders[i] = []Order{ + orders[i] = []*Order{ {ID: id, Amount: rand.Float64(), Date: time.Now().Add(-time.Duration(key) * time.Hour)}, {ID: id + 1, Amount: rand.Float64(), Date: time.Now().Add(-time.Duration(key) * time.Hour)}, } @@ -87,7 +87,7 @@ func LoaderMiddleware(next http.Handler) http.Handler { ldrs.itemsByOrder = &ItemSliceLoader{ wait: wait, maxBatch: 100, - fetch: func(keys []int) ([][]Item, []error) { + fetch: func(keys []int) ([][]*Item, []error) { var keySql []string for _, key := range keys { keySql = append(keySql, strconv.Itoa(key)) @@ -96,10 +96,10 @@ func LoaderMiddleware(next http.Handler) http.Handler { fmt.Printf("SELECT * FROM items JOIN item_order WHERE item_order.order_id IN (%s)\n", strings.Join(keySql, ",")) time.Sleep(5 * time.Millisecond) - items := make([][]Item, len(keys)) + items := make([][]*Item, len(keys)) errors := make([]error, len(keys)) for i := range keys { - items[i] = []Item{ + items[i] = []*Item{ {Name: "item " + strconv.Itoa(rand.Int()%20+20)}, {Name: "item " + strconv.Itoa(rand.Int()%20+20)}, } diff --git a/example/dataloader/generated.go b/example/dataloader/generated.go index 0236490c9c9..2bf7166d442 100644 --- a/example/dataloader/generated.go +++ b/example/dataloader/generated.go @@ -77,15 +77,15 @@ type ComplexityRoot struct { type CustomerResolver interface { Address(ctx context.Context, obj *Customer) (*Address, error) - Orders(ctx context.Context, obj *Customer) ([]Order, error) + Orders(ctx context.Context, obj *Customer) ([]*Order, error) } type OrderResolver interface { - Items(ctx context.Context, obj *Order) ([]Item, error) + Items(ctx context.Context, obj *Order) ([]*Item, error) } type QueryResolver interface { - Customers(ctx context.Context) ([]Customer, error) - Torture1d(ctx context.Context, customerIds []int) ([]Customer, error) - Torture2d(ctx context.Context, customerIds [][]int) ([][]Customer, error) + Customers(ctx context.Context) ([]*Customer, error) + Torture1d(ctx context.Context, customerIds []int) ([]*Customer, error) + Torture2d(ctx context.Context, customerIds [][]int) ([][]*Customer, error) } type executableSchema struct { @@ -572,10 +572,10 @@ func (ec *executionContext) _Customer_orders(ctx context.Context, field graphql. if resTmp == nil { return graphql.Null } - res := resTmp.([]Order) + res := resTmp.([]*Order) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOOrder2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐOrder(ctx, field.Selections, res) + return ec.marshalOOrder2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐOrder(ctx, field.Selections, res) } func (ec *executionContext) _Item_name(ctx context.Context, field graphql.CollectedField, obj *Item) graphql.Marshaler { @@ -704,10 +704,10 @@ func (ec *executionContext) _Order_items(ctx context.Context, field graphql.Coll if resTmp == nil { return graphql.Null } - res := resTmp.([]Item) + res := resTmp.([]*Item) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOItem2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐItem(ctx, field.Selections, res) + return ec.marshalOItem2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐItem(ctx, field.Selections, res) } func (ec *executionContext) _Query_customers(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { @@ -728,10 +728,10 @@ func (ec *executionContext) _Query_customers(ctx context.Context, field graphql. if resTmp == nil { return graphql.Null } - res := resTmp.([]Customer) + res := resTmp.([]*Customer) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOCustomer2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx, field.Selections, res) + return ec.marshalOCustomer2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx, field.Selections, res) } func (ec *executionContext) _Query_torture1d(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { @@ -759,10 +759,10 @@ func (ec *executionContext) _Query_torture1d(ctx context.Context, field graphql. if resTmp == nil { return graphql.Null } - res := resTmp.([]Customer) + res := resTmp.([]*Customer) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOCustomer2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx, field.Selections, res) + return ec.marshalOCustomer2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx, field.Selections, res) } func (ec *executionContext) _Query_torture2d(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { @@ -790,10 +790,10 @@ func (ec *executionContext) _Query_torture2d(ctx context.Context, field graphql. if resTmp == nil { return graphql.Null } - res := resTmp.([][]Customer) + res := resTmp.([][]*Customer) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOCustomer2ᚕᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx, field.Selections, res) + return ec.marshalOCustomer2ᚕᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx, field.Selections, res) } func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { @@ -2182,6 +2182,16 @@ func (ec *executionContext) marshalNCustomer2githubᚗcomᚋ99designsᚋgqlgen return ec._Customer(ctx, sel, &v) } +func (ec *executionContext) marshalNCustomer2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx context.Context, sel ast.SelectionSet, v *Customer) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._Customer(ctx, sel, v) +} + func (ec *executionContext) unmarshalNFloat2float64(ctx context.Context, v interface{}) (float64, error) { return graphql.UnmarshalFloat(v) } @@ -2214,10 +2224,30 @@ func (ec *executionContext) marshalNItem2githubᚗcomᚋ99designsᚋgqlgenᚋexa return ec._Item(ctx, sel, &v) } +func (ec *executionContext) marshalNItem2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐItem(ctx context.Context, sel ast.SelectionSet, v *Item) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._Item(ctx, sel, v) +} + func (ec *executionContext) marshalNOrder2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐOrder(ctx context.Context, sel ast.SelectionSet, v Order) graphql.Marshaler { return ec._Order(ctx, sel, &v) } +func (ec *executionContext) marshalNOrder2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐOrder(ctx context.Context, sel ast.SelectionSet, v *Order) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._Order(ctx, sel, v) +} + func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { return graphql.UnmarshalString(v) } @@ -2506,7 +2536,7 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast return ec.marshalOBoolean2bool(ctx, sel, *v) } -func (ec *executionContext) marshalOCustomer2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx context.Context, sel ast.SelectionSet, v []Customer) graphql.Marshaler { +func (ec *executionContext) marshalOCustomer2ᚕᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx context.Context, sel ast.SelectionSet, v [][]*Customer) graphql.Marshaler { if v == nil { return graphql.Null } @@ -2533,7 +2563,7 @@ func (ec *executionContext) marshalOCustomer2ᚕgithubᚗcomᚋ99designsᚋgqlge if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNCustomer2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx, sel, v[i]) + ret[i] = ec.marshalOCustomer2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx, sel, v[i]) } if isLen1 { f(i) @@ -2546,7 +2576,7 @@ func (ec *executionContext) marshalOCustomer2ᚕgithubᚗcomᚋ99designsᚋgqlge return ret } -func (ec *executionContext) marshalOCustomer2ᚕᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx context.Context, sel ast.SelectionSet, v [][]Customer) graphql.Marshaler { +func (ec *executionContext) marshalOCustomer2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx context.Context, sel ast.SelectionSet, v []*Customer) graphql.Marshaler { if v == nil { return graphql.Null } @@ -2573,7 +2603,7 @@ func (ec *executionContext) marshalOCustomer2ᚕᚕgithubᚗcomᚋ99designsᚋgq if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalOCustomer2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx, sel, v[i]) + ret[i] = ec.marshalNCustomer2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐCustomer(ctx, sel, v[i]) } if isLen1 { f(i) @@ -2650,7 +2680,7 @@ func (ec *executionContext) marshalOInt2ᚕᚕint(ctx context.Context, sel ast.S return ret } -func (ec *executionContext) marshalOItem2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐItem(ctx context.Context, sel ast.SelectionSet, v []Item) graphql.Marshaler { +func (ec *executionContext) marshalOItem2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐItem(ctx context.Context, sel ast.SelectionSet, v []*Item) graphql.Marshaler { if v == nil { return graphql.Null } @@ -2677,7 +2707,7 @@ func (ec *executionContext) marshalOItem2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋ if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNItem2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐItem(ctx, sel, v[i]) + ret[i] = ec.marshalNItem2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐItem(ctx, sel, v[i]) } if isLen1 { f(i) @@ -2690,7 +2720,7 @@ func (ec *executionContext) marshalOItem2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋ return ret } -func (ec *executionContext) marshalOOrder2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐOrder(ctx context.Context, sel ast.SelectionSet, v []Order) graphql.Marshaler { +func (ec *executionContext) marshalOOrder2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐOrder(ctx context.Context, sel ast.SelectionSet, v []*Order) graphql.Marshaler { if v == nil { return graphql.Null } @@ -2717,7 +2747,7 @@ func (ec *executionContext) marshalOOrder2ᚕgithubᚗcomᚋ99designsᚋgqlgen if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNOrder2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐOrder(ctx, sel, v[i]) + ret[i] = ec.marshalNOrder2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋdataloaderᚐOrder(ctx, sel, v[i]) } if isLen1 { f(i) diff --git a/example/dataloader/itemsliceloader_gen.go b/example/dataloader/itemsliceloader_gen.go index 5fa10078401..f5a38508677 100644 --- a/example/dataloader/itemsliceloader_gen.go +++ b/example/dataloader/itemsliceloader_gen.go @@ -10,7 +10,7 @@ import ( // ItemSliceLoaderConfig captures the config to create a new ItemSliceLoader type ItemSliceLoaderConfig struct { // Fetch is a method that provides the data for the loader - Fetch func(keys []int) ([][]Item, []error) + Fetch func(keys []int) ([][]*Item, []error) // Wait is how long wait before sending a batch Wait time.Duration @@ -31,7 +31,7 @@ func NewItemSliceLoader(config ItemSliceLoaderConfig) *ItemSliceLoader { // ItemSliceLoader batches and caches requests type ItemSliceLoader struct { // this method provides the data for the loader - fetch func(keys []int) ([][]Item, []error) + fetch func(keys []int) ([][]*Item, []error) // how long to done before sending a batch wait time.Duration @@ -42,51 +42,51 @@ type ItemSliceLoader struct { // INTERNAL // lazily created cache - cache map[int][]Item + cache map[int][]*Item // the current batch. keys will continue to be collected until timeout is hit, // then everything will be sent to the fetch method and out to the listeners - batch *itemSliceBatch + batch *itemSliceLoaderBatch // mutex to prevent races mu sync.Mutex } -type itemSliceBatch struct { +type itemSliceLoaderBatch struct { keys []int - data [][]Item + data [][]*Item error []error closing bool done chan struct{} } -// Load a item by key, batching and caching will be applied automatically -func (l *ItemSliceLoader) Load(key int) ([]Item, error) { +// Load a Item by key, batching and caching will be applied automatically +func (l *ItemSliceLoader) Load(key int) ([]*Item, error) { return l.LoadThunk(key)() } -// LoadThunk returns a function that when called will block waiting for a item. +// LoadThunk returns a function that when called will block waiting for a Item. // This method should be used if you want one goroutine to make requests to many // different data loaders without blocking until the thunk is called. -func (l *ItemSliceLoader) LoadThunk(key int) func() ([]Item, error) { +func (l *ItemSliceLoader) LoadThunk(key int) func() ([]*Item, error) { l.mu.Lock() if it, ok := l.cache[key]; ok { l.mu.Unlock() - return func() ([]Item, error) { + return func() ([]*Item, error) { return it, nil } } if l.batch == nil { - l.batch = &itemSliceBatch{done: make(chan struct{})} + l.batch = &itemSliceLoaderBatch{done: make(chan struct{})} } batch := l.batch pos := batch.keyIndex(l, key) l.mu.Unlock() - return func() ([]Item, error) { + return func() ([]*Item, error) { <-batch.done - var data []Item + var data []*Item if pos < len(batch.data) { data = batch.data[pos] } @@ -111,14 +111,14 @@ func (l *ItemSliceLoader) LoadThunk(key int) func() ([]Item, error) { // LoadAll fetches many keys at once. It will be broken into appropriate sized // sub batches depending on how the loader is configured -func (l *ItemSliceLoader) LoadAll(keys []int) ([][]Item, []error) { - results := make([]func() ([]Item, error), len(keys)) +func (l *ItemSliceLoader) LoadAll(keys []int) ([][]*Item, []error) { + results := make([]func() ([]*Item, error), len(keys)) for i, key := range keys { results[i] = l.LoadThunk(key) } - items := make([][]Item, len(keys)) + items := make([][]*Item, len(keys)) errors := make([]error, len(keys)) for i, thunk := range results { items[i], errors[i] = thunk() @@ -126,14 +126,36 @@ func (l *ItemSliceLoader) LoadAll(keys []int) ([][]Item, []error) { return items, errors } +// LoadAllThunk returns a function that when called will block waiting for a Items. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *ItemSliceLoader) LoadAllThunk(keys []int) func() ([][]*Item, []error) { + results := make([]func() ([]*Item, error), len(keys)) + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + return func() ([][]*Item, []error) { + items := make([][]*Item, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + items[i], errors[i] = thunk() + } + return items, errors + } +} + // Prime the cache with the provided key and value. If the key already exists, no change is made // and false is returned. // (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).) -func (l *ItemSliceLoader) Prime(key int, value []Item) bool { +func (l *ItemSliceLoader) Prime(key int, value []*Item) bool { l.mu.Lock() var found bool if _, found = l.cache[key]; !found { - l.unsafeSet(key, value) + // make a copy when writing to the cache, its easy to pass a pointer in from a loop var + // and end up with the whole cache pointing to the same value. + cpy := make([]*Item, len(value)) + copy(cpy, value) + l.unsafeSet(key, cpy) } l.mu.Unlock() return !found @@ -146,16 +168,16 @@ func (l *ItemSliceLoader) Clear(key int) { l.mu.Unlock() } -func (l *ItemSliceLoader) unsafeSet(key int, value []Item) { +func (l *ItemSliceLoader) unsafeSet(key int, value []*Item) { if l.cache == nil { - l.cache = map[int][]Item{} + l.cache = map[int][]*Item{} } l.cache[key] = value } // keyIndex will return the location of the key in the batch, if its not found // it will add the key to the batch -func (b *itemSliceBatch) keyIndex(l *ItemSliceLoader, key int) int { +func (b *itemSliceLoaderBatch) keyIndex(l *ItemSliceLoader, key int) int { for i, existingKey := range b.keys { if key == existingKey { return i @@ -179,7 +201,7 @@ func (b *itemSliceBatch) keyIndex(l *ItemSliceLoader, key int) int { return pos } -func (b *itemSliceBatch) startTimer(l *ItemSliceLoader) { +func (b *itemSliceLoaderBatch) startTimer(l *ItemSliceLoader) { time.Sleep(l.wait) l.mu.Lock() @@ -195,7 +217,7 @@ func (b *itemSliceBatch) startTimer(l *ItemSliceLoader) { b.end(l) } -func (b *itemSliceBatch) end(l *ItemSliceLoader) { +func (b *itemSliceLoaderBatch) end(l *ItemSliceLoader) { b.data, b.error = l.fetch(b.keys) close(b.done) } diff --git a/example/dataloader/ordersliceloader_gen.go b/example/dataloader/ordersliceloader_gen.go index e76e24d31f0..ad1e1ba8d8f 100644 --- a/example/dataloader/ordersliceloader_gen.go +++ b/example/dataloader/ordersliceloader_gen.go @@ -10,7 +10,7 @@ import ( // OrderSliceLoaderConfig captures the config to create a new OrderSliceLoader type OrderSliceLoaderConfig struct { // Fetch is a method that provides the data for the loader - Fetch func(keys []int) ([][]Order, []error) + Fetch func(keys []int) ([][]*Order, []error) // Wait is how long wait before sending a batch Wait time.Duration @@ -31,7 +31,7 @@ func NewOrderSliceLoader(config OrderSliceLoaderConfig) *OrderSliceLoader { // OrderSliceLoader batches and caches requests type OrderSliceLoader struct { // this method provides the data for the loader - fetch func(keys []int) ([][]Order, []error) + fetch func(keys []int) ([][]*Order, []error) // how long to done before sending a batch wait time.Duration @@ -42,51 +42,51 @@ type OrderSliceLoader struct { // INTERNAL // lazily created cache - cache map[int][]Order + cache map[int][]*Order // the current batch. keys will continue to be collected until timeout is hit, // then everything will be sent to the fetch method and out to the listeners - batch *orderSliceBatch + batch *orderSliceLoaderBatch // mutex to prevent races mu sync.Mutex } -type orderSliceBatch struct { +type orderSliceLoaderBatch struct { keys []int - data [][]Order + data [][]*Order error []error closing bool done chan struct{} } -// Load a order by key, batching and caching will be applied automatically -func (l *OrderSliceLoader) Load(key int) ([]Order, error) { +// Load a Order by key, batching and caching will be applied automatically +func (l *OrderSliceLoader) Load(key int) ([]*Order, error) { return l.LoadThunk(key)() } -// LoadThunk returns a function that when called will block waiting for a order. +// LoadThunk returns a function that when called will block waiting for a Order. // This method should be used if you want one goroutine to make requests to many // different data loaders without blocking until the thunk is called. -func (l *OrderSliceLoader) LoadThunk(key int) func() ([]Order, error) { +func (l *OrderSliceLoader) LoadThunk(key int) func() ([]*Order, error) { l.mu.Lock() if it, ok := l.cache[key]; ok { l.mu.Unlock() - return func() ([]Order, error) { + return func() ([]*Order, error) { return it, nil } } if l.batch == nil { - l.batch = &orderSliceBatch{done: make(chan struct{})} + l.batch = &orderSliceLoaderBatch{done: make(chan struct{})} } batch := l.batch pos := batch.keyIndex(l, key) l.mu.Unlock() - return func() ([]Order, error) { + return func() ([]*Order, error) { <-batch.done - var data []Order + var data []*Order if pos < len(batch.data) { data = batch.data[pos] } @@ -111,14 +111,14 @@ func (l *OrderSliceLoader) LoadThunk(key int) func() ([]Order, error) { // LoadAll fetches many keys at once. It will be broken into appropriate sized // sub batches depending on how the loader is configured -func (l *OrderSliceLoader) LoadAll(keys []int) ([][]Order, []error) { - results := make([]func() ([]Order, error), len(keys)) +func (l *OrderSliceLoader) LoadAll(keys []int) ([][]*Order, []error) { + results := make([]func() ([]*Order, error), len(keys)) for i, key := range keys { results[i] = l.LoadThunk(key) } - orders := make([][]Order, len(keys)) + orders := make([][]*Order, len(keys)) errors := make([]error, len(keys)) for i, thunk := range results { orders[i], errors[i] = thunk() @@ -126,14 +126,36 @@ func (l *OrderSliceLoader) LoadAll(keys []int) ([][]Order, []error) { return orders, errors } +// LoadAllThunk returns a function that when called will block waiting for a Orders. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *OrderSliceLoader) LoadAllThunk(keys []int) func() ([][]*Order, []error) { + results := make([]func() ([]*Order, error), len(keys)) + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + return func() ([][]*Order, []error) { + orders := make([][]*Order, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + orders[i], errors[i] = thunk() + } + return orders, errors + } +} + // Prime the cache with the provided key and value. If the key already exists, no change is made // and false is returned. // (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).) -func (l *OrderSliceLoader) Prime(key int, value []Order) bool { +func (l *OrderSliceLoader) Prime(key int, value []*Order) bool { l.mu.Lock() var found bool if _, found = l.cache[key]; !found { - l.unsafeSet(key, value) + // make a copy when writing to the cache, its easy to pass a pointer in from a loop var + // and end up with the whole cache pointing to the same value. + cpy := make([]*Order, len(value)) + copy(cpy, value) + l.unsafeSet(key, cpy) } l.mu.Unlock() return !found @@ -146,16 +168,16 @@ func (l *OrderSliceLoader) Clear(key int) { l.mu.Unlock() } -func (l *OrderSliceLoader) unsafeSet(key int, value []Order) { +func (l *OrderSliceLoader) unsafeSet(key int, value []*Order) { if l.cache == nil { - l.cache = map[int][]Order{} + l.cache = map[int][]*Order{} } l.cache[key] = value } // keyIndex will return the location of the key in the batch, if its not found // it will add the key to the batch -func (b *orderSliceBatch) keyIndex(l *OrderSliceLoader, key int) int { +func (b *orderSliceLoaderBatch) keyIndex(l *OrderSliceLoader, key int) int { for i, existingKey := range b.keys { if key == existingKey { return i @@ -179,7 +201,7 @@ func (b *orderSliceBatch) keyIndex(l *OrderSliceLoader, key int) int { return pos } -func (b *orderSliceBatch) startTimer(l *OrderSliceLoader) { +func (b *orderSliceLoaderBatch) startTimer(l *OrderSliceLoader) { time.Sleep(l.wait) l.mu.Lock() @@ -195,7 +217,7 @@ func (b *orderSliceBatch) startTimer(l *OrderSliceLoader) { b.end(l) } -func (b *orderSliceBatch) end(l *OrderSliceLoader) { +func (b *orderSliceLoaderBatch) end(l *OrderSliceLoader) { b.data, b.error = l.fetch(b.keys) close(b.done) } diff --git a/example/dataloader/resolvers.go b/example/dataloader/resolvers.go index 956251cdb0e..5e71026a7ee 100644 --- a/example/dataloader/resolvers.go +++ b/example/dataloader/resolvers.go @@ -40,24 +40,24 @@ func (r *customerResolver) Address(ctx context.Context, obj *Customer) (*Address return ctxLoaders(ctx).addressByID.Load(obj.AddressID) } -func (r *customerResolver) Orders(ctx context.Context, obj *Customer) ([]Order, error) { +func (r *customerResolver) Orders(ctx context.Context, obj *Customer) ([]*Order, error) { return ctxLoaders(ctx).ordersByCustomer.Load(obj.ID) } type orderResolver struct{ *Resolver } -func (r *orderResolver) Items(ctx context.Context, obj *Order) ([]Item, error) { +func (r *orderResolver) Items(ctx context.Context, obj *Order) ([]*Item, error) { return ctxLoaders(ctx).itemsByOrder.Load(obj.ID) } type queryResolver struct{ *Resolver } -func (r *queryResolver) Customers(ctx context.Context) ([]Customer, error) { +func (r *queryResolver) Customers(ctx context.Context) ([]*Customer, error) { fmt.Println("SELECT * FROM customer") time.Sleep(5 * time.Millisecond) - return []Customer{ + return []*Customer{ {ID: 1, Name: "Bob", AddressID: 1}, {ID: 2, Name: "Alice", AddressID: 3}, {ID: 3, Name: "Eve", AddressID: 4}, @@ -65,21 +65,21 @@ func (r *queryResolver) Customers(ctx context.Context) ([]Customer, error) { } // this method is here to test code generation of nested arrays -func (r *queryResolver) Torture1d(ctx context.Context, customerIds []int) ([]Customer, error) { - result := make([]Customer, len(customerIds)) +func (r *queryResolver) Torture1d(ctx context.Context, customerIds []int) ([]*Customer, error) { + result := make([]*Customer, len(customerIds)) for i, id := range customerIds { - result[i] = Customer{ID: id, Name: fmt.Sprintf("%d", i), AddressID: rand.Int() % 10} + result[i] = &Customer{ID: id, Name: fmt.Sprintf("%d", i), AddressID: rand.Int() % 10} } return result, nil } // this method is here to test code generation of nested arrays -func (r *queryResolver) Torture2d(ctx context.Context, customerIds [][]int) ([][]Customer, error) { - result := make([][]Customer, len(customerIds)) +func (r *queryResolver) Torture2d(ctx context.Context, customerIds [][]int) ([][]*Customer, error) { + result := make([][]*Customer, len(customerIds)) for i := range customerIds { - inner := make([]Customer, len(customerIds[i])) + inner := make([]*Customer, len(customerIds[i])) for j := range customerIds[i] { - inner[j] = Customer{ID: customerIds[i][j], Name: fmt.Sprintf("%d %d", i, j), AddressID: rand.Int() % 10} + inner[j] = &Customer{ID: customerIds[i][j], Name: fmt.Sprintf("%d %d", i, j), AddressID: rand.Int() % 10} } result[i] = inner } diff --git a/example/fileupload/fileupload_test.go b/example/fileupload/fileupload_test.go index a23e2f2e126..10fff064b64 100644 --- a/example/fileupload/fileupload_test.go +++ b/example/fileupload/fileupload_test.go @@ -1,3 +1,4 @@ +//go:generate go run ../../testdata/gqlgen.go -stub stubs.go package fileupload import ( @@ -21,20 +22,19 @@ func TestFileUpload(t *testing.T) { client := http.Client{} t.Run("valid single file upload", func(t *testing.T) { - resolver := &Resolver{ - SingleUploadFunc: func(ctx context.Context, file graphql.Upload) (*model.File, error) { - require.NotNil(t, file) - require.NotNil(t, file.File) - content, err := ioutil.ReadAll(file.File) - require.Nil(t, err) - require.Equal(t, string(content), "test") + resolver := &Stub{} + resolver.MutationResolver.SingleUpload = func(ctx context.Context, file graphql.Upload) (*model.File, error) { + require.NotNil(t, file) + require.NotNil(t, file.File) + content, err := ioutil.ReadAll(file.File) + require.Nil(t, err) + require.Equal(t, string(content), "test") - return &model.File{ - ID: 1, - Name: file.Filename, - Content: string(content), - }, nil - }, + return &model.File{ + ID: 1, + Name: file.Filename, + Content: string(content), + }, nil } srv := httptest.NewServer(handler.GraphQL(NewExecutableSchema(Config{Resolvers: resolver}))) defer srv.Close() @@ -62,21 +62,20 @@ func TestFileUpload(t *testing.T) { }) t.Run("valid single file upload with payload", func(t *testing.T) { - resolver := &Resolver{ - SingleUploadWithPayloadFunc: func(ctx context.Context, req model.UploadFile) (*model.File, error) { - require.Equal(t, req.ID, 1) - require.NotNil(t, req.File) - require.NotNil(t, req.File.File) - content, err := ioutil.ReadAll(req.File.File) - require.Nil(t, err) - require.Equal(t, string(content), "test") + resolver := &Stub{} + resolver.MutationResolver.SingleUploadWithPayload = func(ctx context.Context, req model.UploadFile) (*model.File, error) { + require.Equal(t, req.ID, 1) + require.NotNil(t, req.File) + require.NotNil(t, req.File.File) + content, err := ioutil.ReadAll(req.File.File) + require.Nil(t, err) + require.Equal(t, string(content), "test") - return &model.File{ - ID: 1, - Name: req.File.Filename, - Content: string(content), - }, nil - }, + return &model.File{ + ID: 1, + Name: req.File.Filename, + Content: string(content), + }, nil } srv := httptest.NewServer(handler.GraphQL(NewExecutableSchema(Config{Resolvers: resolver}))) defer srv.Close() @@ -103,25 +102,24 @@ func TestFileUpload(t *testing.T) { }) t.Run("valid file list upload", func(t *testing.T) { - resolver := &Resolver{ - MultipleUploadFunc: func(ctx context.Context, files []graphql.Upload) ([]model.File, error) { - require.Len(t, files, 2) - var contents []string - var resp []model.File - for i := range files { - require.NotNil(t, files[i].File) - content, err := ioutil.ReadAll(files[i].File) - require.Nil(t, err) - contents = append(contents, string(content)) - resp = append(resp, model.File{ - ID: i + 1, - Name: files[i].Filename, - Content: string(content), - }) - } - require.ElementsMatch(t, []string{"test1", "test2"}, contents) - return resp, nil - }, + resolver := &Stub{} + resolver.MutationResolver.MultipleUpload = func(ctx context.Context, files []*graphql.Upload) ([]*model.File, error) { + require.Len(t, files, 2) + var contents []string + var resp []*model.File + for i := range files { + require.NotNil(t, files[i].File) + content, err := ioutil.ReadAll(files[i].File) + require.Nil(t, err) + contents = append(contents, string(content)) + resp = append(resp, &model.File{ + ID: i + 1, + Name: files[i].Filename, + Content: string(content), + }) + } + require.ElementsMatch(t, []string{"test1", "test2"}, contents) + return resp, nil } srv := httptest.NewServer(handler.GraphQL(NewExecutableSchema(Config{Resolvers: resolver}))) defer srv.Close() @@ -153,29 +151,28 @@ func TestFileUpload(t *testing.T) { }) t.Run("valid file list upload with payload", func(t *testing.T) { - resolver := &Resolver{ - MultipleUploadWithPayloadFunc: func(ctx context.Context, req []model.UploadFile) ([]model.File, error) { - require.Len(t, req, 2) - var ids []int - var contents []string - var resp []model.File - for i := range req { - require.NotNil(t, req[i].File) - require.NotNil(t, req[i].File.File) - content, err := ioutil.ReadAll(req[i].File.File) - require.Nil(t, err) - ids = append(ids, req[i].ID) - contents = append(contents, string(content)) - resp = append(resp, model.File{ - ID: i + 1, - Name: req[i].File.Filename, - Content: string(content), - }) - } - require.ElementsMatch(t, []int{1, 2}, ids) - require.ElementsMatch(t, []string{"test1", "test2"}, contents) - return resp, nil - }, + resolver := &Stub{} + resolver.MutationResolver.MultipleUploadWithPayload = func(ctx context.Context, req []*model.UploadFile) ([]*model.File, error) { + require.Len(t, req, 2) + var ids []int + var contents []string + var resp []*model.File + for i := range req { + require.NotNil(t, req[i].File) + require.NotNil(t, req[i].File.File) + content, err := ioutil.ReadAll(req[i].File.File) + require.Nil(t, err) + ids = append(ids, req[i].ID) + contents = append(contents, string(content)) + resp = append(resp, &model.File{ + ID: i + 1, + Name: req[i].File.Filename, + Content: string(content), + }) + } + require.ElementsMatch(t, []int{1, 2}, ids) + require.ElementsMatch(t, []string{"test1", "test2"}, contents) + return resp, nil } srv := httptest.NewServer(handler.GraphQL(NewExecutableSchema(Config{Resolvers: resolver}))) defer srv.Close() @@ -207,40 +204,39 @@ func TestFileUpload(t *testing.T) { }) t.Run("valid file list upload with payload and file reuse", func(t *testing.T) { - resolver := &Resolver{ - MultipleUploadWithPayloadFunc: func(ctx context.Context, req []model.UploadFile) ([]model.File, error) { - require.Len(t, req, 2) - var ids []int - var contents []string - var resp []model.File - for i := range req { - require.NotNil(t, req[i].File) - require.NotNil(t, req[i].File.File) - ids = append(ids, req[i].ID) + resolver := &Stub{} + resolver.MutationResolver.MultipleUploadWithPayload = func(ctx context.Context, req []*model.UploadFile) ([]*model.File, error) { + require.Len(t, req, 2) + var ids []int + var contents []string + var resp []*model.File + for i := range req { + require.NotNil(t, req[i].File) + require.NotNil(t, req[i].File.File) + ids = append(ids, req[i].ID) - var got []byte - buf := make([]byte, 2) - for { - n, err := req[i].File.File.Read(buf) - got = append(got, buf[:n]...) - if err != nil { - if err == io.EOF { - break - } - require.Fail(t, "unexpected error while reading", err.Error()) + var got []byte + buf := make([]byte, 2) + for { + n, err := req[i].File.File.Read(buf) + got = append(got, buf[:n]...) + if err != nil { + if err == io.EOF { + break } + require.Fail(t, "unexpected error while reading", err.Error()) } - contents = append(contents, string(got)) - resp = append(resp, model.File{ - ID: i + 1, - Name: req[i].File.Filename, - Content: string(got), - }) } - require.ElementsMatch(t, []int{1, 2}, ids) - require.ElementsMatch(t, []string{"test1", "test1"}, contents) - return resp, nil - }, + contents = append(contents, string(got)) + resp = append(resp, &model.File{ + ID: i + 1, + Name: req[i].File.Filename, + Content: string(got), + }) + } + require.ElementsMatch(t, []int{1, 2}, ids) + require.ElementsMatch(t, []string{"test1", "test1"}, contents) + return resp, nil } operations := `{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id, name, content } }", "variables": { "req": [ { "id": 1, "file": null }, { "id": 2, "file": null } ] } }` diff --git a/example/fileupload/generated.go b/example/fileupload/generated.go index b7a85666b25..54b77979cd2 100644 --- a/example/fileupload/generated.go +++ b/example/fileupload/generated.go @@ -50,8 +50,8 @@ type ComplexityRoot struct { } Mutation struct { - MultipleUpload func(childComplexity int, files []graphql.Upload) int - MultipleUploadWithPayload func(childComplexity int, req []model.UploadFile) int + MultipleUpload func(childComplexity int, files []*graphql.Upload) int + MultipleUploadWithPayload func(childComplexity int, req []*model.UploadFile) int SingleUpload func(childComplexity int, file graphql.Upload) int SingleUploadWithPayload func(childComplexity int, req model.UploadFile) int } @@ -64,8 +64,8 @@ type ComplexityRoot struct { type MutationResolver interface { SingleUpload(ctx context.Context, file graphql.Upload) (*model.File, error) SingleUploadWithPayload(ctx context.Context, req model.UploadFile) (*model.File, error) - MultipleUpload(ctx context.Context, files []graphql.Upload) ([]model.File, error) - MultipleUploadWithPayload(ctx context.Context, req []model.UploadFile) ([]model.File, error) + MultipleUpload(ctx context.Context, files []*graphql.Upload) ([]*model.File, error) + MultipleUploadWithPayload(ctx context.Context, req []*model.UploadFile) ([]*model.File, error) } type QueryResolver interface { Empty(ctx context.Context) (string, error) @@ -117,7 +117,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Mutation.MultipleUpload(childComplexity, args["files"].([]graphql.Upload)), true + return e.complexity.Mutation.MultipleUpload(childComplexity, args["files"].([]*graphql.Upload)), true case "Mutation.multipleUploadWithPayload": if e.complexity.Mutation.MultipleUploadWithPayload == nil { @@ -129,7 +129,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Mutation.MultipleUploadWithPayload(childComplexity, args["req"].([]model.UploadFile)), true + return e.complexity.Mutation.MultipleUploadWithPayload(childComplexity, args["req"].([]*model.UploadFile)), true case "Mutation.singleUpload": if e.complexity.Mutation.SingleUpload == nil { @@ -278,9 +278,9 @@ type Mutation { func (ec *executionContext) field_Mutation_multipleUploadWithPayload_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 []model.UploadFile + var arg0 []*model.UploadFile if tmp, ok := rawArgs["req"]; ok { - arg0, err = ec.unmarshalNUploadFile2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx, tmp) + arg0, err = ec.unmarshalNUploadFile2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx, tmp) if err != nil { return nil, err } @@ -292,9 +292,9 @@ func (ec *executionContext) field_Mutation_multipleUploadWithPayload_args(ctx co func (ec *executionContext) field_Mutation_multipleUpload_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 []graphql.Upload + var arg0 []*graphql.Upload if tmp, ok := rawArgs["files"]; ok { - arg0, err = ec.unmarshalNUpload2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, tmp) + arg0, err = ec.unmarshalNUpload2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, tmp) if err != nil { return nil, err } @@ -546,7 +546,7 @@ func (ec *executionContext) _Mutation_multipleUpload(ctx context.Context, field ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().MultipleUpload(rctx, args["files"].([]graphql.Upload)) + return ec.resolvers.Mutation().MultipleUpload(rctx, args["files"].([]*graphql.Upload)) }) if resTmp == nil { if !ec.HasError(rctx) { @@ -554,10 +554,10 @@ func (ec *executionContext) _Mutation_multipleUpload(ctx context.Context, field } return graphql.Null } - res := resTmp.([]model.File) + res := resTmp.([]*model.File) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalNFile2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx, field.Selections, res) + return ec.marshalNFile2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx, field.Selections, res) } func (ec *executionContext) _Mutation_multipleUploadWithPayload(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { @@ -580,7 +580,7 @@ func (ec *executionContext) _Mutation_multipleUploadWithPayload(ctx context.Cont ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().MultipleUploadWithPayload(rctx, args["req"].([]model.UploadFile)) + return ec.resolvers.Mutation().MultipleUploadWithPayload(rctx, args["req"].([]*model.UploadFile)) }) if resTmp == nil { if !ec.HasError(rctx) { @@ -588,10 +588,10 @@ func (ec *executionContext) _Mutation_multipleUploadWithPayload(ctx context.Cont } return graphql.Null } - res := resTmp.([]model.File) + res := resTmp.([]*model.File) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalNFile2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx, field.Selections, res) + return ec.marshalNFile2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx, field.Selections, res) } func (ec *executionContext) _Query_empty(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { @@ -1929,7 +1929,7 @@ func (ec *executionContext) marshalNFile2githubᚗcomᚋ99designsᚋgqlgenᚋexa return ec._File(ctx, sel, &v) } -func (ec *executionContext) marshalNFile2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx context.Context, sel ast.SelectionSet, v []model.File) graphql.Marshaler { +func (ec *executionContext) marshalNFile2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx context.Context, sel ast.SelectionSet, v []*model.File) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -1953,7 +1953,7 @@ func (ec *executionContext) marshalNFile2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋ if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNFile2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx, sel, v[i]) + ret[i] = ec.marshalNFile2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx, sel, v[i]) } if isLen1 { f(i) @@ -2018,7 +2018,7 @@ func (ec *executionContext) marshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋg return res } -func (ec *executionContext) unmarshalNUpload2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx context.Context, v interface{}) ([]graphql.Upload, error) { +func (ec *executionContext) unmarshalNUpload2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx context.Context, v interface{}) ([]*graphql.Upload, error) { var vSlice []interface{} if v != nil { if tmp1, ok := v.([]interface{}); ok { @@ -2028,9 +2028,9 @@ func (ec *executionContext) unmarshalNUpload2ᚕgithubᚗcomᚋ99designsᚋgqlge } } var err error - res := make([]graphql.Upload, len(vSlice)) + res := make([]*graphql.Upload, len(vSlice)) for i := range vSlice { - res[i], err = ec.unmarshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, vSlice[i]) + res[i], err = ec.unmarshalNUpload2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, vSlice[i]) if err != nil { return nil, err } @@ -2038,20 +2038,38 @@ func (ec *executionContext) unmarshalNUpload2ᚕgithubᚗcomᚋ99designsᚋgqlge return res, nil } -func (ec *executionContext) marshalNUpload2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx context.Context, sel ast.SelectionSet, v []graphql.Upload) graphql.Marshaler { +func (ec *executionContext) marshalNUpload2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx context.Context, sel ast.SelectionSet, v []*graphql.Upload) graphql.Marshaler { ret := make(graphql.Array, len(v)) for i := range v { - ret[i] = ec.marshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, sel, v[i]) + ret[i] = ec.marshalNUpload2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, sel, v[i]) } return ret } +func (ec *executionContext) unmarshalNUpload2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx context.Context, v interface{}) (*graphql.Upload, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, v) + return &res, err +} + +func (ec *executionContext) marshalNUpload2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx context.Context, sel ast.SelectionSet, v *graphql.Upload) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec.marshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, sel, *v) +} + func (ec *executionContext) unmarshalNUploadFile2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx context.Context, v interface{}) (model.UploadFile, error) { return ec.unmarshalInputUploadFile(ctx, v) } -func (ec *executionContext) unmarshalNUploadFile2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx context.Context, v interface{}) ([]model.UploadFile, error) { +func (ec *executionContext) unmarshalNUploadFile2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx context.Context, v interface{}) ([]*model.UploadFile, error) { var vSlice []interface{} if v != nil { if tmp1, ok := v.([]interface{}); ok { @@ -2061,9 +2079,9 @@ func (ec *executionContext) unmarshalNUploadFile2ᚕgithubᚗcomᚋ99designsᚋg } } var err error - res := make([]model.UploadFile, len(vSlice)) + res := make([]*model.UploadFile, len(vSlice)) for i := range vSlice { - res[i], err = ec.unmarshalNUploadFile2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx, vSlice[i]) + res[i], err = ec.unmarshalNUploadFile2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx, vSlice[i]) if err != nil { return nil, err } @@ -2071,6 +2089,14 @@ func (ec *executionContext) unmarshalNUploadFile2ᚕgithubᚗcomᚋ99designsᚋg return res, nil } +func (ec *executionContext) unmarshalNUploadFile2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx context.Context, v interface{}) (*model.UploadFile, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalNUploadFile2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx, v) + return &res, err +} + func (ec *executionContext) marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v introspection.Directive) graphql.Marshaler { return ec.___Directive(ctx, sel, &v) } diff --git a/example/fileupload/resolvers.go b/example/fileupload/resolvers.go deleted file mode 100644 index ad11c383525..00000000000 --- a/example/fileupload/resolvers.go +++ /dev/null @@ -1,59 +0,0 @@ -//go:generate go run ../../testdata/gqlgen.go - -package fileupload - -import ( - "context" - "fmt" - - "github.com/99designs/gqlgen/example/fileupload/model" - "github.com/99designs/gqlgen/graphql" -) - -type Resolver struct { - SingleUploadFunc func(ctx context.Context, file graphql.Upload) (*model.File, error) - SingleUploadWithPayloadFunc func(ctx context.Context, req model.UploadFile) (*model.File, error) - MultipleUploadFunc func(ctx context.Context, files []graphql.Upload) ([]model.File, error) - MultipleUploadWithPayloadFunc func(ctx context.Context, req []model.UploadFile) ([]model.File, error) -} - -func (r *Resolver) Mutation() MutationResolver { - return &mutationResolver{r} -} -func (r *Resolver) Query() QueryResolver { - return &queryResolver{r} -} - -type mutationResolver struct{ *Resolver } - -func (r *mutationResolver) SingleUpload(ctx context.Context, file graphql.Upload) (*model.File, error) { - if r.SingleUploadFunc != nil { - return r.SingleUploadFunc(ctx, file) - } - return nil, fmt.Errorf("not implemented") -} -func (r *mutationResolver) SingleUploadWithPayload(ctx context.Context, req model.UploadFile) (*model.File, error) { - if r.SingleUploadWithPayloadFunc != nil { - return r.SingleUploadWithPayloadFunc(ctx, req) - } - return nil, fmt.Errorf("not implemented") -} -func (r *mutationResolver) MultipleUpload(ctx context.Context, files []graphql.Upload) ([]model.File, error) { - if r.MultipleUploadFunc != nil { - return r.MultipleUploadFunc(ctx, files) - } - return nil, fmt.Errorf("not implemented") -} - -func (r *mutationResolver) MultipleUploadWithPayload(ctx context.Context, req []model.UploadFile) ([]model.File, error) { - if r.MultipleUploadWithPayloadFunc != nil { - return r.MultipleUploadWithPayloadFunc(ctx, req) - } - return nil, fmt.Errorf("not implemented") -} - -type queryResolver struct{ *Resolver } - -func (r *queryResolver) Empty(ctx context.Context) (string, error) { - return "", fmt.Errorf("not implemented") -} diff --git a/example/fileupload/server/server.go b/example/fileupload/server/server.go index fdc72aab812..8ad9c41b237 100644 --- a/example/fileupload/server/server.go +++ b/example/fileupload/server/server.go @@ -27,66 +27,66 @@ func main() { log.Fatal(http.ListenAndServe(":8087", nil)) } -func getResolver() *fileupload.Resolver { - resolver := &fileupload.Resolver{ - SingleUploadFunc: func(ctx context.Context, file graphql.Upload) (*model.File, error) { - content, err := ioutil.ReadAll(file.File) +func getResolver() *fileupload.Stub { + resolver := &fileupload.Stub{} + + resolver.MutationResolver.SingleUpload = func(ctx context.Context, file graphql.Upload) (*model.File, error) { + content, err := ioutil.ReadAll(file.File) + if err != nil { + return nil, err + } + return &model.File{ + ID: 1, + Name: file.Filename, + Content: string(content), + }, nil + } + resolver.MutationResolver.SingleUploadWithPayload = func(ctx context.Context, req model.UploadFile) (*model.File, error) { + content, err := ioutil.ReadAll(req.File.File) + if err != nil { + return nil, err + } + return &model.File{ + ID: 1, + Name: req.File.Filename, + Content: string(content), + }, nil + } + resolver.MutationResolver.MultipleUpload = func(ctx context.Context, files []*graphql.Upload) ([]*model.File, error) { + if len(files) == 0 { + return nil, errors.New("empty list") + } + var resp []*model.File + for i := range files { + content, err := ioutil.ReadAll(files[i].File) if err != nil { - return nil, err + return []*model.File{}, err } - return &model.File{ - ID: 1, - Name: file.Filename, + resp = append(resp, &model.File{ + ID: i + 1, + Name: files[i].Filename, Content: string(content), - }, nil - }, - SingleUploadWithPayloadFunc: func(ctx context.Context, req model.UploadFile) (*model.File, error) { - content, err := ioutil.ReadAll(req.File.File) + }) + } + return resp, nil + } + resolver.MutationResolver.MultipleUploadWithPayload = func(ctx context.Context, req []*model.UploadFile) ([]*model.File, error) { + if len(req) == 0 { + return nil, errors.New("empty list") + } + var resp []*model.File + for i := range req { + content, err := ioutil.ReadAll(req[i].File.File) if err != nil { - return nil, err + return []*model.File{}, err } - return &model.File{ - ID: 1, - Name: req.File.Filename, + resp = append(resp, &model.File{ + ID: i + 1, + Name: req[i].File.Filename, Content: string(content), - }, nil - }, - MultipleUploadFunc: func(ctx context.Context, files []graphql.Upload) ([]model.File, error) { - if len(files) == 0 { - return nil, errors.New("empty list") - } - var resp []model.File - for i := range files { - content, err := ioutil.ReadAll(files[i].File) - if err != nil { - return []model.File{}, err - } - resp = append(resp, model.File{ - ID: i + 1, - Name: files[i].Filename, - Content: string(content), - }) - } - return resp, nil - }, - MultipleUploadWithPayloadFunc: func(ctx context.Context, req []model.UploadFile) ([]model.File, error) { - if len(req) == 0 { - return nil, errors.New("empty list") - } - var resp []model.File - for i := range req { - content, err := ioutil.ReadAll(req[i].File.File) - if err != nil { - return []model.File{}, err - } - resp = append(resp, model.File{ - ID: i + 1, - Name: req[i].File.Filename, - Content: string(content), - }) - } - return resp, nil - }, + }) + } + return resp, nil } return resolver } diff --git a/example/fileupload/stubs.go b/example/fileupload/stubs.go new file mode 100644 index 00000000000..d8935ed1ba0 --- /dev/null +++ b/example/fileupload/stubs.go @@ -0,0 +1,50 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package fileupload + +import ( + "context" + + "github.com/99designs/gqlgen/example/fileupload/model" + "github.com/99designs/gqlgen/graphql" +) + +type Stub struct { + MutationResolver struct { + SingleUpload func(ctx context.Context, file graphql.Upload) (*model.File, error) + SingleUploadWithPayload func(ctx context.Context, req model.UploadFile) (*model.File, error) + MultipleUpload func(ctx context.Context, files []*graphql.Upload) ([]*model.File, error) + MultipleUploadWithPayload func(ctx context.Context, req []*model.UploadFile) ([]*model.File, error) + } + QueryResolver struct { + Empty func(ctx context.Context) (string, error) + } +} + +func (r *Stub) Mutation() MutationResolver { + return &stubMutation{r} +} +func (r *Stub) Query() QueryResolver { + return &stubQuery{r} +} + +type stubMutation struct{ *Stub } + +func (r *stubMutation) SingleUpload(ctx context.Context, file graphql.Upload) (*model.File, error) { + return r.MutationResolver.SingleUpload(ctx, file) +} +func (r *stubMutation) SingleUploadWithPayload(ctx context.Context, req model.UploadFile) (*model.File, error) { + return r.MutationResolver.SingleUploadWithPayload(ctx, req) +} +func (r *stubMutation) MultipleUpload(ctx context.Context, files []*graphql.Upload) ([]*model.File, error) { + return r.MutationResolver.MultipleUpload(ctx, files) +} +func (r *stubMutation) MultipleUploadWithPayload(ctx context.Context, req []*model.UploadFile) ([]*model.File, error) { + return r.MutationResolver.MultipleUploadWithPayload(ctx, req) +} + +type stubQuery struct{ *Stub } + +func (r *stubQuery) Empty(ctx context.Context) (string, error) { + return r.QueryResolver.Empty(ctx) +} diff --git a/example/scalars/generated.go b/example/scalars/generated.go index e73b28e8a12..62a3e059b13 100644 --- a/example/scalars/generated.go +++ b/example/scalars/generated.go @@ -69,7 +69,7 @@ type ComplexityRoot struct { type QueryResolver interface { User(ctx context.Context, id external.ObjectID) (*model.User, error) - Search(ctx context.Context, input *model.SearchArgs) ([]model.User, error) + Search(ctx context.Context, input *model.SearchArgs) ([]*model.User, error) } type UserResolver interface { PrimitiveResolver(ctx context.Context, obj *model.User) (string, error) @@ -476,10 +476,10 @@ func (ec *executionContext) _Query_search(ctx context.Context, field graphql.Col } return graphql.Null } - res := resTmp.([]model.User) + res := resTmp.([]*model.User) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalNUser2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋscalarsᚋmodelᚐUser(ctx, field.Selections, res) + return ec.marshalNUser2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋscalarsᚋmodelᚐUser(ctx, field.Selections, res) } func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { @@ -2095,7 +2095,7 @@ func (ec *executionContext) marshalNUser2githubᚗcomᚋ99designsᚋgqlgenᚋexa return ec._User(ctx, sel, &v) } -func (ec *executionContext) marshalNUser2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋscalarsᚋmodelᚐUser(ctx context.Context, sel ast.SelectionSet, v []model.User) graphql.Marshaler { +func (ec *executionContext) marshalNUser2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋscalarsᚋmodelᚐUser(ctx context.Context, sel ast.SelectionSet, v []*model.User) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -2119,7 +2119,7 @@ func (ec *executionContext) marshalNUser2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋ if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNUser2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋscalarsᚋmodelᚐUser(ctx, sel, v[i]) + ret[i] = ec.marshalNUser2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋscalarsᚋmodelᚐUser(ctx, sel, v[i]) } if isLen1 { f(i) @@ -2132,6 +2132,16 @@ func (ec *executionContext) marshalNUser2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋ return ret } +func (ec *executionContext) marshalNUser2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋscalarsᚋmodelᚐUser(ctx context.Context, sel ast.SelectionSet, v *model.User) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._User(ctx, sel, v) +} + func (ec *executionContext) marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v introspection.Directive) graphql.Marshaler { return ec.___Directive(ctx, sel, &v) } diff --git a/example/scalars/resolvers.go b/example/scalars/resolvers.go index c723b0d5446..661ce1baca8 100644 --- a/example/scalars/resolvers.go +++ b/example/scalars/resolvers.go @@ -34,7 +34,7 @@ func (r *queryResolver) User(ctx context.Context, id external.ObjectID) (*model. }, nil } -func (r *queryResolver) Search(ctx context.Context, input *model.SearchArgs) ([]model.User, error) { +func (r *queryResolver) Search(ctx context.Context, input *model.SearchArgs) ([]*model.User, error) { location := model.Point{X: 1, Y: 2} if input.Location != nil { location = *input.Location @@ -45,7 +45,7 @@ func (r *queryResolver) Search(ctx context.Context, input *model.SearchArgs) ([] created = *input.CreatedAfter } - return []model.User{ + return []*model.User{ { ID: 1, Name: "Test User 1", diff --git a/example/selection/selection_test.go b/example/selection/selection_test.go index 7b0fd9f1a13..a72683a0037 100644 --- a/example/selection/selection_test.go +++ b/example/selection/selection_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/99designs/gqlgen/client" - "github.com/99designs/gqlgen/handler" "github.com/stretchr/testify/require" ) diff --git a/example/starwars/generated/exec.go b/example/starwars/generated/exec.go index 1310d7dc0ef..b70c74cfb85 100644 --- a/example/starwars/generated/exec.go +++ b/example/starwars/generated/exec.go @@ -120,21 +120,21 @@ type DroidResolver interface { FriendsConnection(ctx context.Context, obj *models.Droid, first *int, after *string) (*models.FriendsConnection, error) } type FriendsConnectionResolver interface { - Edges(ctx context.Context, obj *models.FriendsConnection) ([]models.FriendsEdge, error) + Edges(ctx context.Context, obj *models.FriendsConnection) ([]*models.FriendsEdge, error) Friends(ctx context.Context, obj *models.FriendsConnection) ([]models.Character, error) } type HumanResolver interface { Friends(ctx context.Context, obj *models.Human) ([]models.Character, error) FriendsConnection(ctx context.Context, obj *models.Human, first *int, after *string) (*models.FriendsConnection, error) - Starships(ctx context.Context, obj *models.Human) ([]models.Starship, error) + Starships(ctx context.Context, obj *models.Human) ([]*models.Starship, error) } type MutationResolver interface { CreateReview(ctx context.Context, episode models.Episode, review models.Review) (*models.Review, error) } type QueryResolver interface { Hero(ctx context.Context, episode *models.Episode) (models.Character, error) - Reviews(ctx context.Context, episode models.Episode, since *time.Time) ([]models.Review, error) + Reviews(ctx context.Context, episode models.Episode, since *time.Time) ([]*models.Review, error) Search(ctx context.Context, text string) ([]models.SearchResult, error) Character(ctx context.Context, id string) (models.Character, error) Droid(ctx context.Context, id string) (*models.Droid, error) @@ -1155,10 +1155,10 @@ func (ec *executionContext) _FriendsConnection_edges(ctx context.Context, field if resTmp == nil { return graphql.Null } - res := resTmp.([]models.FriendsEdge) + res := resTmp.([]*models.FriendsEdge) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOFriendsEdge2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐFriendsEdge(ctx, field.Selections, res) + return ec.marshalOFriendsEdge2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐFriendsEdge(ctx, field.Selections, res) } func (ec *executionContext) _FriendsConnection_friends(ctx context.Context, field graphql.CollectedField, obj *models.FriendsConnection) graphql.Marshaler { @@ -1478,10 +1478,10 @@ func (ec *executionContext) _Human_starships(ctx context.Context, field graphql. if resTmp == nil { return graphql.Null } - res := resTmp.([]models.Starship) + res := resTmp.([]*models.Starship) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalOStarship2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐStarship(ctx, field.Selections, res) + return ec.marshalOStarship2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐStarship(ctx, field.Selections, res) } func (ec *executionContext) _Mutation_createReview(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { @@ -1655,10 +1655,10 @@ func (ec *executionContext) _Query_reviews(ctx context.Context, field graphql.Co } return graphql.Null } - res := resTmp.([]models.Review) + res := resTmp.([]*models.Review) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalNReview2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐReview(ctx, field.Selections, res) + return ec.marshalNReview2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐReview(ctx, field.Selections, res) } func (ec *executionContext) _Query_search(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { @@ -3819,6 +3819,16 @@ func (ec *executionContext) marshalNFriendsEdge2githubᚗcomᚋ99designsᚋgqlge return ec._FriendsEdge(ctx, sel, &v) } +func (ec *executionContext) marshalNFriendsEdge2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐFriendsEdge(ctx context.Context, sel ast.SelectionSet, v *models.FriendsEdge) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._FriendsEdge(ctx, sel, v) +} + func (ec *executionContext) unmarshalNID2string(ctx context.Context, v interface{}) (string, error) { return graphql.UnmarshalID(v) } @@ -3913,7 +3923,7 @@ func (ec *executionContext) marshalNReview2githubᚗcomᚋ99designsᚋgqlgenᚋe return ec._Review(ctx, sel, &v) } -func (ec *executionContext) marshalNReview2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐReview(ctx context.Context, sel ast.SelectionSet, v []models.Review) graphql.Marshaler { +func (ec *executionContext) marshalNReview2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐReview(ctx context.Context, sel ast.SelectionSet, v []*models.Review) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -3937,7 +3947,7 @@ func (ec *executionContext) marshalNReview2ᚕgithubᚗcomᚋ99designsᚋgqlgen if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNReview2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐReview(ctx, sel, v[i]) + ret[i] = ec.marshalNReview2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐReview(ctx, sel, v[i]) } if isLen1 { f(i) @@ -3950,6 +3960,16 @@ func (ec *executionContext) marshalNReview2ᚕgithubᚗcomᚋ99designsᚋgqlgen return ret } +func (ec *executionContext) marshalNReview2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐReview(ctx context.Context, sel ast.SelectionSet, v *models.Review) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._Review(ctx, sel, v) +} + func (ec *executionContext) unmarshalNReviewInput2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐReview(ctx context.Context, v interface{}) (models.Review, error) { return ec.unmarshalInputReviewInput(ctx, v) } @@ -3999,6 +4019,16 @@ func (ec *executionContext) marshalNStarship2githubᚗcomᚋ99designsᚋgqlgen return ec._Starship(ctx, sel, &v) } +func (ec *executionContext) marshalNStarship2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐStarship(ctx context.Context, sel ast.SelectionSet, v *models.Starship) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._Starship(ctx, sel, v) +} + func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { return graphql.UnmarshalString(v) } @@ -4349,7 +4379,7 @@ func (ec *executionContext) marshalOFloat2float64(ctx context.Context, sel ast.S return graphql.MarshalFloat(v) } -func (ec *executionContext) marshalOFriendsEdge2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐFriendsEdge(ctx context.Context, sel ast.SelectionSet, v []models.FriendsEdge) graphql.Marshaler { +func (ec *executionContext) marshalOFriendsEdge2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐFriendsEdge(ctx context.Context, sel ast.SelectionSet, v []*models.FriendsEdge) graphql.Marshaler { if v == nil { return graphql.Null } @@ -4376,7 +4406,7 @@ func (ec *executionContext) marshalOFriendsEdge2ᚕgithubᚗcomᚋ99designsᚋgq if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNFriendsEdge2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐFriendsEdge(ctx, sel, v[i]) + ret[i] = ec.marshalNFriendsEdge2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐFriendsEdge(ctx, sel, v[i]) } if isLen1 { f(i) @@ -4485,7 +4515,7 @@ func (ec *executionContext) marshalOStarship2githubᚗcomᚋ99designsᚋgqlgen return ec._Starship(ctx, sel, &v) } -func (ec *executionContext) marshalOStarship2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐStarship(ctx context.Context, sel ast.SelectionSet, v []models.Starship) graphql.Marshaler { +func (ec *executionContext) marshalOStarship2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐStarship(ctx context.Context, sel ast.SelectionSet, v []*models.Starship) graphql.Marshaler { if v == nil { return graphql.Null } @@ -4512,7 +4542,7 @@ func (ec *executionContext) marshalOStarship2ᚕgithubᚗcomᚋ99designsᚋgqlge if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNStarship2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐStarship(ctx, sel, v[i]) + ret[i] = ec.marshalNStarship2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋstarwarsᚋmodelsᚐStarship(ctx, sel, v[i]) } if isLen1 { f(i) diff --git a/example/starwars/resolvers.go b/example/starwars/resolvers.go index 7b1f3f4b5f4..e654448c756 100644 --- a/example/starwars/resolvers.go +++ b/example/starwars/resolvers.go @@ -20,7 +20,7 @@ type Resolver struct { humans map[string]models.Human droid map[string]models.Droid starships map[string]models.Starship - reviews map[models.Episode][]models.Review + reviews map[models.Episode][]*models.Review } func (r *Resolver) Droid() generated.DroidResolver { @@ -100,15 +100,15 @@ func (r *Resolver) resolveFriendConnection(ctx context.Context, ids []string, fi }, nil } -func (r *friendsConnectionResolver) Edges(ctx context.Context, obj *models.FriendsConnection) ([]models.FriendsEdge, error) { +func (r *friendsConnectionResolver) Edges(ctx context.Context, obj *models.FriendsConnection) ([]*models.FriendsEdge, error) { friends, err := r.resolveCharacters(ctx, obj.Ids) if err != nil { return nil, err } - edges := make([]models.FriendsEdge, obj.To-obj.From) + edges := make([]*models.FriendsEdge, obj.To-obj.From) for i := range edges { - edges[i] = models.FriendsEdge{ + edges[i] = &models.FriendsEdge{ Cursor: models.EncodeCursor(obj.From + i), Node: friends[obj.From+i], } @@ -130,15 +130,15 @@ func (r *humanResolver) FriendsConnection(ctx context.Context, obj *models.Human return r.resolveFriendConnection(ctx, obj.FriendIds, first, after) } -func (r *humanResolver) Starships(ctx context.Context, obj *models.Human) ([]models.Starship, error) { - var result []models.Starship +func (r *humanResolver) Starships(ctx context.Context, obj *models.Human) ([]*models.Starship, error) { + var result []*models.Starship for _, id := range obj.StarshipIds { char, err := r.Query().Starship(ctx, id) if err != nil { return nil, err } if char != nil { - result = append(result, *char) + result = append(result, char) } } return result, nil @@ -149,7 +149,7 @@ type mutationResolver struct{ *Resolver } func (r *mutationResolver) CreateReview(ctx context.Context, episode models.Episode, review models.Review) (*models.Review, error) { review.Time = time.Now() time.Sleep(1 * time.Second) - r.reviews[episode] = append(r.reviews[episode], review) + r.reviews[episode] = append(r.reviews[episode], &review) return &review, nil } @@ -162,12 +162,12 @@ func (r *queryResolver) Hero(ctx context.Context, episode *models.Episode) (mode return r.droid["2001"], nil } -func (r *queryResolver) Reviews(ctx context.Context, episode models.Episode, since *time.Time) ([]models.Review, error) { +func (r *queryResolver) Reviews(ctx context.Context, episode models.Episode, since *time.Time) ([]*models.Review, error) { if since == nil { return r.reviews[episode], nil } - var filtered []models.Review + var filtered []*models.Review for _, rev := range r.reviews[episode] { if rev.Time.After(*since) { filtered = append(filtered, rev) @@ -366,7 +366,7 @@ func NewResolver() generated.Config { }, } - r.reviews = map[models.Episode][]models.Review{} + r.reviews = map[models.Episode][]*models.Review{} return generated.Config{ Resolvers: &r, diff --git a/example/todo/generated.go b/example/todo/generated.go index 3631a2bdc5b..52a22e61ee9 100644 --- a/example/todo/generated.go +++ b/example/todo/generated.go @@ -68,7 +68,7 @@ type MyMutationResolver interface { type MyQueryResolver interface { Todo(ctx context.Context, id int) (*Todo, error) LastTodo(ctx context.Context) (*Todo, error) - Todos(ctx context.Context) ([]Todo, error) + Todos(ctx context.Context) ([]*Todo, error) } type executableSchema struct { @@ -549,10 +549,10 @@ func (ec *executionContext) _MyQuery_todos(ctx context.Context, field graphql.Co } return graphql.Null } - res := resTmp.([]Todo) + res := resTmp.([]*Todo) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalNTodo2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋtodoᚐTodo(ctx, field.Selections, res) + return ec.marshalNTodo2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋtodoᚐTodo(ctx, field.Selections, res) } func (ec *executionContext) _MyQuery___type(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { @@ -2013,7 +2013,7 @@ func (ec *executionContext) marshalNTodo2githubᚗcomᚋ99designsᚋgqlgenᚋexa return ec._Todo(ctx, sel, &v) } -func (ec *executionContext) marshalNTodo2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋtodoᚐTodo(ctx context.Context, sel ast.SelectionSet, v []Todo) graphql.Marshaler { +func (ec *executionContext) marshalNTodo2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋtodoᚐTodo(ctx context.Context, sel ast.SelectionSet, v []*Todo) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -2037,7 +2037,7 @@ func (ec *executionContext) marshalNTodo2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋ if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNTodo2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋtodoᚐTodo(ctx, sel, v[i]) + ret[i] = ec.marshalNTodo2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋtodoᚐTodo(ctx, sel, v[i]) } if isLen1 { f(i) diff --git a/example/todo/todo.go b/example/todo/todo.go index 86efdabfed9..5c2fa129dc8 100644 --- a/example/todo/todo.go +++ b/example/todo/todo.go @@ -18,7 +18,7 @@ var them = &User{ID: 2, Name: "Them"} func New() Config { c := Config{ Resolvers: &resolvers{ - todos: []Todo{ + todos: []*Todo{ {ID: 1, Text: "A todo not to forget", Done: false, owner: you}, {ID: 2, Text: "This is the most important", Done: false, owner: you}, {ID: 3, Text: "Somebody else's todo", Done: true, owner: them}, @@ -49,7 +49,7 @@ func New() Config { } type resolvers struct { - todos []Todo + todos []*Todo lastID int } @@ -72,7 +72,7 @@ func (r *QueryResolver) Todo(ctx context.Context, id int) (*Todo, error) { for _, todo := range r.todos { if todo.ID == id { - return &todo, nil + return todo, nil } } return nil, errors.New("not found") @@ -82,10 +82,10 @@ func (r *QueryResolver) LastTodo(ctx context.Context) (*Todo, error) { if len(r.todos) == 0 { return nil, errors.New("not found") } - return &r.todos[len(r.todos)-1], nil + return r.todos[len(r.todos)-1], nil } -func (r *QueryResolver) Todos(ctx context.Context) ([]Todo, error) { +func (r *QueryResolver) Todos(ctx context.Context) ([]*Todo, error) { return r.todos, nil } @@ -94,7 +94,7 @@ type MutationResolver resolvers func (r *MutationResolver) CreateTodo(ctx context.Context, todo TodoInput) (*Todo, error) { newID := r.id() - newTodo := Todo{ + newTodo := &Todo{ ID: newID, Text: todo.Text, owner: you, @@ -106,7 +106,7 @@ func (r *MutationResolver) CreateTodo(ctx context.Context, todo TodoInput) (*Tod r.todos = append(r.todos, newTodo) - return &newTodo, nil + return newTodo, nil } func (r *MutationResolver) UpdateTodo(ctx context.Context, id int, changes map[string]interface{}) (*Todo, error) { @@ -114,7 +114,7 @@ func (r *MutationResolver) UpdateTodo(ctx context.Context, id int, changes map[s for i := 0; i < len(r.todos); i++ { if r.todos[i].ID == id { - affectedTodo = &r.todos[i] + affectedTodo = r.todos[i] break } } diff --git a/example/type-system-extension/generated.go b/example/type-system-extension/generated.go index 59e21935626..dd98089c711 100644 --- a/example/type-system-extension/generated.go +++ b/example/type-system-extension/generated.go @@ -77,7 +77,7 @@ type MyMutationResolver interface { CreateTodo(ctx context.Context, todo TodoInput) (*Todo, error) } type MyQueryResolver interface { - Todos(ctx context.Context) ([]Todo, error) + Todos(ctx context.Context) ([]*Todo, error) Todo(ctx context.Context, id string) (*Todo, error) } @@ -496,10 +496,10 @@ func (ec *executionContext) _MyQuery_todos(ctx context.Context, field graphql.Co } return graphql.Null } - res := resTmp.([]Todo) + res := resTmp.([]*Todo) rctx.Result = res ctx = ec.Tracer.StartFieldChildExecution(ctx) - return ec.marshalNTodo2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋtypeᚑsystemᚑextensionᚐTodo(ctx, field.Selections, res) + return ec.marshalNTodo2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋtypeᚑsystemᚑextensionᚐTodo(ctx, field.Selections, res) } func (ec *executionContext) _MyQuery_todo(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { @@ -2007,7 +2007,7 @@ func (ec *executionContext) marshalNTodo2githubᚗcomᚋ99designsᚋgqlgenᚋexa return ec._Todo(ctx, sel, &v) } -func (ec *executionContext) marshalNTodo2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋtypeᚑsystemᚑextensionᚐTodo(ctx context.Context, sel ast.SelectionSet, v []Todo) graphql.Marshaler { +func (ec *executionContext) marshalNTodo2ᚕᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋtypeᚑsystemᚑextensionᚐTodo(ctx context.Context, sel ast.SelectionSet, v []*Todo) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup isLen1 := len(v) == 1 @@ -2031,7 +2031,7 @@ func (ec *executionContext) marshalNTodo2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋ if !isLen1 { defer wg.Done() } - ret[i] = ec.marshalNTodo2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋtypeᚑsystemᚑextensionᚐTodo(ctx, sel, v[i]) + ret[i] = ec.marshalNTodo2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋtypeᚑsystemᚑextensionᚐTodo(ctx, sel, v[i]) } if isLen1 { f(i) diff --git a/example/type-system-extension/resolver.go b/example/type-system-extension/resolver.go index 5158534b83f..0539f267355 100644 --- a/example/type-system-extension/resolver.go +++ b/example/type-system-extension/resolver.go @@ -46,12 +46,8 @@ func (r *resolver) MyMutation() MyMutationResolver { type queryResolver struct{ *resolver } -func (r *queryResolver) Todos(ctx context.Context) ([]Todo, error) { - todos := make([]Todo, 0, len(r.todos)) - for _, todo := range r.todos { - todos = append(todos, *todo) - } - return todos, nil +func (r *queryResolver) Todos(ctx context.Context) ([]*Todo, error) { + return r.todos, nil } func (r *queryResolver) Todo(ctx context.Context, id string) (*Todo, error) { diff --git a/go.mod b/go.mod index fc6dbd18e9c..9ff313d8655 100644 --- a/go.mod +++ b/go.mod @@ -18,9 +18,9 @@ require ( github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0 // indirect github.com/stretchr/testify v1.3.0 github.com/urfave/cli v1.20.0 - github.com/vektah/dataloaden v0.2.0 + github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e github.com/vektah/gqlparser v1.1.2 - golang.org/x/tools v0.0.0-20190511041617-99f201b6807e + golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v2 v2.2.2 sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755 diff --git a/go.sum b/go.sum index 86837ad26a2..b2d54b1f731 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vektah/dataloaden v0.2.0 h1:lhynDrG7c8mNLahboCo0Wq82tMjmu5yOUv2ds/tBmss= github.com/vektah/dataloaden v0.2.0/go.mod h1:vxM6NuRlgiR0M6wbVTJeKp9vQIs81ZMfCYO+4yq/jbE= +github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWpEAyDlU1eKOuk5twTjAjuevXqcJJw8hrg= +github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U= github.com/vektah/gqlparser v1.1.2 h1:ZsyLGn7/7jDNI+y4SEhI4yAxRChlv15pUHMjijT+e68= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -65,6 +67,8 @@ golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190125232054-dbeab5af4b8d3204d444b78cafaba18a9a062a50/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190511041617-99f201b6807e h1:wTxRxdzKt8fn3IQa3+kVlPJMxK2hJj2Orm+M2Mzw9eg= golang.org/x/tools v0.0.0-20190511041617-99f201b6807e/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd h1:oMEQDWVXVNpceQoVd1JN3CQ7LYJJzs5qWqZIUcxXHHw= +golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=