Skip to content

Commit 1c3937d

Browse files
authored
feat: list meters by event type (#3685)
1 parent a9a22dc commit 1c3937d

File tree

8 files changed

+196
-8
lines changed

8 files changed

+196
-8
lines changed

openmeter/ent/db/migrate/schema.go

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

openmeter/ent/schema/meter.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,14 @@ func (Meter) Fields() []ent.Field {
3838

3939
// Indexes of the Meter.
4040
func (Meter) Indexes() []ent.Index {
41-
return []ent.Index{index.Fields("namespace", "key").
42-
Annotations(
43-
entsql.IndexWhere("deleted_at IS NULL"),
44-
).
45-
Unique()}
41+
return []ent.Index{
42+
index.Fields("namespace", "key").
43+
Annotations(
44+
entsql.IndexWhere("deleted_at IS NULL"),
45+
).
46+
Unique(),
47+
index.Fields("namespace", "event_type"),
48+
}
4649
}
4750

4851
// Edges of the Meter.
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package adapter
2+
3+
import (
4+
"crypto/rand"
5+
"log/slog"
6+
"sync"
7+
"testing"
8+
"time"
9+
10+
"github.com/oklog/ulid/v2"
11+
"github.com/samber/lo"
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
14+
15+
entdb "github.com/openmeterio/openmeter/openmeter/ent/db"
16+
"github.com/openmeterio/openmeter/openmeter/meter"
17+
"github.com/openmeterio/openmeter/openmeter/testutils"
18+
"github.com/openmeterio/openmeter/pkg/pagination"
19+
)
20+
21+
func Test_Adapter(t *testing.T) {
22+
env := NewTestEnv(t)
23+
t.Cleanup(func() {
24+
env.Close(t)
25+
})
26+
27+
env.DBSchemaMigrate(t)
28+
29+
namespace := NewTestNamespace(t)
30+
31+
t.Run("Meter", func(t *testing.T) {
32+
t.Run("List", func(t *testing.T) {
33+
meterInputs := []meter.CreateMeterInput{
34+
{
35+
Namespace: namespace,
36+
Name: "Test meter 1",
37+
Key: "test-meter-1",
38+
Aggregation: meter.MeterAggregationSum,
39+
EventType: "om.meter",
40+
ValueProperty: lo.ToPtr("$.value"),
41+
GroupBy: map[string]string{
42+
"group": "$.group",
43+
"group_2": "$.group_2",
44+
},
45+
},
46+
{
47+
Namespace: namespace,
48+
Name: "Test meter 2",
49+
Key: "test-meter-2",
50+
Aggregation: meter.MeterAggregationCount,
51+
EventType: "om.meter",
52+
ValueProperty: nil,
53+
GroupBy: map[string]string{
54+
"group": "$.group",
55+
"group_2": "$.group_2",
56+
},
57+
},
58+
}
59+
60+
for _, input := range meterInputs {
61+
_, err := env.Meter.CreateMeter(t.Context(), input)
62+
require.NoErrorf(t, err, "creating meter must not fail")
63+
}
64+
65+
t.Run("FilterByEventTypes", func(t *testing.T) {
66+
out, err := env.Meter.ListMeters(t.Context(), meter.ListMetersParams{
67+
Page: pagination.Page{
68+
PageSize: 100,
69+
PageNumber: 1,
70+
},
71+
Namespace: namespace,
72+
EventTypes: lo.ToPtr([]string{
73+
"om.meter",
74+
}),
75+
})
76+
require.NoErrorf(t, err, "listing meters must not fail")
77+
78+
require.Lenf(t, out.Items, 2, "expected 2 meters with event type om.meter, got %d", len(out.Items))
79+
80+
for _, m := range out.Items {
81+
assert.Equalf(t, m.EventType, "om.meter", "expected meter event type om.meter, got %s", m.EventType)
82+
}
83+
})
84+
})
85+
})
86+
}
87+
88+
type TestEnv struct {
89+
Logger *slog.Logger
90+
Meter *Adapter
91+
92+
Client *entdb.Client
93+
db *testutils.TestDB
94+
close sync.Once
95+
}
96+
97+
func (e *TestEnv) DBSchemaMigrate(t *testing.T) {
98+
t.Helper()
99+
100+
require.NotNilf(t, e.db, "database must be initialized")
101+
102+
err := e.db.EntDriver.Client().Schema.Create(t.Context())
103+
require.NoErrorf(t, err, "schema migration must not fail")
104+
}
105+
106+
func (e *TestEnv) Close(t *testing.T) {
107+
t.Helper()
108+
109+
e.close.Do(func() {
110+
if e.db != nil {
111+
if err := e.db.EntDriver.Close(); err != nil {
112+
t.Errorf("failed to close ent driver: %v", err)
113+
}
114+
115+
if err := e.db.PGDriver.Close(); err != nil {
116+
t.Errorf("failed to postgres driver: %v", err)
117+
}
118+
}
119+
120+
if e.Client != nil {
121+
if err := e.Client.Close(); err != nil {
122+
t.Errorf("failed to close ent client: %v", err)
123+
}
124+
}
125+
})
126+
}
127+
128+
func NewTestEnv(t *testing.T) *TestEnv {
129+
t.Helper()
130+
131+
// Init logger
132+
logger := testutils.NewDiscardLogger(t)
133+
134+
// Init database
135+
db := testutils.InitPostgresDB(t)
136+
client := db.EntDriver.Client()
137+
138+
// Init meter service
139+
meterAdapter, err := New(Config{
140+
Client: client,
141+
Logger: logger,
142+
})
143+
require.NoErrorf(t, err, "initializing meter adapter must not fail")
144+
require.NotNilf(t, meterAdapter, "meter adapter must not be nil")
145+
146+
return &TestEnv{
147+
Logger: logger,
148+
Meter: meterAdapter,
149+
db: db,
150+
Client: client,
151+
}
152+
}
153+
154+
func NewTestULID(t *testing.T) string {
155+
t.Helper()
156+
157+
return ulid.MustNew(ulid.Timestamp(time.Now().UTC()), rand.Reader).String()
158+
}
159+
160+
var NewTestNamespace = NewTestULID

openmeter/meter/adapter/meter.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,14 @@ func (a *Adapter) ListMeters(ctx context.Context, params meter.ListMetersParams)
3838
query = query.Where(meterdb.KeyIn(*params.SlugFilter...))
3939
}
4040

41+
if params.EventTypes != nil {
42+
query = query.Where(meterdb.EventTypeIn(*params.EventTypes...))
43+
}
44+
4145
// Ordering
4246
if params.Order != "" {
43-
order := []sql.OrderTermOption{}
47+
var order []sql.OrderTermOption
48+
4449
if !params.Order.IsDefaultValue() {
4550
order = entutils.GetOrdering(params.Order)
4651
}

openmeter/meter/service.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ type ListMetersParams struct {
7373

7474
// IncludeDeleted is a flag to include deleted meters in the list.
7575
IncludeDeleted bool
76+
77+
// Filter by event types
78+
EventTypes *[]string
7679
}
7780

7881
// Validate validates the list meters parameters.
@@ -95,7 +98,14 @@ func (p ListMetersParams) Validate() error {
9598
for _, slug := range *p.SlugFilter {
9699
if slug == "" {
97100
errs = append(errs, errors.New("slug filter must not contain empty string"))
98-
break
101+
}
102+
}
103+
}
104+
105+
if p.EventTypes != nil {
106+
for _, eventType := range *p.EventTypes {
107+
if eventType == "" {
108+
errs = append(errs, errors.New("event type filter must not contain empty string"))
99109
}
100110
}
101111
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- reverse: create index "meter_namespace_event_type" to table: "meters"
2+
DROP INDEX "meter_namespace_event_type";
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- create index "meter_namespace_event_type" to table: "meters"
2+
CREATE INDEX "meter_namespace_event_type" ON "meters" ("namespace", "event_type");

tools/migrate/migrations/atlas.sum

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
h1:si+DV0AgNwKY2ymi4F7OArThOL1EUI2xjamCEt/aF7U=
1+
h1:oV9bvgJUO9Q1fn6Efxv39DURWnVmI6HcrgklVNegg6c=
22
20240826120919_init.up.sql h1:tc1V91/smlmaeJGQ8h+MzTEeFjjnrrFDbDAjOYJK91o=
33
20240903155435_entitlement-expired-index.up.sql h1:Hp8u5uckmLXc1cRvWU0AtVnnK8ShlpzZNp8pbiJLhac=
44
20240917172257_billing-entities.up.sql h1:Q1dAMo0Vjiit76OybClNfYPGC5nmvov2/M2W1ioi4Kw=
@@ -140,3 +140,4 @@ h1:si+DV0AgNwKY2ymi4F7OArThOL1EUI2xjamCEt/aF7U=
140140
20251121083843_meter_annotations.up.sql h1:9CB03JH2Py9/0yLIrZtKXyqQZxSaxH+4Dys9L/DwXNE=
141141
20251127184619_invoice-payment-processing-entered-at.up.sql h1:NlpkZurSDozGkRYcCARlNp6nSXbk3jqZeO5YYPEnM20=
142142
20251210162410_make-customer-usage-attribution-optional.up.sql h1:ZtEKJl8Yj1QS2Ke0lBd8fc7ctHBLPb3XkbJ6peeN1zA=
143+
20251211145809_meter_event_type.up.sql h1:sG9Y9qUrA+tIjeHdSasBKHJRisBjXgm6nsMQMh1czXg=

0 commit comments

Comments
 (0)