Skip to content

fix: collect leftover invoices too#4216

Merged
turip merged 6 commits into
mainfrom
fix/collect-leftover-gathering-invoices
Apr 23, 2026
Merged

fix: collect leftover invoices too#4216
turip merged 6 commits into
mainfrom
fix/collect-leftover-gathering-invoices

Conversation

@turip
Copy link
Copy Markdown
Member

@turip turip commented Apr 23, 2026

Overview

This patch adds support for collecting any gathering invoice that is in an old leftover state.

To achieve that we are using the filter package for collected at filtering.

The patch introduces the exists operator and exposes that in an AIP compliant way.

Notes for reviewer

Summary by CodeRabbit

  • New Features

    • Added $exists operator to time-based filters, enabling null value checks on datetime fields
    • Enhanced collection-at filtering for gathering invoices to support existence-based queries
  • Bug Fixes

    • Empty datetime filter values are now handled gracefully instead of being rejected as errors
    • Improved handling of invoices with unset collection dates, with appropriate logging
  • Tests

    • Added test coverage for the $exists operator
    • Added test scenarios for gathering invoice filtering with null collection dates

@turip turip requested a review from a team as a code owner April 23, 2026 14:18
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

This PR adds a new $exists operator to time-based filtering for IS NULL/IS NOT NULL queries, refactors collection-at filtering across the billing layer to use centralized filter logic, and removes error handling for empty bare datetime filters to allow operator-driven validation instead.

Changes

Cohort / File(s) Summary
Filter Core Enhancement
pkg/filter/filter.go, pkg/filter/filter_test.go
Adds Exists nullable boolean operator to FilterTime and FilterTimeUnix, with SQL WHERE and Ent selector predicate generation. New test verifies IsEmpty() correctly identifies non-empty filter when Exists is set.
API Parsing & Validation
api/v3/filters/parse.go, api/v3/filters/parse_test.go
Removes error handling for empty bare datetime filters; empty inputs now proceed to operator switch for operator-driven validation. Test expectations updated to reflect datetime-specific error handling removal.
API Specification & Converter
api/spec/packages/legacy/src/filter.tsp, openmeter/apiconverter/filter.go
Adds optional $exists field to FilterTime spec and configures converter to ignore this operator during v1 API mapping.
Billing Data Access Layer
openmeter/billing/gatheringinvoice.go, openmeter/billing/adapter/gatheringinvoice.go
Changes CollectionAtLTE *time.Time to CollectionAt filter.FilterTime with validation; centralizes collection-at filtering through filter.ApplyToQuery instead of conditional predicates.
Billing Business Logic
openmeter/billing/worker/collect/collect.go
Updates ListCollectableInvoices to use filter.FilterTime for broader results including invoices with missing CollectionAt, adds warning log for invoices with nil NextCollectionAt.
Billing Integration Tests
test/billing/invoice_test.go
Adds new test verifying ListGatheringInvoices correctly excludes invoices with nil NextCollectionAt when filtering by CollectionAt with Lte constraint.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

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

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.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 accurately describes the main objective—collecting leftover invoices—and aligns well with the core changes across the billing and filter packages.
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 fix/collect-leftover-gathering-invoices

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

🧹 Nitpick comments (4)
api/v3/filters/convert_test.go (1)

327-332: Tiny coverage gap: include Exists: false too.

$exists=false is the path that drives IS NULL, so it’s worth asserting the converter preserves a non-nil false pointer.

🧪 Suggested table-driven version
-	t.Run("exists maps to filter.FilterTime Exists", func(t *testing.T) {
-		out, err := FromAPIFilterDateTime(&FilterDateTime{Exists: lo.ToPtr(true)})
-		require.NoError(t, err)
-		require.NotNil(t, out)
-		assert.Equal(t, lo.ToPtr(true), out.Exists)
-	})
+	t.Run("exists maps to filter.FilterTime Exists", func(t *testing.T) {
+		for _, exists := range []bool{true, false} {
+			t.Run(fmt.Sprintf("%v", exists), func(t *testing.T) {
+				out, err := FromAPIFilterDateTime(&FilterDateTime{Exists: lo.ToPtr(exists)})
+				require.NoError(t, err)
+				require.NotNil(t, out)
+				assert.Equal(t, lo.ToPtr(exists), out.Exists)
+			})
+		}
+	})

This also needs fmt in the imports, or use explicit t.Run("true"/"false", ...) cases to avoid the new import.

As per coding guidelines, **/*_test.go: Make sure the tests are comprehensive and cover the changes. Keep a strong focus on unit tests and in-code integration tests.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/v3/filters/convert_test.go` around lines 327 - 332, Add a test that
asserts FromAPIFilterDateTime preserves a non-nil false pointer for
FilterDateTime.Exists (since $exists=false drives IS NULL); update the existing
t.Run block that tests Exists=true to also cover Exists=false either by turning
it into a small table-driven loop over true/false or by adding a separate t.Run
for the false case; if you choose the table-driven approach add the necessary
import (fmt) for subtest names or avoid fmt by using explicit names, and ensure
the calls to FromAPIFilterDateTime and assertions check that out.Exists equals
lo.ToPtr(false) when input is FilterDateTime{Exists: lo.ToPtr(false)}.
pkg/filter/filter_test.go (1)

2412-2418: Please cover the generated IS NULL / IS NOT NULL predicates too.

This new case only exercises IsEmpty(). Since the production change also added SQL builder and Ent predicate generation for FilterTime and FilterTimeUnix, it’d be great to add Exists: true and Exists: false cases there as well.

🧪 Suggested test additions
 func TestFilterTime_SelectAndSelectWhereExpr(t *testing.T) {
@@
 		{
 			name:      "nil filter",
 			filter:    filter.FilterTime{},
 			field:     "created_at",
 			wantEmpty: true,
 		},
+		{
+			name:         "exists filter",
+			filter:       filter.FilterTime{Exists: lo.ToPtr(true)},
+			field:        "created_at",
+			wantExprSQL:  "SELECT * FROM table WHERE created_at IS NOT NULL",
+			wantExprArgs: nil,
+			wantEntSQL:   `SELECT * FROM "test_table" WHERE "test_table"."created_at" IS NOT NULL`,
+			wantEntArgs:  nil,
+		},
+		{
+			name:         "not exists filter",
+			filter:       filter.FilterTime{Exists: lo.ToPtr(false)},
+			field:        "created_at",
+			wantExprSQL:  "SELECT * FROM table WHERE created_at IS NULL",
+			wantExprArgs: nil,
+			wantEntSQL:   `SELECT * FROM "test_table" WHERE "test_table"."created_at" IS NULL`,
+			wantEntArgs:  nil,
+		},
 func TestFilterTimeUnix_SelectAndSelectWhereExpr(t *testing.T) {
@@
 		{
 			name:      "nil filter",
 			filter:    filter.FilterTimeUnix{},
 			field:     "created_at",
 			wantEmpty: true,
 		},
+		{
+			name:         "exists filter",
+			filter:       filter.FilterTimeUnix{FilterTime: filter.FilterTime{Exists: lo.ToPtr(true)}},
+			field:        "created_at",
+			wantExprSQL:  "SELECT * FROM table WHERE created_at IS NOT NULL",
+			wantExprArgs: nil,
+			wantEntSQL:   `SELECT * FROM "test_table" WHERE "test_table"."created_at" IS NOT NULL`,
+			wantEntArgs:  nil,
+		},
+		{
+			name:         "not exists filter",
+			filter:       filter.FilterTimeUnix{FilterTime: filter.FilterTime{Exists: lo.ToPtr(false)}},
+			field:        "created_at",
+			wantExprSQL:  "SELECT * FROM table WHERE created_at IS NULL",
+			wantExprArgs: nil,
+			wantEntSQL:   `SELECT * FROM "test_table" WHERE "test_table"."created_at" IS NULL`,
+			wantEntArgs:  nil,
+		},

As per coding guidelines, **/*_test.go: Make sure the tests are comprehensive and cover the changes. Keep a strong focus on unit tests and in-code integration tests.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/filter/filter_test.go` around lines 2412 - 2418, Add test cases for
FilterTime and FilterTimeUnix that cover Exists: true and Exists: false in
addition to the existing IsEmpty() case; for each new case assert the SQL
builder output contains "IS NOT NULL" when Exists=true and "IS NULL" when
Exists=false, and verify corresponding Ent predicate generation produces the
expected nil/non-nil-null checks (use the same helpers you use elsewhere to
render Ent predicates). Locate the tests around the existing FilterTime block
(look for the FilterTime struct in the tests and the IsEmpty() assertions) and
add parallel table-driven entries for FilterTimeUnix, calling the SQL build
function (e.g., the package's SQL builder helper) and the Ent predicate
generator to validate both SQL and Ent predicate behavior for Exists=true/false.
test/billing/invoice_test.go (1)

689-697: Tiny nit: reuse now for the filter bound.

You've already captured now := time.Now().UTC().Truncate(time.Second) earlier — using a fresh time.Now() for Lte here makes the bound drift slightly and reads a bit inconsistently. Not a correctness issue (any time after the invoice creation works), just a readability papercut.

♻️ Optional tweak
-		CollectionAt: filter.FilterTime{
-			Lte: lo.ToPtr(time.Now()),
-		},
+		CollectionAt: filter.FilterTime{
+			Lte: lo.ToPtr(now),
+		},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/billing/invoice_test.go` around lines 689 - 697, Replace the fresh
time.Now() passed to billing.ListGatheringInvoices's CollectionAt.Lte with the
previously captured now variable so the filter uses the same truncated UTC
timestamp; locate the ListGatheringInvoices call in the test (invoices, err :=
s.BillingService.ListGatheringInvoices(...)) and set CollectionAt:
filter.FilterTime{ Lte: lo.ToPtr(now) } to avoid the minor drift and keep the
test consistent with the earlier now value.
openmeter/billing/worker/collect/collect.go (1)

68-74: Warn log could get noisy on large backlogs.

If a legacy-state customer has many gathering invoices with NextCollectionAt == nil, this will emit a single log line carrying every ID — which can balloon log sizes and make downstream log ingestion unhappy on first rollout. Logging the count (and maybe a capped sample) is usually friendlier for ops, and you still get enough signal to go investigate.

🛠️ Suggested tweak
-	nilCollectionAtInvoiceIDs := lo.FilterMap(resp.Items, func(invoice billing.GatheringInvoice, _ int) (string, bool) {
-		return invoice.ID, invoice.NextCollectionAt == nil
-	})
-
-	if len(nilCollectionAtInvoiceIDs) > 0 {
-		a.logger.WarnContext(ctx, "gathering invoices have nil next collection at; this may indicate legacy or inconsistent state", "invoice_ids", nilCollectionAtInvoiceIDs)
-	}
+	nilCollectionAtInvoiceIDs := lo.FilterMap(resp.Items, func(invoice billing.GatheringInvoice, _ int) (string, bool) {
+		return invoice.ID, invoice.NextCollectionAt == nil
+	})
+
+	if n := len(nilCollectionAtInvoiceIDs); n > 0 {
+		const sampleSize = 10
+		sample := nilCollectionAtInvoiceIDs
+		if n > sampleSize {
+			sample = sample[:sampleSize]
+		}
+		a.logger.WarnContext(ctx, "gathering invoices have nil next collection at; this may indicate legacy or inconsistent state",
+			"count", n, "invoice_ids_sample", sample)
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openmeter/billing/worker/collect/collect.go` around lines 68 - 74, The warn
log in collect.go currently emits all IDs from nilCollectionAtInvoiceIDs which
can be huge; update the logic around nilCollectionAtInvoiceIDs and the call to
a.logger.WarnContext to log only the total count and a capped sample (e.g.,
first 10 IDs) instead of the full slice: compute count :=
len(nilCollectionAtInvoiceIDs), build a small sample (use lo.Take or slice) when
count > sampleSize, and pass "count" and "sample_ids" (not the entire
invoice_ids) to WarnContext so large backlogs don't blow up logs while keeping
enough info to investigate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/v3/filters/filter.go`:
- Around line 40-41: The comment for the Exists field is inaccurate; update the
comment for the Exists *bool field (tagged json:"$exists,omitempty") to reflect
that it can be set via the public API JSON key "$exists" as well as by bare-key
query forms and is accepted by the converter; locate the Exists declaration in
filter.go and replace the comment to state it represents the $exists operator
accepted directly in the API payload and by bare-key shorthand.

---

Nitpick comments:
In `@api/v3/filters/convert_test.go`:
- Around line 327-332: Add a test that asserts FromAPIFilterDateTime preserves a
non-nil false pointer for FilterDateTime.Exists (since $exists=false drives IS
NULL); update the existing t.Run block that tests Exists=true to also cover
Exists=false either by turning it into a small table-driven loop over true/false
or by adding a separate t.Run for the false case; if you choose the table-driven
approach add the necessary import (fmt) for subtest names or avoid fmt by using
explicit names, and ensure the calls to FromAPIFilterDateTime and assertions
check that out.Exists equals lo.ToPtr(false) when input is
FilterDateTime{Exists: lo.ToPtr(false)}.

In `@openmeter/billing/worker/collect/collect.go`:
- Around line 68-74: The warn log in collect.go currently emits all IDs from
nilCollectionAtInvoiceIDs which can be huge; update the logic around
nilCollectionAtInvoiceIDs and the call to a.logger.WarnContext to log only the
total count and a capped sample (e.g., first 10 IDs) instead of the full slice:
compute count := len(nilCollectionAtInvoiceIDs), build a small sample (use
lo.Take or slice) when count > sampleSize, and pass "count" and "sample_ids"
(not the entire invoice_ids) to WarnContext so large backlogs don't blow up logs
while keeping enough info to investigate.

In `@pkg/filter/filter_test.go`:
- Around line 2412-2418: Add test cases for FilterTime and FilterTimeUnix that
cover Exists: true and Exists: false in addition to the existing IsEmpty() case;
for each new case assert the SQL builder output contains "IS NOT NULL" when
Exists=true and "IS NULL" when Exists=false, and verify corresponding Ent
predicate generation produces the expected nil/non-nil-null checks (use the same
helpers you use elsewhere to render Ent predicates). Locate the tests around the
existing FilterTime block (look for the FilterTime struct in the tests and the
IsEmpty() assertions) and add parallel table-driven entries for FilterTimeUnix,
calling the SQL build function (e.g., the package's SQL builder helper) and the
Ent predicate generator to validate both SQL and Ent predicate behavior for
Exists=true/false.

In `@test/billing/invoice_test.go`:
- Around line 689-697: Replace the fresh time.Now() passed to
billing.ListGatheringInvoices's CollectionAt.Lte with the previously captured
now variable so the filter uses the same truncated UTC timestamp; locate the
ListGatheringInvoices call in the test (invoices, err :=
s.BillingService.ListGatheringInvoices(...)) and set CollectionAt:
filter.FilterTime{ Lte: lo.ToPtr(now) } to avoid the minor drift and keep the
test consistent with the earlier now value.
🪄 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: 1ca24faa-8d0b-4589-aa05-877d1722aec0

📥 Commits

Reviewing files that changed from the base of the PR and between d5e2d1a and 32a0013.

📒 Files selected for processing (11)
  • api/v3/filters/convert.go
  • api/v3/filters/convert_test.go
  • api/v3/filters/filter.go
  • api/v3/filters/parse.go
  • api/v3/filters/parse_test.go
  • openmeter/billing/adapter/gatheringinvoice.go
  • openmeter/billing/gatheringinvoice.go
  • openmeter/billing/worker/collect/collect.go
  • pkg/filter/filter.go
  • pkg/filter/filter_test.go
  • test/billing/invoice_test.go

Comment thread api/v3/filters/filter.go Outdated
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.

🧹 Nitpick comments (2)
api/spec/packages/legacy/src/filter.tsp (1)

286-290: Nice addition — consider a slightly richer doc string.

The new $exists operator is a clean, consistent extension of FilterTime. 🎉 One tiny nitpick: the doc "The field must exist." doesn't convey the boolean semantics (i.e. true → field is not null, false → field is null). Since this gets surfaced to SDK users, a hint like the following would save a trip to the source:

📝 Suggested doc tweak
-  /**
-   * The field must exist.
-   */
+  /**
+   * When true, the field must be set (not null); when false, the field must be null.
+   */
   `@extension`("x-omitempty", true)
   $exists?: boolean | null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/spec/packages/legacy/src/filter.tsp` around lines 286 - 290, Update the
docstring for the $exists property to explicitly document its boolean semantics
for SDK users: state that $exists true means the field must exist / be non-null
and $exists false means the field must be null or absent, and include that the
property is optional and can be null (as declared on $exists) so callers know
how to pass it when using FilterTime/$exists.
test/billing/invoice_test.go (1)

690-698: Consider locking down the filter timestamp and adding a positive-case assertion.

Two small things worth considering here:

  1. Lte: lo.ToPtr(time.Now()) uses a fresh wall-clock value rather than the now already established on line 660. Reusing now (or clock.Now()) makes the test deterministic and matches the convention used elsewhere in this file.
  2. The test only proves the "nil NextCollectionAt is excluded" half of the fix. A sibling assertion that a gathering invoice with a non-nil NextCollectionAt ≤ Lte is returned would nail down that the filter still works positively — guarding against a future regression that over-filters everything.
🧪 Sketch of the tweak
-	invoices, err := s.BillingService.ListGatheringInvoices(ctx, billing.ListGatheringInvoicesInput{
-		Namespaces: []string{namespace},
-		Customers:  []string{customerEntity.ID},
-		CollectionAt: filter.FilterTime{
-			Lte: lo.ToPtr(time.Now()),
-		},
-	})
-	require.NoError(s.T(), err)
-	require.Empty(s.T(), invoices.Items)
+	farFuture := now.Add(365 * 24 * time.Hour)
+	invoices, err := s.BillingService.ListGatheringInvoices(ctx, billing.ListGatheringInvoicesInput{
+		Namespaces: []string{namespace},
+		Customers:  []string{customerEntity.ID},
+		CollectionAt: filter.FilterTime{
+			Lte: lo.ToPtr(farFuture),
+		},
+	})
+	require.NoError(s.T(), err)
+	require.Empty(s.T(), invoices.Items, "gathering invoices with nil NextCollectionAt must be filtered out")

…plus an additional positive-case call (without forcing NextCollectionAt = nil) to assert the invoice is returned.

As per coding guidelines ("Make sure the tests are comprehensive and cover the changes").

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/billing/invoice_test.go` around lines 690 - 698, The test uses a fresh
time.Now() for the CollectionAt filter and only asserts the negative case;
update the call to use the existing now (or clock.Now()) variable instead of
time.Now() when building billing.ListGatheringInvoicesInput.CollectionAt to make
the test deterministic, and add a positive-case: create a gathering invoice with
a non-nil NextCollectionAt <= now, call s.BillingService.ListGatheringInvoices
(same ListGatheringInvoicesInput) and assert that invoices.Items contains that
invoice (i.e., it is returned), referencing the
ListGatheringInvoices/ListGatheringInvoicesInput, CollectionAt and
NextCollectionAt symbols to locate the relevant code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@api/spec/packages/legacy/src/filter.tsp`:
- Around line 286-290: Update the docstring for the $exists property to
explicitly document its boolean semantics for SDK users: state that $exists true
means the field must exist / be non-null and $exists false means the field must
be null or absent, and include that the property is optional and can be null (as
declared on $exists) so callers know how to pass it when using
FilterTime/$exists.

In `@test/billing/invoice_test.go`:
- Around line 690-698: The test uses a fresh time.Now() for the CollectionAt
filter and only asserts the negative case; update the call to use the existing
now (or clock.Now()) variable instead of time.Now() when building
billing.ListGatheringInvoicesInput.CollectionAt to make the test deterministic,
and add a positive-case: create a gathering invoice with a non-nil
NextCollectionAt <= now, call s.BillingService.ListGatheringInvoices (same
ListGatheringInvoicesInput) and assert that invoices.Items contains that invoice
(i.e., it is returned), referencing the
ListGatheringInvoices/ListGatheringInvoicesInput, CollectionAt and
NextCollectionAt symbols to locate the relevant code.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fa9fbd94-4744-4320-8990-204d8baef463

📥 Commits

Reviewing files that changed from the base of the PR and between 32a0013 and a7b59b0.

⛔ Files ignored due to path filters (5)
  • api/client/go/client.gen.go is excluded by !api/client/**
  • api/client/javascript/src/client/schemas.ts is excluded by !api/client/**
  • api/client/python/openmeter/_generated/models/_models.py is excluded by !**/_generated/**, !api/client/**
  • api/openapi.cloud.yaml is excluded by !**/openapi.cloud.yaml
  • api/openapi.yaml is excluded by !**/openapi.yaml
📒 Files selected for processing (4)
  • api/api.gen.go
  • api/spec/packages/legacy/src/filter.tsp
  • openmeter/apiconverter/filter.gen.go
  • test/billing/invoice_test.go
✅ Files skipped from review due to trivial changes (1)
  • openmeter/apiconverter/filter.gen.go

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/v3/filters/parse_test.go`:
- Around line 627-629: Add a focused test (near TestParse_FilterDateTime or
alongside TestParse_EmptyTypedValueRejected) that exercises
FilterDateTime/nullable timestamp behavior by asserting the parser's handling of
filter[created_at], filter[created_at][exists], and filter[created_at][nexists];
specifically, enumerate and assert the expected outcomes for an empty datetime
value, an explicit exists=true, and exists=false inputs using the same helper
parsing functions as other tests so the new cases cover the removed
datetime-rejection branch and document the intended nullable timestamp
semantics.
🪄 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: 5828e062-df5b-45b3-9278-ff5aad4214c8

📥 Commits

Reviewing files that changed from the base of the PR and between a7b59b0 and 555917f.

📒 Files selected for processing (3)
  • api/v3/filters/parse.go
  • api/v3/filters/parse_test.go
  • openmeter/apiconverter/filter.go
💤 Files with no reviewable changes (1)
  • api/v3/filters/parse.go

Comment thread api/v3/filters/parse_test.go Outdated
@turip turip enabled auto-merge (squash) April 23, 2026 15:13
@turip turip merged commit 8cd72c3 into main Apr 23, 2026
27 of 28 checks passed
@turip turip deleted the fix/collect-leftover-gathering-invoices branch April 23, 2026 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants