mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
chore: add unknown usage event type error (#19436)
- Adds `usagetypes.UnknownEventTypeError` type, which is returned by `ParseEventWithType` - Changes `ParseEvent` to not be a generic function since it doesn't really need it - Adds `User-Agent` to tallyman requests
This commit is contained in:
@@ -13,6 +13,7 @@ package usagetypes
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
@@ -22,6 +23,10 @@ import (
|
||||
// type `usage_event_type`.
|
||||
type UsageEventType string
|
||||
|
||||
// All event types.
|
||||
//
|
||||
// When adding a new event type, ensure you add it to the Valid method and the
|
||||
// ParseEventWithType function.
|
||||
const (
|
||||
UsageEventTypeDCManagedAgentsV1 UsageEventType = "dc_managed_agents_v1"
|
||||
)
|
||||
@@ -43,38 +48,56 @@ func (e UsageEventType) IsHeartbeat() bool {
|
||||
return e.Valid() && strings.HasPrefix(string(e), "hb_")
|
||||
}
|
||||
|
||||
// ParseEvent parses the raw event data into the specified Go type. It fails if
|
||||
// there is any unknown fields or extra data after the event. The returned event
|
||||
// is validated.
|
||||
func ParseEvent[T Event](data json.RawMessage) (T, error) {
|
||||
// ParseEvent parses the raw event data into the provided event. It fails if
|
||||
// there is any unknown fields or extra data at the end of the JSON. The
|
||||
// returned event is validated.
|
||||
func ParseEvent(data json.RawMessage, out Event) error {
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
dec.DisallowUnknownFields()
|
||||
|
||||
var event T
|
||||
err := dec.Decode(&event)
|
||||
err := dec.Decode(out)
|
||||
if err != nil {
|
||||
return event, xerrors.Errorf("unmarshal %T event: %w", event, err)
|
||||
return xerrors.Errorf("unmarshal %T event: %w", out, err)
|
||||
}
|
||||
if dec.More() {
|
||||
return event, xerrors.Errorf("extra data after %T event", event)
|
||||
return xerrors.Errorf("extra data after %T event", out)
|
||||
}
|
||||
err = event.Valid()
|
||||
err = out.Valid()
|
||||
if err != nil {
|
||||
return event, xerrors.Errorf("invalid %T event: %w", event, err)
|
||||
return xerrors.Errorf("invalid %T event: %w", out, err)
|
||||
}
|
||||
|
||||
return event, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnknownEventTypeError is returned by ParseEventWithType when an unknown event
|
||||
// type is encountered.
|
||||
type UnknownEventTypeError struct {
|
||||
EventType string
|
||||
}
|
||||
|
||||
var _ error = UnknownEventTypeError{}
|
||||
|
||||
// Error implements error.
|
||||
func (e UnknownEventTypeError) Error() string {
|
||||
return fmt.Sprintf("unknown usage event type: %q", e.EventType)
|
||||
}
|
||||
|
||||
// ParseEventWithType parses the raw event data into the specified Go type. It
|
||||
// fails if there is any unknown fields or extra data after the event. The
|
||||
// returned event is validated.
|
||||
//
|
||||
// If the event type is unknown, UnknownEventTypeError is returned.
|
||||
func ParseEventWithType(eventType UsageEventType, data json.RawMessage) (Event, error) {
|
||||
switch eventType {
|
||||
case UsageEventTypeDCManagedAgentsV1:
|
||||
return ParseEvent[DCManagedAgentsV1](data)
|
||||
var event DCManagedAgentsV1
|
||||
if err := ParseEvent(data, &event); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return event, nil
|
||||
default:
|
||||
return nil, xerrors.Errorf("unknown event type: %s", eventType)
|
||||
return nil, UnknownEventTypeError{EventType: string(eventType)}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,29 +13,34 @@ func TestParseEvent(t *testing.T) {
|
||||
|
||||
t.Run("ExtraFields", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := usagetypes.ParseEvent[usagetypes.DCManagedAgentsV1]([]byte(`{"count": 1, "extra": "field"}`))
|
||||
require.ErrorContains(t, err, "unmarshal usagetypes.DCManagedAgentsV1 event")
|
||||
var event usagetypes.DCManagedAgentsV1
|
||||
err := usagetypes.ParseEvent([]byte(`{"count": 1, "extra": "field"}`), &event)
|
||||
require.ErrorContains(t, err, "unmarshal *usagetypes.DCManagedAgentsV1 event")
|
||||
})
|
||||
|
||||
t.Run("ExtraData", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := usagetypes.ParseEvent[usagetypes.DCManagedAgentsV1]([]byte(`{"count": 1}{"count": 2}`))
|
||||
require.ErrorContains(t, err, "extra data after usagetypes.DCManagedAgentsV1 event")
|
||||
var event usagetypes.DCManagedAgentsV1
|
||||
err := usagetypes.ParseEvent([]byte(`{"count": 1}{"count": 2}`), &event)
|
||||
require.ErrorContains(t, err, "extra data after *usagetypes.DCManagedAgentsV1 event")
|
||||
})
|
||||
|
||||
t.Run("DCManagedAgentsV1", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
event, err := usagetypes.ParseEvent[usagetypes.DCManagedAgentsV1]([]byte(`{"count": 1}`))
|
||||
var event usagetypes.DCManagedAgentsV1
|
||||
err := usagetypes.ParseEvent([]byte(`{"count": 1}`), &event)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, usagetypes.DCManagedAgentsV1{Count: 1}, event)
|
||||
require.Equal(t, map[string]any{"count": uint64(1)}, event.Fields())
|
||||
|
||||
_, err = usagetypes.ParseEvent[usagetypes.DCManagedAgentsV1]([]byte(`{"count": "invalid"}`))
|
||||
require.ErrorContains(t, err, "unmarshal usagetypes.DCManagedAgentsV1 event")
|
||||
event = usagetypes.DCManagedAgentsV1{}
|
||||
err = usagetypes.ParseEvent([]byte(`{"count": "invalid"}`), &event)
|
||||
require.ErrorContains(t, err, "unmarshal *usagetypes.DCManagedAgentsV1 event")
|
||||
|
||||
_, err = usagetypes.ParseEvent[usagetypes.DCManagedAgentsV1]([]byte(`{}`))
|
||||
require.ErrorContains(t, err, "invalid usagetypes.DCManagedAgentsV1 event: count must be greater than 0")
|
||||
event = usagetypes.DCManagedAgentsV1{}
|
||||
err = usagetypes.ParseEvent([]byte(`{}`), &event)
|
||||
require.ErrorContains(t, err, "invalid *usagetypes.DCManagedAgentsV1 event: count must be greater than 0")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -45,7 +50,9 @@ func TestParseEventWithType(t *testing.T) {
|
||||
t.Run("UnknownEvent", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := usagetypes.ParseEventWithType(usagetypes.UsageEventType("fake"), []byte(`{}`))
|
||||
require.ErrorContains(t, err, "unknown event type: fake")
|
||||
var unknownEventTypeError usagetypes.UnknownEventTypeError
|
||||
require.ErrorAs(t, err, &unknownEventTypeError)
|
||||
require.Equal(t, "fake", unknownEventTypeError.EventType)
|
||||
})
|
||||
|
||||
t.Run("DCManagedAgentsV1", func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user