Skip to content

Commit 86b0353

Browse files
committed
chore: add unknown usage event type error
1 parent 1a601c3 commit 86b0353

File tree

3 files changed

+55
-23
lines changed

3 files changed

+55
-23
lines changed

coderd/usage/usagetypes/events.go

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ package usagetypes
1313
import (
1414
"bytes"
1515
"encoding/json"
16+
"fmt"
1617
"strings"
1718

1819
"golang.org/x/xerrors"
@@ -22,6 +23,10 @@ import (
2223
// type `usage_event_type`.
2324
type UsageEventType string
2425

26+
// All event types.
27+
//
28+
// When adding a new event type, ensure you add it to the Valid method and the
29+
// ParseEventWithType function.
2530
const (
2631
UsageEventTypeDCManagedAgentsV1 UsageEventType = "dc_managed_agents_v1"
2732
)
@@ -43,38 +48,56 @@ func (e UsageEventType) IsHeartbeat() bool {
4348
return e.Valid() && strings.HasPrefix(string(e), "hb_")
4449
}
4550

46-
// ParseEvent parses the raw event data into the specified Go type. It fails if
47-
// there is any unknown fields or extra data after the event. The returned event
48-
// is validated.
49-
func ParseEvent[T Event](data json.RawMessage) (T, error) {
51+
// ParseEvent parses the raw event data into the provided event. It fails if
52+
// there is any unknown fields or extra data at the end of the JSON. The
53+
// returned event is validated.
54+
func ParseEvent(data json.RawMessage, out Event) error {
5055
dec := json.NewDecoder(bytes.NewReader(data))
5156
dec.DisallowUnknownFields()
5257

53-
var event T
54-
err := dec.Decode(&event)
58+
err := dec.Decode(out)
5559
if err != nil {
56-
return event, xerrors.Errorf("unmarshal %T event: %w", event, err)
60+
return xerrors.Errorf("unmarshal %T event: %w", out, err)
5761
}
5862
if dec.More() {
59-
return event, xerrors.Errorf("extra data after %T event", event)
63+
return xerrors.Errorf("extra data after %T event", out)
6064
}
61-
err = event.Valid()
65+
err = out.Valid()
6266
if err != nil {
63-
return event, xerrors.Errorf("invalid %T event: %w", event, err)
67+
return xerrors.Errorf("invalid %T event: %w", out, err)
6468
}
6569

66-
return event, nil
70+
return nil
71+
}
72+
73+
// UnknownEventTypeError is returned by ParseEventWithType when an unknown event
74+
// type is encountered.
75+
type UnknownEventTypeError struct {
76+
EventType string
77+
}
78+
79+
var _ error = UnknownEventTypeError{}
80+
81+
// Error implements error.
82+
func (e UnknownEventTypeError) Error() string {
83+
return fmt.Sprintf("unknown usage event type: %q", e.EventType)
6784
}
6885

6986
// ParseEventWithType parses the raw event data into the specified Go type. It
7087
// fails if there is any unknown fields or extra data after the event. The
7188
// returned event is validated.
89+
//
90+
// If the event type is unknown, UnknownEventTypeError is returned.
7291
func ParseEventWithType(eventType UsageEventType, data json.RawMessage) (Event, error) {
7392
switch eventType {
7493
case UsageEventTypeDCManagedAgentsV1:
75-
return ParseEvent[DCManagedAgentsV1](data)
94+
var event DCManagedAgentsV1
95+
if err := ParseEvent(data, &event); err != nil {
96+
return nil, err
97+
}
98+
return event, nil
7699
default:
77-
return nil, xerrors.Errorf("unknown event type: %s", eventType)
100+
return nil, UnknownEventTypeError{EventType: string(eventType)}
78101
}
79102
}
80103

coderd/usage/usagetypes/events_test.go

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,34 @@ func TestParseEvent(t *testing.T) {
1313

1414
t.Run("ExtraFields", func(t *testing.T) {
1515
t.Parallel()
16-
_, err := usagetypes.ParseEvent[usagetypes.DCManagedAgentsV1]([]byte(`{"count": 1, "extra": "field"}`))
17-
require.ErrorContains(t, err, "unmarshal usagetypes.DCManagedAgentsV1 event")
16+
var event usagetypes.DCManagedAgentsV1
17+
err := usagetypes.ParseEvent([]byte(`{"count": 1, "extra": "field"}`), &event)
18+
require.ErrorContains(t, err, "unmarshal *usagetypes.DCManagedAgentsV1 event")
1819
})
1920

2021
t.Run("ExtraData", func(t *testing.T) {
2122
t.Parallel()
22-
_, err := usagetypes.ParseEvent[usagetypes.DCManagedAgentsV1]([]byte(`{"count": 1}{"count": 2}`))
23-
require.ErrorContains(t, err, "extra data after usagetypes.DCManagedAgentsV1 event")
23+
var event usagetypes.DCManagedAgentsV1
24+
err := usagetypes.ParseEvent([]byte(`{"count": 1}{"count": 2}`), &event)
25+
require.ErrorContains(t, err, "extra data after *usagetypes.DCManagedAgentsV1 event")
2426
})
2527

2628
t.Run("DCManagedAgentsV1", func(t *testing.T) {
2729
t.Parallel()
2830

29-
event, err := usagetypes.ParseEvent[usagetypes.DCManagedAgentsV1]([]byte(`{"count": 1}`))
31+
var event usagetypes.DCManagedAgentsV1
32+
err := usagetypes.ParseEvent([]byte(`{"count": 1}`), &event)
3033
require.NoError(t, err)
3134
require.Equal(t, usagetypes.DCManagedAgentsV1{Count: 1}, event)
3235
require.Equal(t, map[string]any{"count": uint64(1)}, event.Fields())
3336

34-
_, err = usagetypes.ParseEvent[usagetypes.DCManagedAgentsV1]([]byte(`{"count": "invalid"}`))
35-
require.ErrorContains(t, err, "unmarshal usagetypes.DCManagedAgentsV1 event")
37+
event = usagetypes.DCManagedAgentsV1{}
38+
err = usagetypes.ParseEvent([]byte(`{"count": "invalid"}`), &event)
39+
require.ErrorContains(t, err, "unmarshal *usagetypes.DCManagedAgentsV1 event")
3640

37-
_, err = usagetypes.ParseEvent[usagetypes.DCManagedAgentsV1]([]byte(`{}`))
38-
require.ErrorContains(t, err, "invalid usagetypes.DCManagedAgentsV1 event: count must be greater than 0")
41+
event = usagetypes.DCManagedAgentsV1{}
42+
err = usagetypes.ParseEvent([]byte(`{}`), &event)
43+
require.ErrorContains(t, err, "invalid *usagetypes.DCManagedAgentsV1 event: count must be greater than 0")
3944
})
4045
}
4146

@@ -45,7 +50,9 @@ func TestParseEventWithType(t *testing.T) {
4550
t.Run("UnknownEvent", func(t *testing.T) {
4651
t.Parallel()
4752
_, err := usagetypes.ParseEventWithType(usagetypes.UsageEventType("fake"), []byte(`{}`))
48-
require.ErrorContains(t, err, "unknown event type: fake")
53+
var unknownEventTypeError usagetypes.UnknownEventTypeError
54+
require.ErrorAs(t, err, &unknownEventTypeError)
55+
require.Equal(t, "fake", unknownEventTypeError.EventType)
4956
})
5057

5158
t.Run("DCManagedAgentsV1", func(t *testing.T) {

enterprise/coderd/usage/publisher.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"golang.org/x/xerrors"
1515

1616
"cdr.dev/slog"
17+
"github.com/coder/coder/v2/buildinfo"
1718
"github.com/coder/coder/v2/coderd/database"
1819
"github.com/coder/coder/v2/coderd/database/dbauthz"
1920
"github.com/coder/coder/v2/coderd/database/dbtime"
@@ -396,6 +397,7 @@ func (p *tallymanPublisher) sendPublishRequest(ctx context.Context, deploymentID
396397
if err != nil {
397398
return usagetypes.TallymanV1IngestResponse{}, err
398399
}
400+
r.Header.Set("User-Agent", "coderd/"+buildinfo.Version())
399401
r.Header.Set(usagetypes.TallymanCoderLicenseKeyHeader, licenseJwt)
400402
r.Header.Set(usagetypes.TallymanCoderDeploymentIDHeader, deploymentID.String())
401403

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy