Skip to content

Commit 43de4a7

Browse files
committed
feat(api): add filters to query meters
1 parent 6e7cbe4 commit 43de4a7

File tree

16 files changed

+3479
-2134
lines changed

16 files changed

+3479
-2134
lines changed

api/api.gen.go

Lines changed: 1097 additions & 1046 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/client/go/client.gen.go

Lines changed: 1060 additions & 1019 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/client/javascript/src/client/schemas.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9718,6 +9718,9 @@ export interface components {
97189718
/** @description Client ID
97199719
* Useful to track progress of a query. */
97209720
'MeterQuery.clientId': string
9721+
/** @description The filter for the events encoded as JSON string.
9722+
* Can't be used together with from, to, subject, and filterGroupBy. */
9723+
'MeterQuery.filter': string
97219724
/** @description Simple filter for group bys with exact match.
97229725
*
97239726
* For example: ?filterGroupBy[vendor]=openai&filterGroupBy[model]=gpt-4-turbo */
@@ -10463,6 +10466,8 @@ export type ParameterMeterOrderByOrderingOrderBy =
1046310466
components['parameters']['MeterOrderByOrdering.orderBy']
1046410467
export type ParameterMeterQueryClientId =
1046510468
components['parameters']['MeterQuery.clientId']
10469+
export type ParameterMeterQueryFilter =
10470+
components['parameters']['MeterQuery.filter']
1046610471
export type ParameterMeterQueryFilterGroupBy =
1046710472
components['parameters']['MeterQuery.filterGroupBy']
1046810473
export type ParameterMeterQueryFrom =
@@ -17425,6 +17430,11 @@ export interface operations {
1742517430
*
1742617431
* For example: ?windowTimeZone=UTC */
1742717432
windowTimeZone?: components['parameters']['MeterQuery.windowTimeZone']
17433+
/** @description If not specified a single aggregate will be returned for each subject and time window.
17434+
* `subject` is a reserved group by value.
17435+
*
17436+
* For example: ?groupBy=subject&groupBy=model */
17437+
groupBy?: components['parameters']['MeterQuery.groupBy']
1742817438
/** @description Filtering by multiple subjects.
1742917439
*
1743017440
* For example: ?subject=customer-1&subject=customer-2 */
@@ -17433,11 +17443,9 @@ export interface operations {
1743317443
*
1743417444
* For example: ?filterGroupBy[vendor]=openai&filterGroupBy[model]=gpt-4-turbo */
1743517445
filterGroupBy?: components['parameters']['MeterQuery.filterGroupBy']
17436-
/** @description If not specified a single aggregate will be returned for each subject and time window.
17437-
* `subject` is a reserved group by value.
17438-
*
17439-
* For example: ?groupBy=subject&groupBy=model */
17440-
groupBy?: components['parameters']['MeterQuery.groupBy']
17446+
/** @description The filter for the events encoded as JSON string.
17447+
* Can't be used together with from, to, subject, and filterGroupBy. */
17448+
filter?: components['parameters']['MeterQuery.filter']
1744117449
}
1744217450
header?: never
1744317451
path: {
@@ -20280,15 +20288,18 @@ export interface operations {
2028020288
*
2028120289
* For example: ?windowTimeZone=UTC */
2028220290
windowTimeZone?: components['parameters']['MeterQuery.windowTimeZone']
20283-
/** @description Simple filter for group bys with exact match.
20284-
*
20285-
* For example: ?filterGroupBy[vendor]=openai&filterGroupBy[model]=gpt-4-turbo */
20286-
filterGroupBy?: components['parameters']['MeterQuery.filterGroupBy']
2028720291
/** @description If not specified a single aggregate will be returned for each subject and time window.
2028820292
* `subject` is a reserved group by value.
2028920293
*
2029020294
* For example: ?groupBy=subject&groupBy=model */
2029120295
groupBy?: components['parameters']['MeterQuery.groupBy']
20296+
/** @description Simple filter for group bys with exact match.
20297+
*
20298+
* For example: ?filterGroupBy[vendor]=openai&filterGroupBy[model]=gpt-4-turbo */
20299+
filterGroupBy?: components['parameters']['MeterQuery.filterGroupBy']
20300+
/** @description The filter for the events encoded as JSON string.
20301+
* Can't be used together with from, to, subject, and filterGroupBy. */
20302+
filter?: components['parameters']['MeterQuery.filter']
2029220303
}
2029320304
header?: never
2029420305
path: {

api/openapi.cloud.yaml

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5354,9 +5354,10 @@ paths:
53545354
- $ref: '#/components/parameters/MeterQuery.to'
53555355
- $ref: '#/components/parameters/MeterQuery.windowSize'
53565356
- $ref: '#/components/parameters/MeterQuery.windowTimeZone'
5357+
- $ref: '#/components/parameters/MeterQuery.groupBy'
53575358
- $ref: '#/components/parameters/MeterQuery.subject'
53585359
- $ref: '#/components/parameters/MeterQuery.filterGroupBy'
5359-
- $ref: '#/components/parameters/MeterQuery.groupBy'
5360+
- $ref: '#/components/parameters/MeterQuery.filter'
53605361
description: Query meter for usage.
53615362
summary: Query meter
53625363
responses:
@@ -7674,8 +7675,9 @@ paths:
76747675
- $ref: '#/components/parameters/MeterQuery.to'
76757676
- $ref: '#/components/parameters/MeterQuery.windowSize'
76767677
- $ref: '#/components/parameters/MeterQuery.windowTimeZone'
7677-
- $ref: '#/components/parameters/MeterQuery.filterGroupBy'
76787678
- $ref: '#/components/parameters/MeterQuery.groupBy'
7679+
- $ref: '#/components/parameters/MeterQuery.filterGroupBy'
7680+
- $ref: '#/components/parameters/MeterQuery.filter'
76797681
description: Query meter for consumer portal. This endpoint is publicly exposable to consumers. Query meter for consumer portal. This endpoint is publicly exposable to consumers.
76807682
summary: Query meter Query meter
76817683
responses:
@@ -10617,6 +10619,26 @@ components:
1061710619
maxLength: 36
1061810620
explode: false
1061910621
style: form
10622+
MeterQuery.filter:
10623+
name: filter
10624+
in: query
10625+
required: false
10626+
description: |-
10627+
The filter for the events encoded as JSON string.
10628+
Can't be used together with from, to, subject, and filterGroupBy.
10629+
content:
10630+
application/json:
10631+
schema:
10632+
properties:
10633+
groupBy:
10634+
type: object
10635+
additionalProperties:
10636+
$ref: '#/components/schemas/FilterString'
10637+
subject:
10638+
$ref: '#/components/schemas/FilterString'
10639+
time:
10640+
$ref: '#/components/schemas/FilterTime'
10641+
format: application/json
1062010642
MeterQuery.filterGroupBy:
1062110643
name: filterGroupBy
1062210644
in: query

api/openapi.yaml

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5353,9 +5353,10 @@ paths:
53535353
- $ref: '#/components/parameters/MeterQuery.to'
53545354
- $ref: '#/components/parameters/MeterQuery.windowSize'
53555355
- $ref: '#/components/parameters/MeterQuery.windowTimeZone'
5356+
- $ref: '#/components/parameters/MeterQuery.groupBy'
53565357
- $ref: '#/components/parameters/MeterQuery.subject'
53575358
- $ref: '#/components/parameters/MeterQuery.filterGroupBy'
5358-
- $ref: '#/components/parameters/MeterQuery.groupBy'
5359+
- $ref: '#/components/parameters/MeterQuery.filter'
53595360
description: Query meter for usage.
53605361
summary: Query meter
53615362
responses:
@@ -7673,8 +7674,9 @@ paths:
76737674
- $ref: '#/components/parameters/MeterQuery.to'
76747675
- $ref: '#/components/parameters/MeterQuery.windowSize'
76757676
- $ref: '#/components/parameters/MeterQuery.windowTimeZone'
7676-
- $ref: '#/components/parameters/MeterQuery.filterGroupBy'
76777677
- $ref: '#/components/parameters/MeterQuery.groupBy'
7678+
- $ref: '#/components/parameters/MeterQuery.filterGroupBy'
7679+
- $ref: '#/components/parameters/MeterQuery.filter'
76787680
description: Query meter for consumer portal. This endpoint is publicly exposable to consumers. Query meter for consumer portal. This endpoint is publicly exposable to consumers.
76797681
summary: Query meter Query meter
76807682
responses:
@@ -10359,6 +10361,26 @@ components:
1035910361
maxLength: 36
1036010362
explode: false
1036110363
style: form
10364+
MeterQuery.filter:
10365+
name: filter
10366+
in: query
10367+
required: false
10368+
description: |-
10369+
The filter for the events encoded as JSON string.
10370+
Can't be used together with from, to, subject, and filterGroupBy.
10371+
content:
10372+
application/json:
10373+
schema:
10374+
properties:
10375+
groupBy:
10376+
type: object
10377+
additionalProperties:
10378+
$ref: '#/components/schemas/FilterString'
10379+
subject:
10380+
$ref: '#/components/schemas/FilterString'
10381+
time:
10382+
$ref: '#/components/schemas/FilterTime'
10383+
format: application/json
1036210384
MeterQuery.filterGroupBy:
1036310385
name: filterGroupBy
1036410386
in: query

api/spec/src/meters.tsp

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,16 @@ model MeterQuery {
353353
@example("America/New_York")
354354
windowTimeZone?: string = "UTC";
355355

356+
/**
357+
* If not specified a single aggregate will be returned for each subject and time window.
358+
* `subject` is a reserved group by value.
359+
*
360+
* For example: ?groupBy=subject&groupBy=model
361+
*/
362+
@query(#{ explode: true })
363+
@example(#["model", "type"])
364+
groupBy?: string[];
365+
356366
/**
357367
* Filtering by multiple subjects.
358368
*
@@ -372,14 +382,26 @@ model MeterQuery {
372382
filterGroupBy?: Record<string>;
373383

374384
/**
375-
* If not specified a single aggregate will be returned for each subject and time window.
376-
* `subject` is a reserved group by value.
377-
*
378-
* For example: ?groupBy=subject&groupBy=model
385+
* The filter for the events encoded as JSON string.
386+
* Can't be used together with `from`, `to`, `subject`, and `filterGroupBy`.
379387
*/
380-
@query(#{ explode: true })
381-
@example(#["model", "type"])
382-
groupBy?: string[];
388+
@example(#{
389+
groupBy: #{ `model`: #{ $eq: "gpt-4-turbo" }, type: #{ $eq: "prompt" } },
390+
subject: #{ $eq: "my-event-subject" },
391+
time: #{
392+
$and: #[
393+
#{ $gte: DateTime.fromISO("2025-01-01T00:00:00Z") },
394+
#{ $lte: DateTime.fromISO("2025-01-02T00:00:00Z") }
395+
],
396+
},
397+
})
398+
@query
399+
@encode("application/json")
400+
filter?: {
401+
groupBy?: Record<OpenMeter.FilterString>;
402+
subject?: OpenMeter.FilterString;
403+
time?: OpenMeter.FilterTime;
404+
};
383405
}
384406

385407
/**

openmeter/meter/httphandler/mapping.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import (
88
"github.com/samber/lo"
99

1010
"github.com/openmeterio/openmeter/api"
11+
"github.com/openmeterio/openmeter/openmeter/apiconverter"
1112
"github.com/openmeterio/openmeter/openmeter/meter"
1213
"github.com/openmeterio/openmeter/openmeter/streaming"
14+
"github.com/openmeterio/openmeter/pkg/filter"
1315
"github.com/openmeterio/openmeter/pkg/models"
1416
)
1517

@@ -131,3 +133,54 @@ func ToQueryMeterParams(m meter.Meter, apiParams api.QueryMeterParams) (streamin
131133

132134
return params, nil
133135
}
136+
137+
func ToQueryMeterParamsV2(m meter.Meter, apiParams api.QueryMeterParams) (streaming.QueryParamsV2, error) {
138+
params := streaming.QueryParamsV2{
139+
ClientID: apiParams.ClientId,
140+
}
141+
142+
if apiParams.WindowSize != nil {
143+
params.WindowSize = lo.ToPtr(meter.WindowSize(*apiParams.WindowSize))
144+
}
145+
146+
if apiParams.WindowTimeZone != nil {
147+
tz, err := time.LoadLocation(*apiParams.WindowTimeZone)
148+
if err != nil {
149+
err := fmt.Errorf("invalid time zone: %w", err)
150+
return params, models.NewGenericValidationError(err)
151+
}
152+
params.WindowTimeZone = tz
153+
}
154+
155+
if apiParams.GroupBy != nil {
156+
for _, groupBy := range *apiParams.GroupBy {
157+
// Validate group by, `subject` is a special group by
158+
if ok := groupBy == "subject" || m.GroupBy[groupBy] != ""; !ok {
159+
err := fmt.Errorf("invalid group by: %s", groupBy)
160+
return params, models.NewGenericValidationError(err)
161+
}
162+
163+
params.GroupBy = append(params.GroupBy, groupBy)
164+
}
165+
}
166+
167+
if apiParams.Filter != nil {
168+
if apiParams.Filter.GroupBy != nil {
169+
groupBy := map[string]filter.FilterString{}
170+
for k, v := range *apiParams.Filter.GroupBy {
171+
groupBy[k] = apiconverter.ConvertString(v)
172+
}
173+
params.Filter.GroupBy = &groupBy
174+
}
175+
176+
params.Filter.Subject = apiconverter.ConvertStringPtr(apiParams.Filter.Subject)
177+
params.Filter.Time = apiconverter.ConvertTimePtr(apiParams.Filter.Time)
178+
179+
// Add subject to group by if not already present
180+
if params.Filter.Subject != nil && !slices.Contains(params.GroupBy, "subject") {
181+
params.GroupBy = append(params.GroupBy, "subject")
182+
}
183+
}
184+
185+
return params, nil
186+
}

0 commit comments

Comments
 (0)