agent_patch
runs/2026-02-28__13-38-10__gpt-5-4/flux-pr-1209/flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/agent-logs/agent.patch
14034 bytes
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/execution/engine/execution_engine_grpc_test.go b/app/execution/engine/execution_engine_grpc_test.go
index 15e0a26..0e8055a 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/execution/engine/execution_engine_grpc_test.go
+++ b/app/execution/engine/execution_engine_grpc_test.go
@@ -259,6 +259,27 @@ func TestGRPCSubgraphExecution(t *testing.T) {
require.Equal(t, `{"data":{"user":{"id":"1","name":"User 1"}}}`, response)
})
+ t.Run("should preserve GraphQL aliases in grpc responses", func(t *testing.T) {
+ operation := graphql.Request{
+ OperationName: "UserAliasQuery",
+ Variables: stringify(map[string]any{
+ "id": "1",
+ }),
+ Query: `
+ query UserAliasQuery($id: ID!) {
+ aliasedUser: user(id: $id) {
+ myId: id
+ myName: name
+ }
+ }
+ `,
+ }
+
+ response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping()))
+ require.NoError(t, err)
+ require.Equal(t, `{"data":{"aliasedUser":{"myId":"1","myName":"User 1"}}}`, response)
+ })
+
t.Run("should run complex query", func(t *testing.T) {
operation := graphql.Request{
OperationName: "ComplexFilterTypeQuery",
@@ -390,6 +411,37 @@ func TestGRPCSubgraphExecution(t *testing.T) {
require.Error(t, err)
})
+ t.Run("should resolve union member types for mapped mutation rpcs", func(t *testing.T) {
+ operation := graphql.Request{
+ OperationName: "PerformActionMutation",
+ Variables: stringify(map[string]any{
+ "input": map[string]any{
+ "type": "error_action",
+ "payload": "invalid data",
+ },
+ }),
+ Query: `
+ mutation PerformActionMutation($input: ActionInput!) {
+ result: performAction(input: $input) {
+ __typename
+ ... on ActionSuccess {
+ message
+ timestamp
+ }
+ ... on ActionError {
+ message
+ code
+ }
+ }
+ }
+ `,
+ }
+
+ response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping()))
+ require.NoError(t, err)
+ require.Equal(t, `{"data":{"result":{"__typename":"ActionError","message":"Action failed due to validation error","code":"VALIDATION_ERROR"}}}`, response)
+ })
+
// Category tests to verify enum handling
t.Run("should correctly handle query for all categories with enum values", func(t *testing.T) {
operation := graphql.Request{
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/compiler.go b/app/v2/pkg/engine/datasource/grpc_datasource/compiler.go
index 1b519b7..8b4291b 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/compiler.go
+++ b/app/v2/pkg/engine/datasource/grpc_datasource/compiler.go
@@ -322,8 +322,14 @@ func (p *RPCCompiler) Compile(executionPlan *RPCExecutionPlan, inputData gjson.R
invocations := make([]Invocation, 0, len(executionPlan.Calls))
for _, call := range executionPlan.Calls {
- inputMessage := p.doc.MessageByName(call.Request.Name)
- outputMessage := p.doc.MessageByName(call.Response.Name)
+ inputMessage, ok := p.resolveMessage(call.Request.Name)
+ if !ok {
+ return nil, fmt.Errorf("failed to compile invocation for method %s: request message %s not found in protobuf schema", call.MethodName, call.Request.Name)
+ }
+ outputMessage, ok := p.resolveMessage(call.Response.Name)
+ if !ok {
+ return nil, fmt.Errorf("failed to compile invocation for method %s: response message %s not found in protobuf schema", call.MethodName, call.Response.Name)
+ }
request := p.buildProtoMessage(inputMessage, &call.Request, inputData)
response := p.newEmptyMessage(outputMessage)
@@ -349,6 +355,15 @@ func (p *RPCCompiler) Compile(executionPlan *RPCExecutionPlan, inputData gjson.R
return invocations, nil
}
+func (p *RPCCompiler) resolveMessage(name string) (Message, bool) {
+ ref := p.doc.MessageRefByName(name)
+ if ref == -1 {
+ return Message{}, false
+ }
+
+ return p.doc.MessageByRef(ref), true
+}
+
func (p *RPCCompiler) resolveServiceName(methodName string) (string, bool) {
for _, service := range p.doc.Services {
for _, methodRef := range service.MethodsRefs {
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/compiler_test.go b/app/v2/pkg/engine/datasource/grpc_datasource/compiler_test.go
index 5e098c3..ecc53d8 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/compiler_test.go
+++ b/app/v2/pkg/engine/datasource/grpc_datasource/compiler_test.go
@@ -291,3 +291,43 @@ func TestBuildProtoMessage(t *testing.T) {
require.Equal(t, 1, len(invocations))
}
+
+func TestCompileReturnsClearErrorForMissingRequestMessage(t *testing.T) {
+ compiler, err := NewProtoCompiler(grpctest.MustProtoSchema(t), nil)
+ require.NoError(t, err)
+
+ _, err = compiler.Compile(&RPCExecutionPlan{
+ Calls: []RPCCall{
+ {
+ MethodName: "QueryUser",
+ Request: RPCMessage{
+ Name: "DoesNotExistRequest",
+ },
+ Response: RPCMessage{
+ Name: "QueryUserResponse",
+ },
+ },
+ },
+ }, gjson.Parse(`{}`))
+ require.EqualError(t, err, "failed to compile invocation for method QueryUser: request message DoesNotExistRequest not found in protobuf schema")
+}
+
+func TestCompileReturnsClearErrorForMissingResponseMessage(t *testing.T) {
+ compiler, err := NewProtoCompiler(grpctest.MustProtoSchema(t), nil)
+ require.NoError(t, err)
+
+ _, err = compiler.Compile(&RPCExecutionPlan{
+ Calls: []RPCCall{
+ {
+ MethodName: "QueryUser",
+ Request: RPCMessage{
+ Name: "QueryUserRequest",
+ },
+ Response: RPCMessage{
+ Name: "DoesNotExistResponse",
+ },
+ },
+ },
+ }, gjson.Parse(`{}`))
+ require.EqualError(t, err, "failed to compile invocation for method QueryUser: response message DoesNotExistResponse not found in protobuf schema")
+}
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go b/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go
index 1eb2b6c..159c535 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go
+++ b/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go
@@ -126,11 +126,12 @@ func (r RPCFieldSelectionSet) SelectFieldsForTypes(validTypes []string) RPCField
}
for _, field := range lookupFields {
- if _, found := fieldSet[field.Name]; found {
+ responseKey := field.ResponseKey()
+ if _, found := fieldSet[responseKey]; found {
continue
}
- fieldSet[field.Name] = struct{}{}
+ fieldSet[responseKey] = struct{}{}
fields = append(fields, field)
}
}
@@ -142,10 +143,7 @@ func (r RPCFieldSelectionSet) SelectFieldsForTypes(validTypes []string) RPCField
// It contains all information required to extract data from GraphQL variables
// and construct the appropriate protobuf field.
type RPCField struct {
- // Alias can be used to rename the field in the request message
- // This is needed to make sure that during the json composition,
- // the field names match the GraphQL request naming.
- // TODO implement alias handling
+ // Alias stores the GraphQL response alias for the field when one is defined.
Alias string
// Repeated indicates if the field is a repeated field (array/list)
Repeated bool
@@ -192,6 +190,28 @@ func (r RPCFields) Exists(name string) bool {
return false
}
+func (r RPCFields) ExistsPath(path string) bool {
+ for _, field := range r {
+ if field.JSONPath == path {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (r RPCField) ResponseKey() string {
+ if r.Alias != "" {
+ return r.Alias
+ }
+
+ if r.JSONPath != "" {
+ return r.JSONPath
+ }
+
+ return r.Name
+}
+
func (r *RPCExecutionPlan) String() string {
var result strings.Builder
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go b/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go
index f1ad122..3e0bbb9 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go
+++ b/app/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go
@@ -289,6 +289,7 @@ func (r *rpcPlanVisitor) handleRootField(ref int) error {
// EnterField implements astvisitor.EnterFieldVisitor.
func (r *rpcPlanVisitor) EnterField(ref int) {
fieldName := r.operation.FieldNameString(ref)
+ responsePath := r.operation.FieldAliasOrNameString(ref)
if r.IsRootField() {
if err := r.handleRootField(ref); err != nil {
r.walker.StopWithInternalErr(err)
@@ -311,7 +312,7 @@ func (r *rpcPlanVisitor) EnterField(ref int) {
}
// prevent duplicate fields
- if r.planInfo.currentResponseMessage.Fields.Exists(fieldName) {
+ if r.planInfo.currentResponseMessage.Fields.ExistsPath(responsePath) {
return
}
@@ -331,9 +332,12 @@ func (r *rpcPlanVisitor) EnterField(ref int) {
field := RPCField{
Name: r.resolveFieldMapping(parentTypeName, fieldName),
TypeName: typeName.String(),
- JSONPath: fieldName,
+ JSONPath: responsePath,
Repeated: r.definition.TypeIsList(fdt),
}
+ if r.operation.FieldAliasIsDefined(ref) {
+ field.Alias = responsePath
+ }
if typeName == DataTypeEnum {
field.EnumName = r.definition.FieldDefinitionTypeNameString(fd)
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go b/app/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go
index 71bf396..7a4c59f 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go
+++ b/app/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go
@@ -316,6 +316,44 @@ func Test_DataSource_Load_WithMockService_WithResponseMapping(t *testing.T) {
require.Equal(t, "HARDCODED_NAME_TEST", resp.Data.ComplexFilterType[0].Name)
}
+func Test_DataSource_Load_UsesGraphQLAliasesInResponse(t *testing.T) {
+ conn, cleanup := setupTestGRPCServer(t)
+ t.Cleanup(cleanup)
+
+ query := `query UserAliasQuery($id: ID!) { aliasedUser: user(id: $id) { myId: id myName: name } }`
+ variables := `{"variables":{"id":"1"}}`
+
+ schemaDoc := grpctest.MustGraphQLSchema(t)
+ queryDoc, report := astparser.ParseGraphqlDocumentString(query)
+ require.False(t, report.HasErrors(), report.Error())
+
+ compiler, err := NewProtoCompiler(grpctest.MustProtoSchema(t), nil)
+ require.NoError(t, err)
+
+ ds, err := NewDataSource(conn, DataSourceConfig{
+ Operation: &queryDoc,
+ Definition: &schemaDoc,
+ SubgraphName: "Products",
+ Compiler: compiler,
+ Mapping: testMapping(),
+ })
+ require.NoError(t, err)
+
+ output := new(bytes.Buffer)
+ err = ds.Load(context.Background(), []byte(`{"query":"`+query+`","body":`+variables+`}`), output)
+ require.NoError(t, err)
+
+ var resp struct {
+ Data map[string]map[string]string `json:"data"`
+ }
+ require.NoError(t, json.Unmarshal(output.Bytes(), &resp))
+ require.Contains(t, resp.Data, "aliasedUser")
+ require.Equal(t, map[string]string{
+ "myId": "1",
+ "myName": "User 1",
+ }, resp.Data["aliasedUser"])
+}
+
// Test_DataSource_Load_WithGrpcError tests how the datasource handles gRPC errors
// and formats them as GraphQL errors in the response
func Test_DataSource_Load_WithGrpcError(t *testing.T) {
diff --git a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/grpctest/schema.go b/app/v2/pkg/grpctest/schema.go
index 0e2a9bf..8f3702a 100644
--- a/tmp/agent-patch-flux-pr-1209.1-of-1.2026-02-28__13-38-10__gpt-5-4/app/v2/pkg/grpctest/schema.go
+++ b/app/v2/pkg/grpctest/schema.go
@@ -166,6 +166,16 @@ func GetFieldConfigurations() plan.FieldConfigurations {
},
},
},
+ {
+ TypeName: "Query",
+ FieldName: "categoriesByKinds",
+ Arguments: []plan.ArgumentConfiguration{
+ {
+ Name: "kinds",
+ SourceType: plan.FieldArgumentSource,
+ },
+ },
+ },
{
TypeName: "Query",
FieldName: "filterCategories",
@@ -176,6 +186,16 @@ func GetFieldConfigurations() plan.FieldConfigurations {
},
},
},
+ {
+ TypeName: "Query",
+ FieldName: "search",
+ Arguments: []plan.ArgumentConfiguration{
+ {
+ Name: "input",
+ SourceType: plan.FieldArgumentSource,
+ },
+ },
+ },
{
TypeName: "Query",
FieldName: "calculateTotals",
@@ -196,6 +216,16 @@ func GetFieldConfigurations() plan.FieldConfigurations {
},
},
},
+ {
+ TypeName: "Mutation",
+ FieldName: "performAction",
+ Arguments: []plan.ArgumentConfiguration{
+ {
+ Name: "input",
+ SourceType: plan.FieldArgumentSource,
+ },
+ },
+ },
}
}
@@ -228,17 +258,22 @@ func GetDataSourceMetadata() *plan.DataSourceMetadata {
"typeFilterWithArguments",
"typeWithMultipleFilterFields",
"complexFilterType",
+ "calculateTotals",
"categories",
"categoriesByKind",
+ "categoriesByKinds",
"filterCategories",
"randomPet",
"allPets",
+ "search",
+ "randomSearchResult",
},
},
{
TypeName: "Mutation",
FieldNames: []string{
"createUser",
+ "performAction",
},
},
},
@@ -347,6 +382,20 @@ func GetDataSourceMetadata() *plan.DataSourceMetadata {
"barkVolume",
},
},
+ {
+ TypeName: "ActionSuccess",
+ FieldNames: []string{
+ "message",
+ "timestamp",
+ },
+ },
+ {
+ TypeName: "ActionError",
+ FieldNames: []string{
+ "message",
+ "code",
+ },
+ },
{
TypeName: "UserInput",
FieldNames: []string{