Skip to content

feat: add filter and sort to billing profile#4377

Open
borosr wants to merge 3 commits into
mainfrom
feat/billing-v3-api-filters
Open

feat: add filter and sort to billing profile#4377
borosr wants to merge 3 commits into
mainfrom
feat/billing-v3-api-filters

Conversation

@borosr
Copy link
Copy Markdown
Collaborator

@borosr borosr commented May 18, 2026

Add filters and sorting to the List App endpoint

What

Adds filter[*] query parameters and a sort query parameter to GET /api/v3/openmeter/apps.

Supported filters: id, name.

Supported sort fields: id, name, createdAt (default), updatedAt with optional asc/desc suffix.

Why

Callers need a way to narrow down billing profile results without client-side filtering, e.g. finding all billing profiles for a given type or sorted by creation time.

How

  • Create the TypeSpec model with ListBillingProfilesParamsFilter and wired sort/filter query params into list
  • Regenerated OpenAPI spec and Go server bindings
  • Updated the HTTP handler to parse and validate filter/sort params, then pass them into ListBillingProfilesRequest
  • Propagated the new fields through the service and adapter layers

Testing

Filter by billing profile name:

curl -s "http://localhost:8888/api/v3/openmeter/profiles?sort=createdAt%20asc&filter%5Bname%5D%5Bcontains%5D=sandbox"

Sort by creation date descending:

curl -s  "http://localhost:8888/api/v3/openmeter/profiles?sort=createdAt%20asc"

Summary by CodeRabbit

  • New Features
    • Added filtering (by ID and name) and sorting to the billing profiles list endpoint.
  • Bug Fixes
    • Invalid sort or filter inputs now return clear 400 Bad Request responses tied to the specific query parameter.
  • Tests
    • Added tests covering filtering, sorting, validation, and error cases for billing profiles listing.

Review Change Stack

@borosr borosr requested a review from tothandras May 18, 2026 13:58
@borosr borosr self-assigned this May 18, 2026
@borosr borosr requested a review from a team as a code owner May 18, 2026 13:58
@borosr borosr added the release-note/ignore Ignore this change when generating release notes label May 18, 2026
@borosr borosr force-pushed the feat/billing-v3-api-filters branch from 588e62e to 962d629 Compare May 18, 2026 13:59
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

📝 Walkthrough

Walkthrough

This PR adds deep-object filter (id, name) and sort query support to billing profiles listing: API model and docs, handler parsing with validation, domain OrderBy and filter types/validation, adapter query application, HTTP driver mapping, and service tests.

Changes

Billing profiles filtering and sorting

Layer / File(s) Summary
OrderBy type and ListProfilesInput contract
openmeter/billing/profile.go
Adds OrderBy type and OrderBy* constants, imports pkg/filter, adds ID *filter.FilterULID and Name *filter.FilterString to ListProfilesInput, and updates Validate() to check filters and order-by.
API specification for filtering and sorting
api/spec/packages/aip/src/billing/operations.tsp
Adds ListBillingProfilesParamsFilter model (id, name) and extends BillingProfilesOperations.list with sort?: Common.SortQuery and filter?: ListBillingProfilesParamsFilter (deepObject, explode).
HTTP handler query parameter parsing
api/v3/handlers/billingprofiles/list.go
Imports api/v3/filters and api/v3/request, parses params.Sort and params.Filter, maps to req.OrderBy, req.Order, req.ID, and req.Name, and returns parameter-scoped 400 errors for invalid values.
HTTP driver parameter mapping
openmeter/billing/httpdriver/profile.go
Casts the optional API params.OrderBy into billing.OrderBy(...) when building the request while keeping the existing default when nil.
Adapter query construction
openmeter/billing/adapter/profile.go
Removes api import, adds pkg/filter import, applies filter.ApplyToQuery for ID and Name, and switches ordering logic to use billing.OrderBy* constants for Ent ordering.
Service behavior tests
openmeter/billing/service/profile_test.go
Adds recordingAdapter and newServiceForProfileTest, and TestListProfiles table-driven cases verifying filter operator translation (Eq, Contains, In), ID mapping, sort mapping, and validation error handling for conflicting operators.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

release-note/feature

Suggested reviewers

  • tothandras
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 clearly and concisely summarizes the main change: adding filter and sort functionality to the billing profile list endpoint.
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/billing-v3-api-filters

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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openmeter/billing/profile.go (1)

409-437: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Collect namespace/expand errors into errs too for Validate() consistency.

This method now aggregates some errors, but Line 410 and Line 414 still return early. To match project validation behavior, collect all field errors and return a single joined validation error.

As per coding guidelines, openmeter/**/*.go: “For Validate() error methods, collect all validation issues into var errs []error and return models.NewNillableGenericValidationError(errors.Join(errs...)) instead of returning on the first invalid field”.

🤖 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 `@openmeter/billing/profile.go` around lines 409 - 437,
ListProfilesInput.Validate currently returns early for Namespace and Expand
errors instead of collecting them; update the method
(ListProfilesInput.Validate) to append validation errors for Namespace and for
i.Expand.Validate() into the errs slice (same as for ID/Name/OrderBy) and then
return models.NewNillableGenericValidationError(errors.Join(errs...)); ensure
you still validate Namespace (e.g., non-empty) and call i.Expand.Validate(), but
push their resulting errors into errs rather than returning immediately.
🤖 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/billing/operations.tsp`:
- Around line 54-58: The example query in the `@query-decorated` filter
declaration is incorrect for the deep-object operator-style filter; update the
example to use operator form compatible with ListBillingProfilesParamsFilter
(e.g., use filter[name][eq]=my-profile or filter[name][contains]=my to match the
deepObject/explode=true shape) so clients send valid operator-style filter
parameters for the filter property.

In `@openmeter/billing/profile.go`:
- Around line 372-385: OrderBy.Values() currently returns only OrderByCreatedAt,
OrderByUpdatedAt, OrderByName, so OrderByID is wrongly excluded and
OrderBy.Validate() rejects "id"; update OrderBy.Values() to include OrderByID
(and any other missing OrderBy constants) so that OrderBy.Validate() sees "id"
as valid, and verify the same fix is applied to the other Values()/Validate()
pair referenced (lines around OrderByID) to ensure consistency.

In `@openmeter/billing/service/profile_test.go`:
- Around line 49-133: Add a new test case to the cases slice in profile_test.go
that exercises sorting by ID: create a test with name "SortByID" that sets input
billing.ListProfilesInput{Namespace: ns, OrderBy: "id", Order: sortx.OrderAsc
(or Desc)} and in assertAdapter assert that got.OrderBy == billing.OrderByID and
got.Order == the expected sortx order; this mirrors the existing
"SortByNameAsc"/"SortByUpdatedAt" cases and locks the new sort contract for
billing.ListProfilesInput and the mapping to billing.OrderByID.

---

Outside diff comments:
In `@openmeter/billing/profile.go`:
- Around line 409-437: ListProfilesInput.Validate currently returns early for
Namespace and Expand errors instead of collecting them; update the method
(ListProfilesInput.Validate) to append validation errors for Namespace and for
i.Expand.Validate() into the errs slice (same as for ID/Name/OrderBy) and then
return models.NewNillableGenericValidationError(errors.Join(errs...)); ensure
you still validate Namespace (e.g., non-empty) and call i.Expand.Validate(), but
push their resulting errors into errs rather than returning immediately.
🪄 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: ac63c23c-897a-444a-a18d-d5ad4b622855

📥 Commits

Reviewing files that changed from the base of the PR and between 499cdc5 and 962d629.

⛔ Files ignored due to path filters (1)
  • api/v3/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (7)
  • api/spec/packages/aip/src/billing/operations.tsp
  • api/v3/api.gen.go
  • api/v3/handlers/billingprofiles/list.go
  • openmeter/billing/adapter/profile.go
  • openmeter/billing/httpdriver/profile.go
  • openmeter/billing/profile.go
  • openmeter/billing/service/profile_test.go

Comment thread api/spec/packages/aip/src/billing/operations.tsp
Comment thread openmeter/billing/profile.go
Comment thread openmeter/billing/service/profile_test.go
rolosp
rolosp previously approved these changes May 18, 2026
@borosr borosr force-pushed the feat/billing-v3-api-filters branch from f28b4c6 to b796418 Compare May 19, 2026 09:08
@borosr borosr requested a review from rolosp May 19, 2026 13:31
@borosr borosr force-pushed the feat/billing-v3-api-filters branch from b796418 to 9ee3534 Compare May 20, 2026 07:41
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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openmeter/billing/profile.go (1)

410-417: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Please aggregate all validation errors in ListProfilesInput.Validate().

Nice addition overall, but Line 411 and Line 415 still return early. That breaks the repo’s Validate() aggregation rule and can hide additional invalid fields in one request.

Suggested patch
 func (i ListProfilesInput) Validate() error {
-	if i.Namespace == "" {
-		return errors.New("namespace is required")
-	}
-
-	if err := i.Expand.Validate(); err != nil {
-		return fmt.Errorf("error validating expand: %w", err)
-	}
-
 	var errs []error
+	if i.Namespace == "" {
+		errs = append(errs, errors.New("namespace is required"))
+	}
+
+	if err := i.Expand.Validate(); err != nil {
+		errs = append(errs, fmt.Errorf("error validating expand: %w", err))
+	}
+
 	if i.ID != nil {
 		if err := i.ID.Validate(); err != nil {
 			errs = append(errs, err)
 		}
 	}
@@
 	if i.OrderBy != "" {
 		if err := i.OrderBy.Validate(); err != nil {
 			errs = append(errs, err)
 		}
 	}
 
 	return models.NewNillableGenericValidationError(errors.Join(errs...))
 }

As per coding guidelines, openmeter/**/*.go: “For Validate() error methods, collect all validation issues into var errs []error and return models.NewNillableGenericValidationError(errors.Join(errs...)) instead of returning on the first invalid field”.

Also applies to: 419-437

🤖 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 `@openmeter/billing/profile.go` around lines 410 - 417, Change
ListProfilesInput.Validate() to collect all field validation errors instead of
returning early: create a slice var errs []error, append an error when
i.Namespace == "" and append the wrapped error from i.Expand.Validate() if it
fails (referencing i.Expand.Validate), then at the end if len(errs) > 0 return
models.NewNillableGenericValidationError(errors.Join(errs...)) else return nil;
apply the same aggregation pattern for the other checks in the function (lines
handling pagination/filters referenced in the same Validate method).
🤖 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.

Outside diff comments:
In `@openmeter/billing/profile.go`:
- Around line 410-417: Change ListProfilesInput.Validate() to collect all field
validation errors instead of returning early: create a slice var errs []error,
append an error when i.Namespace == "" and append the wrapped error from
i.Expand.Validate() if it fails (referencing i.Expand.Validate), then at the end
if len(errs) > 0 return
models.NewNillableGenericValidationError(errors.Join(errs...)) else return nil;
apply the same aggregation pattern for the other checks in the function (lines
handling pagination/filters referenced in the same Validate method).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 141c3060-607d-45e8-bf77-4a05b9c8d852

📥 Commits

Reviewing files that changed from the base of the PR and between b796418 and 9ee3534.

⛔ Files ignored due to path filters (1)
  • api/v3/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (7)
  • api/spec/packages/aip/src/billing/operations.tsp
  • api/v3/api.gen.go
  • api/v3/handlers/billingprofiles/list.go
  • openmeter/billing/adapter/profile.go
  • openmeter/billing/httpdriver/profile.go
  • openmeter/billing/profile.go
  • openmeter/billing/service/profile_test.go

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

Labels

release-note/ignore Ignore this change when generating release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants