Skip to content

Commit

Permalink
feat(graphql): support directives
Browse files Browse the repository at this point in the history
  • Loading branch information
bastianccm committed Jun 23, 2022
1 parent 984798f commit f3d747e
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 15 deletions.
26 changes: 26 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,15 @@ func (m *plugin) GenerateCode(data *codegen.Data) error {
"gmethod": func(from, to string) string {
return m.types.resolver[from][to][2]
},
"gdpkg": func(name string) string {
return m.types.directives["@"+name][0]
},
"gdtyp": func(name string) string {
return m.types.directives["@"+name][1]
},
"gdmethod": func(name string) string {
return m.types.directives["@"+name][2]
},
},
PackageDoc: "//+build !graphql\n",
Template: `
Expand Down Expand Up @@ -231,6 +240,9 @@ type {{$root.TypeName}} struct {
{{lcFirst $root.TypeName}}{{$object.Name}} *{{lcFirst $root.TypeName}}{{$object.Name}}
{{- end }}
{{- end }}
{{ range $directive := .AllDirectives }}
{{$directive.Name}}Resolver *{{lookupImport (gdpkg $directive.Name)}}.{{gdtyp $directive.Name}}
{{- end }}
}
func (r *{{$root.TypeName}}) Inject (
Expand All @@ -239,12 +251,26 @@ func (r *{{$root.TypeName}}) Inject (
{{lcFirst $root.TypeName}}{{$object.Name}} *{{lcFirst $root.TypeName}}{{$object.Name}},
{{- end }}
{{- end }}
{{ range $directive := .AllDirectives }}
{{$directive.Name}}Resolver *{{lookupImport (gdpkg $directive.Name)}}.{{gdtyp $directive.Name}},
{{- end }}
) {
{{- range $object := .Objects }}
{{- if $object.HasResolvers }}
r.{{lcFirst $root.TypeName}}{{$object.Name}} = {{lcFirst $root.TypeName}}{{$object.Name}}
{{- end }}
{{- end }}
{{ range $directive := .AllDirectives }}
r.{{$directive.Name}}Resolver = {{$directive.Name}}Resolver
{{- end }}
}
func (r *{{$root.TypeName}}) directives() DirectiveRoot {
return DirectiveRoot{
{{ range $directive := .AllDirectives -}}
{{ucFirst $directive.Name}}: r.{{$directive.Name}}Resolver.{{gdmethod $directive.Name}},
{{end}}
}
}
{{ range $object := .Objects -}}
Expand Down
44 changes: 42 additions & 2 deletions example/graphql/generated.go

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

2 changes: 1 addition & 1 deletion example/graphql/module.go

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

22 changes: 17 additions & 5 deletions example/graphql/resolver.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ type User {
attributes: User_Attributes!
}

type User_Attributes {
directive @userAttributeFilter(prefix: String!) on OBJECT

type User_Attributes @userAttributeFilter(prefix: "secret") {
keys: [String!]!
get(key: String!): String!
}
Expand Down
3 changes: 2 additions & 1 deletion example/user/infrastructure/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ func (us *UserServiceImpl) UserByID(_ context.Context, id string) (*domain.User,
Name: "User " + id,
Nicknames: []string{"nick", id},
Attributes: map[string]interface{}{
"movie": "starwars",
"movie": "starwars",
"secret_crush": "r2d2",
},
}, nil
}
21 changes: 21 additions & 0 deletions example/user/interfaces/graphql/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package graphql

import (
"context"
"fmt"
"strings"

"flamingo.me/graphql/example/user/domain"
"github.com/99designs/gqlgen/graphql"
)

// UserQueryResolver resolver for the user service
Expand All @@ -21,3 +24,21 @@ func (r *UserQueryResolver) Inject(userService domain.UserService) *UserQueryRes
func (r *UserQueryResolver) User(ctx context.Context, id string) (*domain.User, error) {
return r.userService.UserByID(ctx, id)
}

// UserAttributeFilter directive
func (r *UserQueryResolver) UserAttributeFilter(ctx context.Context, obj interface{}, next graphql.Resolver, prefix string) (res interface{}, err error) {
rawAttributes, err := next(ctx)
if err != nil {
return nil, err
}
attributes, ok := rawAttributes.(domain.Attributes)
if !ok {
return nil, fmt.Errorf("no attributes returned")
}
for k := range attributes {
if strings.HasPrefix(k, prefix) {
delete(attributes, k)
}
}
return attributes, nil
}
4 changes: 3 additions & 1 deletion example/user/interfaces/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ type User {
attributes: User_Attributes!
}

type User_Attributes {
directive @userAttributeFilter(prefix: String!) on OBJECT

type User_Attributes @userAttributeFilter(prefix: "secret") {
keys: [String!]!
get(key: String!): String!
}
Expand Down
3 changes: 3 additions & 0 deletions example/user/interfaces/graphql/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package graphql

import (
// embed schema.grapqhl

_ "embed"

"flamingo.me/graphql"
Expand All @@ -24,4 +25,6 @@ func (*Service) Types(types *graphql.Types) {
types.Map("User", domain.User{})
types.Map("User_Attributes", domain.Attributes{})
types.Resolve("Query", "User", UserQueryResolver{}, "User")

types.Directive("@userAttributeFilter", UserQueryResolver{}, "UserAttributeFilter")
}
19 changes: 16 additions & 3 deletions helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (

// Types represent information on Object->Go type mappings and resolvers
type Types struct {
names map[string]string
resolver map[string]map[string][3]string
fields map[string]map[string]string
names map[string]string
resolver map[string]map[string][3]string
fields map[string]map[string]string
directives map[string][3]string
}

// Map references a graphql type to a go type
Expand Down Expand Up @@ -53,6 +54,18 @@ func (tc *Types) GoField(graphqlType, graphqlField, goField string) {
tc.fields[graphqlType][graphqlField] = goField
}

// Directive specifies a directive resolver for a graphql directive
func (tc *Types) Directive(graphqlDirective string, typ interface{}, method string) {
if tc.directives == nil {
tc.directives = make(map[string][3]string)
}
t := reflect.TypeOf(typ)
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
tc.directives[graphqlDirective] = [3]string{t.PkgPath(), t.Name(), method}
}

// FlamingoQueryResolver always resolves to the string "flamingo" for the default schemas.
type FlamingoQueryResolver struct{}

Expand Down
2 changes: 1 addition & 1 deletion templates/module.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type Module struct{}
// Configure sets the graphql.ExecutableSchema binding via a provider, passing in the correct root resolver
func (*Module) Configure(injector *dingo.Injector) {
injector.Bind(new(graphql.ExecutableSchema)).ToProvider(func(root *rootResolver) graphql.ExecutableSchema {
return NewExecutableSchema(Config{Resolvers: root})
return NewExecutableSchema(Config{Resolvers: root, Directives: root.directives()})
})

injector.BindMulti(new(cobra.Command)).ToProvider(func(root *rootResolver) *cobra.Command {
Expand Down

0 comments on commit f3d747e

Please sign in to comment.