Skip to content

feat: add Body scope for request body matching in ApisixRoute#413

Open
AlinsRan wants to merge 6 commits intomasterfrom
feat/body-scope-matching
Open

feat: add Body scope for request body matching in ApisixRoute#413
AlinsRan wants to merge 6 commits intomasterfrom
feat/body-scope-matching

Conversation

@AlinsRan
Copy link
Copy Markdown
Contributor

@AlinsRan AlinsRan commented May 9, 2026

Summary

  • Add ScopeBody = "Body" constant to ApisixRouteHTTPMatchExprSubject.Scope
  • Translates to APISIX's post_arg.* variable, supporting application/json, application/x-www-form-urlencoded, and multipart/form-data
  • Supports dot-notation JSON path in Name field (e.g. model.version, messages[*].role)
  • Update error message and inline documentation to reflect the new scope

Usage

```yaml
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: body-match-route
spec:
http:

  • name: rule1
    match:
    paths: ["/api/submit"]
    methods: ["POST"]
    exprs:

    Match a simple form/JSON field

    • subject:
      scope: Body
      name: action
      op: Equal
      value: "login"

    Match a nested JSON path

    • subject:
      scope: Body
      name: model.version
      op: Equal
      value: "gpt-4"
      backends:
    • serviceName: my-service
      servicePort: 80
      ```

Test Plan

  • TestToVars_ScopeBody_SimpleField — simple field maps to post_arg.action
  • TestToVars_ScopeBody_NestedJSONPath — dot-notation maps to post_arg.model.version
  • TestToVars_ScopeBody_EmptyName_ReturnsError — empty Name returns error

Closes #399

Summary by CodeRabbit

  • New Features

    • Support matching HTTP request body fields in route expressions, including dot-notation JSON paths (e.g., model.version) mapped to POST variables (post_arg.).
  • Documentation / Validation

    • CRD/schema and docs updated to document Body scope, enumerate allowed scopes, and require a name when scope is not Path.
  • Tests

    • Added unit and end-to-end tests covering body-based matches for form and JSON payloads.

Review Change Stack

…y matching

Add ScopeBody to ApisixRouteHTTPMatchExprSubject.Scope, which maps to
APISIX's post_arg.* variable. This supports request body matching for
application/json, application/x-www-form-urlencoded, and multipart/form-data
content types, and allows dot-notation JSON path expressions such as
'model.version' and 'messages[*].role'.

Closes #399
Copilot AI review requested due to automatic review settings May 9, 2026 05:54
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Body scope to HTTP match expressions: defines ScopeBody, extends type and CRD validation to include Body and require name when appropriate, implements ToVars mapping to post_arg.<name>, and adds unit and e2e tests covering simple, nested, and error cases.

Changes

Body Matching in HTTP Match Expressions

Layer / File(s) Summary
Scope Constant
api/v2/shared_types.go
Adds ScopeBody constant with docs describing dot-notation JSON path support and mapping to APISIX post_arg.*.
Types / CRD Validation
api/v2/apisixroute_types.go, config/crd/bases/apisix.apache.org_apisixroutes.yaml, docs/en/latest/reference/api-reference.md
ApisixRouteHTTPMatchExprSubject enum and docs include Body; CRD schema scope enum expanded and x-kubernetes-validations added to require name when scope != Path.
ToVars Implementation
api/v2/apisixroute_types.go
ApisixRouteHTTPMatchExprs.ToVars adds ScopeBody case that maps subject names to post_arg.<name>; invalid-scope error text updated to include Body.
Unit Tests
api/v2/apisixroute_types_test.go
Adds CEL/XValidation rule evaluation and ToVars() tests for ScopeBody: simple field, nested JSON path, and empty-name error; includes a strPtr helper.
End-to-end Tests
test/e2e/crds/v2/route.go
Adds two e2e cases exercising body-scoped matches for urlencoded form and JSON POST bodies with positive and negative assertions.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant APISIX
  participant Controller
  Client->>APISIX: send POST (form/json) with body fields
  APISIX->>Controller: evaluate route vars (post_arg.*)
  Controller->>APISIX: match decision (uses ToVars mapping to post_arg.<name>)
  APISIX-->>Client: respond 200 (match) or 404 (no match)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • shreemaan-abhishek
🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main feature: adding Body scope for request body matching in ApisixRoute, directly matching the primary change across all modified files.
Linked Issues check ✅ Passed The PR fulfills issue #399 objectives by introducing ScopeBody support, enabling request body matching through post_arg mapping, supporting dot-notation JSON paths, and including required validation and documentation.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing Body scope support: type definitions, validation rules, tests, CRD updates, documentation, and e2e tests—no unrelated modifications detected.
E2e Test Quality Review ✅ Passed E2E tests cover form/JSON body matching with dot-notation paths. Error handling complete. Tests well-named and readable. No race conditions. PR solves issue #399.
Security Check ✅ Passed Security check passed. No data exposure, unencrypted secrets, authorization bypasses, access violations, TLS misconfigurations, or missing secret resolution detected in Body scope addition.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/body-scope-matching

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
api/v2/apisixroute_types_test.go (1)

52-70: ⚡ Quick win

Add a wildcard-path body test to lock in the documented behavior.

You document messages[*].role; adding a direct test for that path will prevent regressions.

🧪 Suggested test addition
 func TestToVars_ScopeBody_NestedJSONPath(t *testing.T) {
@@
 	assert.Equal(t, "post_arg.model.version", vars[0][0].StrVal)
 }
+
+func TestToVars_ScopeBody_ArrayWildcardJSONPath(t *testing.T) {
+	exprs := ApisixRouteHTTPMatchExprs{
+		{
+			Subject: ApisixRouteHTTPMatchExprSubject{
+				Scope: ScopeBody,
+				Name:  "messages[*].role",
+			},
+			Op:    OpEqual,
+			Value: strPtr("user"),
+		},
+	}
+
+	vars, err := exprs.ToVars()
+	require.NoError(t, err)
+	require.Len(t, vars, 1)
+	assert.Equal(t, "post_arg.messages[*].role", vars[0][0].StrVal)
+	assert.Equal(t, "==", vars[0][1].StrVal)
+	assert.Equal(t, "user", vars[0][2].StrVal)
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v2/apisixroute_types_test.go` around lines 52 - 70, The test suite lacks
a case for wildcard JSONPath body fields (e.g., messages[*].role) to lock in the
documented behavior; add a new test similar to
TestToVars_ScopeBody_NestedJSONPath that builds an ApisixRouteHTTPMatchExprs
with Subject.Scope = ScopeBody and Subject.Name = "messages[*].role", call
exprs.ToVars(), require no error and correct length, and assert the produced var
maps the wildcard to the expected post_arg path (e.g.,
"post_arg.messages.*.role") so the dot/wildcard conversion logic in ToVars is
covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@api/v2/apisixroute_types.go`:
- Line 316: Update the validation error message that mentions subject.scope so
it uses the canonical capitalized scope literals: replace the lowercase list
"[query, header, cookie, path, variable, body]" with "[Query, Header, Cookie,
Path, Variable, Body]" in the error returned (the line that currently returns
errors.New("invalid http match expr: subject.scope should be one of [...]")) so
users see the exact accepted enum values for subject.scope.

---

Nitpick comments:
In `@api/v2/apisixroute_types_test.go`:
- Around line 52-70: The test suite lacks a case for wildcard JSONPath body
fields (e.g., messages[*].role) to lock in the documented behavior; add a new
test similar to TestToVars_ScopeBody_NestedJSONPath that builds an
ApisixRouteHTTPMatchExprs with Subject.Scope = ScopeBody and Subject.Name =
"messages[*].role", call exprs.ToVars(), require no error and correct length,
and assert the produced var maps the wildcard to the expected post_arg path
(e.g., "post_arg.messages.*.role") so the dot/wildcard conversion logic in
ToVars is covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8c45306f-e298-4c06-a11f-bc4608c33330

📥 Commits

Reviewing files that changed from the base of the PR and between 85ae1bb and 3f1f3f0.

📒 Files selected for processing (3)
  • api/v2/apisixroute_types.go
  • api/v2/apisixroute_types_test.go
  • api/v2/shared_types.go

Comment thread api/v2/apisixroute_types.go Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class request-body matching support to the ApisixRoute HTTP match expression model by introducing a new Body scope and translating it into APISIX vars using post_arg.* semantics.

