Skip to content

feat(api): query meters v2 with filters #2718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

tothandras
Copy link
Contributor

@tothandras tothandras commented Apr 24, 2025

Summary by CodeRabbit

  • New Features

    • Introduced an advanced JSON-based filter query parameter for meter queries, supporting flexible filtering on groupBy, subject, and time.
    • Added support for grouping by multiple keys with a new groupBy parameter.
    • Enhanced query endpoints to accept the new filter parameter with mutual exclusivity to simpler filters.
    • Improved parameter validation to enforce exclusive use of advanced filtering.
    • Added a new query path utilizing the advanced filtering capabilities.
  • Bug Fixes

    • Strengthened validation to prevent simultaneous use of conflicting filter parameters.
  • Tests

    • Added extensive tests covering SQL generation, filtering, grouping, and windowing scenarios for the new query parameters.
  • Documentation

    • Updated API specs and documentation to include the new filtering and grouping parameters.

@tothandras tothandras added area/api release-note/feature Release note: Exciting New Features labels Apr 24, 2025
Copy link

coderabbitai bot commented Apr 24, 2025

Warning

Rate limit exceeded

@tothandras has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 13 minutes and 21 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 43de4a7 and fc5de55.

📒 Files selected for processing (14)
  • api/client/javascript/src/client/schemas.ts (5 hunks)
  • api/openapi.cloud.yaml (3 hunks)
  • api/openapi.yaml (3 hunks)
  • api/spec/src/meters.tsp (2 hunks)
  • openmeter/meter/httphandler/mapping.go (2 hunks)
  • openmeter/meter/httphandler/query.go (6 hunks)
  • openmeter/server/server_test.go (1 hunks)
  • openmeter/streaming/clickhouse/connector.go (2 hunks)
  • openmeter/streaming/clickhouse/meter_query_test.go (1 hunks)
  • openmeter/streaming/clickhouse/meter_query_v2.go (1 hunks)
  • openmeter/streaming/clickhouse/meter_query_v2_test.go (1 hunks)
  • openmeter/streaming/connector.go (1 hunks)
  • openmeter/streaming/query_params.go (3 hunks)
  • openmeter/streaming/testutils/streaming.go (1 hunks)
📝 Walkthrough

Walkthrough

This change set introduces a new, advanced filtering mechanism for querying meter data. A JSON-encoded filter query parameter is added to both the API specification and server implementation, enabling clients to express complex filter logic on groupBy, subject, and time fields. The new filter is mutually exclusive with the simpler from, to, subject, and filterGroupBy parameters. Supporting this, the backend introduces new parameter structs, validation logic, and a QueryMeterV2 method across the service, interface, and ClickHouse connector layers. Comprehensive tests and OpenAPI documentation updates accompany these changes.

Changes

Files/Paths Change Summary
api/client/javascript/src/client/schemas.ts, api/openapi.cloud.yaml, api/openapi.yaml Added a new filter query parameter (JSON-encoded) to meter query endpoints and schemas, with mutual exclusivity to existing filters; updated OpenAPI documentation and parameter ordering.
api/spec/src/meters.tsp Extended MeterQuery with a new groupBy query parameter and a JSON-encoded filter parameter for advanced filtering; removed previous groupBy from filter context.
openmeter/meter/httphandler/mapping.go Added ToQueryMeterParamsV2 to convert API params with advanced filtering into streaming query params; validated groupings and filter fields; original ToQueryMeterParams unchanged.
openmeter/meter/httphandler/query.go Capitalized struct fields for export; added Validate() to QueryMeterParams for filter exclusivity; routed requests to V2 logic if filter is present; updated error handling and CSV response construction.
openmeter/server/server_test.go, openmeter/streaming/testutils/streaming.go Added QueryMeterV2 mock methods to test connectors for V2 query support.
openmeter/streaming/clickhouse/connector.go Added QueryMeterV2 and supporting internal methods for advanced filter queries; integrated new parameter struct and filtering logic; adjusted window handling for filtered queries.
openmeter/streaming/clickhouse/meter_query_v2.go New file implementing queryMeterTableV2 struct and methods to generate SQL for V2 queries with advanced filtering, grouping, and windowing.
openmeter/streaming/clickhouse/meter_query_v2_test.go New test file with comprehensive unit tests for V2 query SQL generation, covering filtering, grouping, windowing, and edge cases.
openmeter/streaming/clickhouse/meter_query_test.go Added test case for precedence of meter's EventFrom over query From in SQL generation.
openmeter/streaming/connector.go Extended Connector interface with QueryMeterV2 method for advanced queries.
openmeter/streaming/query_params.go Introduced QueryParamsV2 and Filter structs with validation methods; updated existing validation logic to accumulate errors.
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@tothandras tothandras force-pushed the feat/meter-query-v2 branch 2 times, most recently from 947735f to 367308b Compare April 25, 2025 18:11
@tothandras tothandras marked this pull request as ready for review April 25, 2025 18:11
@tothandras tothandras requested a review from a team as a code owner April 25, 2025 18:11
Copy link

@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: 8

🔭 Outside diff range comments (2)
openmeter/meter/httphandler/mapping.go (2)

75-105: ⚠️ Potential issue

From/To and FilterGroupBy are no longer forwarded – legacy queries will break

ToQueryMeterParams used to copy from, to, and filterGroupBy from the public api.QueryMeterParams into the internal streaming.QueryParams, but those assignments disappeared during the refactor.
The ClickHouse connector still relies on params.From, params.To, and params.FilterGroupBy (see queryMeter), so any v1 request that omits the new filter parameter will now return all time results and ignore filterGroupBy.

 func ToQueryMeterParams(m meter.Meter, apiParams api.QueryMeterParams) (streaming.QueryParams, error) {
     params := streaming.QueryParams{
         ClientID: apiParams.ClientId,
     }
+
+    // restore time & filter mappings for v1
+    params.From = apiParams.From
+    params.To   = apiParams.To
+    if apiParams.FilterGroupBy != nil {
+        params.FilterGroupBy = *apiParams.FilterGroupBy
+    }

Without this, every existing client that still uses from/to will get incorrect data.


118-166: 🛠️ Refactor suggestion

Duplicate validation logic & missing deterministic ordering

ToQueryMeterParamsV2 re-implements almost the same group-by validation that exists above. Consider extracting a shared helper to avoid divergence.

In addition, params.GroupBy is left in request order. queryMeter (v1) explicitly sorted to guarantee the column order used for SQL generation and row-scanning. The V2 path omits that, but the reader in queryMeterV2 assumes the same order when mapping scan args (for i, key := range params.GroupBy { … }).
If the caller passes "b,a" instead of "a,b", the scan will mismatch columns.

+    // ensure deterministic order like v1
+    sort.Strings(params.GroupBy)

This keeps both SQL and result parsing stable, simplifies testing, and avoids subtle data shifts.

🧹 Nitpick comments (11)
openmeter/server/server_test.go (1)

651-655: Implement conditional logic based on filter parameters.

The V2 implementation always returns the subject value from mockQueryValue, unlike the V1 implementation which conditionally sets Subject to nil based on filtering parameters. This mock doesn't accurately reflect how filtering would work in the real implementation.

Consider adding conditional logic to handle filter parameters:

func (c *MockStreamingConnector) QueryMeterV2(ctx context.Context, namespace string, m meter.Meter, params streaming.QueryParamsV2) ([]meter.MeterQueryRow, error) {
	value := mockQueryValue

+	// Apply filtering based on params.Filter
+	if params.Filter != nil {
+		// Handle subject filtering
+		if params.Filter.Subject == nil || len(params.Filter.Subject.In) == 0 {
+			value.Subject = nil
+		}
+		
+		// Handle other filter conditions
+	}

	return []meter.MeterQueryRow{value}, nil
}
api/client/javascript/src/client/schemas.ts (1)

17446-17448: Consider adding an example JSON structure for the filter parameter

While the documentation clearly states the purpose and constraints of the filter parameter, it would be helpful to include an example of the expected JSON structure to guide API users.

/** @description The filter for the events encoded as JSON string.
-     Can't be used together with from, to, subject, and filterGroupBy. */
+     Can't be used together with from, to, subject, and filterGroupBy.
+     
+     For example: ?filter={"groupBy":{"model":"gpt-4"},"time":{"from":"2023-01-01T00:00:00Z","to":"2023-01-02T00:00:00Z"}} */
api/openapi.cloud.yaml (1)

10622-10642: Refine the JSON schema definition for the filter parameter.
The current schema omits an explicit type: object and includes an unnecessary format field. Consider this diff to improve clarity and correctness:

          content:
            application/json:
              schema:
-               properties:
+               type: object
+               properties:
                groupBy:
                  type: object
                  additionalProperties:
                    $ref: '#/components/schemas/FilterString'
                subject:
                  $ref: '#/components/schemas/FilterString'
                time:
                  $ref: '#/components/schemas/FilterTime'
-               format: application/json

Adding type: object clarifies that the filter is a JSON object, and removing format avoids misuse of the field.

openmeter/streaming/clickhouse/meter_query_v2_test.go (2)

15-438: Comprehensive test cases for toSQL() method, but consider splitting this large function.

The test function covers multiple scenarios including different aggregation types, subject filtering, time range filtering, windowing, timezone handling, and group filtering. All test cases are well structured with clear inputs and expected outputs.

Consider splitting this large test function into smaller, more focused test functions or subtest groups for better maintainability. You could group tests by functionality (e.g., basic queries, filtering, windowing, etc.).

-func TestQueryMeterV2(t *testing.T) {
+func TestQueryMeterV2_BasicQueries(t *testing.T) {
   // Setup common test variables...
   
   tests := []struct {
     // Test structure...
   }{
     // Basic query test cases only...
   }
   
   // Test execution...
}

+func TestQueryMeterV2_GroupingAndFiltering(t *testing.T) {
+  // Setup common test variables...
+  
+  tests := []struct {
+    // Test structure...
+  }{
+    // Group filtering test cases only...
+  }
+  
+  // Test execution...
+}

428-436: Add test cases for error scenarios.

The code handles errors from toSQL(), but no test case is designed to trigger an error path. Consider adding test cases that exercise the error handling code.

Add a test case with invalid inputs that would trigger an error:

+		{
+			name: "error case",
+			query: queryMeterTableV2{
+				// Invalid input that should trigger an error
+				Database:        "", // Empty database name
+				EventsTableName: "om_events",
+				Namespace:       "my_namespace",
+				Meter: meter.Meter{
+					Key:           "meter1",
+					EventType:     "event1",
+					Aggregation:   "invalid_aggregation", // Invalid aggregation
+				},
+			},
+			wantError: true,
+		},

Then update the test execution to check for expected errors:

 		t.Run("", func(t *testing.T) {
 			gotSql, gotArgs, err := tt.query.toSQL()
-			if err != nil {
-				t.Error(err)
-				return
-			}
+			if tt.wantError {
+				assert.Error(t, err)
+				return
+			} else if err != nil {
+				t.Error(err)
+				return
+			}
 
 			assert.Equal(t, tt.wantSQL, gotSql)
 			assert.Equal(t, tt.wantArgs, gotArgs)
 		})
openmeter/streaming/clickhouse/connector.go (1)

647-657: Stale TODO & redundant validation call

The comment says “TODO: Add validation for params once we have ValidateV2”, yet params.Validate() is already invoked.
Either delete the TODO or remove the call – having both is confusing.

openmeter/meter/httphandler/query.go (2)

85-90: Validation message could be clearer & cover all legacy fields

The current check blocks from, to, filterGroupBy, or subject when filter is set, but omits windowSize and windowTimeZone, which are also semantically orthogonal in V2.
Consider extending the guard and making the error actionable:

if p.Filter != nil && (p.From != nil || p.To != nil ||
    p.FilterGroupBy != nil || p.Subject != nil) {
    return models.NewGenericValidationError(
        fmt.Errorf("the top-level filter is mutually exclusive with from, to, filterGroupBy and subject parameters"))
}

197-209: CSV header order may fluctuate – sort for deterministic output

groupBy originates from the converted params slice, which preserves user order. The downstream CSV builder iterates that slice to emit headers, so two identical queries with "b,a" vs "a,b" yield different CSV layouts.

For stable exports (important when users automate parsing) sort before passing:

- groupBy = params.GroupBy
+ groupBy = append([]string(nil), params.GroupBy...)
+ sort.Strings(groupBy)
openmeter/streaming/clickhouse/meter_query_v2.go (3)

62-63: Align error return signatures

return "", nil, err vs return "", []interface{}{}, err is inconsistent.
Prefer nil for empty slices to avoid unnecessary allocations.

- return "", []interface{}{}, fmt.Errorf("invalid aggregation type: %s", q.Meter.Aggregation)
+ return "", nil, fmt.Errorf("invalid aggregation type: %s", q.Meter.Aggregation)

Also applies to: 90-90


194-195: Prefer getColumn over hard-coded names for consistency

Using literals bypasses the column-aliasing logic encapsulated in
columnFactory, making future refactors harder and risking mismatches if
the physical column names ever change.

- query.Where(query.Equal("namespace", q.Namespace))
- query.Where(query.Equal("type", q.Meter.EventType))
+ query.Where(query.Equal(getColumn("namespace"), q.Namespace))
+ query.Where(query.Equal(getColumn("type"), q.Meter.EventType))

23-24: Use consistent method receivers

toSQL has a value receiver while toCountRowSQL uses a pointer.
Stick to one style (pointer is usual for objects with many fields) to avoid
unexpected copies.

Also applies to: 184-184

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e7cbe4 and 367308b.

📒 Files selected for processing (14)
  • api/client/javascript/src/client/schemas.ts (5 hunks)
  • api/openapi.cloud.yaml (3 hunks)
  • api/openapi.yaml (3 hunks)
  • api/spec/src/meters.tsp (2 hunks)
  • openmeter/meter/httphandler/mapping.go (2 hunks)
  • openmeter/meter/httphandler/query.go (6 hunks)
  • openmeter/server/server_test.go (1 hunks)
  • openmeter/streaming/clickhouse/connector.go (2 hunks)
  • openmeter/streaming/clickhouse/meter_query_test.go (1 hunks)
  • openmeter/streaming/clickhouse/meter_query_v2.go (1 hunks)
  • openmeter/streaming/clickhouse/meter_query_v2_test.go (1 hunks)
  • openmeter/streaming/connector.go (1 hunks)
  • openmeter/streaming/query_params.go (3 hunks)
  • openmeter/streaming/testutils/streaming.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
openmeter/streaming/testutils/streaming.go (2)
openmeter/meter/meter.go (2)
  • Meter (152-164)
  • MeterQueryRow (333-339)
openmeter/streaming/query_params.go (1)
  • QueryParamsV2 (23-29)
openmeter/streaming/connector.go (2)
openmeter/meter/meter.go (2)
  • Meter (152-164)
  • MeterQueryRow (333-339)
openmeter/streaming/query_params.go (1)
  • QueryParamsV2 (23-29)
openmeter/server/server_test.go (3)
openmeter/streaming/testutils/streaming.go (1)
  • MockStreamingConnector (32-35)
openmeter/meter/meter.go (2)
  • Meter (152-164)
  • MeterQueryRow (333-339)
openmeter/streaming/query_params.go (1)
  • QueryParamsV2 (23-29)
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: E2E
  • GitHub Check: CI
  • GitHub Check: Quickstart
  • GitHub Check: Developer environment
  • GitHub Check: Commit hooks
  • GitHub Check: Test
  • GitHub Check: Lint
  • GitHub Check: Build
  • GitHub Check: Analyze (go)
🔇 Additional comments (18)
openmeter/streaming/clickhouse/meter_query_test.go (1)

295-311: LGTM! The test case correctly verifies the EventFrom behavior.

This test case properly verifies that when the meter's EventFrom time is later than the query's From time, the SQL query uses the later EventFrom time as the effective start time filter in the WHERE clause.

api/client/javascript/src/client/schemas.ts (3)

9721-9723: LGTM: The filter parameter addition is well-documented

The new filter parameter is clearly defined with appropriate documentation that indicates its purpose and mutual exclusivity constraints with existing parameters.


10469-10470: LGTM: Proper type export for the new filter parameter

The type export for the new filter parameter follows the established pattern in the codebase and ensures type safety for API client users.


20296-20302: LGTM: Parameters correctly added to queryPortalMeter operation

The filterGroupBy and filter parameters are correctly added to the queryPortalMeter operation with consistent documentation.

api/openapi.cloud.yaml (2)

5357-5360: Ensure consistent parameter ordering for the new filter query parameter.
The filter parameter is correctly added after filterGroupBy in this endpoint. Please confirm that this ordering matches the core OpenAPI spec (api/openapi.yaml) and that generated client code will respect this sequence.


7679-7680: Mirror the new filter parameter in the portal endpoint.
The portal endpoint now lists filter immediately after filterGroupBy. Verify that its description and mutual-exclusivity rules are identical to the primary meter query endpoint.

openmeter/streaming/clickhouse/meter_query_v2_test.go (2)

1-13: Well-structured imports with appropriate packages.

The imports cover all needed functionality including testing, time handling, assertions, and OpenMeter-specific packages.


440-611: Well-structured test cases for toCountRowSQL() method.

The tests are well-organized with clear names and descriptions. Each test case has appropriate assertions and covers important scenarios like filtering, subject handling, and time constraints.

openmeter/streaming/query_params.go (5)

8-8: Appropriate import for filter package.

Adding the filter package import supports the new filtering capabilities.


23-29: Well-structured QueryParamsV2 definition.

The new struct provides a clear parameter structure for the advanced filtering capabilities with appropriate field types.


45-49: Clean Filter struct definition with clear purpose.

The Filter struct encapsulates the filtering capabilities for group, subject, and time dimensions.


51-79: Thorough validation logic for Filter.

The validation method covers all fields and properly handles nil case. The complexity validation and error accumulation approach is well-implemented.


81-100: Improved validation pattern for QueryParams.

The validation method now accumulates errors rather than returning early, improving the error reporting experience. The consistent use of error joining and the NillableGenericValidationError helper is good.

api/spec/src/meters.tsp (1)

356-365: Clear definition for the new groupBy parameter.

The parameter is well-documented with appropriate examples and explode behavior. The documentation clearly explains the purpose and usage.

api/openapi.yaml (2)

5356-5359: Approve addition of filter to V2 endpoint parameters
The new filter reference is correctly positioned after filterGroupBy, reflecting the updated API contract for advanced JSON‐encoded filtering. This aligns with the mutual‐exclusivity rules defined in the component schema.


7678-7679: Approve addition of filter to portal endpoint parameters
Consistent with the primary meter query endpoint, the portal API now exposes the filter parameter after filterGroupBy. This maintains symmetry between the two routes and supports the new V2 filtering semantics.

openmeter/streaming/clickhouse/connector.go (1)

670-683: Window overwrite only honours gte/lte, ignores gt/lt

getTimeRangeFromFilter returns Gte/Lte only. When a client specifies { "time": { "gt": … } } the post-processing silently leaves WindowStart zero, causing an inconsistent response (SQL was filtered, but the window header is wrong).

Consider:

 func getTimeRangeFromFilter(tf *filter.FilterTime) (*time.Time, *time.Time) {
-    if tf == nil {
-        return nil, nil
-    }
-    return tf.Gte, tf.Lte
+    if tf == nil {
+        return nil, nil
+    }
+    // prefer inclusive bounds, fall back to exclusive ones
+    from := lo.Coalesce(tf.Gte, tf.Gt)
+    to   := lo.Coalesce(tf.Lte, tf.Lt)
+    return from, to
 }
openmeter/streaming/clickhouse/meter_query_v2.go (1)

111-114:

❌ Incorrect review comment

Nil-map lookup can panic when a groupBy key is missing

q.Meter.GroupBy[groupByKey] assumes the key exists. If the API user
requests an arbitrary key the meter wasn’t configured for, the server will
panic.

Add an existence check and surface a validation error instead.


Map lookup on missing key won’t panic in Go

Reading a non-existent key (or from a nil map) returns the zero value ("") rather than causing a panic. If the real concern is generating an invalid JSON path or SQL snippet when groupByKey is absent, you can add an existence check to emit a clear validation error instead of proceeding with an empty path.

• In Go, q.Meter.GroupBy[groupByKey] returns "" if the key isn’t found—no panic occurs.
• To prevent invalid SQL, explicitly verify that groupByKey exists in q.Meter.GroupBy and return a user-friendly error when it doesn’t.

Likely an incorrect or invalid review comment.

@tothandras tothandras force-pushed the feat/meter-query-v2 branch 2 times, most recently from eeaa879 to 43de4a7 Compare April 25, 2025 18:21
Copy link

@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 (1)
openmeter/streaming/query_params.go (1)

38-40: ⚠️ Potential issue

Missing nil check for Filter before validation.

The code tries to call p.Filter.Validate() without first checking if p.Filter is nil, which can lead to a nil pointer dereference. This was previously flagged in a review and supposedly fixed, but the fix isn't present here.

Apply this fix:

-if err := p.Filter.Validate(); err != nil {
+if p.Filter != nil {
+	if err := p.Filter.Validate(); err != nil {
		errs = append(errs, err)
+	}
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 367308b and 43de4a7.

📒 Files selected for processing (14)
  • api/client/javascript/src/client/schemas.ts (5 hunks)
  • api/openapi.cloud.yaml (3 hunks)
  • api/openapi.yaml (3 hunks)
  • api/spec/src/meters.tsp (2 hunks)
  • openmeter/meter/httphandler/mapping.go (2 hunks)
  • openmeter/meter/httphandler/query.go (6 hunks)
  • openmeter/server/server_test.go (1 hunks)
  • openmeter/streaming/clickhouse/connector.go (2 hunks)
  • openmeter/streaming/clickhouse/meter_query_test.go (1 hunks)
  • openmeter/streaming/clickhouse/meter_query_v2.go (1 hunks)
  • openmeter/streaming/clickhouse/meter_query_v2_test.go (1 hunks)
  • openmeter/streaming/connector.go (1 hunks)
  • openmeter/streaming/query_params.go (3 hunks)
  • openmeter/streaming/testutils/streaming.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • openmeter/streaming/testutils/streaming.go
  • openmeter/meter/httphandler/mapping.go
  • openmeter/streaming/connector.go
  • openmeter/server/server_test.go
  • openmeter/streaming/clickhouse/meter_query_test.go
  • api/client/javascript/src/client/schemas.ts
  • openmeter/streaming/clickhouse/meter_query_v2.go
  • openmeter/streaming/clickhouse/meter_query_v2_test.go
  • api/openapi.yaml
  • openmeter/streaming/clickhouse/connector.go
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Quickstart
  • GitHub Check: E2E
  • GitHub Check: CI
  • GitHub Check: Lint
  • GitHub Check: Developer environment
  • GitHub Check: Commit hooks
  • GitHub Check: Test
  • GitHub Check: Analyze (go)
  • GitHub Check: Build
🔇 Additional comments (20)
openmeter/streaming/query_params.go (3)

51-79: Good implementation of validation with complexity limits.

The validation method properly checks for nil before validating each filter component and enforces complexity limits for filters, which helps prevent overly complicated queries that could impact performance.


85-87: Good improvement in error handling.

The change to append errors rather than returning immediately allows for more comprehensive validation by collecting all validation errors.


99-99: Good use of utility function for error handling.

Using models.NewNillableGenericValidationError with errors.Join creates a clean pattern for handling multiple validation errors.

api/spec/src/meters.tsp (2)

356-365: Well-designed groupBy parameter with clear documentation.

The new groupBy parameter is properly implemented as an exploded array of strings with good documentation explaining its usage and defaults.


384-405:

✅ Verification successful

Well-structured filter parameter with comprehensive documentation.

The filter parameter implementation includes:

  • Clear documentation noting mutual exclusivity with other parameters
  • Detailed examples showing the structure with operators ($eq, $gte, $lte, $and)
  • Proper encoding as application/json
  • Well-defined schema for groupBy, subject, and time filters

Make sure that the validation logic enforcing the mutual exclusivity constraint is implemented in the request handling code.


🏁 Script executed:

#!/bin/bash
# Look for validation logic that enforces filter parameter mutual exclusivity
rg -A 10 "filter.*from|from.*filter|filter.*to|to.*filter|filter.*subject|subject.*filter|filter.*filterGroupBy|filterGroupBy.*filter" --glob "*.go" | grep -i "error\\|valid\\|exclusive"

Length of output: 7506


Validation in place for mutual exclusivity of filter

The filter parameter is well-structured and documented, with:

  • Clear JSDoc noting that filter can’t be used with from, to, subject, or filterGroupBy
  • A comprehensive @example demonstrating operators ($eq, $gte, $lte, $and)
  • Proper @encode("application/json") annotation
  • Precise TypeScript schema for groupBy, subject, and time

The request handler in openmeter/meter/httphandler/query.go already enforces this constraint by returning a validation error if filter is combined with any of the other parameters:

return models.NewGenericValidationError(
    fmt.Errorf("filter and from, to, filter group by or subject cannot be set at the same time"),
)

No further changes required—approving these updates.

api/openapi.cloud.yaml (2)

5357-5360: Correctly append groupBy and filter parameters
The new MeterQuery.groupBy and MeterQuery.filter references are added in the correct order within the cloud meter query endpoint, preserving mutual‐exclusivity semantics while enabling advanced filtering.


7679-7680: Synchronize filter parameter on portal endpoint
MeterQuery.filter is properly inserted after filterGroupBy in the portal meter query operation, ensuring feature parity with the main cloud API.

openmeter/meter/httphandler/query.go (13)

14-14: Added import for validation errors

The added import for models package is necessary for the new validation logic that uses the NewGenericValidationError function.


27-28: Field capitalization improves consistency

The capitalization of Namespace and IdOrSlug in the ListSubjectsRequest struct makes these fields exported, which ensures consistent visibility across similar structs in the codebase.


80-82: Field capitalization for consistent struct field exposure

Similar to the changes in ListSubjectsRequest, capitalizing fields in QueryMeterRequest ensures proper exportability across the codebase.


84-90: Well-implemented validation for mutually exclusive filters

Good implementation of the validation logic to ensure the new Filter parameter is mutually exclusive with the existing filter parameters. This prevents ambiguous or conflicting query specifications.


101-103: Early validation prevents invalid requests

Adding validation early in the request processing pipeline prevents invalid requests from reaching the service layer, which is a good practice.


111-117: Error handling simplification

The error from GetMeterByIDOrSlug is now returned directly without wrapping it in additional context. This seems to be a deliberate simplification across the file.


119-148: Clean implementation of filter-based query path selection

The conditional logic based on Filter presence is well-structured and follows a consistent pattern:

  1. Convert parameters based on filter type
  2. Validate parameters
  3. Call the appropriate query method

This ensures a clear separation between the standard query path and the new filtered query path.


150-150: Response formatting is consistent regardless of query path

The same response formatting function is used regardless of which query path was taken, ensuring consistent response structure.


178-180: Consistent validation pattern in CSV handler

The same validation pattern is applied in the CSV handler, maintaining consistency with the JSON handler implementation.


196-228: Consistent conditional logic in CSV handler

The CSV handler follows the same pattern as the JSON handler for determining the query path based on filter presence, ensuring consistency between endpoints.


196-197: Good variable initialization before conditional logic

The variables for rows and groupBy are declared before the conditional block, which ensures they are in scope for the entire function body.


208-208: Consistent extraction of groupBy parameter

The groupBy extraction is handled consistently in both branches of the condition, making the code more maintainable.

Also applies to: 223-223


230-230: Improved CSV result construction

The CSV result now uses the meter key from the retrieved meter object rather than from the request, which is more robust as it ensures the data source is authoritative.

@tothandras tothandras force-pushed the feat/meter-query-v2 branch from 43de4a7 to fc5de55 Compare April 25, 2025 18:27
@tothandras tothandras marked this pull request as draft April 25, 2025 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/api release-note/feature Release note: Exciting New Features
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant