Skip to content

Commit

Permalink
Restructured error management in BackendAccess; Introduced BackendMoo…
Browse files Browse the repository at this point in the history
…dleApiUnreachableException.cs and new error message in LmsLoginDialog.razor
  • Loading branch information
MarvinHo64 committed Oct 23, 2024
1 parent 2484b65 commit f344ece
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 5 deletions.
4 changes: 4 additions & 0 deletions BackendAccess/BackendEntities/ErrorBE.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ public class ErrorBE
{
// ReSharper disable once UnassignedGetOnlyAutoProperty
public string? Detail { set; get; }

public string? Type { set; get; }

public string? StatusCode { set; get; }
}
8 changes: 5 additions & 3 deletions BackendAccess/BackendServices/UserWebApiServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,12 @@ public async Task<UserInformationBE> GetUserInformationAsync(string token)
return await SendHttpGetRequestAsync<UserInformationBE>("Users/UserData",
parameters);
}
catch (HttpRequestException httpReqEx)
catch (BackendHttpRequestException httpReqEx)
{
if (httpReqEx.Message == "The provided token is invalid")
if (httpReqEx.ErrorType == ErrorCodes.LmsTokenInvalid)
throw new BackendInvalidTokenException(httpReqEx.Message, httpReqEx);
if (httpReqEx.ErrorType == ErrorCodes.LmsError)
throw new BackendMoodleApiUnreachableException(httpReqEx.Message, httpReqEx);
throw;
}
}
Expand Down Expand Up @@ -379,7 +381,7 @@ private async Task HandleErrorMessage(HttpResponseMessage apiResp)
var error = await apiResp.Content.ReadAsStringAsync();

var problemDetails = TryRead<ErrorBE>(error);
throw new HttpRequestException(problemDetails.Detail, null, apiResp.StatusCode);
throw new BackendHttpRequestException(problemDetails.Detail, null, apiResp.StatusCode, problemDetails.Type);
}


Expand Down
29 changes: 27 additions & 2 deletions BackendAccessTest/BackendServices/UserWebApiServicesUt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ public void GetUserTokenAsync_AnyOtherHttpRequestException_ThrowsException()

var userWebApiServices = CreateTestableUserWebApiServices(httpClientFactory: mockHttpClientFactory);

var ex = Assert.ThrowsAsync<HttpRequestException>(async () =>
var ex = Assert.ThrowsAsync<BackendHttpRequestException>(async () =>
await userWebApiServices.GetUserTokenAsync("username", "password"));
Assert.That(ex!.Message, Is.EqualTo("Error Message"));
}
Expand Down Expand Up @@ -322,7 +322,7 @@ public void GetUserInformationAsync_InvalidToken_ThrowsException()
{
var mockedHttp = new MockHttpMessageHandler();

var exception = new HttpRequestException("The provided token is invalid");
var exception = new BackendHttpRequestException("The provided token is invalid", null, null, ErrorCodes.LmsTokenInvalid);
mockedHttp
.When("*")
.Throw(exception);
Expand All @@ -338,6 +338,31 @@ public void GetUserInformationAsync_InvalidToken_ThrowsException()
Assert.That(ex!.Message, Is.EqualTo("The provided token is invalid"));
Assert.That(ex.InnerException, Is.EqualTo(exception));
}

[Test]
// ANF-ID: [AHO21]
public void GetUserInformationAsync_MoodleUnreachable_ThrowsException()
{
var mockedHttp = new MockHttpMessageHandler();

var exception = new BackendHttpRequestException(
"Das Ergebnis der Moodle Web Api konnte nicht gelesen werden. Response string is: 404 page not found\n",
null, null, ErrorCodes.LmsError);
mockedHttp
.When("*")
.Throw(exception);
var mockHttpClientFactory = Substitute.For<IHttpClientFactory>();
mockHttpClientFactory
.CreateClient(Arg.Any<ProgressMessageHandler>())
.Returns(mockedHttp.ToHttpClient());

var userWebApiServices = CreateTestableUserWebApiServices(httpClientFactory: mockHttpClientFactory);

var ex = Assert.ThrowsAsync<BackendMoodleApiUnreachableException>(async () =>
await userWebApiServices.GetUserInformationAsync("token"));
Assert.That(ex!.Message, Is.EqualTo("Das Ergebnis der Moodle Web Api konnte nicht gelesen werden. Response string is: 404 page not found\n"));
Assert.That(ex.InnerException, Is.EqualTo(exception));
}

[Test]
// ANF-ID: [AHO21]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Net;

namespace BusinessLogic.ErrorManagement.BackendAccess;

public class BackendHttpRequestException : HttpRequestException
{

public string? ErrorType { get; set; }

public BackendHttpRequestException(string? message, Exception? inner, HttpStatusCode? statusCode, string? type)
: base(message, inner, statusCode)
{
ErrorType = type;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace BusinessLogic.ErrorManagement.BackendAccess;

public class BackendMoodleApiUnreachableException : Exception
{
public BackendMoodleApiUnreachableException()
{
}

public BackendMoodleApiUnreachableException(string message) : base(message)
{
}

public BackendMoodleApiUnreachableException(string message, Exception innerException) : base(message, innerException)
{
}
}
44 changes: 44 additions & 0 deletions BusinessLogic/ErrorManagement/BackendAccess/ErrorCodes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
namespace BusinessLogic.ErrorManagement.BackendAccess;

public static class ErrorCodes
{
/// <summary>
/// Error code for using an invalid token
/// </summary>
public const string LmsTokenInvalid = "invalid_token";

/// <summary>
/// Error code for wrong username or password
/// </summary>
public const string InvalidLogin = "invalid_login";

/// <summary>
/// Error if a resource is not found
/// </summary>
public const string NotFound = "not_found";

/// <summary>
/// Error if a resource is forbidden
/// </summary>
public const string Forbidden = "forbidden";

/// <summary>
/// Error if a request is invalid
/// </summary>
public const string ValidationError = "validation_error";

/// <summary>
/// Error code for unknown errors
/// </summary>
public const string UnknownError = "unknown_error";

/// <summary>
/// Error code for generic LMS errors
/// </summary>
public const string LmsError = "lms_error";

/// <summary>
/// Error code when a world could not be created
/// </summary>
public const string WorldCreationErrorDuplicate = "world_creation_error";
}
23 changes: 23 additions & 0 deletions IntegrationTest/Dialogues/LmsLoginDialogIt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,29 @@ public async Task DialogCreated_IsLmsConnectedThrowsBackendApiUnreachableExcepti
Assert.That(errorText, Is.EqualTo("API is unreachable"));
});
}

[Test]
// ANF-ID: [AHO21]
public async Task DialogCreated_IsLmsConnectedThrowsMoodleUnreachableException_ShowsErrorMessage()
{
_presentationLogic.IsLmsConnected().Throws(x => throw new BackendMoodleApiUnreachableException());

Localizer["DialogContent.Error.MoodleUnreachable"]
.Returns(new LocalizedString("DialogContent.Error.MoodleUnreachable","Moodle is unreachable"));

await OpenDialogAndGetDialogReferenceAsync();

var mudTexts = DialogProvider.FindComponents<MudText>();
Assert.That(mudTexts, Has.Count.EqualTo(5));
DialogProvider.WaitForAssertion(() =>
{
var errorElement = DialogProvider.Find("h6.mud-error-text");
Assert.That(errorElement, Is.Not.Null);

var errorText = errorElement.TextContent.Trim();
Assert.That(errorText, Is.EqualTo("Moodle is unreachable"));
});
}

[Test]
// ANF-ID: [AHO21]
Expand Down
11 changes: 11 additions & 0 deletions Presentation/Components/Dialogues/LmsLoginDialog.razor
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@
@Localizer["DialogContent.Error.TokenInvalid"]
</MudText>
}
@if (_showErrorMoodleUnreachable)
{
<MudText Class="invalid-login-error" Color="Color.Error" Typo="Typo.subtitle2">
@Localizer["DialogContent.Error.MoodleUnreachable"]
</MudText>
}
</div>
<div class="flex flex-col">
<!--TODO: Password reset link and API link should be set in the config - we need a proper configuration dialog -->
Expand Down Expand Up @@ -262,6 +268,7 @@

private bool _isPasswordVisible = false;
private bool _showErrorTokenInvalid = false;
private bool _showErrorMoodleUnreachable = false;
private bool _spinnerActive = false;
private InputType PasswordInputType => _isPasswordVisible ? InputType.Text : InputType.Password;
private string ShowPasswordIcon => _isPasswordVisible ? Icons.Material.Filled.Visibility : Icons.Material.Filled.VisibilityOff;
Expand Down Expand Up @@ -307,6 +314,10 @@
_showErrorTokenInvalid = true;
Logout();
}
catch (BackendMoodleApiUnreachableException)
{
_showErrorMoodleUnreachable = true;
}
finally
{
StopSpinner();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,7 @@ Mit dieser ausgeführten Aktion des Löschens werden die Kurse unwiderruflich au
<data name="DialogContent.AdLerServer.ErrorMessage.Refresh" xml:space="preserve">
<value>Fehler beim Versuch, die Liste der LMS-Welten abzurufen</value>
</data>
<data name="DialogContent.Error.MoodleUnreachable" xml:space="preserve">
<value>Moodle ist nicht erreichbar.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,7 @@ This deletion action also irrevocably removes the courses from Moodle.</value>
<data name="DialogContent.AdLerServer.ErrorMessage.Refresh" xml:space="preserve">
<value>Error while trying to get the LMS world list</value>
</data>
<data name="DialogContent.Error.MoodleUnreachable" xml:space="preserve">
<value>Couldn't reach Moodle.</value>
</data>
</root>

0 comments on commit f344ece

Please sign in to comment.