Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
20e396e
feat: support multiple and nested keys
Noroth Aug 4, 2025
5b22725
chore: remove debug statement
Noroth Aug 4, 2025
79d1be9
Merge branch 'master' of github.com:wundergraph/graphql-go-tools into…
Noroth Aug 5, 2025
96991d5
chore: check error
Noroth Aug 5, 2025
6b23a53
chore: improve mapping logic for key fields
Noroth Aug 6, 2025
8042e65
chore: fix alias behavior for nullable fields
Noroth Aug 6, 2025
4f02d15
Merge branch 'master' into ludwig/eng-7007-support-of-multiple-and-ne…
Noroth Aug 6, 2025
04af7d6
chore: remove invalid test scenarios
Noroth Aug 6, 2025
d7cc0e8
chore: simplify selection set stripping logic
Noroth Aug 6, 2025
90100bd
Merge branch 'master' into ludwig/eng-7007-support-of-multiple-and-ne…
Noroth Aug 6, 2025
ce4d445
chore: remove comma switch
Noroth Aug 6, 2025
45227e2
chore: address review comments
Noroth Aug 6, 2025
35c8fec
Merge branch 'master' of github.com:wundergraph/graphql-go-tools into…
Noroth Aug 6, 2025
add1241
chore: address review comments
Noroth Aug 7, 2025
681f2ae
chore: proper error checks
Noroth Aug 7, 2025
6bd3025
chore: use InvalidRef
Noroth Aug 7, 2025
4e36ffb
chore: handle entity order correctly
Noroth Aug 8, 2025
1041158
Merge branch 'master' into ludwig/eng-7007-support-of-multiple-and-ne…
Noroth Aug 8, 2025
6fcc131
chore: add error handling when mapping was not found
Noroth Aug 8, 2025
94457e9
chore: improve error handling
Noroth Aug 8, 2025
3aaa45c
chore: improve handling for shared objects
Noroth Aug 8, 2025
ae0493b
chore: remove type from schema
Noroth Aug 8, 2025
7c51e39
chore: remove type from entity union
Noroth Aug 8, 2025
4c9efdb
chore: address some review comments
Noroth Aug 8, 2025
ca89a2a
chore: check if typename already exists
Noroth Aug 8, 2025
6cb8617
chore: add proper comments for json builder
Noroth Aug 8, 2025
f28d67f
chore: shorten function
Noroth Aug 8, 2025
42a4532
chore: prevent protential int overflow
Noroth Aug 8, 2025
b454dd1
chore: improve comments
Noroth Aug 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions execution/engine/execution_engine_grpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func TestGRPCSubgraphExecution(t *testing.T) {
Query: "query UserQuery { users { id name } }",
}

response, err := executeOperation(t, conn, operation)
response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.MustDefaultGRPCMapping(t)))
require.NoError(t, err)
require.Equal(t, `{"data":{"users":[{"id":"user-1","name":"User 1"},{"id":"user-2","name":"User 2"},{"id":"user-3","name":"User 3"}]}}`, response)
})
Expand All @@ -255,7 +255,7 @@ func TestGRPCSubgraphExecution(t *testing.T) {
`,
}

response, err := executeOperation(t, conn, operation)
response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.MustDefaultGRPCMapping(t)))
require.NoError(t, err)
require.Equal(t, `{"data":{"user":{"id":"1","name":"User 1"}}}`, response)
})
Expand Down
24 changes: 24 additions & 0 deletions v2/pkg/astvisitor/visitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3998,3 +3998,27 @@ func (w *Walker) FieldDefinitionDirectiveArgumentValueByName(field int, directiv

return w.definition.DirectiveArgumentValueByName(directive, argumentName)
}

// InRootField returns true if the current field is a root field.
func (w *Walker) InRootField() bool {
return w.CurrentKind == ast.NodeKindField &&
len(w.Ancestors) == 2 &&
w.Ancestors[0].Kind == ast.NodeKindOperationDefinition
}
Comment thread
Noroth marked this conversation as resolved.

// ResolveInlineFragment returns the inline fragment ref if the current field is inside of
// an inline fragment.
// It returns the inline fragment ref and true if the current field is inside of an inline fragment.
// It returns -1 and false if the current field is not inside of an inline fragment.
func (w *Walker) ResolveInlineFragment() (int, bool) {
if len(w.Ancestors) < 2 {
return -1, false
Comment thread
Noroth marked this conversation as resolved.
Outdated
}

node := w.Ancestors[len(w.Ancestors)-2]
if node.Kind != ast.NodeKindInlineFragment {
return -1, false
}

return node.Ref, true
}
Original file line number Diff line number Diff line change
Expand Up @@ -368,11 +368,12 @@ func (p *Planner[T]) ConfigureFetch() resolve.FetchConfiguration {
}

dataSource, err = grpcdatasource.NewDataSource(p.grpcClient, grpcdatasource.DataSourceConfig{
Operation: &opDocument,
Definition: p.config.schemaConfiguration.upstreamSchemaAst,
Mapping: p.config.grpc.Mapping,
Compiler: p.config.grpc.Compiler,
Disabled: p.config.grpc.Disabled,
Operation: &opDocument,
Definition: p.config.schemaConfiguration.upstreamSchemaAst,
Mapping: p.config.grpc.Mapping,
Compiler: p.config.grpc.Compiler,
Disabled: p.config.grpc.Disabled,
FederationConfigs: p.dataSourcePlannerConfig.RequiredFields,
// TODO: remove fallback logic in visitor for subgraph name and
// add proper error handling if the subgraph name is not set in the mapping
SubgraphName: p.dataSourceConfig.Name(),
Expand Down
102 changes: 101 additions & 1 deletion v2/pkg/engine/datasource/grpc_datasource/configuration.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package grpcdatasource

import (
"strings"

"github.com/wundergraph/graphql-go-tools/v2/pkg/lexer/runes"
)

type (
// RPCConfigMap is a map of RPC names to RPC configurations
RPCConfigMap map[string]RPCConfig
Expand All @@ -17,7 +23,7 @@ type GRPCMapping struct {
// SubscriptionRPCs maps GraphQL subscription fields to the corresponding gRPC RPC configurations
SubscriptionRPCs RPCConfigMap
// EntityRPCs defines how GraphQL types are resolved as entities using specific RPCs
EntityRPCs map[string]EntityRPCConfig
EntityRPCs map[string][]EntityRPCConfig
// Fields defines the field mappings between GraphQL types and gRPC messages
Fields map[string]FieldMap
// EnumValues defines the enum values for each enum type
Expand Down Expand Up @@ -121,3 +127,97 @@ func (g *GRPCMapping) ResolveEnumValue(enumName, enumValue string) (string, bool

return "", false
}

func (g *GRPCMapping) ResolveEntityRPCConfig(typeName, key string) (RPCConfig, bool) {
Comment thread
devsergiy marked this conversation as resolved.
rpcConfig, ok := g.EntityRPCs[typeName]
if !ok {
return RPCConfig{}, false
}

for _, ei := range rpcConfig {
if compareKeyFields(ei.Key, key) {
return ei.RPCConfig, true
}

}

return RPCConfig{}, false
}

type keySet map[string]struct{}

func (k keySet) add(keys ...string) {
for _, key := range keys {
trimmedKey := strings.TrimSpace(key)
if trimmedKey != "" {
k[trimmedKey] = struct{}{}
}
}
}

func (k keySet) equals(other keySet) bool {
if len(k) != len(other) {
return false
}

for key := range k {
if _, ok := other[key]; !ok {
return false
}
}

return true
}

// We compare only top level key
func compareKeyFields(left, right string) bool {
Comment thread
devsergiy marked this conversation as resolved.
if left == right {
return true
}

left = stripSelectionSets(left)
right = stripSelectionSets(right)

leftKeys := strings.Split(left, " ")
rightKeys := strings.Split(right, " ")

leftSet := make(keySet)
leftSet.add(leftKeys...)

rightSet := make(keySet)
rightSet.add(rightKeys...)

return leftSet.equals(rightSet)
}

func stripSelectionSets(keyString string) string {
depth := 0

var prev rune

keyString = strings.ReplaceAll(keyString, ",", " ")

var sb strings.Builder

for _, r := range keyString {
switch r {
case runes.LBRACE:
depth++
case runes.RBRACE:
if depth == 0 {
continue
}

depth--
default:
if depth != 0 || (r == runes.SPACE && prev == runes.SPACE) {
break
}

sb.WriteRune(r)
prev = r
}
}

return strings.TrimSpace(sb.String())
}
Loading
Loading