-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is there a way to auto deside when to call a resolver with a @goField(forceResolver: true)
#3446
Comments
It's hard to find the generate code about |
I am trying to impl this with Trie Tree rather than using the example in docs, but I found it's impossible to write tests, because I can't create a package ignore
import (
"context"
"fmt"
"strings"
"github.com/99designs/gqlgen/graphql"
)
type TrieNode struct {
children map[string]*TrieNode
}
func NewTrie() *TrieNode {
return &TrieNode{
children: make(map[string]*TrieNode),
}
}
func (t *TrieNode) Insert(path string) {
parts := strings.Split(path, ".")
current := t
for _, part := range parts {
if _, exists := current.children[part]; !exists {
current.children[part] = &TrieNode{
children: make(map[string]*TrieNode),
}
}
current = current.children[part]
}
}
func (t *TrieNode) IsCommonPath(ctx context.Context) (bool, error) {
selection := graphql.CollectFieldsCtx(ctx, nil)
if len(selection) == 0 {
return true, nil
}
// Check if all fields have the same structure
firstChild := selection[0]
firstChildTrie := t.children[firstChild.Name]
if firstChildTrie == nil {
return false, nil
}
// Verify all other fields match the first field's structure
for _, field := range selection[1:] {
childTrie := t.children[field.Name]
if childTrie == nil || !compareTrie(firstChildTrie, childTrie) {
return false, nil
}
}
// Recursively check child selections
for _, field := range selection {
childCtx := graphql.WithFieldContext(ctx, &graphql.FieldContext{
Field: field,
})
common, err := firstChildTrie.IsCommonPath(childCtx)
if err != nil {
return false, err
}
if !common {
return false, nil
}
}
return true, nil
}
func compareTrie(a, b *TrieNode) bool {
if len(a.children) != len(b.children) {
return false
}
for key := range a.children {
if b.children[key] == nil {
return false
}
}
return true
}
func Ignore(ctx context.Context, ignore []string) (bool, error) {
if ctx == nil {
return false, fmt.Errorf("context cannot be nil")
}
// Build trie from selection paths
trie := NewTrie()
for _, path := range ignore {
if path == "" {
return false, fmt.Errorf("ignore path cannot be empty")
}
trie.Insert(path)
}
common, err := trie.IsCommonPath(ctx)
if err != nil {
return false, fmt.Errorf("error checking common path: %w", err)
}
return common, nil
} package ignore
import (
"context"
"testing"
"github.com/99designs/gqlgen/graphql"
"github.com/stretchr/testify/assert"
"github.com/vektah/gqlparser/v2/ast"
)
func TestIgnore(t *testing.T) {
tests := []struct {
name string
sel []string
fields []graphql.CollectedField
expected bool
}{
{
name: "empty selection",
sel: []string{"a.b.c"},
fields: []graphql.CollectedField{},
expected: true,
},
{
name: "exact match",
sel: []string{"a.b.c"},
fields: []graphql.CollectedField{
{
Field: &ast.Field{
Name: "a",
SelectionSet: ast.SelectionSet{
&ast.Field{
Name: "b",
SelectionSet: ast.SelectionSet{
&ast.Field{Name: "c"},
},
},
},
},
},
},
expected: true,
},
{
name: "partial match",
sel: []string{"a.b.c"},
fields: []graphql.CollectedField{
{
Field: &ast.Field{
Name: "a",
SelectionSet: ast.SelectionSet{
&ast.Field{Name: "b"},
},
},
},
},
expected: false,
},
{
name: "multiple paths common structure",
sel: []string{"a.b.c", "a.b.d"},
fields: []graphql.CollectedField{
{
Field: &ast.Field{
Name: "a",
SelectionSet: ast.SelectionSet{
&ast.Field{
Name: "b",
SelectionSet: ast.SelectionSet{
&ast.Field{Name: "c"},
&ast.Field{Name: "d"},
},
},
},
},
},
},
expected: true,
},
{
name: "different structures",
sel: []string{"a.b.c", "a.b.d"},
fields: []graphql.CollectedField{
{
Field: &ast.Field{
Name: "a",
SelectionSet: ast.SelectionSet{
&ast.Field{
Name: "b",
SelectionSet: ast.SelectionSet{
&ast.Field{Name: "c"},
},
},
&ast.Field{Name: "d"},
},
},
},
},
expected: false,
},
{
name: "empty input",
sel: []string{},
fields: []graphql.CollectedField{},
expected: true,
},
{
name: "nested paths",
sel: []string{"a.b.c.d", "a.b.c.e"},
fields: []graphql.CollectedField{
{
Field: &ast.Field{
Name: "a",
SelectionSet: ast.SelectionSet{
&ast.Field{
Name: "b",
SelectionSet: ast.SelectionSet{
&ast.Field{
Name: "c",
SelectionSet: ast.SelectionSet{
&ast.Field{Name: "d"},
&ast.Field{Name: "e"},
},
},
},
},
},
},
},
},
expected: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := Ignore(context.Background(), tt.sel)
assert.NoError(t, err)
assert.Equal(t, tt.expected, result)
})
}
} |
if you don't put a |
What happened?
If I set a
@goField(forceResolver: true)
directive, it will be called anywayWhat did you expect?
We need a mechanism to automatically determine whether to invoke a resolver, instead of manually parsing fields to determine whether to execute.
like this:
the query will not call the
products
resolver inProject
the query will call resolver
Minimal graphql.schema and models to reproduce
None
versions
go run github.com/99designs/gqlgen version
?v0.17.61
go version
?go version go1.23.4 linux/amd64
The text was updated successfully, but these errors were encountered: