Skip to content

Commit 35a558c

Browse files
authored
refactor: support for reserved event types (#3686)
1 parent 31968db commit 35a558c

File tree

4 files changed

+132
-49
lines changed

4 files changed

+132
-49
lines changed

openmeter/meter/meter.go

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ var groupByKeyRegExp = regexp.MustCompile(`^[a-zA-Z_][0-9a-zA-Z_]*$`)
1414

1515
type EventTypePattern = regexp.Regexp
1616

17+
func NewEventTypeValidator(reserved []*EventTypePattern) models.ValidatorFunc[string] {
18+
return func(eventType string) error {
19+
for _, pattern := range reserved {
20+
if pattern == nil {
21+
continue
22+
}
23+
24+
if ok := pattern.MatchString(eventType); ok {
25+
return fmt.Errorf("event type '%s' matched reserved pattern '%s'", eventType, pattern.String())
26+
}
27+
}
28+
29+
return nil
30+
}
31+
}
32+
1733
type MeterAggregation string
1834

1935
// Note: keep values up to date in the meter package
@@ -137,6 +153,11 @@ func (f OrderBy) Values() []OrderBy {
137153
}
138154
}
139155

156+
var (
157+
_ models.Validator = (*Meter)(nil)
158+
_ models.CustomValidator[Meter] = (*Meter)(nil)
159+
)
160+
140161
// Meter is the meter model
141162
type Meter struct {
142163
models.ManagedResource `mapstructure:",squash"`
@@ -151,77 +172,81 @@ type Meter struct {
151172
GroupBy map[string]string
152173
}
153174

154-
func (m1 Meter) Equal(m2 Meter) error {
155-
if m1.Namespace != m2.Namespace {
175+
func (m Meter) ValidateWith(validators ...models.ValidatorFunc[Meter]) error {
176+
return models.Validate(m, validators...)
177+
}
178+
179+
func (m Meter) Equal(m2 Meter) error {
180+
if m.Namespace != m2.Namespace {
156181
return errors.New("namespace mismatch")
157182
}
158183

159-
if m1.Key != m2.Key {
184+
if m.Key != m2.Key {
160185
return errors.New("key mismatch")
161186
}
162187

163-
if m1.Name != m2.Name {
188+
if m.Name != m2.Name {
164189
return errors.New("name mismatch")
165190
}
166191

167-
if m1.Description != nil && m2.Description != nil {
168-
if *m1.Description != *m2.Description {
192+
if m.Description != nil && m2.Description != nil {
193+
if *m.Description != *m2.Description {
169194
return errors.New("description mismatch")
170195
}
171196
}
172197

173-
if m1.Description == nil && m2.Description != nil {
198+
if m.Description == nil && m2.Description != nil {
174199
return errors.New("description mismatch")
175200
}
176201

177-
if m1.Description != nil && m2.Description == nil {
202+
if m.Description != nil && m2.Description == nil {
178203
return errors.New("description mismatch")
179204
}
180205

181-
if m1.Aggregation != m2.Aggregation {
206+
if m.Aggregation != m2.Aggregation {
182207
return errors.New("aggregation mismatch")
183208
}
184209

185-
if m1.EventType != m2.EventType {
210+
if m.EventType != m2.EventType {
186211
return errors.New("event type mismatch")
187212
}
188213

189-
if m1.ValueProperty != nil && m2.ValueProperty != nil {
190-
if *m1.ValueProperty != *m2.ValueProperty {
214+
if m.ValueProperty != nil && m2.ValueProperty != nil {
215+
if *m.ValueProperty != *m2.ValueProperty {
191216
return errors.New("value property mismatch")
192217
}
193218
}
194219

195-
if m1.ValueProperty == nil && m2.ValueProperty != nil {
220+
if m.ValueProperty == nil && m2.ValueProperty != nil {
196221
return errors.New("value property mismatch")
197222
}
198223

199-
if m1.ValueProperty != nil && m2.ValueProperty == nil {
224+
if m.ValueProperty != nil && m2.ValueProperty == nil {
200225
return errors.New("value property mismatch")
201226
}
202227

203-
if len(m1.GroupBy) != len(m2.GroupBy) {
228+
if len(m.GroupBy) != len(m2.GroupBy) {
204229
return errors.New("group by mismatch")
205230
}
206231

207-
for key, value := range m1.GroupBy {
232+
for key, value := range m.GroupBy {
208233
if m2Value, ok := m2.GroupBy[key]; !ok || value != m2Value {
209234
return errors.New("group by mismatch")
210235
}
211236
}
212237

213-
if !m1.Metadata.Equal(m2.Metadata) {
238+
if !m.Metadata.Equal(m2.Metadata) {
214239
return errors.New("metadata mismatch")
215240
}
216241

217-
if !m1.Annotations.Equal(m2.Annotations) {
242+
if !m.Annotations.Equal(m2.Annotations) {
218243
return errors.New("annotations mismatch")
219244
}
220245

221246
return nil
222247
}
223248

224-
func (m *Meter) Validate() error {
249+
func (m Meter) Validate() error {
225250
var errs []error
226251

227252
if err := m.ManagedResource.Validate(); err != nil {

openmeter/meter/meter_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ package meter
22

33
import (
44
"fmt"
5+
"regexp"
56
"testing"
67
"time"
78

89
"github.com/oklog/ulid/v2"
910
"github.com/samber/lo"
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
1013

1114
"github.com/openmeterio/openmeter/pkg/models"
1215
)
@@ -334,3 +337,51 @@ func TestMeterValidation(t *testing.T) {
334337
})
335338
}
336339
}
340+
341+
func Test_EventTypeFilter(t *testing.T) {
342+
var reservedEventTypePatterns []*EventTypePattern
343+
344+
patterns := []string{
345+
`^om\.`,
346+
`^_\.`,
347+
}
348+
349+
for _, pattern := range patterns {
350+
re, err := regexp.Compile(pattern)
351+
require.NoErrorf(t, err, "invalid regexp pattern '%s'", pattern)
352+
353+
reservedEventTypePatterns = append(reservedEventTypePatterns, re)
354+
}
355+
356+
validator := NewEventTypeValidator(reservedEventTypePatterns)
357+
358+
tests := []struct {
359+
name string
360+
eventType string
361+
362+
expectedMatch bool
363+
}{
364+
{
365+
name: "Random",
366+
eventType: "event-type-1",
367+
expectedMatch: false,
368+
},
369+
{
370+
name: "Openmeter",
371+
eventType: "om.event-type-1",
372+
expectedMatch: true,
373+
},
374+
{
375+
name: "System",
376+
eventType: "_.event-type-1",
377+
expectedMatch: true,
378+
},
379+
}
380+
381+
for _, test := range tests {
382+
t.Run(test.name, func(t *testing.T) {
383+
err := validator(test.eventType)
384+
assert.Equal(t, test.expectedMatch, err != nil)
385+
})
386+
}
387+
}

openmeter/meter/service.go

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -143,26 +143,6 @@ func (i CreateMeterInput) ValidateWith(validators ...models.ValidatorFunc[Create
143143
return models.Validate(i, validators...)
144144
}
145145

146-
func ValidateCreateMeterInputWithReservedEventTypes(reserved []*EventTypePattern) models.ValidatorFunc[CreateMeterInput] {
147-
return func(input CreateMeterInput) error {
148-
if input.AllowReservedEventTypes {
149-
return nil
150-
}
151-
152-
for _, pattern := range reserved {
153-
if pattern == nil {
154-
continue
155-
}
156-
157-
if ok := pattern.MatchString(input.EventType); ok {
158-
return fmt.Errorf("event type '%s' is reserved: matched pattern '%s'", input.EventType, pattern.String())
159-
}
160-
}
161-
162-
return nil
163-
}
164-
}
165-
166146
// Validate validates the create meter input.
167147
func (i CreateMeterInput) Validate() error {
168148
var errs []error
@@ -216,6 +196,8 @@ type UpdateMeterInput struct {
216196
GroupBy map[string]string
217197
Metadata models.Metadata
218198
Annotations *models.Annotations
199+
200+
inputOptions
219201
}
220202

221203
// Validate validates the create meter input.
@@ -246,6 +228,8 @@ func (i UpdateMeterInput) Validate(valueProperty *string) error {
246228
type DeleteMeterInput struct {
247229
Namespace string
248230
IDOrSlug string
231+
232+
inputOptions
249233
}
250234

251235
// Validate validates the delete meter input.

openmeter/meter/service/manage.go

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type ManageService struct {
2121
adapter *adapter.Adapter
2222
publisher eventbus.Publisher
2323
namespaceManager *namespace.Manager
24-
reservedEventTypes []*meter.EventTypePattern
24+
eventTypeValidator models.ValidatorFunc[string]
2525
}
2626

2727
func NewManage(
@@ -35,7 +35,7 @@ func NewManage(
3535
adapter: adapter,
3636
publisher: publisher,
3737
namespaceManager: namespaceManager,
38-
reservedEventTypes: reservedEventTypes,
38+
eventTypeValidator: meter.NewEventTypeValidator(reservedEventTypes),
3939
}
4040
}
4141

@@ -51,11 +51,12 @@ func (s *ManageService) CreateMeter(ctx context.Context, input meter.CreateMeter
5151
return meter.Meter{}, fmt.Errorf("invalid create meter params: %w", err)
5252
}
5353

54-
if err := input.ValidateWith(
55-
// Validate with reserved event types
56-
meter.ValidateCreateMeterInputWithReservedEventTypes(s.reservedEventTypes),
57-
); err != nil {
58-
return meter.Meter{}, fmt.Errorf("invalid create meter params: %w", err)
54+
// Validate input with reserved event types
55+
if !input.AllowReservedEventTypes {
56+
err := s.eventTypeValidator(input.EventType)
57+
if err != nil {
58+
return meter.Meter{}, models.NewGenericValidationError(fmt.Errorf("invalid event type: %w", err))
59+
}
5960
}
6061

6162
// Create the meter
@@ -82,7 +83,10 @@ func (s *ManageService) CreateMeter(ctx context.Context, input meter.CreateMeter
8283
// DeleteMeter deletes a meter
8384
func (s *ManageService) DeleteMeter(ctx context.Context, input meter.DeleteMeterInput) error {
8485
// Get the meter
85-
getMeter, err := s.GetMeterByIDOrSlug(ctx, meter.GetMeterInput(input))
86+
getMeter, err := s.GetMeterByIDOrSlug(ctx, meter.GetMeterInput{
87+
Namespace: input.Namespace,
88+
IDOrSlug: input.IDOrSlug,
89+
})
8690
if err != nil {
8791
return err
8892
}
@@ -92,6 +96,14 @@ func (s *ManageService) DeleteMeter(ctx context.Context, input meter.DeleteMeter
9296
return meter.NewMeterNotFoundError(getMeter.Key)
9397
}
9498

99+
// Validate input with reserved event types
100+
if !input.AllowReservedEventTypes {
101+
err = s.eventTypeValidator(getMeter.EventType)
102+
if err != nil {
103+
return models.NewGenericValidationError(fmt.Errorf("invalid event type: %w", err))
104+
}
105+
}
106+
95107
// Check if the meter has active features
96108
hasFeatures, err := s.adapter.HasActiveFeatureForMeter(ctx, input.Namespace, getMeter.Key)
97109
if err != nil {
@@ -125,7 +137,10 @@ func (s *ManageService) DeleteMeter(ctx context.Context, input meter.DeleteMeter
125137
}
126138

127139
// Get the deleted meter
128-
deletedMeter, err := s.GetMeterByIDOrSlug(ctx, meter.GetMeterInput(input))
140+
deletedMeter, err := s.GetMeterByIDOrSlug(ctx, meter.GetMeterInput{
141+
Namespace: input.Namespace,
142+
IDOrSlug: input.IDOrSlug,
143+
})
129144
if err != nil {
130145
return err
131146
}
@@ -150,7 +165,15 @@ func (s *ManageService) UpdateMeter(ctx context.Context, input meter.UpdateMeter
150165
return meter.Meter{}, err
151166
}
152167

153-
if err := input.Validate(currentMeter.ValueProperty); err != nil {
168+
// Validate input with reserved event types
169+
if !input.AllowReservedEventTypes {
170+
err = s.eventTypeValidator(currentMeter.EventType)
171+
if err != nil {
172+
return meter.Meter{}, models.NewGenericValidationError(fmt.Errorf("invalid event type: %w", err))
173+
}
174+
}
175+
176+
if err = input.Validate(currentMeter.ValueProperty); err != nil {
154177
return meter.Meter{}, models.NewGenericValidationError(err)
155178
}
156179

0 commit comments

Comments
 (0)