Changes:

  • Introduces ScopeBody = "Body" in the match expression subject scope constants.
  • Extends ApisixRouteHTTPMatchExprs.ToVars() to translate ScopeBody into post_arg.<name> subjects and updates the invalid-scope error message/docs.
  • Adds unit tests covering body scope translation and validation.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
api/v2/shared_types.go Adds ScopeBody constant and inline documentation describing body-scope behavior.
api/v2/apisixroute_types.go Adds ScopeBody handling in ToVars() and updates match-subject documentation/error message.
api/v2/apisixroute_types_test.go Adds tests validating body-scope translation and empty-name error behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread api/v2/shared_types.go Outdated
Comment on lines +91 to +92
// and maps to APISIX's post_arg.* variable, which supports application/json,
// application/x-www-form-urlencoded, and multipart/form-data content types.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: comment updated to post_arg.<name> to accurately reflect the implementation.

Comment thread api/v2/apisixroute_types.go Outdated
subj = "post_arg." + expr.Subject.Name
default:
return result, errors.New("invalid http match expr: subject.scope should be one of [query, header, cookie, path, variable]")
return result, errors.New("invalid http match expr: subject.scope should be one of [query, header, cookie, path, variable, body]")
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: error message now uses the canonical capitalized scope values.

Comment thread api/v2/apisixroute_types.go Outdated
// "messages[*].role") and maps to APISIX's post_arg.* variable, which works with
// application/json, application/x-www-form-urlencoded, and multipart/form-data.
Scope string `json:"scope" yaml:"scope"`
// Name is the name of the header or query parameter.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: Name field comment updated to describe it generically across all scopes, with scope-specific behavior noted.

- Add +kubebuilder:validation:Enum marker to Scope field (Header/Query/Path/Cookie/Variable/Body)
- Add +kubebuilder:validation:XValidation CEL rule enforcing Name is required
  when Scope is not Path: 'self.scope == "Path" || self.name != ""'
- Regenerate CRD YAML with the new enum and x-kubernetes-validations rules
- Add unit tests verifying the CEL expression correctness via cel-go evaluation
- Add e2e tests for Body scope: urlencoded form field matching and JSON body matching
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
api/v2/apisixroute_types_test.go (1)

28-50: ⚡ Quick win

Avoid duplicating the CEL rule string in the test.

This test hardcodes the rule instead of checking the generated schema or a shared source of truth, so it can pass while the shipped CRD drifts. That already happened in this PR: this constant is valid, but the generated CRD contains a different quote character.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v2/apisixroute_types_test.go` around lines 28 - 50, The test duplicates
the CEL expression in the local const celSubjectRule; instead remove that
hardcoded constant and have evalCELSubjectRule obtain the rule dynamically from
the source of truth (the ApisixRouteHTTPMatchExprSubject validation metadata) so
the test tracks the CRD. Concretely: delete celSubjectRule, update
evalCELSubjectRule to read the XValidation/OpenAPI validation string for
ApisixRouteHTTPMatchExprSubject (via the generated CRD/OpenAPI schema or the
kubebuilder tag on the ApisixRouteHTTPMatchExprSubject type) and use that value
when env.Compile is called, keeping the rest of evalCELSubjectRule unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@config/crd/bases/apisix.apache.org_apisixroutes.yaml`:
- Around line 229-235: The schema currently lists "name" inside the required
array which forces all subjects to include name; remove "name" from the required
list under the object schema so the x-kubernetes-validations CEL rule
(x-kubernetes-validations -> rule: self.scope == 'Path' || self.name != '') can
actually allow omission for scope == 'Path'; also fix the rule quoting to use
regular single quotes and an empty-string comparison (self.name != '') so the
expression is valid.
- Around line 233-235: The CEL validation rule under x-kubernetes-validations
uses a typographic smart quote at the end of the expression, breaking the rule;
update the rule for the validation entry (x-kubernetes-validations) so the
expression reads self.scope == 'Path' || self.name != '' (replace the smart
quote “ ” with standard single quotes), ensuring the rule in the CRD (the rule
field referencing self.scope and self.name) uses proper ASCII single quotes.

---

