Skip to content

Commit

Permalink
Merge branch 'feature/external-uuid-base' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
UlrichEckhardt committed Jan 6, 2024
2 parents a3112dc + 47cd62d commit fc654a4
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 16 deletions.
5 changes: 5 additions & 0 deletions broker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"time"

"github.com/gofrs/uuid"
"github.com/inconshreveable/log15"
)

Expand Down Expand Up @@ -157,6 +158,7 @@ func (handler *RequestProcessor) startApiCall(ctx context.Context, request *requ
// emit event that a request was started
handler.store.Insert(
ctx,
uuid.Nil,
APIRequestEvent{
Attempt: attempt,
},
Expand All @@ -171,6 +173,7 @@ func (handler *RequestProcessor) startApiCall(ctx context.Context, request *requ
func() {
_, err := handler.store.Insert(
ctx,
uuid.Nil,
APITimeoutEvent{
Attempt: attempt,
},
Expand All @@ -192,6 +195,7 @@ func (handler *RequestProcessor) startApiCall(ctx context.Context, request *requ
if response != nil {
handler.store.Insert(
ctx,
uuid.Nil,
APIResponseEvent{
Attempt: attempt,
Response: *response,
Expand All @@ -201,6 +205,7 @@ func (handler *RequestProcessor) startApiCall(ctx context.Context, request *requ
} else if err != nil {
handler.store.Insert(
ctx,
uuid.Nil,
APIFailureEvent{
Attempt: attempt,
Failure: err.Error(),
Expand Down
6 changes: 6 additions & 0 deletions broker/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"api-broker-prototype/events"
"testing"
"time"

"github.com/gofrs/uuid"
)

// mock for the events.Envelope interface
Expand All @@ -13,6 +15,10 @@ func (envelope envelopeMock) ID() int32 {
return 42
}

func (envelope envelopeMock) ExternalUUID() uuid.UUID {
return uuid.FromStringOrNil("22428f46-a2d8-4d51-b6b5-bc8551bd0921")
}

func (envelope envelopeMock) Created() time.Time {
return time.Now()
}
Expand Down
8 changes: 8 additions & 0 deletions events/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package events

// This file collects error types.

import "errors"

// DuplicateEventUUID is used to signal that the UUID identifying an event is already in use
var DuplicateEventUUID = errors.New("duplicate event identifier UUID")
15 changes: 15 additions & 0 deletions events/errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package events

import (
"errors"
"testing"
)

func TestDuplicateEventUUID(t *testing.T) {
// make sure the type implements the `error` interface
var err error = DuplicateEventUUID

if !errors.Is(err, DuplicateEventUUID) {
t.Error("error type is not recognized")
}
}
12 changes: 11 additions & 1 deletion events/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ package events
import (
"context"
"time"

"github.com/gofrs/uuid"
)

// The Envelope is a container for the actual event, which it carries as
Expand All @@ -24,6 +26,8 @@ type Envelope interface {
ID() int32
// Created returns the time the event was persisted.
Created() time.Time
// ExternalUUID returns the (optional) UUID assigned to the event
ExternalUUID() uuid.UUID
// CausationID returns the ID of the event that caused this event.
// This can be zero if this event was not caused by another event.
CausationID() int32
Expand Down Expand Up @@ -56,9 +60,15 @@ type EventStore interface {

// Insert an event as payload into the store.
// The event is wrapped in an envelope and returned.
// The external UUID can be used to attach a client-supplied identifier
// to the event. If the UUID is not the nil UUID, it must be unique.
// If the UUID is already used, DuplicateEventUUID is returned as error.
// The causation ID is that of the preceding event that caused this new
// event. It can be zero when its cause is not a preceding event.
Insert(ctx context.Context, event Event, causationID int32) (Envelope, error)
Insert(ctx context.Context, externalUUID uuid.UUID, event Event, causationID int32) (Envelope, error)

// Resolve an external UUID to the according internal ID
ResolveUUID(ctx context.Context, externalUUID uuid.UUID) (int32, error)

// Retrieve just the event with the given ID.
RetrieveOne(ctx context.Context, id int32) (Envelope, error)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module api-broker-prototype
go 1.21

require (
github.com/gofrs/uuid v4.0.0+incompatible
github.com/inconshreveable/log15 v2.16.0+incompatible
github.com/jackc/pgtype v1.14.0
github.com/jackc/pgx/v5 v5.5.1
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
Expand Down
15 changes: 11 additions & 4 deletions logging/logging_decorator.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"api-broker-prototype/events"
"context"
"errors"

"github.com/gofrs/uuid"
"github.com/inconshreveable/log15"
)

Expand Down Expand Up @@ -44,9 +46,9 @@ func (s *LoggingDecoratorEventStore) Close() error {
return s.eventstore.Close()
}

func (s *LoggingDecoratorEventStore) Insert(ctx context.Context, event events.Event, causationID int32) (events.Envelope, error) {
s.logger.Debug("Inserting event.", "class", event.Class(), "causation_id", causationID)
env, err := s.eventstore.Insert(ctx, event, causationID)
func (s *LoggingDecoratorEventStore) Insert(ctx context.Context, externalUUID uuid.UUID, event events.Event, causationID int32) (events.Envelope, error) {
s.logger.Debug("Inserting event.", "external_id", externalUUID, "class", event.Class(), "causation_id", causationID)
env, err := s.eventstore.Insert(ctx, externalUUID, event, causationID)
if err == nil {
s.logger.Debug("Inserted event.", "id", env.ID())
} else {
Expand All @@ -55,11 +57,15 @@ func (s *LoggingDecoratorEventStore) Insert(ctx context.Context, event events.Ev
return env, err
}

func (s *LoggingDecoratorEventStore) ResolveUUID(ctx context.Context, externalUUID uuid.UUID) (int32, error) {
return s.eventstore.ResolveUUID(ctx, externalUUID)
}

func (s *LoggingDecoratorEventStore) RetrieveOne(ctx context.Context, id int32) (events.Envelope, error) {
s.logger.Debug("Loading event.", "id", id)
env, err := s.eventstore.RetrieveOne(ctx, id)
if err == nil {
s.logger.Debug("Loaded event.", "class", env.Event().Class(), "causation_id", env.CausationID(), "created", env.Created())
s.logger.Debug("Loaded event.", "external_id", env.ExternalUUID(), "class", env.Event().Class(), "causation_id", env.CausationID(), "created", env.Created())
} else {
s.logger.Debug("Failed to load event.", "error", err)
}
Expand Down Expand Up @@ -142,6 +148,7 @@ func (s *LoggingDecoratorEventStore) FollowEvents(ctx context.Context, startAfte
s.logger.Debug(
"Loaded event.",
"id", env.ID(),
"external_id", env.ExternalUUID(),
"class", env.Event().Class(),
"causation_id", env.CausationID(),
"created", env.Created(),
Expand Down
36 changes: 33 additions & 3 deletions logging/logging_decorator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"api-broker-prototype/events"
"context"
"errors"
"github.com/inconshreveable/log15"
"testing"

"github.com/gofrs/uuid"
"github.com/inconshreveable/log15"
)

var notImplemented error = errors.New("not implemented")
Expand All @@ -25,10 +27,14 @@ func (store *eventstoreMock) Close() error {
return notImplemented
}

func (store *eventstoreMock) Insert(ctx context.Context, event events.Event, causationID int32) (events.Envelope, error) {
func (store *eventstoreMock) Insert(ctx context.Context, externalUUID uuid.UUID, event events.Event, causationID int32) (events.Envelope, error) {
return nil, notImplemented
}

func (store *eventstoreMock) ResolveUUID(ctx context.Context, externalUUID uuid.UUID) (int32, error) {
return 0, notImplemented
}

func (store *eventstoreMock) RetrieveOne(ctx context.Context, id int32) (events.Envelope, error) {
return nil, notImplemented
}
Expand Down Expand Up @@ -140,9 +146,14 @@ func TestInsert(t *testing.T) {

ctx := context.Background()

externalUUID, err := uuid.NewV4()
if err != nil {
t.Errorf("failed to generate UUID")
}

event := eventMock{}

res, err := decorator.Insert(ctx, &event, 0)
res, err := decorator.Insert(ctx, externalUUID, &event, 0)

if res != nil {
t.Errorf("expected nil as result")
Expand All @@ -152,6 +163,25 @@ func TestInsert(t *testing.T) {
}
}

func TestResolveUUID(t *testing.T) {
decorator := createMock()

ctx := context.Background()

externalUUID, err := uuid.NewV4()
if err != nil {
t.Errorf("failed to generate UUID")
}

res, err := decorator.ResolveUUID(ctx, externalUUID)
if res != 0 {
t.Errorf("expected zero as result")
}
if err != notImplemented {
t.Errorf("unexpected error")
}
}

func TestRetrieveOne(t *testing.T) {
decorator := createMock()

Expand Down
43 changes: 37 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os/signal"
"time"

"github.com/gofrs/uuid"
"github.com/inconshreveable/log15"
"github.com/urfave/cli/v2" // imports as package "cli"
)
Expand Down Expand Up @@ -62,6 +63,11 @@ func main() {
Usage: "Insert a configuration event into the store.",
ArgsUsage: " ",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "external-uuid",
Value: "",
Usage: "external identifier for the request",
},
&cli.IntFlag{
Name: "retries",
Value: -1,
Expand All @@ -77,14 +83,23 @@ func main() {
if c.NArg() > 0 {
return errors.New("no arguments expected")
}
return configureMain(c.Context, c.Int("retries"), c.Float64("timeout"))
externalUUID, err := parseUUID(c.String("external-uuid"))
if err != nil {
return err
}
return configureMain(c.Context, externalUUID, c.Int("retries"), c.Float64("timeout"))
},
},
{
Name: "insert",
Usage: "Insert an event into the store.",
ArgsUsage: "<event>",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "external-uuid",
Value: "",
Usage: "external identifier for the request",
},
&cli.StringFlag{
Name: "causation",
Value: "0",
Expand All @@ -96,7 +111,11 @@ func main() {
if args.Len() != 2 {
return errors.New("exactly two arguments expected")
}
return insertMain(c.Context, args.Get(0), args.Get(1), c.String("causation"))
externalUUID, err := parseUUID(c.String("external-uuid"))
if err != nil {
return err
}
return insertMain(c.Context, args.Get(0), args.Get(1), externalUUID, c.String("causation"))
},
},
{
Expand Down Expand Up @@ -203,6 +222,17 @@ func main() {
}
}

// parse a string to a UUID
// Only a malformed string creates an error, an empty one will return a
// nil UUID value.
func parseUUID(arg string) (uuid.UUID, error) {
if arg == "" {
return uuid.Nil, nil
}

return uuid.FromString(arg)
}

// configure mock API from optional flags passed on the commandline
func configureAPIStub(c *cli.Context) {
mock_api.Configure(
Expand Down Expand Up @@ -254,7 +284,7 @@ func finalizeEventStore(store events.EventStore) {
}

// insert a configuration event
func configureMain(ctx context.Context, retries int, timeout float64) error {
func configureMain(ctx context.Context, externalUUID uuid.UUID, retries int, timeout float64) error {
store, err := initEventStore()
if err != nil {
return err
Expand All @@ -266,7 +296,7 @@ func configureMain(ctx context.Context, retries int, timeout float64) error {
Timeout: timeout,
}

envelope, err := store.Insert(ctx, event, 0)
envelope, err := store.Insert(ctx, externalUUID, event, 0)
if err != nil {
return err
}
Expand All @@ -276,7 +306,7 @@ func configureMain(ctx context.Context, retries int, timeout float64) error {
}

// insert a new event
func insertMain(ctx context.Context, class string, data string, causation string) error {
func insertMain(ctx context.Context, class string, data string, externalUUID uuid.UUID, causation string) error {
store, err := initEventStore()
if err != nil {
return err
Expand Down Expand Up @@ -305,7 +335,7 @@ func insertMain(ctx context.Context, class string, data string, causation string
}

// insert a document
envelope, err := store.Insert(ctx, event, causationID)
envelope, err := store.Insert(ctx, externalUUID, event, causationID)
if err != nil {
return err
}
Expand Down Expand Up @@ -342,6 +372,7 @@ func listMain(ctx context.Context, lastProcessed string) error {
logger.Info(
"event",
"id", envelope.ID(),
"external_uuid", envelope.ExternalUUID(),
"class", envelope.Event().Class(),
"created", envelope.Created().Format(time.RFC3339),
"causation_id", envelope.CausationID(),
Expand Down
Loading

0 comments on commit fc654a4

Please sign in to comment.