-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #273 from xmidt-org/feature/event
Feature/event
- Loading branch information
Showing
4 changed files
with
290 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} |