< Summary

Information
Class: UIBlazor.VS.VsBridge
Assembly: UIBlazor
File(s): /home/runner/work/InvAit/InvAit/UIBlazor/VS/VsBridge.cs
Tag: 14_22728831704
Line coverage
0%
Covered lines: 0
Uncovered lines: 108
Coverable lines: 108
Total lines: 237
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 52
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
InitializeAsync()0%2040%
ExecuteToolAsync()0%210140%
SwitchModeAsync(...)0%620%
Convert(...)0%7280%
SendRequestAsync()0%2040%
HandleVsMessageAsync(...)0%4260%
HandleVsResponseAsync(...)0%7280%
EnsureInitializedAsync()0%620%
Dispose()0%2040%

File(s)

/home/runner/work/InvAit/InvAit/UIBlazor/VS/VsBridge.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using Microsoft.JSInterop;
 3using Radzen;
 4using UIBlazor.Services;
 5using UIBlazor.Services.Settings;
 6
 7namespace UIBlazor.VS;
 8
 9public class VsBridge : IVsBridge, IDisposable
 10{
 11    private readonly IJSRuntime _jsRuntime;
 12    private readonly NotificationService _notificationService;
 13    private readonly ICommonSettingsProvider _commonOptions;
 14    private readonly IVsCodeContextService _vsCodeContextService;
 15    private DotNetObjectReference<VsBridge> _dotNetRef;
 16    private readonly ConcurrentDictionary<string, TaskCompletionSource<VsResponse>> _pendingRequests;
 17    private bool _isInitialized;
 18
 19    public event Action<AppMode>? OnModeSwitched;
 20
 021    public VsBridge(IJSRuntime jsRuntime,
 022         NotificationService notificationService,
 023         ICommonSettingsProvider commonSettingsProvider,
 024         IVsCodeContextService vsCodeContextService)
 25    {
 026        _jsRuntime = jsRuntime;
 027        _notificationService = notificationService;
 028        _commonOptions = commonSettingsProvider;
 029        _vsCodeContextService = vsCodeContextService;
 030        _pendingRequests = new ConcurrentDictionary<string, TaskCompletionSource<VsResponse>>();
 031    }
 32
 33    public async Task InitializeAsync()
 34    {
 035        if (!_isInitialized)
 36        {
 037            _dotNetRef = DotNetObjectReference.Create(this);
 038            var result = await _jsRuntime.InvokeAsync<string>("setVsBridgeHandler", _dotNetRef);
 039            _isInitialized = result == "OK";
 40
 041            if (_isInitialized)
 42            {
 43                // Notify Host that we are ready to receive messages (e.g. initial context)
 044                await _jsRuntime.InvokeVoidAsync("postVsMessage", new VsRequest { Action = BasicEnum.UIReady });
 45            }
 46        }
 047    }
 48
 49    public async Task<VsToolResult> ExecuteToolAsync(string name, IReadOnlyDictionary<string, object>? args = null)
 50    {
 051        if (name == BasicEnum.SwitchMode && args != null && args.TryGetValue("param1", out var modeObj))
 52        {
 053            if (Enum.TryParse<AppMode>(modeObj.ToString(), true, out var mode))
 54            {
 055                await SwitchModeAsync(mode);
 056                return new VsToolResult
 057                {
 058                    Success = true,
 059                    Result = $"Switched to {mode} mode successfully. Now you have access to different set of tools. See 
 060                };
 61            }
 62        }
 63
 064        var request = new VsRequest
 065        {
 066            Action = name,
 067            Payload = args != null ? JsonUtils.Serialize(args) : null
 068        };
 69
 070        var response = await SendRequestAsync(request);
 71
 072        if (!response.Success)
 73        {
 074            _notificationService.Notify(new NotificationMessage
 075            {
 076                Severity = NotificationSeverity.Error,
 077                Summary = $"Error getting response from Visual Studio '{request.Action}'",
 078                Detail = response.Error ?? string.Empty,
 079                Duration = 6_000,
 080                ShowProgress = true
 081            });
 82        }
 83
 084        return Convert(request, response);
 085    }
 86
 87    public Task SwitchModeAsync(AppMode mode)
 88    {
 089        OnModeSwitched?.Invoke(mode);
 090        return Task.CompletedTask;
 91    }
 92
 93    private VsToolResult Convert(VsRequest vsRequest, VsResponse vsResponse)
 94    {
 095        return new VsToolResult
 096        {
 097            Args = vsRequest.Payload ?? string.Empty,
 098            ErrorMessage = vsResponse.Error ?? string.Empty,
 099            Name = vsRequest.Action,
 0100            Result = vsResponse.Payload ?? vsResponse.Error ?? string.Empty,
 0101            Success = vsResponse.Success
 0102        };
 103    }
 104
 105    private async Task<VsResponse> SendRequestAsync(VsRequest request)
 106    {
 0107        await EnsureInitializedAsync();
 108
 0109        var tcs = new TaskCompletionSource<VsResponse>();
 0110        _pendingRequests.TryAdd(request.CorrelationId, tcs);
 111
 112        try
 113        {
 114            // Отправляем запрос
 0115            var result = await _jsRuntime.InvokeAsync<string>("postVsMessage", request);
 116
 0117            if (result != "OK")
 118            {
 0119                return new VsResponse
 0120                {
 0121                    Success = false,
 0122                    Error = $"WebView2 API is`t find."
 0123                };
 124            }
 125
 0126            var timeOut = TimeSpan.FromSeconds(_commonOptions.Current.ToolTimeoutMs);
 127#if DEBUG
 128            // Дебаг не быстрый)
 129            timeOut = TimeSpan.FromDays(1);
 130#endif
 0131            using var timeoutCts = new CancellationTokenSource(timeOut);
 0132            timeoutCts.Token.Register(() =>
 0133                tcs.TrySetException(new TimeoutException("Request timed out")));
 134
 135            // Ждем ответа
 0136            return await tcs.Task;
 137        }
 0138        catch (Exception ex)
 139        {
 0140            return new VsResponse
 0141            {
 0142                Success = false,
 0143                Error = $"Error getting response: {ex.Message}"
 0144            };
 145        }
 146        finally
 147        {
 148            // Удаляем из ожидающих запросов
 0149            _pendingRequests.TryRemove(request.CorrelationId, out _);
 150        }
 0151    }
 152
 153    [JSInvokable("HandleVsMessage")]
 154    public Task HandleVsMessageAsync(VsMessage message)
 155    {
 0156        switch (message.Action)
 157        {
 158            case "UpdateCodeContext":
 0159                if (!string.IsNullOrEmpty(message.Payload))
 160                {
 161                    try
 162                    {
 0163                        var context = JsonUtils.Deserialize<VsCodeContext>(message.Payload);
 0164                        if (context != null)
 165                        {
 0166                            _vsCodeContextService.UpdateContext(context);
 167                        }
 0168                    }
 0169                    catch (Exception ex)
 170                    {
 0171                        Console.WriteLine($"Error deserializing VsCodeContext: {ex.Message}");
 0172                    }
 173                }
 174                break;
 175            default:
 0176                Console.WriteLine($"Unknown message action: {message.Action}");
 177                break;
 178        }
 179
 0180        return Task.CompletedTask;
 181    }
 182
 183    [JSInvokable("HandleVsResponse")]
 184    public Task HandleVsResponseAsync(VsResponse response)
 185    {
 186        try
 187        {
 0188            if (string.IsNullOrEmpty(response.CorrelationId))
 189            {
 0190                Console.WriteLine("Invalid response received");
 0191                return Task.CompletedTask;
 192            }
 193
 0194            if (_pendingRequests.TryRemove(response.CorrelationId, out var tcs))
 195            {
 0196                if (response.Success)
 197                {
 0198                    tcs.SetResult(response);
 199                }
 200                else
 201                {
 0202                    tcs.SetException(new Exception(response.Error ?? "Request failed"));
 203                }
 204            }
 205            else
 206            {
 0207                Console.WriteLine($"No pending request found for correlationId: {response.CorrelationId}");
 208            }
 0209        }
 0210        catch (Exception ex)
 211        {
 0212            Console.WriteLine($"Error handling VS response: {ex.Message}");
 0213        }
 214
 0215        return Task.CompletedTask;
 0216    }
 217
 218    private async Task EnsureInitializedAsync()
 219    {
 0220        if (!_isInitialized)
 221        {
 0222            await InitializeAsync();
 223        }
 0224    }
 225
 226    public void Dispose()
 227    {
 0228        _dotNetRef?.Dispose();
 229
 230        // Отменяем все ожидающие запросы
 0231        foreach (var tcs in _pendingRequests.Values)
 232        {
 0233            tcs.TrySetCanceled();
 234        }
 0235        _pendingRequests.Clear();
 0236    }
 237}