Skip to content

Commit af5bb93

Browse files
authored
Merge pull request #462 from graph-gophers/fix-typename-trace
internal/exec: assign parent type name to __typename fields
2 parents 091f91f + 8b5d032 commit af5bb93

File tree

2 files changed

+160
-2
lines changed

2 files changed

+160
-2
lines changed

graphql_test.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"sync"
78
"testing"
89
"time"
910

1011
"github.com/graph-gophers/graphql-go"
1112
gqlerrors "github.com/graph-gophers/graphql-go/errors"
1213
"github.com/graph-gophers/graphql-go/example/starwars"
1314
"github.com/graph-gophers/graphql-go/gqltesting"
15+
"github.com/graph-gophers/graphql-go/introspection"
16+
"github.com/graph-gophers/graphql-go/trace"
1417
)
1518

1619
type helloWorldResolver1 struct{}
@@ -3999,3 +4002,153 @@ func TestNullable(t *testing.T) {
39994002
},
40004003
})
40014004
}
4005+
4006+
type testTracer struct {
4007+
mu *sync.Mutex
4008+
fields []fieldTrace
4009+
queries []queryTrace
4010+
}
4011+
4012+
type fieldTrace struct {
4013+
label string
4014+
typeName string
4015+
fieldName string
4016+
isTrivial bool
4017+
args map[string]interface{}
4018+
err *gqlerrors.QueryError
4019+
}
4020+
4021+
type queryTrace struct {
4022+
document string
4023+
opName string
4024+
variables map[string]interface{}
4025+
varTypes map[string]*introspection.Type
4026+
errors []*gqlerrors.QueryError
4027+
}
4028+
4029+
func (t *testTracer) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, trace.TraceFieldFinishFunc) {
4030+
return ctx, func(qe *gqlerrors.QueryError) {
4031+
t.mu.Lock()
4032+
defer t.mu.Unlock()
4033+
4034+
ft := fieldTrace{
4035+
label: label,
4036+
typeName: typeName,
4037+
fieldName: fieldName,
4038+
isTrivial: trivial,
4039+
args: args,
4040+
err: qe,
4041+
}
4042+
4043+
t.fields = append(t.fields, ft)
4044+
}
4045+
}
4046+
4047+
func (t *testTracer) TraceQuery(ctx context.Context, document string, opName string, vars map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, trace.TraceQueryFinishFunc) {
4048+
return ctx, func(qe []*gqlerrors.QueryError) {
4049+
t.mu.Lock()
4050+
defer t.mu.Unlock()
4051+
4052+
qt := queryTrace{
4053+
document: document,
4054+
opName: opName,
4055+
variables: vars,
4056+
varTypes: varTypes,
4057+
errors: qe,
4058+
}
4059+
4060+
t.queries = append(t.queries, qt)
4061+
}
4062+
}
4063+
4064+
var _ trace.Tracer = (*testTracer)(nil)
4065+
4066+
func TestTracer(t *testing.T) {
4067+
t.Parallel()
4068+
4069+
tracer := &testTracer{mu: &sync.Mutex{}}
4070+
4071+
schema, err := graphql.ParseSchema(starwars.Schema, &starwars.Resolver{}, graphql.Tracer(tracer))
4072+
if err != nil {
4073+
t.Fatalf("graphql.ParseSchema: %s", err)
4074+
}
4075+
4076+
ctx := context.Background()
4077+
doc := `
4078+
query TestTracer($id: ID!) {
4079+
HanSolo: human(id: $id) {
4080+
__typename
4081+
name
4082+
}
4083+
}
4084+
`
4085+
opName := "TestTracer"
4086+
variables := map[string]interface{}{
4087+
"id": "1002",
4088+
}
4089+
4090+
_ = schema.Exec(ctx, doc, opName, variables)
4091+
4092+
tracer.mu.Lock()
4093+
defer tracer.mu.Unlock()
4094+
4095+
if len(tracer.queries) != 1 {
4096+
t.Fatalf("expected one query trace, but got %d: %#v", len(tracer.queries), tracer.queries)
4097+
}
4098+
4099+
qt := tracer.queries[0]
4100+
if qt.document != doc {
4101+
t.Errorf("mismatched query trace document:\nwant: %q\ngot : %q", doc, qt.document)
4102+
}
4103+
if qt.opName != opName {
4104+
t.Errorf("mismated query trace operationName:\nwant: %q\ngot : %q", opName, qt.opName)
4105+
}
4106+
4107+
expectedFieldTraces := []fieldTrace{
4108+
{fieldName: "human", typeName: "Query"},
4109+
{fieldName: "__typename", typeName: "Human"},
4110+
{fieldName: "name", typeName: "Human"},
4111+
}
4112+
4113+
checkFieldTraces(t, expectedFieldTraces, tracer.fields)
4114+
}
4115+
4116+
func checkFieldTraces(t *testing.T, want, have []fieldTrace) {
4117+
if len(want) != len(have) {
4118+
t.Errorf("mismatched field traces: expected %d but got %d: %#v", len(want), len(have), have)
4119+
}
4120+
4121+
type comparsion struct {
4122+
want fieldTrace
4123+
have fieldTrace
4124+
}
4125+
4126+
m := map[string]comparsion{}
4127+
4128+
for _, ft := range want {
4129+
m[ft.fieldName] = comparsion{want: ft}
4130+
}
4131+
4132+
for _, ft := range have {
4133+
c := m[ft.fieldName]
4134+
c.have = ft
4135+
m[ft.fieldName] = c
4136+
}
4137+
4138+
for _, c := range m {
4139+
if err := stringsEqual(c.want.fieldName, c.have.fieldName); err != "" {
4140+
t.Error("mismatched field name:", err)
4141+
}
4142+
if err := stringsEqual(c.want.typeName, c.have.typeName); err != "" {
4143+
t.Error("mismatched field parent type:", err)
4144+
}
4145+
}
4146+
}
4147+
4148+
func stringsEqual(want, have string) string {
4149+
if want != have {
4150+
return fmt.Sprintf("mismatched values:\nwant: %q\nhave: %q", want, have)
4151+
}
4152+
4153+
return ""
4154+
}

internal/exec/exec.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,16 @@ func collectFieldsToResolve(sels []selected.Selection, s *resolvable.Schema, res
130130
case *selected.TypenameField:
131131
_, ok := fieldByAlias[sel.Alias]
132132
if !ok {
133+
res := reflect.ValueOf(typeOf(sel, resolver))
134+
f := s.FieldTypename
135+
f.TypeName = res.String()
136+
133137
sf := &selected.SchemaField{
134-
Field: s.Meta.FieldTypename,
138+
Field: f,
135139
Alias: sel.Alias,
136-
FixedResult: reflect.ValueOf(typeOf(sel, resolver)),
140+
FixedResult: res,
137141
}
142+
138143
field := &fieldToExec{field: sf, resolver: resolver}
139144
*fields = append(*fields, field)
140145
fieldByAlias[sel.Alias] = field

0 commit comments

Comments
 (0)