From 7b5f8a98824657d7e92ea61462adf956c2c1ca0d Mon Sep 17 00:00:00 2001 From: Tom Wiesing Date: Thu, 9 Nov 2023 14:58:19 +0100 Subject: [PATCH] websocket actions: Refactor registration --- .../component/server/admin/socket/actions.go | 86 +++++++++++++------ .../server/admin/socket/proto/info.go | 36 ++++++++ .../server/admin/socket/{ => proto}/proto.go | 31 +------ .../component/server/admin/socket/socket.go | 36 +------- 4 files changed, 102 insertions(+), 87 deletions(-) create mode 100644 internal/dis/component/server/admin/socket/proto/info.go rename internal/dis/component/server/admin/socket/{ => proto}/proto.go (84%) diff --git a/internal/dis/component/server/admin/socket/actions.go b/internal/dis/component/server/admin/socket/actions.go index a33e662..3ccccfb 100644 --- a/internal/dis/component/server/admin/socket/actions.go +++ b/internal/dis/component/server/admin/socket/actions.go @@ -3,42 +3,80 @@ package socket import ( "context" "io" + + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/admin/socket/actions" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/admin/socket/proto" + "github.com/rs/zerolog" ) -func (sockets *Sockets) Actions() ActionMap { - actions := make(ActionMap, len(sockets.dependencies.Actions)+len(sockets.dependencies.IActions)) +func (sockets *Sockets) Actions(ctx context.Context) proto.ActionMap { + logger := zerolog.Ctx(ctx) + actions := make(proto.ActionMap, len(sockets.dependencies.Actions)+len(sockets.dependencies.IActions)) // setup basic actions for _, a := range sockets.dependencies.Actions { - a := a - meta := a.Action() - actions[meta.Name] = Action{ - NumParams: meta.NumParams, - Scope: meta.Scope, - ScopeParam: meta.ScopeParam, - - Handle: a.Act, + name, action := sockets.regularAction(a) + if _, ok := actions[name]; ok { + logger.Warn().Str("name", name).Str("type", "regular").Msg("duplicate websocket action") } + actions[name] = action + + logger.Info(). + Str("name", name). + Str("type", "regular"). + Int("params", action.NumParams). + Str("scope", string(action.Scope)). + Str("scopeParam", action.ScopeParam). + Msg("registering websocket action") } // setup instance actions for _, a := range sockets.dependencies.IActions { - a := a - meta := a.Action() - actions[meta.Name] = Action{ - NumParams: meta.NumParams + 1, - Scope: meta.Scope, - ScopeParam: meta.ScopeParam, - - Handle: func(ctx context.Context, in io.Reader, out io.Writer, params ...string) error { - instance, err := sockets.dependencies.Instances.WissKI(ctx, params[0]) - if err != nil { - return err - } - return a.Act(ctx, instance, in, out, params[1:]...) - }, + name, action := sockets.instanceAction(a) + if _, ok := actions[name]; ok { + zerolog.Ctx(ctx).Warn().Str("name", name).Str("type", "instance").Msg("duplicate websocket action") } + actions[name] = action + + logger.Info(). + Str("name", name). + Str("type", "instance"). + Int("params", action.NumParams-1). + Str("scope", string(action.Scope)). + Str("scopeParam", action.ScopeParam). + Msg("registering websocket action") } return actions } + +func (sockets *Sockets) regularAction(a actions.WebsocketAction) (name string, action proto.Action) { + meta := a.Action() + + return meta.Name, proto.Action{ + NumParams: meta.NumParams + 1, + Scope: meta.Scope, + ScopeParam: meta.ScopeParam, + + Handle: a.Act, + } +} + +func (sockets *Sockets) instanceAction(a actions.WebsocketInstanceAction) (name string, action proto.Action) { + meta := a.Action() + + return meta.Name, proto.Action{ + NumParams: meta.NumParams + 1, + Scope: meta.Scope, + ScopeParam: meta.ScopeParam, + + Handle: func(ctx context.Context, in io.Reader, out io.Writer, params ...string) error { + instance, err := sockets.dependencies.Instances.WissKI(ctx, params[0]) + if err != nil { + return err + } + return a.Act(ctx, instance, in, out, params[1:]...) + }, + } + +} diff --git a/internal/dis/component/server/admin/socket/proto/info.go b/internal/dis/component/server/admin/socket/proto/info.go new file mode 100644 index 0000000..ee14403 --- /dev/null +++ b/internal/dis/component/server/admin/socket/proto/info.go @@ -0,0 +1,36 @@ +package proto + +import ( + "context" + "io" + + "github.com/FAU-CDI/wisski-distillery/internal/dis/component" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/scopes" +) + +// Action is anything that can be retrieved from a +type Action struct { + // NumPara + NumParams int + + // Scope and ScopeParam indicate the scope required by the caller. + // TODO(twiesing): Once we actually include scopes, make them dynamic + Scope component.Scope + ScopeParam string + + // Handle handles this action. + // + // ctx is closed once the underlying connection is closed. + // out is an io.Writer that is automatically sent to the client. + // params holds exactly NumParams parameters. + Handle func(ctx context.Context, in io.Reader, out io.Writer, params ...string) error +} + +// scope returns the actual scope required by this action. +// If the caller did not provide an actual scope, uses ScopeNever +func (action Action) scope() component.Scope { + if action.Scope == "" { + return scopes.ScopeNever + } + return action.Scope +} diff --git a/internal/dis/component/server/admin/socket/proto.go b/internal/dis/component/server/admin/socket/proto/proto.go similarity index 84% rename from internal/dis/component/server/admin/socket/proto.go rename to internal/dis/component/server/admin/socket/proto/proto.go index ce5ea63..852fb64 100644 --- a/internal/dis/component/server/admin/socket/proto.go +++ b/internal/dis/component/server/admin/socket/proto/proto.go @@ -1,4 +1,4 @@ -package socket +package proto import ( "context" @@ -9,9 +9,7 @@ import ( "sync" "time" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/scopes" "github.com/gorilla/websocket" "github.com/tkw1536/pkglib/httpx" ) @@ -195,33 +193,6 @@ func (wf WriteFunc) Write(b []byte) (int, error) { return wf(b) } -// Action is something that can be handled via a WebSocket connection. -type Action struct { - // NumPara - NumParams int - - // Scope and ScopeParam indicate the scope required by the caller. - // TODO(twiesing): Once we actually include scopes, make them dynamic - Scope component.Scope - ScopeParam string - - // Handle handles this action. - // - // ctx is closed once the underlying connection is closed. - // out is an io.Writer that is automatically sent to the client. - // params holds exactly NumParams parameters. - Handle func(ctx context.Context, in io.Reader, out io.Writer, params ...string) error -} - -// scope returns the actual scope required by this action. -// If the caller did not provide an actual scope, uses ScopeNever -func (action Action) scope() component.Scope { - if action.Scope == "" { - return scopes.ScopeNever - } - return action.Scope -} - // CallMessage is sent by the client to the server to invoke a remote procedure type CallMessage struct { Call string `json:"call"` diff --git a/internal/dis/component/server/admin/socket/socket.go b/internal/dis/component/server/admin/socket/socket.go index 89ddab7..c620e49 100644 --- a/internal/dis/component/server/admin/socket/socket.go +++ b/internal/dis/component/server/admin/socket/socket.go @@ -2,7 +2,6 @@ package socket import ( "context" - "io" "net/http" "github.com/FAU-CDI/wisski-distillery/internal/dis/component" @@ -13,7 +12,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances/purger" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/provision" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/admin/socket/actions" - "github.com/FAU-CDI/wisski-distillery/internal/wisski" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/admin/socket/proto" "github.com/rs/zerolog" "github.com/tkw1536/pkglib/httpx" "github.com/tkw1536/pkglib/lazy" @@ -22,7 +21,7 @@ import ( type Sockets struct { component.Base - actions lazy.Lazy[ActionMap] + actions lazy.Lazy[proto.ActionMap] dependencies struct { Actions []actions.WebsocketAction @@ -58,37 +57,8 @@ func (sockets *Sockets) HandleRoute(ctx context.Context, path string) (http.Hand // Serve handles a connection to the websocket api func (socket *Sockets) Serve(conn httpx.WebSocketConnection) { // handle the websocket connection! - name, err := socket.actions.Get(socket.Actions).Handle(socket.dependencies.Auth, conn) + name, err := socket.actions.Get(func() proto.ActionMap { return socket.Actions(conn.Context()) }).Handle(socket.dependencies.Auth, conn) if err != nil { zerolog.Ctx(conn.Context()).Err(err).Str("name", name).Msg("Error handling websocket") } } - -// Generic returns a new action that calls handler with the provided number of parameters -func (sockets *Sockets) Generic(scope component.Scope, scopeParam string, numParams int, handler func(ctx context.Context, socket *Sockets, in io.Reader, out io.Writer, params ...string) error) Action { - return Action{ - Scope: scope, - ScopeParam: scopeParam, - NumParams: numParams, - Handle: func(ctx context.Context, in io.Reader, out io.Writer, params ...string) error { - return handler(ctx, sockets, in, out, params...) - }, - } -} - -// Insstance returns a new action that calls handler with a specific WissKI instance -func (sockets *Sockets) Instance(scope component.Scope, scopeParam string, numParams int, handler func(ctx context.Context, sockets *Sockets, instance *wisski.WissKI, in io.Reader, out io.Writer, params ...string) error) Action { - return Action{ - Scope: scope, - ScopeParam: scopeParam, - - NumParams: numParams + 1, - Handle: func(ctx context.Context, in io.Reader, out io.Writer, params ...string) error { - instance, err := sockets.dependencies.Instances.WissKI(ctx, params[0]) - if err != nil { - return err - } - return handler(ctx, sockets, instance, in, out, params[1:]...) - }, - } -}