Nitpick comments:
In `@api/v2/apisixroute_types_test.go`:
- Around line 28-50: The test duplicates the CEL expression in the local const
celSubjectRule; instead remove that hardcoded constant and have
evalCELSubjectRule obtain the rule dynamically from the source of truth (the
ApisixRouteHTTPMatchExprSubject validation metadata) so the test tracks the CRD.
Concretely: delete celSubjectRule, update evalCELSubjectRule to read the
XValidation/OpenAPI validation string for ApisixRouteHTTPMatchExprSubject (via
the generated CRD/OpenAPI schema or the kubebuilder tag on the
ApisixRouteHTTPMatchExprSubject type) and use that value when env.Compile is
called, keeping the rest of evalCELSubjectRule unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 174c79e5-0e48-4d68-b694-757fd8be2018

📥 Commits

Reviewing files that changed from the base of the PR and between 3f1f3f0 and a653ef9.

📒 Files selected for processing (4)
  • api/v2/apisixroute_types.go
  • api/v2/apisixroute_types_test.go
  • config/crd/bases/apisix.apache.org_apisixroutes.yaml
  • test/e2e/crds/v2/route.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/v2/apisixroute_types.go

Comment thread config/crd/bases/apisix.apache.org_apisixroutes.yaml Outdated
Comment thread config/crd/bases/apisix.apache.org_apisixroutes.yaml Outdated
Copilot AI review requested due to automatic review settings May 9, 2026 06:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

config/crd/bases/apisix.apache.org_apisixroutes.yaml:231

  • The schema/doc says name is "Required for all scopes except Path", but this CRD still marks name as always required (required: [name, scope]) and allows empty string (no MinLength/XValidation). This is inconsistent with the docs and will allow manifests that pass CRD validation but later fail controller validation (e.g., Body/Header/etc with empty name). Consider making name optional (omitempty) and adding an XValidation rule like self.scope == 'Path' || self.name != '', or adjust the description to match the actual schema constraints.
                                  name:
                                    description: |-
                                      Name is the name of the header, query parameter, cookie, variable, or body field.
                                      Required for all scopes except Path.
                                    type: string
                                  scope:
                                    description: |-
                                      Scope specifies the subject scope.
                                      Supported values: `Header`, `Query`, `Path`, `Cookie`, `Variable`, `Body`.
                                      When Scope is `Path`, Name will be ignored.
                                      When Scope is `Body`, Name supports dot-notation JSON path (e.g., "model.version",
                                      "messages[*].role") and maps to APISIX's post_arg.* variable, which works with
                                      application/json, application/x-www-form-urlencoded, and multipart/form-data.
                                    enum:
                                    - Header
                                    - Query
                                    - Path
                                    - Cookie
                                    - Variable
                                    - Body
                                    type: string
                                required:
                                - name
                                - scope

Comment thread api/v2/apisixroute_types.go Outdated
Comment on lines 414 to 426
// ApisixRouteHTTPMatchExprSubject describes the subject of a route matching expression.
type ApisixRouteHTTPMatchExprSubject struct {
// Scope specifies the subject scope and can be `Header`, `Query`, or `Path`.
// Scope specifies the subject scope.
// Supported values: `Header`, `Query`, `Path`, `Cookie`, `Variable`, `Body`.
// When Scope is `Path`, Name will be ignored.
// When Scope is `Body`, Name supports dot-notation JSON path (e.g., "model.version",
// "messages[*].role") and maps to APISIX's post_arg.* variable, which works with
// application/json, application/x-www-form-urlencoded, and multipart/form-data.
// +kubebuilder:validation:Enum=Header;Query;Path;Cookie;Variable;Body
Scope string `json:"scope" yaml:"scope"`
// Name is the name of the header or query parameter.
// Name is the name of the header, query parameter, cookie, variable, or body field.
// Required for all scopes except Path.
Name string `json:"name" yaml:"name"`
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditional enforcement via CEL XValidation was intentionally omitted. The runtime check in ToVars() returns an error when name is empty for non-Path scopes, which provides adequate protection at the controller level without adding CRD-level CEL complexity.

Comment thread test/e2e/crds/v2/route.go Outdated
}
Eventually(request).WithTimeout(20 * time.Second).ProbeEvery(time.Second).Should(Equal(http.StatusOK))

