Compare commits

...

3 Commits
v2.0.2 ... main

Author SHA1 Message Date
7dc2fb0e77 allow custom cleanups 2025-07-25 14:40:04 +02:00
c6bf54943a better handler registration and dispatch 2025-07-10 10:23:03 +02:00
f27890e55e better ValueAs 2025-07-09 23:44:57 +02:00
16 changed files with 204 additions and 86 deletions

View File

@ -2,10 +2,11 @@ package serverevents
import (
gocontext "context"
di "git.apihub24.de/admin/generic-di"
"github.com/coder/websocket"
"sync"
"time"
di "git.apihub24.de/admin/generic-di"
"github.com/coder/websocket"
)
func init() {
@ -20,8 +21,8 @@ type IContext interface {
Set(key string, data any)
Get(key string) (any, bool)
RemoveMetaData(key string)
CleanupIn(lifetime time.Duration)
Dispatch(eventName string, data any, filter func(c IContext) bool)
CleanupIn(lifetime time.Duration, onCleanup ...func())
Dispatch(event IEvent, filter func(c IContext) bool)
IsCaller(c IContext) bool
}
@ -84,12 +85,10 @@ func (context *context) RemoveMetaData(key string) {
context.mutex.Lock()
defer context.mutex.Unlock()
if _, ok := context.metadata[key]; ok {
delete(context.metadata, key)
}
}
func (context *context) CleanupIn(lifetime time.Duration) {
func (context *context) CleanupIn(lifetime time.Duration, onCleanup ...func()) {
context.mutex.Lock()
defer context.mutex.Unlock()
@ -105,15 +104,18 @@ func (context *context) CleanupIn(lifetime time.Duration) {
if context.timer != nil {
di.Destroy[IContext](context.id)
for _, cleaner := range onCleanup {
cleaner()
}
context.timer = nil
}
}(context.id)
}
func (context *context) Dispatch(eventName string, data any, filter func(c IContext) bool) {
func (context *context) Dispatch(event IEvent, filter func(c IContext) bool) {
ev := Event{
Type: eventName,
Data: data,
Type: event.GetEventName(),
Data: event.GetEventData(),
IsBackendOnly: filter == nil,
Filter: filter,
}

View File

@ -1,5 +1,7 @@
package serverevents
import "encoding/json"
func CreateMetaDataFilter[T any](c IContext, metaDataSelector func(IContext) T, vgl func(a T, b T) bool) func(context IContext) bool {
return func(context IContext) bool {
metaA := metaDataSelector(c)
@ -15,9 +17,13 @@ func MetadataAs[T any](context IContext, key string) (T, bool) {
func ValueAs[T any](data any) (T, bool) {
var v T
if data == nil {
str, err := json.Marshal(data)
if err != nil {
return v, false
}
res, ok := data.(T)
return res, ok
err = json.Unmarshal(str, &v)
if err != nil {
return v, false
}
return v, true
}

View File

@ -6,3 +6,8 @@ type Event struct {
IsBackendOnly bool `json:"-"`
Filter func(c IContext) bool `json:"-"`
}
type IEvent interface {
GetEventName() string
GetEventData() any
}

View File

@ -1,6 +1,7 @@
package serverevents
type IEventHandler interface {
GetConnectedEventName() string
CanExecute(IContext) bool
Handle(IContext, any)
}

View File

@ -0,0 +1,5 @@
package dto
type Greeting struct {
Message string `json:"message"`
}

View File

@ -0,0 +1,26 @@
package definitions
import (
serverevents "git.apihub24.de/admin/server_events/v2"
"git.apihub24.de/admin/server_events/v2/implementation_test/data/dto"
)
const GreeterEventName = "greet"
type greetEvent struct {
data dto.Greeting
}
func NewGreetEvent(data dto.Greeting) serverevents.IEvent {
return &greetEvent{
data: data,
}
}
func (ev *greetEvent) GetEventName() string {
return GreeterEventName
}
func (ev *greetEvent) GetEventData() any {
return ev.data
}

View File

@ -0,0 +1,23 @@
package definitions
import serverevents "git.apihub24.de/admin/server_events/v2"
const GreetMeEventName = "greet me"
type greetMeEvent struct {
data string
}
func NewGreetMeEvent(name string) serverevents.IEvent {
return &greetMeEvent{
data: name,
}
}
func (ev *greetMeEvent) GetEventName() string {
return GreetMeEventName
}
func (ev *greetMeEvent) GetEventData() any {
return ev.data
}

View File

@ -0,0 +1,19 @@
package definitions
import serverevents "git.apihub24.de/admin/server_events/v2"
const PingEventName = "ping"
type pingEvent struct{}
func NewPingEvent() serverevents.IEvent {
return &pingEvent{}
}
func (ev *pingEvent) GetEventName() string {
return PingEventName
}
func (ev *pingEvent) GetEventData() any {
return nil
}

View File

@ -0,0 +1,19 @@
package definitions
import serverevents "git.apihub24.de/admin/server_events/v2"
const PongEventName = "pong"
type pongEvent struct{}
func NewPongEvent() serverevents.IEvent {
return &pongEvent{}
}
func (ev *pongEvent) GetEventName() string {
return PongEventName
}
func (ev *pongEvent) GetEventData() any {
return nil
}

View File

@ -1,39 +0,0 @@
package events
import (
"fmt"
serverevents "git.apihub24.de/admin/server_events/v2"
)
func withSameUserName(context serverevents.IContext) func(context serverevents.IContext) bool {
return serverevents.CreateMetaDataFilter(context, func(c serverevents.IContext) string {
userName, _ := serverevents.MetadataAs[string](context, "UserName")
return userName
}, func(a string, b string) bool {
return a == b
})
}
type greeting struct {
Message string
}
type greeterEventHandler struct{}
func (handler greeterEventHandler) CanExecute(_ serverevents.IContext) bool {
return true
}
func (handler greeterEventHandler) Handle(context serverevents.IContext, data any) {
userName, ok := serverevents.ValueAs[string](data)
if !ok || len(userName) < 1 {
userName = "Anonymous"
}
context.Dispatch("greet", greeting{
Message: fmt.Sprintf("Hello, %s", userName),
}, withSameUserName(context))
}
func NewGreeterEventHandler() serverevents.IEventHandler {
return &greeterEventHandler{}
}

View File

@ -0,0 +1,34 @@
package handler
import (
"fmt"
serverevents "git.apihub24.de/admin/server_events/v2"
"git.apihub24.de/admin/server_events/v2/implementation_test/data/dto"
"git.apihub24.de/admin/server_events/v2/implementation_test/events/definitions"
"git.apihub24.de/admin/server_events/v2/implementation_test/filter"
)
type greetMeEventHandler struct{}
func NewGreetMeEventHandler() serverevents.IEventHandler {
return &greetMeEventHandler{}
}
func (handler *greetMeEventHandler) GetConnectedEventName() string {
return definitions.GreetMeEventName
}
func (handler *greetMeEventHandler) CanExecute(_ serverevents.IContext) bool {
return true
}
func (handler *greetMeEventHandler) Handle(context serverevents.IContext, data any) {
userName, ok := serverevents.ValueAs[string](data)
if !ok || len(userName) < 1 {
userName = "Anonymous"
}
context.Dispatch(definitions.NewGreetEvent(dto.Greeting{
Message: fmt.Sprintf("Hello, %s", userName),
}), filter.WithSameUserName(context))
}

View File

@ -0,0 +1,24 @@
package handler
import (
serverevents "git.apihub24.de/admin/server_events/v2"
"git.apihub24.de/admin/server_events/v2/implementation_test/events/definitions"
)
type pingEventHandler struct{}
func NewPingEventHandler() serverevents.IEventHandler {
return &pingEventHandler{}
}
func (handler *pingEventHandler) GetConnectedEventName() string {
return definitions.PingEventName
}
func (handler *pingEventHandler) CanExecute(_ serverevents.IContext) bool {
return true
}
func (handler *pingEventHandler) Handle(context serverevents.IContext, _ any) {
context.Dispatch(definitions.NewPongEvent(), context.IsCaller)
}

View File

@ -1,19 +0,0 @@
package events
import (
serverevents "git.apihub24.de/admin/server_events/v2"
)
type pingEventHandler struct{}
func NewPingEventHandler() serverevents.IEventHandler {
return &pingEventHandler{}
}
func (p pingEventHandler) CanExecute(_ serverevents.IContext) bool {
return true
}
func (p pingEventHandler) Handle(context serverevents.IContext, _ any) {
context.Dispatch("pong", nil, context.IsCaller)
}

View File

@ -0,0 +1,12 @@
package filter
import serverevents "git.apihub24.de/admin/server_events/v2"
func WithSameUserName(context serverevents.IContext) func(context serverevents.IContext) bool {
return serverevents.CreateMetaDataFilter(context, func(c serverevents.IContext) string {
userName, _ := serverevents.MetadataAs[string](context, "UserName")
return userName
}, func(a string, b string) bool {
return a == b
})
}

View File

@ -1,20 +1,21 @@
package main
import (
di "git.apihub24.de/admin/generic-di"
serverevents "git.apihub24.de/admin/server_events/v2"
"git.apihub24.de/admin/server_events/v2/implementation_test/events"
"log"
"net/http"
"time"
di "git.apihub24.de/admin/generic-di"
serverevents "git.apihub24.de/admin/server_events/v2"
"git.apihub24.de/admin/server_events/v2/implementation_test/events/handler"
)
func main() {
serverEventsMiddleware := di.Inject[serverevents.IMiddleware]()
eventRegistration := di.Inject[serverevents.IEventHandlerRegistration]()
eventRegistration.Add("ping", events.NewPingEventHandler)
eventRegistration.Add("greet me", events.NewGreeterEventHandler)
eventRegistration.Use(handler.NewPingEventHandler)
eventRegistration.Use(handler.NewGreetMeEventHandler)
router := http.NewServeMux()

View File

@ -2,8 +2,9 @@ package serverevents
import (
"fmt"
di "git.apihub24.de/admin/generic-di"
"sync"
di "git.apihub24.de/admin/generic-di"
)
func init() {
@ -11,7 +12,7 @@ func init() {
}
type IEventHandlerRegistration interface {
Add(eventType string, creator func() IEventHandler)
Use(creator func() IEventHandler)
GetHandler(eventType string) (IEventHandler, error)
}
@ -29,16 +30,14 @@ func newEventHandlerRegistration() IEventHandlerRegistration {
}
}
func (registration *eventHandlerRegistration) Add(eventType string, creator func() IEventHandler) {
func (registration *eventHandlerRegistration) Use(creator func() IEventHandler) {
registration.mutex.Lock()
defer registration.mutex.Unlock()
if _, ok := registration.instances[eventType]; ok {
tmp := creator()
eventType := tmp.GetConnectedEventName()
delete(registration.instances, eventType)
}
if _, ok := registration.creators[eventType]; ok {
delete(registration.creators, eventType)
}
registration.creators[eventType] = creator
}