From 4fa94024e22b76f6a0abac6135523b72ba5282d3 Mon Sep 17 00:00:00 2001 From: andcan Date: Tue, 4 Feb 2020 15:17:11 +0100 Subject: [PATCH 1/2] add AllowFieldFunc and AllowInputValueFunc to Introspection extension --- graphql/handler/extension/introspection.go | 48 ++++++++- .../handler/extension/introspection_test.go | 99 +++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/graphql/handler/extension/introspection.go b/graphql/handler/extension/introspection.go index 6cfc8bace5a..dcdd43fed3c 100644 --- a/graphql/handler/extension/introspection.go +++ b/graphql/handler/extension/introspection.go @@ -3,16 +3,23 @@ package extension import ( "context" + "github.com/99designs/gqlgen/graphql/introspection" + "github.com/99designs/gqlgen/graphql" "github.com/vektah/gqlparser/gqlerror" ) // EnableIntrospection enables clients to reflect all of the types available on the graph. -type Introspection struct{} +type Introspection struct { + AllowFieldFunc func(ctx context.Context, t *introspection.Type, field *introspection.Field) (bool, error) + + AllowInputValueFunc func(ctx context.Context, t *introspection.Type, inputValue *introspection.InputValue) (bool, error) +} var _ interface { graphql.OperationContextMutator graphql.HandlerExtension + graphql.FieldInterceptor } = Introspection{} func (c Introspection) ExtensionName() string { @@ -27,3 +34,42 @@ func (c Introspection) MutateOperationContext(ctx context.Context, rc *graphql.O rc.DisableIntrospection = false return nil } + +func (c Introspection) InterceptField(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { + res, err = next(ctx) + + fc := graphql.GetFieldContext(ctx) + t := fc.Parent.Result.(*introspection.Type) + if fields, ok := res.([]introspection.Field); ok { + if c.AllowFieldFunc == nil { + return + } + var newFields []introspection.Field + for _, field := range fields { + allow, err := c.AllowFieldFunc(ctx, t, &field) + if err != nil { + return nil, err + } + if allow { + newFields = append(newFields, field) + } + } + res = newFields + } else if fields, ok := res.([]introspection.InputValue); ok { + if c.AllowInputValueFunc == nil { + return + } + var newFields []introspection.InputValue + for _, field := range fields { + allow, err := c.AllowInputValueFunc(ctx, t, &field) + if err != nil { + return nil, err + } + if allow { + newFields = append(newFields, field) + } + } + res = newFields + } + return res, err +} diff --git a/graphql/handler/extension/introspection_test.go b/graphql/handler/extension/introspection_test.go index e001eb529f0..b7aa2b3b8c1 100644 --- a/graphql/handler/extension/introspection_test.go +++ b/graphql/handler/extension/introspection_test.go @@ -2,9 +2,14 @@ package extension import ( "context" + "strings" "testing" "github.com/99designs/gqlgen/graphql" + + "github.com/99designs/gqlgen/graphql/introspection" + "github.com/vektah/gqlparser/ast" + "github.com/stretchr/testify/require" ) @@ -15,3 +20,97 @@ func TestIntrospection(t *testing.T) { require.Nil(t, Introspection{}.MutateOperationContext(context.Background(), rc)) require.Equal(t, false, rc.DisableIntrospection) } + +func TestIntrospection_InterceptField(t *testing.T) { + type fields struct { + AllowFieldFunc func(ctx context.Context, t *introspection.Type, field *introspection.Field) (bool, error) + AllowInputValueFunc func(ctx context.Context, t *introspection.Type, inputValue *introspection.InputValue) (bool, error) + } + type args struct { + kind ast.DefinitionKind + } + tests := []struct { + name string + fields fields + args args + wantRes []string + }{ + { + name: "field", + fields: fields{ + AllowFieldFunc: func(ctx context.Context, t *introspection.Type, field *introspection.Field) (b bool, err error) { + if *t.Name() == "TestType1" { + return !strings.HasSuffix(field.Name, "1"), nil + } + return true, nil + }, + }, + args: args{kind: ast.Object}, + wantRes: []string{"testField2", "testField3"}, + }, + { + name: "inputValue", + fields: fields{ + AllowInputValueFunc: func(ctx context.Context, t *introspection.Type, inputValue *introspection.InputValue) (b bool, err error) { + if *t.Name() == "TestType1" { + return !strings.HasSuffix(inputValue.Name, "1"), nil + } + return true, nil + }, + }, + args: args{kind: ast.InputObject}, + wantRes: []string{"testField2", "testField3"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := Introspection{ + AllowFieldFunc: tt.fields.AllowFieldFunc, + AllowInputValueFunc: tt.fields.AllowInputValueFunc, + } + typ := introspection.WrapTypeFromDef(nil, &ast.Definition{ + Kind: tt.args.kind, + Name: "TestType1", + Fields: []*ast.FieldDefinition{ + {Name: "testField1"}, + {Name: "testField2"}, + {Name: "testField3"}, + }, + }) + ctx := graphql.WithFieldContext(context.Background(), &graphql.FieldContext{Result: typ}) + ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{ + Field: graphql.CollectedField{ + Field: &ast.Field{ + Name: tt.name, + }, + }, + }) + gotRes, err := c.InterceptField(ctx, func(ctx context.Context) (res interface{}, err error) { + switch tt.args.kind { + case ast.Object: + return typ.Fields(false), nil + case ast.InputObject: + return typ.InputFields(), nil + } + require.Fail(t, "unexpected ast.DefinitionKind: %v", tt.args.kind) + return nil, nil + }) + require.NoError(t, err) + + var actualFields []string + switch tt.args.kind { + case ast.Object: + for _, field := range gotRes.([]introspection.Field) { + actualFields = append(actualFields, field.Name) + } + case ast.InputObject: + for _, field := range gotRes.([]introspection.InputValue) { + actualFields = append(actualFields, field.Name) + } + default: + require.FailNow(t, "", "unexpected ast.DefinitionKind: %v", tt.args.kind) + } + require.Equal(t, tt.wantRes, actualFields) + }) + } +} From 223046a0ad0b167fff3b3eec51f72bc0fbdf21b3 Mon Sep 17 00:00:00 2001 From: andcan Date: Tue, 4 Feb 2020 15:48:55 +0100 Subject: [PATCH 2/2] * add test case for nil AllowFieldFunc and AllowInputValueFunc * fix failing tests --- graphql/handler/extension/introspection.go | 3 ++- graphql/handler/extension/introspection_test.go | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/graphql/handler/extension/introspection.go b/graphql/handler/extension/introspection.go index dcdd43fed3c..adac3ab0014 100644 --- a/graphql/handler/extension/introspection.go +++ b/graphql/handler/extension/introspection.go @@ -39,11 +39,11 @@ func (c Introspection) InterceptField(ctx context.Context, next graphql.Resolver res, err = next(ctx) fc := graphql.GetFieldContext(ctx) - t := fc.Parent.Result.(*introspection.Type) if fields, ok := res.([]introspection.Field); ok { if c.AllowFieldFunc == nil { return } + t := fc.Parent.Result.(*introspection.Type) var newFields []introspection.Field for _, field := range fields { allow, err := c.AllowFieldFunc(ctx, t, &field) @@ -59,6 +59,7 @@ func (c Introspection) InterceptField(ctx context.Context, next graphql.Resolver if c.AllowInputValueFunc == nil { return } + t := fc.Parent.Result.(*introspection.Type) var newFields []introspection.InputValue for _, field := range fields { allow, err := c.AllowInputValueFunc(ctx, t, &field) diff --git a/graphql/handler/extension/introspection_test.go b/graphql/handler/extension/introspection_test.go index b7aa2b3b8c1..ea6f9c2da57 100644 --- a/graphql/handler/extension/introspection_test.go +++ b/graphql/handler/extension/introspection_test.go @@ -61,6 +61,16 @@ func TestIntrospection_InterceptField(t *testing.T) { args: args{kind: ast.InputObject}, wantRes: []string{"testField2", "testField3"}, }, + { + name: "nil AllowFieldFunc", + args: args{kind: ast.Object}, + wantRes: []string{"testField1", "testField2", "testField3"}, + }, + { + name: "nil AllowInputValueFunc", + args: args{kind: ast.Object}, + wantRes: []string{"testField1", "testField2", "testField3"}, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {