Skip to content

Commit

Permalink
Merge pull request #273 from xmidt-org/feature/event
Browse files Browse the repository at this point in the history
Feature/event
  • Loading branch information
johnabass authored Aug 12, 2024
2 parents 74c4ff5 + a56b559 commit 9eb8bba
Show file tree
Hide file tree
Showing 4 changed files with 290 additions and 30 deletions.
32 changes: 2 additions & 30 deletions .reuse/dep5
Original file line number Diff line number Diff line change
Expand Up @@ -39,38 +39,10 @@ Files: README.md
Copyright: SPDX-FileCopyrightText: 2019 Comcast Cable Communications Management, LLC
License: Apache-2.0

Files: basculehttp/README.md
Copyright: SPDX-FileCopyrightText: 2019 Comcast Cable Communications Management, LLC
License: Apache-2.0

Files: basculehttp/notfoundbehavior_string.go
Copyright: SPDX-FileCopyrightText: 2019 Comcast Cable Communications Management, LLC
License: Apache-2.0

Files: .whitesource
Copyright: SPDX-FileCopyrightText: 2020 Comcast Cable Communications Management, LLC
License: Apache-2.0

Files: examples/acquirer/go.mod
Copyright: SPDX-FileCopyrightText: 2019 Comcast Cable Communications Management, LLC
License: Apache-2.0

Files: examples/acquirer/go.sum
Copyright: SPDX-FileCopyrightText: 2019 Comcast Cable Communications Management, LLC
License: Apache-2.0

Files: examples/basculehttp/go.mod
Copyright: SPDX-FileCopyrightText: 2019 Comcast Cable Communications Management, LLC
License: Apache-2.0

Files: examples/basculehttp/go.sum
Copyright: SPDX-FileCopyrightText: 2019 Comcast Cable Communications Management, LLC
License: Apache-2.0

Files: v2/go.mod
Copyright: SPDX-FileCopyrightText: 2019 Comcast Cable Communications Management, LLC
License: Apache-2.0

Files: v2/go.sum
Copyright: SPDX-FileCopyrightText: 2019 Comcast Cable Communications Management, LLC
Files: eventType_string.go
Copyright: SPDX-FileCopyrightText: 2020 Comcast Cable Communications Management, LLC
License: Apache-2.0
27 changes: 27 additions & 0 deletions eventType_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

105 changes: 105 additions & 0 deletions listener.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// SPDX-FileCopyrightText: 2024 Comcast Cable Communications Management, LLC
// SPDX-License-Identifier: Apache-2.0

package bascule

//go:generate stringer -type=EventType -trimprefix=EventType -output=eventType_string.go

// EventType describes the kind of event bascule has dispatched.
type EventType int

const (
// EventTypeSuccess represents a completely successful authorization.
// The event's Token field will be set to resulting token.
EventTypeSuccess EventType = iota

// EventTypeMissingCredentials represents a source that missing any
// kind of credentials that are recognizable to the way bascule
// has been configured.
EventTypeMissingCredentials

// EventTypeInvalidCredentials indicates that credentials were present
// in the source, but were unparseable.
EventTypeInvalidCredentials

// EventTypeFailedAuthentication indicates that valid, parseable credentials
// were present in the source, but the token failed authentication.
//
// The Token field will be set in the Event.
EventTypeFailedAuthentication

// EventTypeFailedAuthorization indicates that valid, parseable credentials
// were present in the source and that the resulting token was authentic.
// However, the token did not have access to the resource(s) being requested.
//
// The Token field will be set in the Event.
EventTypeFailedAuthorization
)

// Event holds information about the result of a attempting to obtain a token.
type Event[S any] struct {
// Type is the kind of event. This field is always set.
Type EventType

// Source is the source of credentials. This field is always set.
Source S

// Token is the parsed token from the source. This field will always be set
// if the token successfully parsed, even if it wasn't authentic or authorized.
Token Token

// Err is any error that occurred from the bascule infrastructure. This will
// always be nil for a successful Event. This field MAY BE set if the
// configured infrastructure gave more information about why the attempt to
// get a token failed.
Err error
}

// Success is a convenience for checking if the event's Type field represents
// a successful token. Using this method ensures that client code will work
// in future versions.
func (e Event[S]) Success() bool {
return e.Type == EventTypeSuccess
}

// Listener is a sink for bascule events.
type Listener[S any] interface {
// OnEvent receives a bascule event. This method must not block or panic.
OnEvent(Event[S])
}

// ListenerFunc is a closure that can act as a Listener.
type ListenerFunc[S any] func(Event[S])

// OnEvent satisfies the Listener interface.
func (lf ListenerFunc[S]) OnEvent(e Event[S]) { lf(e) }

// Listeners is an aggregate Listener.
type Listeners[S any] []Listener[S]

// Append adds more listeners to this aggregate. The (possibly new)
// aggregate Listeners is returned. This method has the same
// semantics as the built-in append.
func (ls Listeners[S]) Append(more ...Listener[S]) Listeners[S] {
return append(ls, more...)
}

// AppendFunc is a more convenient version of Append when using
// closures as listeners.
func (ls Listeners[S]) AppendFunc(more ...ListenerFunc[S]) Listeners[S] {
for _, lf := range more {
if lf != nil { // handle the nil interface case
ls = ls.Append(lf)
}
}

return ls
}

// OnEvent dispatches the given event to all listeners
// contained by this aggregate.
func (ls Listeners[S]) OnEvent(e Event[S]) {
for _, l := range ls {
l.OnEvent(e)
}
}
156 changes: 156 additions & 0 deletions listener_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// SPDX-FileCopyrightText: 2024 Comcast Cable Communications Management, LLC
// SPDX-License-Identifier: Apache-2.0

package bascule

import (
"errors"
"fmt"
"testing"

"github.com/stretchr/testify/suite"
)

type ListenerTestSuite struct {
suite.Suite
}

func (suite *ListenerTestSuite) TestEvent() {
suite.Run("Success", func() {
testCases := []struct {
name string
event Event[int]
expected bool
}{
{
name: "type success",
event: Event[int]{
Type: EventTypeSuccess,
},
expected: true,
},
{
name: "type missing credentials",
event: Event[int]{
Type: EventTypeMissingCredentials,
},
expected: false,
},
}

for _, testCase := range testCases {
suite.Run(testCase.name, func() {
suite.Equal(
testCase.expected,
testCase.event.Success(),
)
})
}
})

// just check that strings are unique
suite.Run("String", func() {
var (
eventTypes = []EventType{
EventTypeSuccess,
EventTypeMissingCredentials,
EventTypeInvalidCredentials,
EventTypeFailedAuthentication,
EventTypeFailedAuthorization,
EventType(256), // random weird value should still work
}

strings = make(map[string]bool)
)

for _, et := range eventTypes {
strings[et.String()] = true
}

suite.Equal(len(eventTypes), len(strings))
})
}

func (suite *ListenerTestSuite) TestListenerFunc() {
var (
called bool

l Listener[int] = ListenerFunc[int](func(e Event[int]) {
suite.Equal(EventTypeMissingCredentials, e.Type)
called = true
})
)

l.OnEvent(Event[int]{
Type: EventTypeMissingCredentials,
})

suite.True(called)
}

func (suite *ListenerTestSuite) TestListeners() {
suite.Run("Empty", func() {
var ls Listeners[int]
ls.OnEvent(Event[int]{}) // should be fine
})

suite.Run("Append", func() {
for _, count := range []int{1, 2, 5} {
suite.Run(fmt.Sprintf("count=%d", count), func() {
var (
called int
expectedEvent = Event[int]{
Type: EventTypeInvalidCredentials,
Source: 1234,
Err: errors.New("expected"),
}

ls Listeners[int]
)

for i := 0; i < count; i++ {
var l Listener[int] = ListenerFunc[int](func(e Event[int]) {
suite.Equal(expectedEvent, e)
called++
})

ls = ls.Append(l)
}

ls.OnEvent(expectedEvent)
suite.Equal(count, called)
})
}
})

suite.Run("AppendFunc", func() {
for _, count := range []int{1, 2, 5} {
suite.Run(fmt.Sprintf("count=%d", count), func() {
var (
called int
expectedEvent = Event[int]{
Type: EventTypeInvalidCredentials,
Source: 1234,
Err: errors.New("expected"),
}

ls Listeners[int]
)

for i := 0; i < count; i++ {
ls = ls.AppendFunc(func(e Event[int]) {
suite.Equal(expectedEvent, e)
called++
})
}

ls.OnEvent(expectedEvent)
suite.Equal(count, called)
})
}
})
}

func TestListener(t *testing.T) {
suite.Run(t, new(ListenerTestSuite))
}

0 comments on commit 9eb8bba

Please sign in to comment.