Private/Open-MyWebApiRealtimeConnection.ps1
|
function Open-MyWebApiRealtimeConnection { # Builds and starts a hub connection for the given hub path, returns a wrapper object. # * Named 'Open-' (not 'New-') on purpose: it has a real side effect (opens a live # network connection via StartAsync()), so the 'New' verb would make # PSUseShouldProcessForStateChangingFunctions flag it. It is Private/unexported and # always called unconditionally by the public Connect-MT4Realtime/Connect-MT5Realtime # cmdlets, so ShouldProcess plumbing here would be unreachable boilerplate. [CmdletBinding()] param( [Parameter(Mandatory)][ValidateSet('mt4','mt5')][string] $Hub, [string] $TradePlatform ) if (-not ('MyWebApi.RealtimeSink' -as [type])) { throw 'Real-time support requires the SignalR client assemblies. Reinstall the packaged module, or run scripts/restore-lib.sh when working from source.' } if (-not $script:MyWebApiContext) { throw 'Not connected. Call Connect-MyWebApi first.' } $ctx = $script:MyWebApiContext # * The v2 hub's OnConnectedAsync REQUIRES a `tradePlatform` query parameter (a GUID # the credential is authorized for); without it the server throws a HubException and # closes the socket right after the handshake (StartAsync succeeds, then State flips # to Disconnected). Resolve it exactly like the REST pipeline does. $tp = if ($TradePlatform) { $TradePlatform } else { $ctx.DefaultTradePlatform } if (-not $tp) { throw 'No trade platform: pass -TradePlatform or set -DefaultTradePlatform on Connect-MyWebApi.' } $token = Get-MyWebApiToken $url = "$($ctx.BaseUrl)/hubs/$Hub/v2?tradePlatform=$([uri]::EscapeDataString($tp))&signalr_token=$([uri]::EscapeDataString($token))" $builder = [Microsoft.AspNetCore.SignalR.Client.HubConnectionBuilder]::new() $null = [Microsoft.AspNetCore.SignalR.Client.HubConnectionBuilderHttpExtensions]::WithUrl($builder, $url) $null = [Microsoft.AspNetCore.SignalR.Client.HubConnectionBuilderExtensions]::WithAutomaticReconnect($builder) $connection = $builder.Build() $sink = [MyWebApi.RealtimeSink]::new($connection) $sink.On('OnConnectionStatus') if ($Hub -eq 'mt4') { $sink.On('OnTick') } # * [void]: GetResult() on a void Task returns a VoidTaskResult sentinel that would # otherwise leak into the pipeline, making this function emit an array instead of # the single connection object. [void]$sink.StartAsync().GetAwaiter().GetResult() [pscustomobject]@{ PSTypeName = 'MyWebApi.RealtimeConnection' Hub = $Hub TradePlatform = $tp Sink = $sink } } |