agent_patch
runs/2026-02-21__03-36-16__gpt-5-1-codex-mini/flux-pr-817/flux-pr-817.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/agent-logs/agent.patch
23496 bytes
diff --git a/tmp/agent-patch-flux-pr-817.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/app/v2/pkg/engine/datasource/pubsub_datasource/nats_event_manager.go b/app/v2/pkg/engine/datasource/pubsub_datasource/nats_event_manager.go
index 1cb2914..26b5b45 100644
--- a/tmp/agent-patch-flux-pr-817.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/app/v2/pkg/engine/datasource/pubsub_datasource/nats_event_manager.go
+++ b/app/v2/pkg/engine/datasource/pubsub_datasource/nats_event_manager.go
@@ -3,7 +3,8 @@ package pubsub_datasource
import (
"encoding/json"
"fmt"
- "github.com/wundergraph/graphql-go-tools/v2/pkg/ast"
+ "strings"
+
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/plan"
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/resolve"
"slices"
@@ -35,37 +36,37 @@ type NatsEventManager struct {
}
func (p *NatsEventManager) extractEventSubject(ref int, subject string) (string, error) {
- matches := eventSubjectRegex.FindAllStringSubmatch(subject, -1)
- if len(matches) != 1 || len(matches[0]) != 2 {
- return "", fmt.Errorf("expected subject to match regex")
- }
- argumentName := matches[0][1]
- // We need to find the argument in the operation
- argumentRef, ok := p.visitor.Operation.FieldArgument(ref, []byte(argumentName))
- if !ok {
- return "", fmt.Errorf("argument \"%s\" is not defined", argumentName)
- }
- argumentValue := p.visitor.Operation.ArgumentValue(argumentRef)
- if argumentValue.Kind != ast.ValueKindVariable {
- return "", fmt.Errorf("expected argument \"%s\" kind to be \"ValueKindVariable\" but received \"%s\"", argumentName, argumentValue.Kind)
- }
- variableName := p.visitor.Operation.VariableValueNameBytes(argumentValue.Ref)
- variableDefinition, ok := p.visitor.Operation.VariableDefinitionByNameAndOperation(p.visitor.Walker.Ancestors[0].Ref, variableName)
- if !ok {
- return "", fmt.Errorf("expected definition to exist for variable \"%s\"", variableName)
+ parser := plan.NewArgumentTemplateParser(p.visitor.Operation, p.visitor.Definition, p.visitor.Walker, ref)
+ rendererFactory := func(variableTypeRef int, variablePath []string) (resolve.VariableRenderer, error) {
+ return resolve.NewPlainVariableRendererWithValidationFromTypeRef(p.visitor.Operation, p.visitor.Definition, variableTypeRef, variablePath...)
}
- variableTypeRef := p.visitor.Operation.VariableDefinitions[variableDefinition].Type
- renderer, err := resolve.NewPlainVariableRendererWithValidationFromTypeRef(p.visitor.Operation, p.visitor.Definition, variableTypeRef, string(variableName))
+ segments, err := parser.Parse(subject, rendererFactory)
if err != nil {
- return "", err
+ return "", fmt.Errorf("failed to parse event subject template %q: %w", subject, err)
+ }
+
+ var builder strings.Builder
+ var seenVariable bool
+ for _, segment := range segments {
+ switch segment.SegmentType {
+ case resolve.StaticSegmentType:
+ builder.Write(segment.Data)
+ case resolve.VariableSegmentType:
+ seenVariable = true
+ contextVariable := &resolve.ContextVariable{
+ Path: segment.VariableSourcePath,
+ Renderer: segment.Renderer,
+ }
+ placeholder, _ := p.variables.AddVariable(contextVariable)
+ builder.WriteString(placeholder)
+ default:
+ return "", fmt.Errorf("unexpected segment type %d in subject template %q", segment.SegmentType, subject)
+ }
}
- contextVariable := &resolve.ContextVariable{
- Path: []string{string(variableName)},
- Renderer: renderer,
+ if !seenVariable {
+ return "", fmt.Errorf("subject %q must contain at least one argument template", subject)
}
- // We need to replace the template literal with the variable placeholder (and reuse if it already exists)
- variablePlaceHolder, _ := p.variables.AddVariable(contextVariable) // $$0$$
- return eventSubjectRegex.ReplaceAllLiteralString(subject, variablePlaceHolder), nil
+ return builder.String(), nil
}
func (p *NatsEventManager) eventDataBytes(ref int) ([]byte, error) {
diff --git a/tmp/agent-patch-flux-pr-817.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/app/v2/pkg/engine/datasource/pubsub_datasource/pubsub_datasource.go b/app/v2/pkg/engine/datasource/pubsub_datasource/pubsub_datasource.go
index 02b05d3..4c62615 100644
--- a/tmp/agent-patch-flux-pr-817.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/app/v2/pkg/engine/datasource/pubsub_datasource/pubsub_datasource.go
+++ b/app/v2/pkg/engine/datasource/pubsub_datasource/pubsub_datasource.go
@@ -5,7 +5,6 @@ import (
"context"
"encoding/json"
"fmt"
- "regexp"
"strings"
"github.com/jensneuse/abstractlogger"
@@ -23,8 +22,6 @@ const (
EventTypeSubscribe EventType = "subscribe"
)
-var eventSubjectRegex = regexp.MustCompile(`{{ args.([a-zA-Z0-9_]+) }}`)
-
func EventTypeFromString(s string) (EventType, error) {
et := EventType(strings.ToLower(s))
switch et {
diff --git a/app/v2/pkg/engine/plan/argument_template_parser.go b/app/v2/pkg/engine/plan/argument_template_parser.go
new file mode 100644
index 0000000..39dfd92
--- /dev/null
+++ b/app/v2/pkg/engine/plan/argument_template_parser.go
@@ -0,0 +1,219 @@
+package plan
+
+import (
+ "fmt"
+ "regexp"
+ "strings"
+
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/ast"
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/astvisitor"
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/engine/resolve"
+)
+
+var argumentTemplateRegex = regexp.MustCompile(`{{\s*args((?:\.[a-zA-Z0-9_]+)+)\s*}}`)
+
+// VariableRendererFactory returns a renderer for a context variable identified by its type and path.
+type VariableRendererFactory func(variableTypeRef int, variablePath []string) (resolve.VariableRenderer, error)
+
+// ArgumentTemplateParser parses GraphQL argument templates that target field arguments.
+type ArgumentTemplateParser struct {
+ operation *ast.Document
+ definition *ast.Document
+ walker *astvisitor.Walker
+ fieldRef int
+}
+
+// NewArgumentTemplateParser builds a parser for the provided field.
+func NewArgumentTemplateParser(operation, definition *ast.Document, walker *astvisitor.Walker, fieldRef int) *ArgumentTemplateParser {
+ return &ArgumentTemplateParser{
+ operation: operation,
+ definition: definition,
+ walker: walker,
+ fieldRef: fieldRef,
+ }
+}
+
+// Parse scans value for argument templates and returns a segment list suitable for InputTemplate usage.
+// rendererFactory constructs a renderer for each detected variable segment. If nil, a plain renderer is used.
+func (p *ArgumentTemplateParser) Parse(value string, rendererFactory VariableRendererFactory) ([]resolve.TemplateSegment, error) {
+ if rendererFactory == nil {
+ rendererFactory = func(variableTypeRef int, variablePath []string) (resolve.VariableRenderer, error) {
+ return resolve.NewPlainVariableRenderer(), nil
+ }
+ }
+
+ payload := []byte(value)
+ matches := argumentTemplateRegex.FindAllSubmatchIndex(payload, -1)
+ if len(matches) == 0 {
+ return []resolve.TemplateSegment{{SegmentType: resolve.StaticSegmentType, Data: append([]byte(nil), payload...)}}, nil
+ }
+
+ segments := make([]resolve.TemplateSegment, 0, len(matches)*2)
+ last := 0
+
+ for _, match := range matches {
+ start, end := match[0], match[1]
+ if start > last {
+ segments = append(segments, resolve.TemplateSegment{SegmentType: resolve.StaticSegmentType, Data: append([]byte(nil), payload[last:start]...)})
+ }
+
+ segment, err := p.buildVariableSegment(payload[start:end], payload[match[2]:match[3]], rendererFactory)
+ if err != nil {
+ return nil, err
+ }
+ segments = append(segments, segment)
+ last = end
+ }
+
+ if last < len(payload) {
+ segments = append(segments, resolve.TemplateSegment{SegmentType: resolve.StaticSegmentType, Data: append([]byte(nil), payload[last:]...)})
+ }
+
+ return segments, nil
+}
+
+func (p *ArgumentTemplateParser) buildVariableSegment(literal []byte, pathBytes []byte, rendererFactory VariableRendererFactory) (resolve.TemplateSegment, error) {
+ argumentPath := strings.Split(string(pathBytes[1:]), ".")
+ if len(argumentPath) == 0 {
+ return resolve.TemplateSegment{}, fmt.Errorf("argument template %q is incomplete", literal)
+ }
+
+ argumentName := argumentPath[0]
+ argumentRef, ok := p.operation.FieldArgument(p.fieldRef, []byte(argumentName))
+ if !ok {
+ return resolve.TemplateSegment{}, fmt.Errorf("argument template %q references undefined argument %q", literal, argumentName)
+ }
+
+ argumentValue := p.operation.ArgumentValue(argumentRef)
+ if argumentValue.Kind != ast.ValueKindVariable {
+ return resolve.TemplateSegment{}, fmt.Errorf("argument template %q expects argument %q to be a variable", literal, argumentName)
+ }
+
+ variableName := p.operation.VariableValueNameString(argumentValue.Ref)
+ variableDefRef, ok := p.operation.VariableDefinitionByNameAndOperation(p.walker.Ancestors[0].Ref, []byte(variableName))
+ if !ok {
+ return resolve.TemplateSegment{}, fmt.Errorf("argument template %q references unknown variable %q", literal, variableName)
+ }
+
+ variableTypeRef := p.operation.VariableDefinitions[variableDefRef].Type
+ nestedSegments := argumentPath[1:]
+ fullPath := "args." + strings.Join(argumentPath, ".")
+
+ if err := p.validateArgumentPath(variableTypeRef, nestedSegments, fullPath); err != nil {
+ return resolve.TemplateSegment{}, err
+ }
+
+ variablePath := make([]string, 0, 1+len(nestedSegments))
+ variablePath = append(variablePath, variableName)
+ variablePath = append(variablePath, nestedSegments...)
+
+ renderer, err := rendererFactory(variableTypeRef, variablePath)
+ if err != nil {
+ return resolve.TemplateSegment{}, fmt.Errorf("argument template %q: %w", literal, err)
+ }
+ if renderer == nil {
+ renderer = resolve.NewPlainVariableRenderer()
+ }
+
+ return resolve.TemplateSegment{
+ SegmentType: resolve.VariableSegmentType,
+ VariableKind: resolve.ContextVariableKind,
+ VariableSourcePath: append([]string(nil), variablePath...),
+ Renderer: renderer,
+ }, nil
+}
+
+func (p *ArgumentTemplateParser) validateArgumentPath(variableTypeRef int, nestedSegments []string, fullPath string) error {
+ if len(nestedSegments) == 0 {
+ if !isLeafType(p.operation, p.definition, variableTypeRef) {
+ return fmt.Errorf("argument template %s must resolve to a leaf type", fullPath)
+ }
+ return nil
+ }
+
+ currentNode, err := p.inputObjectNodeFromOperationType(variableTypeRef, fullPath)
+ if err != nil {
+ return fmt.Errorf("argument template %s: %w", fullPath, err)
+ }
+
+ for idx, segment := range nestedSegments {
+ fieldRef, ok := p.definition.NodeInputFieldDefinitionByName(currentNode, []byte(segment))
+ if !ok {
+ return fmt.Errorf("argument template %s references unknown field %q", fullPath, segment)
+ }
+ fieldTypeRef := p.definition.InputValueDefinitions[fieldRef].Type
+ if idx == len(nestedSegments)-1 {
+ if !isLeafType(p.definition, p.definition, fieldTypeRef) {
+ return fmt.Errorf("argument template %s must resolve to a leaf type", fullPath)
+ }
+ break
+ }
+ nextNode, err := p.inputObjectNodeFromDefinitionType(fieldTypeRef)
+ if err != nil {
+ return fmt.Errorf("argument template %s: %w", fullPath, err)
+ }
+ currentNode = nextNode
+ }
+
+ return nil
+}
+
+func (p *ArgumentTemplateParser) inputObjectNodeFromOperationType(typeRef int, fullPath string) (ast.Node, error) {
+ baseRef := unwrapNonNull(p.operation, typeRef)
+ if baseRef >= len(p.operation.Types) {
+ return ast.Node{}, fmt.Errorf("argument template %s references invalid type", fullPath)
+ }
+ if p.operation.Types[baseRef].TypeKind == ast.TypeKindList {
+ return ast.Node{}, fmt.Errorf("argument template %s references a list type", fullPath)
+ }
+ if p.operation.Types[baseRef].TypeKind != ast.TypeKindNamed {
+ return ast.Node{}, fmt.Errorf("argument template %s references a non-named type", fullPath)
+ }
+ typeName := p.operation.TypeNameString(baseRef)
+ node, ok := p.definition.Index.FirstNodeByNameStr(typeName)
+ if !ok || node.Kind != ast.NodeKindInputObjectTypeDefinition {
+ return ast.Node{}, fmt.Errorf("argument template %s references non-input object type %s", fullPath, typeName)
+ }
+ return node, nil
+}
+
+func (p *ArgumentTemplateParser) inputObjectNodeFromDefinitionType(typeRef int) (ast.Node, error) {
+ baseRef := unwrapNonNull(p.definition, typeRef)
+ if baseRef >= len(p.definition.Types) {
+ return ast.Node{}, fmt.Errorf("type reference %d is invalid", typeRef)
+ }
+ if p.definition.Types[baseRef].TypeKind == ast.TypeKindList {
+ return ast.Node{}, fmt.Errorf("type %s is a list", p.definition.TypeNameString(baseRef))
+ }
+ if p.definition.Types[baseRef].TypeKind != ast.TypeKindNamed {
+ return ast.Node{}, fmt.Errorf("type reference is not a named type")
+ }
+ typeName := p.definition.TypeNameString(baseRef)
+ node, ok := p.definition.Index.FirstNodeByNameStr(typeName)
+ if !ok || node.Kind != ast.NodeKindInputObjectTypeDefinition {
+ return ast.Node{}, fmt.Errorf("type %s is not an input object", typeName)
+ }
+ return node, nil
+}
+
+func unwrapNonNull(doc *ast.Document, typeRef int) int {
+ for typeRef < len(doc.Types) && doc.Types[typeRef].TypeKind == ast.TypeKindNonNull {
+ typeRef = doc.Types[typeRef].OfType
+ }
+ return typeRef
+}
+
+func isLeafType(doc, definition *ast.Document, typeRef int) bool {
+ if typeRef >= len(doc.Types) {
+ return false
+ }
+ switch doc.Types[typeRef].TypeKind {
+ case ast.TypeKindNonNull:
+ return isLeafType(doc, definition, doc.Types[typeRef].OfType)
+ case ast.TypeKindList:
+ return isLeafType(doc, definition, doc.Types[typeRef].OfType)
+ case ast.TypeKindNamed:
+ return doc.TypeIsScalar(typeRef, definition) || doc.TypeIsEnum(typeRef, definition)
+ }
+ return false
+}
diff --git a/app/v2/pkg/engine/plan/argument_template_parser_test.go b/app/v2/pkg/engine/plan/argument_template_parser_test.go
new file mode 100644
index 0000000..c197ba3
--- /dev/null
+++ b/app/v2/pkg/engine/plan/argument_template_parser_test.go
@@ -0,0 +1,95 @@
+package plan
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/ast"
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/astnormalization"
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/asttransform"
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/astvalidation"
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/astvisitor"
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/engine/resolve"
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/internal/unsafeparser"
+ "github.com/wundergraph/graphql-go-tools/v2/pkg/operationreport"
+)
+
+func setupArgumentTemplateParser(t *testing.T) *ArgumentTemplateParser {
+ t.Helper()
+
+ const schema = `schema { query: Query }
+ type Query {
+ hero(filter: Input!, second: String!): String
+ }
+
+ input Input {
+ nested: NestedInput!
+ }
+
+ input NestedInput {
+ name: String!
+ }
+ `
+ const operation = `query Template($a: Input!, $b: String!) {
+ hero(filter: $a, second: $b)
+ }`
+
+ def := unsafeparser.ParseGraphqlDocumentString(schema)
+ op := unsafeparser.ParseGraphqlDocumentString(operation)
+ require.NoError(t, asttransform.MergeDefinitionWithBaseSchema(&def))
+
+ var report operationreport.Report
+ normalizer := astnormalization.NewNormalizer(true, true)
+ normalizer.NormalizeOperation(&op, &def, &report)
+ validator := astvalidation.DefaultOperationValidator()
+ validator.Validate(&op, &def, &report)
+ require.False(t, report.HasErrors(), "operation did not validate: %v", report.Error())
+
+ walker := &astvisitor.Walker{
+ Ancestors: []ast.Node{
+ {
+ Kind: ast.NodeKindOperationDefinition,
+ Ref: 0,
+ },
+ },
+ }
+
+ selectionSet := op.OperationDefinitions[0].SelectionSet
+ fieldSelections := op.SelectionSetFieldSelections(selectionSet)
+ require.Len(t, fieldSelections, 1)
+
+ return NewArgumentTemplateParser(&op, &def, walker, fieldSelections[0])
+}
+
+func TestArgumentTemplateParser_MultipleTemplates(t *testing.T) {
+ parser := setupArgumentTemplateParser(t)
+
+ segments, err := parser.Parse("topic.{{ args.filter.nested.name }}.{{ args.second }}.suffix", nil)
+ require.NoError(t, err)
+ require.Len(t, segments, 5)
+
+ require.Equal(t, []byte("topic."), segments[0].Data)
+ require.Equal(t, resolve.VariableSegmentType, segments[1].SegmentType)
+ require.Equal(t, []string{"a", "nested", "name"}, segments[1].VariableSourcePath)
+ require.NotNil(t, segments[1].Renderer)
+
+ require.Equal(t, []byte("."), segments[2].Data)
+ require.Equal(t, resolve.VariableSegmentType, segments[3].SegmentType)
+ require.Equal(t, []string{"b"}, segments[3].VariableSourcePath)
+ require.NotNil(t, segments[3].Renderer)
+
+ require.Equal(t, []byte(".suffix"), segments[4].Data)
+}
+
+func TestArgumentTemplateParser_InvalidPathFails(t *testing.T) {
+ parser := setupArgumentTemplateParser(t)
+
+ _, err := parser.Parse("{{ args.filter }}", nil)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "must resolve to a leaf type")
+
+ _, err = parser.Parse("{{ args.filter.missing }}", nil)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "unknown field")
+}
diff --git a/tmp/agent-patch-flux-pr-817.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/app/v2/pkg/engine/plan/configuration_visitor.go b/app/v2/pkg/engine/plan/configuration_visitor.go
index b5e2ed1..512e89e 100644
--- a/tmp/agent-patch-flux-pr-817.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/app/v2/pkg/engine/plan/configuration_visitor.go
+++ b/app/v2/pkg/engine/plan/configuration_visitor.go
@@ -1,9 +1,7 @@
package plan
import (
- "bytes"
"fmt"
- "regexp"
"slices"
"strings"
@@ -870,85 +868,19 @@ func (c *configurationVisitor) buildSubscriptionFilterCondition(condition Subscr
return filter
}
-var (
- // subscriptionFieldFilterRegex is used to extract the variable name from the subscription filter condition
- // e.g. {{ args.id }} -> id
- // e.g. {{ args.input.id }} -> input.id
- subscriptionFieldFilterRegex = regexp.MustCompile(`{{\s*args((?:\.[a-zA-Z0-9_]+)+)\s*}}`)
-)
-
-// ContainsTemplateString checks if the value contains a template string
-func ContainsTemplateString(value []byte) bool {
- return bytes.Contains(value, []byte("{{"))
-}
-
func (c *configurationVisitor) buildSubscriptionFieldFilter(condition *SubscriptionFieldCondition) *resolve.SubscriptionFieldFilter {
- filter := &resolve.SubscriptionFieldFilter{}
- filter.FieldPath = condition.FieldPath
- filter.Values = make([]resolve.InputTemplate, len(condition.Values))
+ filter := &resolve.SubscriptionFieldFilter{
+ FieldPath: condition.FieldPath,
+ Values: make([]resolve.InputTemplate, len(condition.Values)),
+ }
+ parser := NewArgumentTemplateParser(c.operation, c.definition, c.walker, c.fieldRef)
for i, value := range condition.Values {
- matches := subscriptionFieldFilterRegex.FindAllStringSubmatchIndex(value, -1)
- if len(matches) == 0 {
- filter.Values[i].Segments = []resolve.TemplateSegment{
- {
- SegmentType: resolve.StaticSegmentType,
- Data: []byte(value),
- },
- }
- continue
+ segments, err := parser.Parse(value, nil)
+ if err != nil {
+ c.walker.StopWithInternalErr(fmt.Errorf("failed to parse subscription filter template %q: %w", value, err))
+ return nil
}
- if len(matches) == 1 && len(matches[0]) == 4 {
- prefix := value[:matches[0][0]]
- hasPrefix := len(prefix) > 0
- // the path begins with ".", so ignore the first empty string element with trailing [1:]
- argumentPath := strings.Split(value[matches[0][2]:matches[0][3]][1:], ".")
- argumentName := argumentPath[0]
- argumentRef, ok := c.operation.FieldArgument(c.fieldRef, []byte(argumentName))
- if !ok {
- c.walker.StopWithInternalErr(fmt.Errorf(`field argument "%s" is not defined`, argumentName))
- return nil
- }
- argumentValue := c.operation.ArgumentValue(argumentRef)
- if argumentValue.Kind != ast.ValueKindVariable {
- c.walker.StopWithInternalErr(fmt.Errorf(`expected argument "%s" kind to be "ValueKindVariable" but received "%s"`, argumentName, argumentValue.Kind))
- return nil
- }
- variableName := c.operation.VariableValueNameString(argumentValue.Ref)
- // the variable path should be the variable name, e.g., "a", and then the 2nd element from the path onwards
- variablePath := append([]string{variableName}, argumentPath[1:]...)
- suffix := value[matches[0][1]:]
- hasSuffix := len(suffix) > 0
- size := 1
- if hasPrefix {
- size++
- }
- if hasSuffix {
- size++
- }
- filter.Values[i].Segments = make([]resolve.TemplateSegment, size)
- idx := 0
- if hasPrefix {
- filter.Values[i].Segments[idx] = resolve.TemplateSegment{
- SegmentType: resolve.StaticSegmentType,
- Data: []byte(prefix),
- }
- idx++
- }
- filter.Values[i].Segments[idx] = resolve.TemplateSegment{
- SegmentType: resolve.VariableSegmentType,
- VariableKind: resolve.ContextVariableKind,
- Renderer: resolve.NewPlainVariableRenderer(),
- VariableSourcePath: variablePath,
- }
- if hasSuffix {
- filter.Values[i].Segments[idx+1] = resolve.TemplateSegment{
- SegmentType: resolve.StaticSegmentType,
- Data: []byte(suffix),
- }
- }
- continue
- }
- return nil
+ filter.Values[i].Segments = segments
}
return filter
}
diff --git a/tmp/agent-patch-flux-pr-817.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/app/v2/pkg/engine/plan/subscription_filter_test.go b/app/v2/pkg/engine/plan/subscription_filter_test.go
index d6c51d9..1301ef5 100644
--- a/tmp/agent-patch-flux-pr-817.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/app/v2/pkg/engine/plan/subscription_filter_test.go
+++ b/app/v2/pkg/engine/plan/subscription_filter_test.go
@@ -262,7 +262,7 @@ func TestPlanSubscriptionFilter(t *testing.T) {
},
))
- t.Run("subscription with IN field invalid filter multiple templates", test(
+ t.Run("subscription with IN field multi template filter", test(
schema, `
subscription { heroByID(id: "1") { id name } }
`, "",
@@ -271,6 +271,33 @@ func TestPlanSubscriptionFilter(t *testing.T) {
Trigger: resolve.GraphQLSubscriptionTrigger{
Input: []byte{},
},
+ Filter: &resolve.SubscriptionFilter{
+ In: &resolve.SubscriptionFieldFilter{
+ FieldPath: []string{"id"},
+ Values: []resolve.InputTemplate{
+ {
+ Segments: []resolve.TemplateSegment{
+ {
+ SegmentType: resolve.VariableSegmentType,
+ VariableKind: resolve.ContextVariableKind,
+ VariableSourcePath: []string{"a"},
+ Renderer: resolve.NewPlainVariableRenderer(),
+ },
+ {
+ SegmentType: resolve.StaticSegmentType,
+ Data: []byte("."),
+ },
+ {
+ SegmentType: resolve.VariableSegmentType,
+ VariableKind: resolve.ContextVariableKind,
+ VariableSourcePath: []string{"a"},
+ Renderer: resolve.NewPlainVariableRenderer(),
+ },
+ },
+ },
+ },
+ },
+ },
Response: &resolve.GraphQLResponse{
Data: &resolve.Object{
Fields: []*resolve.Field{
@@ -318,7 +345,7 @@ func TestPlanSubscriptionFilter(t *testing.T) {
SubscriptionFilterCondition: &SubscriptionFilterCondition{
In: &SubscriptionFieldCondition{
FieldPath: []string{"id"},
- Values: []string{"{{ args.a }}.{{ args.b }}"},
+ Values: []string{"{{ args.id }}.{{ args.id }}"},
},
},
},