refactor: logging for exceptions

This commit is contained in:
estrogen elf
2025-08-09 08:47:17 -05:00
parent fd57aad4dd
commit 09cc3320e5
6 changed files with 157 additions and 78 deletions

View File

@@ -47,21 +47,31 @@ public partial class MainWindowViewModel
_schemaPaneService.SchemaPaneItems.Subscribe(async item => _schemaPaneService.SchemaPaneItems.Subscribe(async item =>
{ {
var document = item.Tab switch try
{ {
Pages.Endpoints.Tab.LCU => await documentService.GetLcuSchemaDocumentAsync(), var document = item.Tab switch
Pages.Endpoints.Tab.GameClient => await documentService.GetLolClientDocumentAsync(), {
_ => throw new NotImplementedException() Pages.Endpoints.Tab.LCU => await documentService.GetLcuSchemaDocumentAsync(),
}; Pages.Endpoints.Tab.GameClient => await documentService.GetLolClientDocumentAsync(),
var propertyClassViewModel = OpenApiHelpers.WalkSchema(document.OpenApiDocument.Components.Schemas[item.Key], document.OpenApiDocument); _ => throw new NotImplementedException()
var schemaViewModel = new SchemaViewModel(propertyClassViewModel); };
if (Schemas.ToList().Find(schema => schema.Id == schemaViewModel.Id) == null) var propertyClassViewModel = OpenApiHelpers.WalkSchema(document.OpenApiDocument.Components.Schemas[item.Key], document.OpenApiDocument);
{ var schemaViewModel = new SchemaViewModel(propertyClassViewModel);
Schemas.Add(schemaViewModel); if (Schemas.ToList().Find(schema => schema.Id == schemaViewModel.Id) == null)
IsPaneOpen = true; {
Schemas.Add(schemaViewModel);
IsPaneOpen = true;
OpenSchemaPaneCommand.NotifyCanExecuteChanged(); OpenSchemaPaneCommand.NotifyCanExecuteChanged();
CloseSchemaAllCommand.NotifyCanExecuteChanged(); CloseSchemaAllCommand.NotifyCanExecuteChanged();
}
}
catch (Exception ex)
{
this.Log()
.Error(ex, "Failed to load schema pane item.");
_notificationService.Notify("Schema pane", ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error);
throw;
} }
}); });
@@ -92,25 +102,35 @@ public partial class MainWindowViewModel
if (value == null) return; if (value == null) return;
Task.Run(async () => Task.Run(async () =>
{ {
var document = value.Tab switch try
{ {
Pages.Endpoints.Tab.LCU => await _documentService.GetLcuSchemaDocumentAsync(), var document = value.Tab switch
Pages.Endpoints.Tab.GameClient => await _documentService.GetLolClientDocumentAsync(),
_ => throw new NotImplementedException()
};
var propertyClassViewModel = OpenApiHelpers.WalkSchema(document.OpenApiDocument.Components.Schemas[value.Key], document.OpenApiDocument);
var schemaViewModel = new SchemaViewModel(propertyClassViewModel);
Dispatcher.UIThread.Post(() =>
{
if (Schemas.ToList().Find(schema => schema.Id == schemaViewModel.Id) == null)
{ {
Schemas.Add(schemaViewModel); Pages.Endpoints.Tab.LCU => await _documentService.GetLcuSchemaDocumentAsync(),
IsPaneOpen = true; Pages.Endpoints.Tab.GameClient => await _documentService.GetLolClientDocumentAsync(),
_ => throw new NotImplementedException()
};
var propertyClassViewModel = OpenApiHelpers.WalkSchema(document.OpenApiDocument.Components.Schemas[value.Key], document.OpenApiDocument);
var schemaViewModel = new SchemaViewModel(propertyClassViewModel);
Dispatcher.UIThread.Post(() =>
{
if (Schemas.ToList().Find(schema => schema.Id == schemaViewModel.Id) == null)
{
Schemas.Add(schemaViewModel);
IsPaneOpen = true;
OpenSchemaPaneCommand.NotifyCanExecuteChanged(); OpenSchemaPaneCommand.NotifyCanExecuteChanged();
CloseSchemaAllCommand.NotifyCanExecuteChanged(); CloseSchemaAllCommand.NotifyCanExecuteChanged();
} }
}); });
}
catch (Exception ex)
{
this.Log()
.Error(ex, "Failed to load schema search details.");
_notificationService.Notify("Schema search", ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error);
throw;
}
}); });
} }
@@ -131,14 +151,23 @@ public partial class MainWindowViewModel
{ {
if (searchText == null) return []; if (searchText == null) return [];
var lcuSchemaDocument = await _documentService.GetLcuSchemaDocumentAsync(cancellationToken); try
var gameClientDocument = await _documentService.GetLolClientDocumentAsync(cancellationToken); {
var lcuResults = lcuSchemaDocument.OpenApiDocument.Components.Schemas.Keys.Where(key => key.Contains(searchText, StringComparison.OrdinalIgnoreCase)) var lcuSchemaDocument = await _documentService.GetLcuSchemaDocumentAsync(cancellationToken);
.Select(key => new SchemaSearchDetailsViewModel(key, Pages.Endpoints.Tab.LCU)); var gameClientDocument = await _documentService.GetLolClientDocumentAsync(cancellationToken);
var gameClientResults = gameClientDocument.OpenApiDocument.Components.Schemas.Keys.Where(key => key.Contains(searchText, StringComparison.OrdinalIgnoreCase)) var lcuResults = lcuSchemaDocument.OpenApiDocument.Components.Schemas.Keys.Where(key => key.Contains(searchText, StringComparison.OrdinalIgnoreCase))
.Select(key => new SchemaSearchDetailsViewModel(key, Pages.Endpoints.Tab.GameClient)); .Select(key => new SchemaSearchDetailsViewModel(key, Pages.Endpoints.Tab.LCU));
var gameClientResults = gameClientDocument.OpenApiDocument.Components.Schemas.Keys.Where(key => key.Contains(searchText, StringComparison.OrdinalIgnoreCase))
return Enumerable.Concat(lcuResults, gameClientResults); .Select(key => new SchemaSearchDetailsViewModel(key, Pages.Endpoints.Tab.GameClient));
return Enumerable.Concat(lcuResults, gameClientResults);
}
catch (Exception ex)
{
this.Log()
.Error(ex, "Failed to load populate.");
_notificationService.Notify("Schema search", ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error);
throw;
}
} }
[RelayCommand(CanExecute = nameof(CanOpenSchemaPane))] [RelayCommand(CanExecute = nameof(CanOpenSchemaPane))]

View File

@@ -1,21 +1,26 @@
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Needlework.Net.Extensions;
using Needlework.Net.Services; using Needlework.Net.Services;
using Needlework.Net.ViewModels.Shared; using Needlework.Net.ViewModels.Shared;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Needlework.Net.ViewModels.Pages.Console; namespace Needlework.Net.ViewModels.Pages.Console;
public partial class ConsoleViewModel : PageBase public partial class ConsoleViewModel : PageBase, IEnableLogger
{ {
private readonly DocumentService _documentService; private readonly DocumentService _documentService;
private readonly NotificationService _notificationService;
public ConsoleViewModel(DocumentService documentService, NotificationService notificationService) : base("Console", "fa-solid fa-terminal") public ConsoleViewModel(DocumentService documentService, NotificationService notificationService) : base("Console", "fa-solid fa-terminal")
{ {
_request = new(notificationService, Endpoints.Tab.LCU); _request = new(notificationService, Endpoints.Tab.LCU);
_documentService = documentService; _documentService = documentService;
_notificationService = notificationService;
} }
public List<string> RequestMethods { get; } = ["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS", "TRACE"]; public List<string> RequestMethods { get; } = ["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS", "TRACE"];
@@ -28,13 +33,22 @@ public partial class ConsoleViewModel : PageBase
public override async Task InitializeAsync() public override async Task InitializeAsync()
{ {
var document = await _documentService.GetLcuSchemaDocumentAsync(); try
Dispatcher.UIThread.Invoke(() =>
{ {
RequestPaths.Clear(); var document = await _documentService.GetLcuSchemaDocumentAsync();
RequestPaths.AddRange(document.Paths); Dispatcher.UIThread.Invoke(() =>
}); {
IsBusy = false; RequestPaths.Clear();
RequestPaths.AddRange(document.Paths);
});
IsBusy = false;
}
catch (Exception ex)
{
this.Log()
.Error(ex, "Failed to load console.");
_notificationService.Notify("Console", ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error);
}
} }
[RelayCommand] [RelayCommand]

View File

@@ -2,6 +2,7 @@
using AvaloniaEdit.Utils; using AvaloniaEdit.Utils;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Needlework.Net.Extensions;
using Needlework.Net.Models; using Needlework.Net.Models;
using Needlework.Net.Services; using Needlework.Net.Services;
using System; using System;
@@ -16,7 +17,7 @@ public enum Tab
GameClient GameClient
} }
public partial class EndpointsViewModel : PageBase public partial class EndpointsViewModel : PageBase, IEnableLogger
{ {
private readonly DocumentService _documentService; private readonly DocumentService _documentService;
@@ -36,32 +37,50 @@ public partial class EndpointsViewModel : PageBase
public override async Task InitializeAsync() public override async Task InitializeAsync()
{ {
await AddEndpoint(Tab.LCU); try
IsBusy = false; {
await AddEndpoint(Tab.LCU);
IsBusy = false;
}
catch (Exception ex)
{
this.Log()
.Error(ex, "Failed to load endpoints.");
_notificationService.Notify("Endpoints", ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error);
}
} }
[RelayCommand] [RelayCommand]
private async Task AddEndpoint(Tab tab) private async Task AddEndpoint(Tab tab)
{ {
Document document = tab switch try
{ {
Tab.LCU => await _documentService.GetLcuSchemaDocumentAsync(), Document document = tab switch
Tab.GameClient => await _documentService.GetLolClientDocumentAsync(),
_ => throw new NotImplementedException(),
};
await Dispatcher.UIThread.InvokeAsync(() =>
{
Plugins.Clear();
Plugins.AddRange(document.Plugins.Keys);
var vm = new EndpointTabItemContentViewModel(_notificationService, Plugins, OnEndpointNavigation, AddEndpointCommand, document, tab);
Endpoints.Add(new()
{ {
Content = vm, Tab.LCU => await _documentService.GetLcuSchemaDocumentAsync(),
Header = vm.Title, Tab.GameClient => await _documentService.GetLolClientDocumentAsync(),
Selected = true _ => throw new NotImplementedException(),
};
await Dispatcher.UIThread.InvokeAsync(() =>
{
Plugins.Clear();
Plugins.AddRange(document.Plugins.Keys);
var vm = new EndpointTabItemContentViewModel(_notificationService, Plugins, OnEndpointNavigation, AddEndpointCommand, document, tab);
Endpoints.Add(new()
{
Content = vm,
Header = vm.Title,
Selected = true
});
}); });
}); }
catch (Exception ex)
{
this.Log()
.Error(ex, "Failed to add endpoint.");
_notificationService.Notify("Endpoints", ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error);
}
} }
private void OnEndpointNavigation(string? title, Guid guid) private void OnEndpointNavigation(string? title, Guid guid)

View File

@@ -19,11 +19,14 @@ public partial class HomeViewModel : PageBase, IEnableLogger
{ {
private readonly HextechDocsService _hextechDocsService; private readonly HextechDocsService _hextechDocsService;
private readonly NotificationService _notificationService;
private readonly IDisposable _carouselNextDisposable; private readonly IDisposable _carouselNextDisposable;
public HomeViewModel(HextechDocsService hextechDocsService) : base("Home", "fa-solid fa-house") public HomeViewModel(HextechDocsService hextechDocsService, NotificationService notificationService) : base("Home", "fa-solid fa-house")
{ {
_hextechDocsService = hextechDocsService; _hextechDocsService = hextechDocsService;
_notificationService = notificationService;
_carouselNextDisposable = Observable.Timer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)) _carouselNextDisposable = Observable.Timer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5))
.Select(time => Unit.Default) .Select(time => Unit.Default)
@@ -75,6 +78,7 @@ public partial class HomeViewModel : PageBase, IEnableLogger
{ {
this.Log() this.Log()
.Error(ex, "Failed to get posts from HextechDocs."); .Error(ex, "Failed to get posts from HextechDocs.");
_notificationService.Notify("Home", ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error);
} }
} }
} }

View File

@@ -2,6 +2,7 @@
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using DebounceThrottle; using DebounceThrottle;
using Needlework.Net.Extensions;
using Needlework.Net.Helpers; using Needlework.Net.Helpers;
using Needlework.Net.Services; using Needlework.Net.Services;
using Needlework.Net.ViewModels.Pages.Endpoints; using Needlework.Net.ViewModels.Pages.Endpoints;
@@ -12,7 +13,7 @@ using System.Threading.Tasks;
namespace Needlework.Net.ViewModels.Pages.Schemas namespace Needlework.Net.ViewModels.Pages.Schemas
{ {
public partial class SchemasViewModel : PageBase public partial class SchemasViewModel : PageBase, IEnableLogger
{ {
private readonly DebounceDispatcher _debounceDispatcher = new(TimeSpan.FromMilliseconds(500)); private readonly DebounceDispatcher _debounceDispatcher = new(TimeSpan.FromMilliseconds(500));
@@ -20,12 +21,15 @@ namespace Needlework.Net.ViewModels.Pages.Schemas
private readonly SchemaPaneService _schemaPaneService; private readonly SchemaPaneService _schemaPaneService;
private readonly NotificationService _notificationService;
private List<SchemaSearchDetailsViewModel> _schemas = []; private List<SchemaSearchDetailsViewModel> _schemas = [];
public SchemasViewModel(DocumentService documentService, SchemaPaneService schemaPaneService) : base("Schemas", "fa-solid fa-file-lines") public SchemasViewModel(DocumentService documentService, SchemaPaneService schemaPaneService, NotificationService notificationService) : base("Schemas", "fa-solid fa-file-lines")
{ {
_documentService = documentService; _documentService = documentService;
_schemaPaneService = schemaPaneService; _schemaPaneService = schemaPaneService;
_notificationService = notificationService;
} }
[ObservableProperty] [ObservableProperty]
@@ -60,18 +64,27 @@ namespace Needlework.Net.ViewModels.Pages.Schemas
public override async Task InitializeAsync() public override async Task InitializeAsync()
{ {
var lcuSchemaDocument = await _documentService.GetLcuSchemaDocumentAsync(); try
var lolClientDocument = await _documentService.GetLolClientDocumentAsync();
Dispatcher.UIThread.Invoke(() =>
{ {
var schemas = Enumerable.Concat( var lcuSchemaDocument = await _documentService.GetLcuSchemaDocumentAsync();
lcuSchemaDocument.OpenApiDocument.Components.Schemas.Values.Select(schema => new SchemaSearchDetailsViewModel(Tab.LCU, OpenApiHelpers.WalkSchema(schema, lcuSchemaDocument.OpenApiDocument), _schemaPaneService)), var lolClientDocument = await _documentService.GetLolClientDocumentAsync();
lolClientDocument.OpenApiDocument.Components.Schemas.Values.Select(schema => new SchemaSearchDetailsViewModel(Tab.GameClient, OpenApiHelpers.WalkSchema(schema, lolClientDocument.OpenApiDocument), _schemaPaneService)) Dispatcher.UIThread.Invoke(() =>
).ToList(); {
_schemas = schemas; var schemas = Enumerable.Concat(
SchemaItems = schemas.ToList(); lcuSchemaDocument.OpenApiDocument.Components.Schemas.Values.Select(schema => new SchemaSearchDetailsViewModel(Tab.LCU, OpenApiHelpers.WalkSchema(schema, lcuSchemaDocument.OpenApiDocument), _schemaPaneService)),
IsBusy = false; lolClientDocument.OpenApiDocument.Components.Schemas.Values.Select(schema => new SchemaSearchDetailsViewModel(Tab.GameClient, OpenApiHelpers.WalkSchema(schema, lolClientDocument.OpenApiDocument), _schemaPaneService))
}); ).ToList();
_schemas = schemas;
SchemaItems = schemas.ToList();
IsBusy = false;
});
}
catch (Exception ex)
{
this.Log()
.Error(ex, "Failed to load schemas.");
_notificationService.Notify("Schemas", ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error);
}
} }
} }
} }

View File

@@ -103,7 +103,7 @@ public partial class WebSocketViewModel : PageBase, IEnableLogger
var message = "Failed to get event types from GitHub. Please check your internet connection or try again later."; var message = "Failed to get event types from GitHub. Please check your internet connection or try again later.";
this.Log() this.Log()
.Error(ex, message); .Error(ex, message);
_notificationService.Notify(AppInfo.Name, message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error); _notificationService.Notify("WebSocket", message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error);
} }
} }