package socket import ( protoSocket "git.toki-labs.com/toki/proto-socket/go" altv1 "git.toki-labs.com/toki/alt/packages/contracts/gen/go/alt/v1" "git.toki-labs.com/toki/alt/services/api/internal/workerclient" ) // sessionHandler is one request-response unit attached to a freshly connected // client communicator. Splitting registration into independent units keeps the // api hub control-plane surface thin: later command/query handlers attach // through the same registry instead of growing a single registration function. type sessionHandler struct { // requestType is the ALT protobuf request type name the handler answers. // It lets the registry detect duplicate or missing handler coverage without // a live socket connection. requestType string // register attaches the handler onto the client communicator. register func(*protoSocket.WsClient) } // sessionHandlers returns the ordered set of handlers registered on every API // client session. Hello is registered through this registry so its behaviour // stays identical while gaining the shared registration path. func sessionHandlers(worker workerclient.WorkerClient) []sessionHandler { handlers := []sessionHandler{ helloHandler(worker), } handlers = append(handlers, marketHandlers(worker)...) handlers = append(handlers, backtestHandlers(worker)...) handlers = append(handlers, paperHandlers(worker)...) return handlers } // registerSessionHandlers wires every session handler onto a newly connected // client. It is the OnClientConnected entrypoint for the API socket server. func registerSessionHandlers(worker workerclient.WorkerClient) func(*protoSocket.WsClient) { return func(client *protoSocket.WsClient) { registerHandlers(client, sessionHandlers(worker)) } } // registerHandlers attaches the given handlers onto a client. nil registrars // are skipped so a malformed registry entry cannot panic the connection setup // path. func registerHandlers(client *protoSocket.WsClient, handlers []sessionHandler) { for _, handler := range handlers { if handler.register == nil { continue } handler.register(client) } } func helloHandler(worker workerclient.WorkerClient) sessionHandler { return sessionHandler{ requestType: protoSocket.TypeNameOf(&altv1.HelloRequest{}), register: func(client *protoSocket.WsClient) { protoSocket.AddRequestListenerTyped[*altv1.HelloRequest, *altv1.HelloResponse](&client.Communicator, func(req *altv1.HelloRequest) (*altv1.HelloResponse, error) { return handleHello(worker, req) }) }, } } func handleHello(worker workerclient.WorkerClient, req *altv1.HelloRequest) (*altv1.HelloResponse, error) { protocolVersion := req.GetAltProtocolVersion() if protocolVersion == "" { protocolVersion = defaultAltProtocolVersion } return &altv1.HelloResponse{ ServerName: serverName, ServerVersion: serverVersion, AltProtocolVersion: protocolVersion, Capabilities: capabilitiesForSession(worker), }, nil } func capabilitiesForSession(worker workerclient.WorkerClient) []string { caps := []string{"hello", "request-response", "market-read", "market-import", "backtest-read", "backtest-start", "worker-execution", "paper-trading"} if worker != nil && worker.IsConnected() { caps = append(caps, "worker-available") } else { caps = append(caps, "worker-unavailable") } return caps }