By("verify non-matching POST without action field returns 404")
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: step description updated to 'non-matching POST with wrong action value'.

Comment thread test/e2e/crds/v2/route.go Outdated
Comment on lines +342 to +364
It("Test ApisixRoute match by body vars (JSON path)", func() {
const apisixRouteSpec = `
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: default
namespace: %s
spec:
ingressClassName: %s
http:
- name: rule0
match:
paths:
- /*
methods:
- POST
exprs:
- subject:
scope: Body
name: model
op: Equal
value: gpt-4
backends:
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: test renamed to 'JSON nested path' and updated to use actual dot-notation (model.version) with a nested JSON body {"model": {"version": "gpt-4"}}.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
api/v2/apisixroute_types.go (1)

316-316: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use canonical scope literals in the invalid-scope error message.

Line 316 still lists lowercase scope names, while accepted enum values are capitalized (Query, Header, Cookie, Path, Variable, Body), which can mislead users.

💡 Suggested patch
-			return result, errors.New("invalid http match expr: subject.scope should be one of [query, header, cookie, path, variable, body]")
+			return result, errors.New("invalid http match expr: subject.scope should be one of [Query, Header, Cookie, Path, Variable, Body]")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v2/apisixroute_types.go` at line 316, Update the invalid-scope error
string so it uses the canonical, capitalized enum literals instead of lowercase;
specifically modify the error returned in the line that currently returns
errors.New("invalid http match expr: subject.scope should be one of [query,
header, cookie, path, variable, body]") to list [Query, Header, Cookie, Path,
Variable, Body] so the message matches the accepted enum values (keep the
surrounding text and use the same return of result and errors.New).
🧹 Nitpick comments (1)
api/v2/apisixroute_types_test.go (1)

52-70: ⚡ Quick win

Add a wildcard JSON-path test to match documented behavior.

Current coverage validates dotted paths, but docs/examples also claim support for paths like messages[*].role. A dedicated test here would prevent regressions in the advertised behavior.

💡 Suggested test addition
+func TestToVars_ScopeBody_WildcardJSONPath(t *testing.T) {
+	exprs := ApisixRouteHTTPMatchExprs{
+		{
+			Subject: ApisixRouteHTTPMatchExprSubject{
+				Scope: ScopeBody,
+				Name:  "messages[*].role",
+			},
+			Op:    OpEqual,
+			Value: strPtr("user"),
+		},
+	}
+
+	vars, err := exprs.ToVars()
+	require.NoError(t, err)
+	require.Len(t, vars, 1)
+	assert.Equal(t, "post_arg.messages[*].role", vars[0][0].StrVal)
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v2/apisixroute_types_test.go` around lines 52 - 70, Add a new unit test
to cover wildcard JSON-path handling: extend or add a test (e.g., near
TestToVars_ScopeBody_NestedJSONPath) that constructs an
ApisixRouteHTTPMatchExprs entry with Subject.Scope = ScopeBody and Name =
"messages[*].role", calls exprs.ToVars(), asserts no error, asserts vars length,
and verifies the produced mapping uses the expected post_arg JSON-path
translation (e.g., that the generated var string matches the documented wildcard
mapping such as "post_arg.messages[*].role" or the project's canonical
representation). This ensures ToVars correctly preserves wildcard JSON-paths for
ScopeBody.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@api/v2/apisixroute_types.go`:
- Line 316: Update the invalid-scope error string so it uses the canonical,
capitalized enum literals instead of lowercase; specifically modify the error
returned in the line that currently returns errors.New("invalid http match expr:
subject.scope should be one of [query, header, cookie, path, variable, body]")
to list [Query, Header, Cookie, Path, Variable, Body] so the message matches the
accepted enum values (keep the surrounding text and use the same return of
result and errors.New).

---

Nitpick comments:
In `@api/v2/apisixroute_types_test.go`:
- Around line 52-70: Add a new unit test to cover wildcard JSON-path handling:
extend or add a test (e.g., near TestToVars_ScopeBody_NestedJSONPath) that
constructs an ApisixRouteHTTPMatchExprs entry with Subject.Scope = ScopeBody and
Name = "messages[*].role", calls exprs.ToVars(), asserts no error, asserts vars
length, and verifies the produced mapping uses the expected post_arg JSON-path
translation (e.g., that the generated var string matches the documented wildcard
mapping such as "post_arg.messages[*].role" or the project's canonical
representation). This ensures ToVars correctly preserves wildcard JSON-paths for
ScopeBody.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f780a057-a57e-4053-9177-d5726adea1dd

📥 Commits

Reviewing files that changed from the base of the PR and between a653ef9 and 24f7e0c.

📒 Files selected for processing (3)
  • api/v2/apisixroute_types.go
  • api/v2/apisixroute_types_test.go
  • config/crd/bases/apisix.apache.org_apisixroutes.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • config/crd/bases/apisix.apache.org_apisixroutes.yaml

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 9, 2026

conformance test report - apisix-standalone mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-05-09T09:00:48Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: success
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests succeeded.
- core:
    failedTests:
    - HTTPRouteInvalidBackendRefUnknownKind
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests partially succeeded
    with 1 test skips.
- core:
    result: partial
    skippedTests:
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 10
      Skipped: 1
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 1 test skips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 9, 2026

conformance test report - apisix mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-05-09T09:00:58Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: success
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests succeeded.
- core:
    failedTests:
    - HTTPRouteInvalidBackendRefUnknownKind
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests partially succeeded
    with 1 test skips.
- core:
    result: partial
    skippedTests:
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 10
      Skipped: 1
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 1 test skips.

AlinsRan added 2 commits May 9, 2026 14:41
- Fix error message scope values to use canonical casing (Query/Header/etc.)
- Fix ScopeBody doc comment: post_arg.* -> post_arg.<name>
- Update Name field comment to be generic across all scopes
- Make Name field optional (omitempty) so scope:Path does not require name
- Fix e2e step description: 'without action field' -> 'wrong action value'
- Fix e2e JSON path test to use actual dot-notation (model.version)
Add +kubebuilder:validation:XValidation rule requiring name to be
non-empty when scope is not Path. Regenerate CRD YAML and update
CRD reference docs accordingly. All unit and CEL tests pass.
Copilot AI review requested due to automatic review settings May 9, 2026 06:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

| --- | --- |
| `scope` _string_ | Scope specifies the subject scope and can be `Header`, `Query`, or `Path`. When Scope is `Path`, Name will be ignored. |
| `name` _string_ | Name is the name of the header or query parameter. |
| `scope` _string_ | Scope specifies the subject scope. Supported values: `Header`, `Query`, `Path`, `Cookie`, `Variable`, `Body`. When Scope is `Path`, Name will be ignored. When Scope is `Body`, Name supports dot-notation JSON path (e.g., "model.version", "messages[*].role") and maps to APISIX's post_arg.<name> variable, which works with application/json, application/x-www-form-urlencoded, and multipart/form-data. |
// Supported values: `Header`, `Query`, `Path`, `Cookie`, `Variable`, `Body`.
// When Scope is `Path`, Name will be ignored.
// When Scope is `Body`, Name supports dot-notation JSON path (e.g., "model.version",
// "messages[*].role") and maps to APISIX's post_arg.<name> variable, which works with
Supported values: `Header`, `Query`, `Path`, `Cookie`, `Variable`, `Body`.
When Scope is `Path`, Name will be ignored.
When Scope is `Body`, Name supports dot-notation JSON path (e.g., "model.version",
"messages[*].role") and maps to APISIX's post_arg.<name> variable, which works with
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
api/v2/apisixroute_types_test.go (2)

131-145: ⚡ Quick win

Use require.Error before dereferencing err.Error()

At Line [144], assert.Error does not stop execution; the next line may panic if err is nil. Prefer require.Error here to make the failure deterministic.

Proposed fix
-	assert.Error(t, err)
+	require.Error(t, err)
 	assert.Contains(t, err.Error(), "empty subject.name")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v2/apisixroute_types_test.go` around lines 131 - 145, In
TestToVars_ScopeBody_EmptyName_ReturnsError, ensure the test fails fast by
asserting that err is non-nil before calling err.Error(); replace the non-fatal
assertion with a fatal one (use require.Error) for the result of exprs.ToVars()
so the subsequent call to err.Error() cannot panic if err is nil.

77-85: ⚡ Quick win

Prefer asserting the CEL rule from parsed CRD structure, not only raw text

Raw assert.Contains on the full YAML can pass even if the rule is misplaced. Consider traversing x-kubernetes-validations in the unmarshaled CRD and asserting the rule there for stronger signal.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/v2/apisixroute_types_test.go` around lines 77 - 85, The test currently
checks the CEL rule by inspecting raw := string(data); instead, unmarshal the
CRD YAML (data) into a map[string]interface{} or typed struct, traverse to the
x-kubernetes-validations field under the CRD's validation schema (e.g., locate
the map sequence at keys like "spec"/"validation"/"openAPIV3Schema"/"properties"
or directly "x-kubernetes-validations"), find the validation entry that contains
the "rule" (CEL) expression and assert that its value equals the expected
"self.scope == 'Path' || self.name != ''" (use assert.Equal or assert.Contains
on that field) and also perform the NotContains checks on that extracted rule
string to ensure no smart quotes; reference the variables/functions in the test
(data, raw, and the assertions) when making the change so the assertion targets
the parsed CRD structure rather than the raw YAML text.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@api/v2/apisixroute_types_test.go`:
- Around line 131-145: In TestToVars_ScopeBody_EmptyName_ReturnsError, ensure
the test fails fast by asserting that err is non-nil before calling err.Error();
replace the non-fatal assertion with a fatal one (use require.Error) for the
result of exprs.ToVars() so the subsequent call to err.Error() cannot panic if
err is nil.
- Around line 77-85: The test currently checks the CEL rule by inspecting raw :=
string(data); instead, unmarshal the CRD YAML (data) into a
map[string]interface{} or typed struct, traverse to the x-kubernetes-validations
field under the CRD's validation schema (e.g., locate the map sequence at keys
like "spec"/"validation"/"openAPIV3Schema"/"properties" or directly
"x-kubernetes-validations"), find the validation entry that contains the "rule"
(CEL) expression and assert that its value equals the expected "self.scope ==
'Path' || self.name != ''" (use assert.Equal or assert.Contains on that field)
and also perform the NotContains checks on that extracted rule string to ensure
no smart quotes; reference the variables/functions in the test (data, raw, and
the assertions) when making the change so the assertion targets the parsed CRD
structure rather than the raw YAML text.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4f11df85-6d61-4e59-8993-42d1cfa7249c

📥 Commits

Reviewing files that changed from the base of the PR and between 930df17 and 64ffc78.

📒 Files selected for processing (4)
  • api/v2/apisixroute_types.go
  • api/v2/apisixroute_types_test.go
  • config/crd/bases/apisix.apache.org_apisixroutes.yaml
  • docs/en/latest/reference/api-reference.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • api/v2/apisixroute_types.go
  • config/crd/bases/apisix.apache.org_apisixroutes.yaml

Replace self.name != '' with size(self.name) > 0 in the XValidation
rule. The trailing '' in a YAML plain scalar was being mangled by
kustomize during make install, producing an invalid rule. Using size()
avoids any string-literal quoting at line end. Update test to walk
the parsed YAML structure instead of raw substring match.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 9, 2026

conformance test report

apiVersion: gateway.networking.k8s.io/v1
date: "2026-05-09T09:21:54Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    failedTests:
    - GRPCExactMethodMatching
    - GRPCRouteHeaderMatching
    - GRPCRouteListenerHostnameMatching
    - GatewayModifyListeners
    result: failure
    statistics:
      Failed: 4
      Passed: 8
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests failed with 4 test failures.
- core:
    failedTests:
    - GatewayModifyListeners
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    failedTests:
    - HTTPRouteBackendProtocolWebSocket
    result: failure
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 1
      Passed: 10
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests failed with 1 test
    failures.
- core:
    failedTests:
    - GatewayModifyListeners
    - TLSRouteSimpleSameNamespace
    result: failure
    statistics:
      Failed: 2
      Passed: 9
      Skipped: 0
  name: GATEWAY-TLS
  summary: Core tests failed with 2 test failures.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

enhancement: support request body matching in Gateway API and ApisixRoute

2 participants