Skip to content

feat(api): update aip field filter types#4301

Merged
tothandras merged 4 commits into
mainfrom
feat/aip-field-filter-types
May 6, 2026
Merged

feat(api): update aip field filter types#4301
tothandras merged 4 commits into
mainfrom
feat/aip-field-filter-types

Conversation

@tothandras
Copy link
Copy Markdown
Contributor

@tothandras tothandras commented May 6, 2026

Summary by CodeRabbit

  • New Features

    • Added a standalone OpenAPI test spec and embedded test artifact; new endpoint and model for exercising field-filter scenarios.
  • Enhancements

    • Reworked field-filter shapes into explicit, granular variants and improved label parsing.
    • Removed private-only annotations from several public operations and introduced a new private-extension marker.
  • Tests / Chores

    • Added comprehensive tests for filter validation and parsing.
    • Integrated test-spec generation into the build and added a post-processing step to normalize schemas.

@tothandras tothandras requested a review from a team as a code owner May 6, 2026 12:53
@tothandras tothandras added the release-note/feature Release note: Exciting New Features label May 6, 2026
@tothandras tothandras changed the title feat(api): update aip ffeld filter types feat(api): update aip field filter types May 6, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Redesigns field-filter schemas into explicit oneOf variants, adds a TypeSpec test and generated OpenAPI test spec (embedded), updates Go filter parsing to handle label pointer variants and merged label shapes, adds extensive validation/parsing tests, adds a YAML post-processor (seal-object-schemas.mjs) and Makefile wiring, and removes or adds some TypeSpec extension metadata/constants.

Changes

Field Filter Type Redesign & Test Flow

Layer / File(s) Summary
Data Shape (spec)
api/spec/packages/aip/src/common/parameters.tsp
Rewrote Boolean/Numeric/String/StringExact/ULID/DateTime filters into explicit oneOf variant models; converted LabelsFieldFilter to Record<StringFieldFilter>.
Test TypeSpec
api/spec/packages/aip/src/test.tsp, api/spec/packages/aip/src/main.tsp
Added FieldFilters model and FieldFiltersEndpoints; imported test.tsp in main.tsp.
OpenAPI Test Document
api/v3/test/openapi.test.yaml
Added generated OpenAPI v3 test document describing /field-filters and component schemas covering all filter variants.
Embed Generated Spec
api/v3/test/embed.go, api/v3/api.gen.go
Added OpenAPITestSpec via go:embed and appended embedded swaggerSpec chunks to the generated API file.
Parser & Tests
api/v3/filters/parse.go, api/v3/filters/parse_test.go, api/v3/test/filters_test.go
Updated parse.go to support FilterLabels and *FilterLabels, added pointer-aware handling and merged label parsing with applyLabelOp; added comprehensive validation and parsing tests and adjusted test fixtures to use FilterLabels.

Spec Generation & Post-processing

Layer / File(s) Summary
Generation pipeline
api/spec/packages/aip/package.json, api/spec/Makefile, api/spec/packages/aip/tspconfig.yaml
Extended the generate script to run seal-object-schemas.mjs; Makefile copies generated test OpenAPI YAML into ../v3/test/openapi.test.yaml; enabled seal-object-schemas: true.
Schema Seal Script
api/spec/packages/aip/scripts/seal-object-schemas.mjs
New Node.js CLI that rewrites OpenAPI schema additionalProperties (converts certain non-empty objects to false) across YAML inputs to avoid deepObject decoding issues.

API Extension Metadata

Layer / File(s) Summary
Constants
api/spec/packages/aip/src/shared/consts.tsp
Added PrivateExtension = "x-private" and documentation comments for UnstableExtension/InternalExtension.
Decorator removals
api/spec/packages/aip/src/.../operations.tsp
api/spec/packages/aip/src/currencies/..., customers/charges, customers/credits, defaults, llmcost, currencies/cost-bases
Removed @extension(Shared.PrivateExtension, true) annotations from multiple operations across several modules; some defaults operations replaced with UnstableExtension and InternalExtension annotations.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant Server as Server
  participant Validator as Validator
  participant Parser as Parser
  participant Handler as Handler

  Client->>Server: GET /field-filters?filter=...
  Server->>Validator: Validate request against openapi.test.yaml
  alt validation OK
    Validator-->>Server: OK
    Server->>Parser: Parse query into typed filters
    alt parsed successfully
      Parser-->>Server: typed filters
      Server->>Handler: Invoke handler with typed filters
      Handler-->>Server: 204 / response
      Server-->>Client: 204
    else parse error
      Parser-->>Server: parse error
      Server-->>Client: 400 (parse error)
    end
  else validation error
    Validator-->>Server: validation errors
    Server-->>Client: 400 (validation error payload)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

area/api

Suggested reviewers

  • borosr
  • solkimicreb
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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 objective of the changeset: updating AIP field filter types across the specification and implementation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/aip-field-filter-types

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@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 (4)
api/v3/test/openapi.Test.yaml (1)

129-148: ⚖️ Poor tradeoff

Note: items on type: string is technically non-standard OpenAPI 3.0 (informational only)

Fields like ocontains and oeq emit:

type: string
items:
  type: string
format: ArrayEncoding.commaDelimited

items is only valid for type: array per the OpenAPI 3.0 spec. This is the TypeSpec OpenAPI3 emitter's way of encoding @encode(ArrayEncoding.commaDelimited) arrays as query params. Since x-go-type extensions drive Go codegen, this doesn't break anything today, but external validators or SDK generators consuming this spec may trip on it.

No action needed in this file since it's generated — just good to have on the radar in case client SDK generation is added later.

🤖 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/v3/test/openapi.Test.yaml` around lines 129 - 148, The OpenAPI emitter is
outputting items on a string schema for the query fields ocontains and oeq;
update the emitter so that for parameters annotated with
ArrayEncoding.commaDelimited (the ocontains and oeq parameter schemas) the
schema emits type: array with items: { type: string } and the existing format:
ArrayEncoding.commaDelimited (instead of type: string plus items), ensuring
external validators/SDK generators accept the spec.
api/v3/test/filters_test.go (2)

237-240: 💤 Low value

Tiny nit: defer the response body close.

If a require.* later fails between http.Get and resp.Body.Close(), the body won't be closed. Not a real leak here (the test ends and httptest server gets cleaned up), but defer resp.Body.Close() right after the NoError check is the idiomatic shape and saves you from forgetting it on future edits. Same comment applies to lines 570–573.

♻️ Suggested tweak
 			resp, err := http.Get(url)
 			require.NoError(t, err)
+			defer resp.Body.Close()
 			body, _ := io.ReadAll(resp.Body)
-			resp.Body.Close()
🤖 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/v3/test/filters_test.go` around lines 237 - 240, After the
require.NoError(t, err) check replace the explicit resp.Body.Close() call with a
deferred close (defer resp.Body.Close()) immediately after NoError to ensure the
response body is always closed even if subsequent assertions fail; update the
same pattern for the other occurrence referenced (the block using http.Get,
resp, io.ReadAll and resp.Body.Close around lines 570–573) so both use defer
resp.Body.Close() right after require.NoError.

555-560: 💤 Low value

Heads up: the unknown substring assertion is a bit loose.

wantBodySubstr: "unknown" will match any error mentioning "unknown" anywhere in the body, which makes this test a bit too forgiving if the parser's error wording drifts. If the parser returns something like unknown filter field: unknown, consider tightening to that phrase so a regression in error categorization actually fails the test.

🤖 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/v3/test/filters_test.go` around lines 555 - 560, The test case named
"unknown filter field rejected" uses a loose substring assertion
(wantBodySubstr: "unknown"); update the test case's wantBodySubstr in the same
test table entry to the tighter, explicit error phrase expected from the parser
(for example "unknown filter field: unknown" or the exact message your parser
emits) so the assertion references the precise error text; locate the table
entry by the name field "unknown filter field rejected" and change the
wantBodySubstr value accordingly.
api/spec/packages/aip/src/common/parameters.tsp (1)

313-318: 💤 Low value

Naming nit: LabelsFieldEqualsFilter breaks the *EQFilter pattern.

Every other "equals" variant in this file is named <X>FieldEQFilter (e.g. BooleanFieldEQFilter, NumericFieldEQFilter, StringFieldEQFilter, ULIDFieldEQFilter, DateTimeFieldEQFilter), but the labels union calls it LabelsFieldEqualsFilter. Worth aligning for consistency in the generated schemas/types.

♻️ Suggested rename
   `@summary`("Equals")
-  LabelsFieldEqualsFilter: {
+  LabelsFieldEQFilter: {
     /** Value strictly equals the given string value. */
     eq: string,
   },
🤖 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/spec/packages/aip/src/common/parameters.tsp` around lines 313 - 318,
Rename the type LabelsFieldEqualsFilter to LabelsFieldEQFilter to match the
existing <X>FieldEQFilter naming convention; update its declaration (currently
labeled "Equals") and all references/usages/unions/exports that mention
LabelsFieldEqualsFilter so generated schemas/types stay consistent, and keep the
same fields (eq: string) and summary text but adjust the summary if you want to
match the "EQ" naming convention.
🤖 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/v3/test/filters_test.go`:
- Line 32: The test fixture's fieldFiltersTarget struct uses the wrong type for
Labels (currently *filters.FilterString); update the Labels field to use
filters.FilterLabels (a map[string]FilterLabel) so it matches the OpenAPI
x-go-type and the production handler shape—locate the fieldFiltersTarget
definition in api/v3/test/filters_test.go and replace the Labels field type with
filters.FilterLabels, then run the tests to ensure compatibility with
TestParse_FilterLabels behavior.

In `@api/v3/test/openapi.Test.yaml`:
- Around line 1-5: The file name in the repo is incorrectly cased: rename
openapi.Test.yaml to lowercase openapi.test.yaml so it matches the //go:embed
pattern in api/v3/test/embed.go (the embed directive "//go:embed
openapi.test.yaml"); ensure the rename is committed so case-sensitive CI/Linux
builds find the embedded file.

---

Nitpick comments:
In `@api/spec/packages/aip/src/common/parameters.tsp`:
- Around line 313-318: Rename the type LabelsFieldEqualsFilter to
LabelsFieldEQFilter to match the existing <X>FieldEQFilter naming convention;
update its declaration (currently labeled "Equals") and all
references/usages/unions/exports that mention LabelsFieldEqualsFilter so
generated schemas/types stay consistent, and keep the same fields (eq: string)
and summary text but adjust the summary if you want to match the "EQ" naming
convention.

In `@api/v3/test/filters_test.go`:
- Around line 237-240: After the require.NoError(t, err) check replace the
explicit resp.Body.Close() call with a deferred close (defer resp.Body.Close())
immediately after NoError to ensure the response body is always closed even if
subsequent assertions fail; update the same pattern for the other occurrence
referenced (the block using http.Get, resp, io.ReadAll and resp.Body.Close
around lines 570–573) so both use defer resp.Body.Close() right after
require.NoError.
- Around line 555-560: The test case named "unknown filter field rejected" uses
a loose substring assertion (wantBodySubstr: "unknown"); update the test case's
wantBodySubstr in the same test table entry to the tighter, explicit error
phrase expected from the parser (for example "unknown filter field: unknown" or
the exact message your parser emits) so the assertion references the precise
error text; locate the table entry by the name field "unknown filter field
rejected" and change the wantBodySubstr value accordingly.

In `@api/v3/test/openapi.Test.yaml`:
- Around line 129-148: The OpenAPI emitter is outputting items on a string
schema for the query fields ocontains and oeq; update the emitter so that for
parameters annotated with ArrayEncoding.commaDelimited (the ocontains and oeq
parameter schemas) the schema emits type: array with items: { type: string } and
the existing format: ArrayEncoding.commaDelimited (instead of type: string plus
items), ensuring external validators/SDK generators accept the spec.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a008ac04-898f-49b5-957e-b95580117028

📥 Commits

Reviewing files that changed from the base of the PR and between 80d86ac and 3f3dc50.

⛔ Files ignored due to path filters (1)
  • api/v3/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (15)
  • api/spec/Makefile
  • api/spec/packages/aip/src/common/parameters.tsp
  • api/spec/packages/aip/src/currencies/cost-bases/operations.tsp
  • api/spec/packages/aip/src/currencies/operations.tsp
  • api/spec/packages/aip/src/customers/charges/operations.tsp
  • api/spec/packages/aip/src/customers/credits/operations.tsp
  • api/spec/packages/aip/src/defaults/operations.tsp
  • api/spec/packages/aip/src/llmcost/operations.tsp
  • api/spec/packages/aip/src/main.tsp
  • api/spec/packages/aip/src/shared/consts.tsp
  • api/spec/packages/aip/src/test.tsp
  • api/v3/api.gen.go
  • api/v3/test/embed.go
  • api/v3/test/filters_test.go
  • api/v3/test/openapi.Test.yaml
💤 Files with no reviewable changes (6)
  • api/spec/packages/aip/src/customers/charges/operations.tsp
  • api/spec/packages/aip/src/defaults/operations.tsp
  • api/spec/packages/aip/src/llmcost/operations.tsp
  • api/spec/packages/aip/src/currencies/cost-bases/operations.tsp
  • api/spec/packages/aip/src/currencies/operations.tsp
  • api/spec/packages/aip/src/customers/credits/operations.tsp

Comment thread api/v3/test/filters_test.go Outdated
Comment thread api/v3/test/openapi.test.yaml
Copy link
Copy Markdown
Contributor

@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

♻️ Duplicate comments (2)
api/v3/test/filters_test.go (1)

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

Labels field type still doesn't match the OpenAPI x-go-type.

Labels is typed as *filters.FilterString in this fixture, but the OpenAPI spec maps LabelsFieldFilter to filters.FilterLabels (which is a map[string]FilterLabel). Your own comment at lines 479-480 calls this out, and there's a separate TestParse_FilterLabels covering the map shape — but as it stands this suite never exercises the production-wired FilterLabels shape end-to-end through the OAS validator, so a divergence between schema and Go-side parsing could slip through unnoticed.

If switching the fixture to filters.FilterLabels is too invasive for this PR (since the parser API differs), consider adding at least one test that uses the real FilterLabels target so the validator + parser combo is exercised against the declared schema.

🤖 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/v3/test/filters_test.go` at line 32, The Labels field in the test fixture
is using the wrong type: it's declared as *filters.FilterString but the OpenAPI
x-go-type maps LabelsFieldFilter to filters.FilterLabels
(map[string]FilterLabel); update the fixture to use filters.FilterLabels for the
Labels field (or add an additional test case) so the validator + parser exercise
the real map shape end-to-end—specifically change the Labels declaration in the
affected test struct to filters.FilterLabels (or create a new test that
constructs a filters.FilterLabels instance and call the OAS validator + parser,
e.g., mirror the coverage of TestParse_FilterLabels) to ensure parity between
the OAS mapping and Go parsing.
api/v3/test/openapi.test.yaml (1)

129-148: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The ocontains / oeq schemas across this file look malformed (root cause is in TypeSpec).

These (and the matching ones in NumericFieldFilter lines 181-191, StringFieldFilter lines 255-274, StringFieldFilterExact lines 327-336, ULIDFieldFilter lines 368-377) emit shapes like:

ocontains:
  type: string
  items:
    type: string
  format: ArrayEncoding.commaDelimited

Two problems here:

  1. items is only valid when type: array — on type: string it's silently ignored by every validator.
  2. format: ArrayEncoding.commaDelimited isn't a recognized OpenAPI format; it looks like the TypeSpec @encode(ArrayEncoding.commaDelimited) enum identifier leaked verbatim into the schema. In OpenAPI 3, comma-delimited serialization is expressed at the parameter level via style/explode, not as a schema format.

Net effect: the spec ends up describing each oeq/ocontains operator as "any string", so the OAS validator accepts e.g. oeq=anything-at-all without enforcing the underlying item type (ULID pattern, number, etc.). That's also why the validator-side tests for these operators are pretty loose — they're actually only checking that a string was sent.

Since this YAML is generated, no change needed here directly — I've left the substantive comment on parameters.tsp where this originates. (Also nice work on the lowercase filename — that resolves the previous embed mismatch.)

🤖 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/v3/test/openapi.test.yaml` around lines 129 - 148, The generated OpenAPI
schemas for the ocontains/oeq operators (and their counterparts in
NumericFieldFilter, StringFieldFilter, StringFieldFilterExact, ULIDFieldFilter)
are malformed because TypeSpec emitted `type: string` with `items:` and a leaked
`format: ArrayEncoding.commaDelimited`; fix the TypeSpec generator in
parameters.tsp so these operators are emitted as arrays (type: array + items: {
...item schema... }) when they represent multi-value filters, and remove the
enum identifier from schema.format; instead surface comma-delimited
serialization at the parameter level using OpenAPI's style/explode (or map
`@encode`(ArrayEncoding.commaDelimited) to appropriate parameter serialization) so
the schema validates item types (e.g., ULID pattern, number) rather than
allowing any string.
🧹 Nitpick comments (3)
api/spec/packages/aip/src/common/parameters.tsp (1)

314-318: 💤 Low value

Naming nit: LabelsFieldEqualsFilter is the odd one out.

Every other variant in this file uses the *Field<OP>Filter shorthand (StringFieldEQFilter, NumericFieldEQFilter, ULIDFieldEQFilter, DateTimeFieldEQFilter), but this one spells out Equals. Renaming to LabelsFieldEQFilter keeps the family consistent and makes generated schema names predictable for downstream consumers.

♻️ Suggested rename
   `@summary`("Equals")
-  LabelsFieldEqualsFilter: {
+  LabelsFieldEQFilter: {
     /** Value strictly equals the given string value. */
     eq: string,
   },
🤖 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/spec/packages/aip/src/common/parameters.tsp` around lines 314 - 318, The
type name LabelsFieldEqualsFilter is inconsistent with the other field filter
types; rename the declaration and all references from LabelsFieldEqualsFilter to
LabelsFieldEQFilter (update the type definition and any imports/usage sites such
as validators, tests, or schema generation code that reference
LabelsFieldEqualsFilter) so the family matches StringFieldEQFilter,
NumericFieldEQFilter, ULIDFieldEQFilter, and DateTimeFieldEQFilter and
downstream generated schema names remain predictable.
api/spec/packages/aip/src/test.tsp (1)

18-33: 💤 Low value

Heads up: property names that shadow built-in scalar names.

Properties like boolean, numeric, string, ulid, datetime reuse the same identifiers as built-in scalars / Common types. TypeSpec itself is fine with this, but since multiple codegen toolchains consume these specs (and each has its quirks), it can occasionally trip up generators or schema renderers. Since this is a test fixture this is mostly fine, but worth keeping an eye on if you ever see a generator complain — quoting the keys (e.g. "string"?: ...) or prefixing them is an easy out.

🤖 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/spec/packages/aip/src/test.tsp` around lines 18 - 33, The FieldFilters
model uses property names that shadow built-in scalar identifiers (e.g.,
boolean, numeric, string, string_exact, ulid, datetime, labels) which can trip
some codegen/schema tools; update the FieldFilters model to avoid shadowing by
either quoting the problematic keys (e.g., "string"?: ...) or renaming/prefixing
them (e.g., string_field?: Common.StringFieldFilter) consistently across the
model and any consumers/tests that reference FieldFilters to ensure generators
and renderers handle the keys reliably.
api/v3/test/filters_test.go (1)

237-241: 💤 Low value

Tiny nit: defer resp.Body.Close() reads cleaner.

Just a style nudge — using defer resp.Body.Close() right after the require.NoError(t, err) keeps the cleanup co-located with the resource and survives any future early returns added between the read and the close. Totally optional for these short subtests.

♻️ Sketch
 			resp, err := http.Get(url)
 			require.NoError(t, err)
+			defer resp.Body.Close()
 			body, _ := io.ReadAll(resp.Body)
-			resp.Body.Close()

Also applies to: 570-574

🤖 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/v3/test/filters_test.go` around lines 237 - 241, The test currently calls
resp.Body.Close() explicitly after reading the body; change this to defer
resp.Body.Close() immediately after require.NoError(t, err) so the response body
is closed even if future early returns are added—i.e., after the http.Get(url)
and require.NoError(t, err) keep resp variable and replace the later explicit
resp.Body.Close() with the single defer; apply the same change to the other
occurrence around the http.Get(...) / require.NoError(...) block referenced
(lines ~570-574).
🤖 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/spec/packages/aip/src/common/parameters.tsp`:
- Around line 69-74: The `@encode`(ArrayEncoding.commaDelimited) annotations
inside the union variants (e.g., NumericFieldOEQFilter.oeq and analogous
ocontains fields in other filters) are producing invalid OpenAPI (string with
items and a TypeSpec identifier as format); remove these `@encode` annotations
from the nested variants so the schema emits a proper type: array with items: {
type: ... } and let comma-delimited serialization be handled at the
parameter/operation level (style/explode) or by the request parser
(filters.Parse) instead; update all affected variants (NumericFieldOEQFilter.oeq
and similar ocontains/oeq fields referenced in the review) to use plain array
types and, if necessary, move comma-delimited serialization metadata to the
top-level parameter definitions rather than the nested object schemas.

---

Duplicate comments:
In `@api/v3/test/filters_test.go`:
- Line 32: The Labels field in the test fixture is using the wrong type: it's
declared as *filters.FilterString but the OpenAPI x-go-type maps
LabelsFieldFilter to filters.FilterLabels (map[string]FilterLabel); update the
fixture to use filters.FilterLabels for the Labels field (or add an additional
test case) so the validator + parser exercise the real map shape
end-to-end—specifically change the Labels declaration in the affected test
struct to filters.FilterLabels (or create a new test that constructs a
filters.FilterLabels instance and call the OAS validator + parser, e.g., mirror
the coverage of TestParse_FilterLabels) to ensure parity between the OAS mapping
and Go parsing.

In `@api/v3/test/openapi.test.yaml`:
- Around line 129-148: The generated OpenAPI schemas for the ocontains/oeq
operators (and their counterparts in NumericFieldFilter, StringFieldFilter,
StringFieldFilterExact, ULIDFieldFilter) are malformed because TypeSpec emitted
`type: string` with `items:` and a leaked `format:
ArrayEncoding.commaDelimited`; fix the TypeSpec generator in parameters.tsp so
these operators are emitted as arrays (type: array + items: { ...item schema...
}) when they represent multi-value filters, and remove the enum identifier from
schema.format; instead surface comma-delimited serialization at the parameter
level using OpenAPI's style/explode (or map
`@encode`(ArrayEncoding.commaDelimited) to appropriate parameter serialization) so
the schema validates item types (e.g., ULID pattern, number) rather than
allowing any string.

---

Nitpick comments:
In `@api/spec/packages/aip/src/common/parameters.tsp`:
- Around line 314-318: The type name LabelsFieldEqualsFilter is inconsistent
with the other field filter types; rename the declaration and all references
from LabelsFieldEqualsFilter to LabelsFieldEQFilter (update the type definition
and any imports/usage sites such as validators, tests, or schema generation code
that reference LabelsFieldEqualsFilter) so the family matches
StringFieldEQFilter, NumericFieldEQFilter, ULIDFieldEQFilter, and
DateTimeFieldEQFilter and downstream generated schema names remain predictable.

In `@api/spec/packages/aip/src/test.tsp`:
- Around line 18-33: The FieldFilters model uses property names that shadow
built-in scalar identifiers (e.g., boolean, numeric, string, string_exact, ulid,
datetime, labels) which can trip some codegen/schema tools; update the
FieldFilters model to avoid shadowing by either quoting the problematic keys
(e.g., "string"?: ...) or renaming/prefixing them (e.g., string_field?:
Common.StringFieldFilter) consistently across the model and any consumers/tests
that reference FieldFilters to ensure generators and renderers handle the keys
reliably.

In `@api/v3/test/filters_test.go`:
- Around line 237-241: The test currently calls resp.Body.Close() explicitly
after reading the body; change this to defer resp.Body.Close() immediately after
require.NoError(t, err) so the response body is closed even if future early
returns are added—i.e., after the http.Get(url) and require.NoError(t, err) keep
resp variable and replace the later explicit resp.Body.Close() with the single
defer; apply the same change to the other occurrence around the http.Get(...) /
require.NoError(...) block referenced (lines ~570-574).
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 25815b62-3711-4c97-ad0e-2c250b9e2b0b

📥 Commits

Reviewing files that changed from the base of the PR and between 3f3dc50 and 90f7a48.

⛔ Files ignored due to path filters (1)
  • api/v3/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (8)
  • api/spec/Makefile
  • api/spec/packages/aip/src/common/parameters.tsp
  • api/spec/packages/aip/src/main.tsp
  • api/spec/packages/aip/src/test.tsp
  • api/v3/api.gen.go
  • api/v3/test/embed.go
  • api/v3/test/filters_test.go
  • api/v3/test/openapi.test.yaml

Comment thread api/spec/packages/aip/src/common/parameters.tsp
Copy link
Copy Markdown
Contributor

@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/v3/test/filters_test.go (1)

254-264: ⚡ Quick win

Make invalid-parameter assertions order-independent.

Nice coverage here. One small reliability tweak: with MultiError: true (Line 71), relying on invalid_parameters[0] can get flaky if ordering changes. It’s safer to match any entry that fits the expected field/rule/reason.

Proposed change
-			ip := got.InvalidParameters[0]
-			assert.Equal(t, "query", ip.Source)
-			if tc.wantField != "" {
-				assert.Equal(t, tc.wantField, ip.Field)
-			}
-			if tc.wantRule != "" {
-				assert.Equal(t, tc.wantRule, ip.Rule)
-			}
-			if tc.wantReasonSubstr != "" {
-				assert.Contains(t, ip.Reason, tc.wantReasonSubstr)
-			}
+			var matched *validatorInvalidParameter
+			for i := range got.InvalidParameters {
+				ip := &got.InvalidParameters[i]
+				if ip.Source != "query" {
+					continue
+				}
+				if tc.wantField != "" && ip.Field != tc.wantField {
+					continue
+				}
+				if tc.wantRule != "" && ip.Rule != tc.wantRule {
+					continue
+				}
+				matched = ip
+				break
+			}
+
+			require.NotNil(t, matched, "no matching invalid parameter in %+v", got.InvalidParameters)
+			if tc.wantReasonSubstr != "" {
+				assert.Contains(t, matched.Reason, tc.wantReasonSubstr)
+			}
🤖 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/v3/test/filters_test.go` around lines 254 - 264, The test currently
assumes order by using got.InvalidParameters[0], which can fail when MultiError:
true produces different ordering; instead, scan got.InvalidParameters for any
entry matching the expectations (compare ip.Field to tc.wantField if non-empty,
ip.Rule to tc.wantRule if non-empty, and ip.Reason contains tc.wantReasonSubstr
if non-empty), set that as the matched ip, and assert that a matching entry was
found before asserting its fields—use a simple loop or helper to find the
matching entry rather than a fixed index.
🤖 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/v3/test/filters_test.go`:
- Around line 479-480: The comment describing LabelsFieldFilter is out of date:
it says the labels field is handled as *FilterString but the fixture and cases
now use *filters.FilterLabels (see LabelsFieldFilter and filters.FilterLabels in
filters_test.go); update the inline comment to accurately state that
LabelsFieldFilter is handled as *filters.FilterLabels (or remove the remark if
redundant) so the comment matches the test fixture and the cases below.

---

Nitpick comments:
In `@api/v3/test/filters_test.go`:
- Around line 254-264: The test currently assumes order by using
got.InvalidParameters[0], which can fail when MultiError: true produces
different ordering; instead, scan got.InvalidParameters for any entry matching
the expectations (compare ip.Field to tc.wantField if non-empty, ip.Rule to
tc.wantRule if non-empty, and ip.Reason contains tc.wantReasonSubstr if
non-empty), set that as the matched ip, and assert that a matching entry was
found before asserting its fields—use a simple loop or helper to find the
matching entry rather than a fixed index.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9e83628e-197d-40e4-8320-3ec6df9115c6

📥 Commits

Reviewing files that changed from the base of the PR and between 90f7a48 and 99b576e.

📒 Files selected for processing (1)
  • api/v3/test/filters_test.go

Comment thread api/v3/test/filters_test.go
@tothandras
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tothandras tothandras merged commit f553b3c into main May 6, 2026
26 of 28 checks passed
@tothandras tothandras deleted the feat/aip-field-filter-types branch May 6, 2026 15:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/feature Release note: Exciting New Features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants