Skip to content

Commit

Permalink
Rework error handling for HTTP API deserialization
Browse files Browse the repository at this point in the history
  • Loading branch information
pizzaboxer committed Sep 3, 2024
1 parent 6868037 commit f0ffdbc
Show file tree
Hide file tree
Showing 13 changed files with 125 additions and 80 deletions.
12 changes: 7 additions & 5 deletions Bloxstrap/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,22 +110,24 @@ public static void FinalizeExceptionHandling(Exception ex, bool log = true)
public static async Task<GithubRelease?> GetLatestRelease()
{
const string LOG_IDENT = "App::GetLatestRelease";

GithubRelease? releaseInfo = null;

try
{
releaseInfo = await Http.GetJson<GithubRelease>($"https://api.github.com/repos/{ProjectRepository}/releases/latest");
var releaseInfo = await Http.GetJson<GithubRelease>($"https://api.github.com/repos/{ProjectRepository}/releases/latest");

if (releaseInfo is null || releaseInfo.Assets is null)
{
Logger.WriteLine(LOG_IDENT, "Encountered invalid data");
return null;
}

return releaseInfo;
}
catch (Exception ex)
{
Logger.WriteException(LOG_IDENT, ex);
}

return releaseInfo;
return null;
}

protected override void OnStartup(StartupEventArgs e)
Expand Down
13 changes: 13 additions & 0 deletions Bloxstrap/Exceptions/InvalidHTTPResponseException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Bloxstrap.Exceptions
{
internal class InvalidHTTPResponseException : Exception
{
public InvalidHTTPResponseException(string message) : base(message) { }
}
}
22 changes: 12 additions & 10 deletions Bloxstrap/Integrations/ActivityWatcher.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Bloxstrap.Integrations
using System.Windows;

namespace Bloxstrap.Integrations
{
public class ActivityWatcher : IDisposable
{
Expand Down Expand Up @@ -372,29 +374,29 @@ public async Task<string> GetServerLocation()
string location = "";
var ipInfo = await Http.GetJson<IPInfoResponse>($"https://ipinfo.io/{Data.MachineAddress}/json");

if (ipInfo is null)
return $"? ({Strings.ActivityTracker_LookupFailed})";
if (String.IsNullOrEmpty(ipInfo.City))
throw new InvalidHTTPResponseException("Reported city was blank");

if (string.IsNullOrEmpty(ipInfo.Country))
location = "?";
else if (ipInfo.City == ipInfo.Region)
if (ipInfo.City == ipInfo.Region)
location = $"{ipInfo.Region}, {ipInfo.Country}";
else
location = $"{ipInfo.City}, {ipInfo.Region}, {ipInfo.Country}";

if (!InGame)
return $"? ({Strings.ActivityTracker_LeftGame})";

GeolocationCache[Data.MachineAddress] = location;

if (!InGame)
return "";

return location;
}
catch (Exception ex)
{
App.Logger.WriteLine(LOG_IDENT, $"Failed to get server location for {Data.MachineAddress}");
App.Logger.WriteException(LOG_IDENT, ex);

return $"? ({Strings.ActivityTracker_LookupFailed})";
Frontend.ShowMessageBox($"{Strings.ActivityWatcher_LocationQueryFailed}\n\n{ex.Message}", MessageBoxImage.Warning);

return "?";
}
}

Expand Down
24 changes: 15 additions & 9 deletions Bloxstrap/Integrations/DiscordRichPresence.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using DiscordRPC;
using System.Windows;

using DiscordRPC;

namespace Bloxstrap.Integrations
{
Expand Down Expand Up @@ -204,17 +206,21 @@ public async Task<bool> SetCurrentGame()

if (activity.UniverseDetails is null)
{
await UniverseDetails.FetchSingle(activity.UniverseId);
try
{
await UniverseDetails.FetchSingle(activity.UniverseId);
}
catch (Exception ex)
{
App.Logger.WriteException(LOG_IDENT, ex);
Frontend.ShowMessageBox($"{Strings.ActivityWatcher_RichPresenceLoadFailed}\n\n{ex.Message}", MessageBoxImage.Warning);
return false;
}

activity.UniverseDetails = UniverseDetails.LoadFromCache(activity.UniverseId);
}

var universeDetails = activity.UniverseDetails;

if (universeDetails is null)
{
Frontend.ShowMessageBox(Strings.ActivityTracker_RichPresenceLoadFailed, System.Windows.MessageBoxImage.Warning);
return false;
}
var universeDetails = activity.UniverseDetails!;

icon = universeDetails.Thumbnail.ImageUrl;

Expand Down
2 changes: 1 addition & 1 deletion Bloxstrap/JsonManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public virtual void Load(bool alertFailure = true)
message = Strings.JsonManager_FastFlagsLoadFailed;

if (!String.IsNullOrEmpty(message))
Frontend.ShowMessageBox($"{message}\n\n{ex.GetType()}: {ex.Message}", System.Windows.MessageBoxImage.Warning);
Frontend.ShowMessageBox($"{message}\n\n{ex.Message}", System.Windows.MessageBoxImage.Warning);
}

Save();
Expand Down
16 changes: 8 additions & 8 deletions Bloxstrap/Models/UniverseDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@ public class UniverseDetails
return null;
}

public static Task<bool> FetchSingle(long id) => FetchBulk(id.ToString());
public static Task FetchSingle(long id) => FetchBulk(id.ToString());

public static async Task<bool> FetchBulk(string ids)
public static async Task FetchBulk(string ids)
{
var gameDetailResponse = await Http.GetJson<ApiArrayResponse<GameDetailResponse>>($"https://games.roblox.com/v1/games?universeIds={ids}");
if (gameDetailResponse is null || !gameDetailResponse.Data.Any())
return false;

if (!gameDetailResponse.Data.Any())
throw new InvalidHTTPResponseException("Roblox API for Game Details returned invalid data");

var universeThumbnailResponse = await Http.GetJson<ApiArrayResponse<ThumbnailResponse>>($"https://thumbnails.roblox.com/v1/games/icons?universeIds={ids}&returnPolicy=PlaceHolder&size=128x128&format=Png&isCircular=false");
if (universeThumbnailResponse is null || !universeThumbnailResponse.Data.Any())
return false;

if (!universeThumbnailResponse.Data.Any())
throw new InvalidHTTPResponseException("Roblox API for Game Thumbnails returned invalid data");

foreach (string strId in ids.Split(','))
{
Expand All @@ -43,8 +45,6 @@ public static async Task<bool> FetchBulk(string ids)
Thumbnail = universeThumbnailResponse.Data.Where(x => x.TargetId == id).First(),
});
}

return true;
}
}
}
19 changes: 5 additions & 14 deletions Bloxstrap/Resources/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 4 additions & 7 deletions Bloxstrap/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,6 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ActivityTracker.LeftGame" xml:space="preserve">
<value>left game</value>
</data>
<data name="ActivityTracker.LookupFailed" xml:space="preserve">
<value>lookup failed</value>
</data>
<data name="Bootstrapper.AutoUpdateFailed" xml:space="preserve">
<value>Bloxstrap was unable to automatically update to version {0}. Please update it manually by downloading and running it from the website.</value>
</data>
Expand Down Expand Up @@ -1171,10 +1165,13 @@ Are you sure you want to continue?</value>
<data name="ContextMenu.GameHistory.Rejoin" xml:space="preserve">
<value>Rejoin</value>
</data>
<data name="ActivityTracker.RichPresenceLoadFailed" xml:space="preserve">
<data name="ActivityWatcher.RichPresenceLoadFailed" xml:space="preserve">
<value>Your current game will not show on your Discord presence because an error occurred when loading the game information.</value>
</data>
<data name="ContextMenu.GameHistory.Description" xml:space="preserve">
<value>Game history is only recorded for your current Roblox session. Games will appear here as you leave them or teleport within them.</value>
</data>
<data name="ActivityWatcher.LocationQueryFailed" xml:space="preserve">
<value>Failed to query server location.</value>
</data>
</root>
22 changes: 18 additions & 4 deletions Bloxstrap/UI/Elements/ContextMenu/ServerHistory.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:models="clr-namespace:Bloxstrap.UI.ViewModels.ContextMenu"
xmlns:resources="clr-namespace:Bloxstrap.Resources"
xmlns:enums="clr-namespace:Bloxstrap.Enums"
d:DataContext="{d:DesignInstance Type=models:ServerHistoryViewModel}"
mc:Ignorable="d"
Title="{x:Static resources:Strings.ContextMenu_GameHistory_Title}"
Expand All @@ -30,11 +31,24 @@

<TextBlock Grid.Row="1" Margin="16,8,16,0" Text="{x:Static resources:Strings.ContextMenu_GameHistory_Description}" TextWrapping="Wrap" />

<TextBlock Grid.Row="2" Margin="16,8,16,0" Text="{Binding Error, Mode=OneWay}" TextWrapping="Wrap">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding LoadState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Failed}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</TextBlock.Style>
</TextBlock>

<Border Grid.Row="2">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding GameHistory, Mode=OneWay}" Value="{x:Null}">
<DataTrigger Binding="{Binding LoadState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Unknown}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
Expand All @@ -49,11 +63,11 @@
<ListView.Style>
<Style TargetType="ListView" BasedOn="{StaticResource {x:Type ListView}}">
<Style.Triggers>
<DataTrigger Binding="{Binding GameHistory, Mode=OneWay}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed" />
<DataTrigger Binding="{Binding LoadState, Mode=OneWay}" Value="{x:Static enums:GenericTriState.Successful}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
<Setter Property="Visibility" Value="Visible" />
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</ListView.Style>
<ListView.ItemTemplate>
Expand Down
4 changes: 4 additions & 0 deletions Bloxstrap/UI/NotifyIconWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ public async void OnGameJoin(object? sender, EventArgs e)
return;

string serverLocation = await _activityWatcher.GetServerLocation();

if (string.IsNullOrEmpty(serverLocation))
return;

string title = _activityWatcher.Data.ServerType switch
{
ServerType.Public => Strings.ContextMenu_ServerInformation_Notification_Title_Public,
Expand Down
26 changes: 25 additions & 1 deletion Bloxstrap/UI/ViewModels/ContextMenu/ServerHistoryViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ internal class ServerHistoryViewModel : NotifyPropertyChangedViewModel

public List<ActivityData>? GameHistory { get; private set; }

public GenericTriState LoadState { get; private set; } = GenericTriState.Unknown;

public string Error { get; private set; } = String.Empty;

public ICommand CloseWindowCommand => new RelayCommand(RequestClose);

public EventHandler? RequestCloseEvent;
Expand All @@ -25,15 +29,32 @@ public ServerHistoryViewModel(ActivityWatcher activityWatcher)

private async void LoadData()
{
LoadState = GenericTriState.Unknown;
OnPropertyChanged(nameof(LoadState));

var entries = _activityWatcher.History.Where(x => x.UniverseDetails is null);

if (entries.Any())
{
// TODO: this will duplicate universe ids
string universeIds = String.Join(',', entries.Select(x => x.UniverseId));

if (!await UniverseDetails.FetchBulk(universeIds))
try
{
await UniverseDetails.FetchBulk(universeIds);
}
catch (Exception ex)
{
App.Logger.WriteException("ServerHistoryViewModel::LoadData", ex);

Error = ex.Message;
OnPropertyChanged(nameof(Error));

LoadState = GenericTriState.Failed;
OnPropertyChanged(nameof(LoadState));

return;
}

foreach (var entry in entries)
entry.UniverseDetails = UniverseDetails.LoadFromCache(entry.UniverseId);
Expand Down Expand Up @@ -64,6 +85,9 @@ private async void LoadData()
}

OnPropertyChanged(nameof(GameHistory));

LoadState = GenericTriState.Successful;
OnPropertyChanged(nameof(LoadState));
}

private void RequestClose() => RequestCloseEvent?.Invoke(this, EventArgs.Empty);
Expand Down
8 changes: 0 additions & 8 deletions Bloxstrap/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@ namespace Bloxstrap
{
static class Utilities
{
/// <summary>
/// Is process running as administrator
/// https://stackoverflow.com/a/11660205
/// </summary>
public static bool IsAdministrator =>
new WindowsPrincipal(WindowsIdentity.GetCurrent())
.IsInRole(WindowsBuiltInRole.Administrator);

public static void ShellExecute(string website)
{
try
Expand Down
Loading

0 comments on commit f0ffdbc

Please sign in to comment.