Skip to content

Commit 38a3627

Browse files
committed
chore: Cleanup Access Layer
1 parent 850d55b commit 38a3627

31 files changed

+357
-192
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ Prefix your items with `(Template)` if the change is about the template and not
88
## 3.10.X
99
- Added Dependency Injection validation in the development environment.
1010
- Cleaned up the persistence configuration files (removed unused parameters and updated documentation).
11+
- Updated Contributing documentation.
1112
- Adding a workaround for a bug with the language change on android.
13+
- Cleanup the 'Access' layer's code (renamed repositories into API clients, removed unused namespaces, and sealed some classes).
1214

1315
## 3.9.X
1416
- Removed unnecessary `IsExternalInit.cs` files.
@@ -21,7 +23,6 @@ Prefix your items with `(Template)` if the change is about the template and not
2123
- Optimized the .NET workloads install process.
2224
- Fixed the iOS application icon size.
2325
- Added VM Disposal in Functional Tests.
24-
- Updated Contributing documentation.
2526

2627
## 3.8.X
2728
- Updated from .NET 8 to .NET 9.

src/app/ApplicationTemplate.Access/ApiClients/Authentication/AuthenticationApiClientMock.cs

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,25 @@ namespace ApplicationTemplate.DataAccess;
1010

1111
public sealed class AuthenticationApiClientMock : IAuthenticationApiClient
1212
{
13+
private const int TokenExpirationSeconds = 600;
14+
1315
private readonly JsonSerializerOptions _serializerOptions;
1416
private readonly IOptionsMonitor<MockOptions> _mockOptionsMonitor;
17+
private readonly TimeProvider _timeProvider;
1518

16-
public AuthenticationApiClientMock(JsonSerializerOptions serializerOptions, IOptionsMonitor<MockOptions> mockOptionsMonitor)
19+
public AuthenticationApiClientMock(JsonSerializerOptions serializerOptions, IOptionsMonitor<MockOptions> mockOptionsMonitor, TimeProvider timeProvider)
1720
{
1821
_serializerOptions = serializerOptions;
1922
_mockOptionsMonitor = mockOptionsMonitor;
23+
_timeProvider = timeProvider;
2024
}
2125

2226
public async Task<AuthenticationData> CreateAccount(CancellationToken ct, string email, string password)
2327
{
2428
await SimulateDelay(ct);
2529

26-
// We authenticate the user on account creation, since we don't have a backend to register and validate the user
27-
return CreateAuthenticationData();
30+
// We authenticate the user on account creation, since we don't have a backend to register and validate the user.
31+
return CreateAuthenticationData(email: email);
2832
}
2933

3034
public async Task ResetPassword(CancellationToken ct, string email)
@@ -36,7 +40,7 @@ public async Task<AuthenticationData> Login(CancellationToken ct, string email,
3640
{
3741
await SimulateDelay(ct);
3842

39-
return CreateAuthenticationData();
43+
return CreateAuthenticationData(email: email);
4044
}
4145

4246
public async Task<AuthenticationData> RefreshToken(CancellationToken ct, AuthenticationData unauthorizedToken)
@@ -45,41 +49,58 @@ public async Task<AuthenticationData> RefreshToken(CancellationToken ct, Authent
4549

4650
await SimulateDelay(ct);
4751

48-
return CreateAuthenticationData(unauthorizedToken.AccessTokenPayload);
49-
}
50-
51-
private AuthenticationData CreateAuthenticationData(AuthenticationToken token = null, TimeSpan? timeToLive = null)
52-
{
53-
var encodedJwt = CreateJsonWebToken(token, timeToLive);
54-
var jwt = new JwtData<AuthenticationToken>(encodedJwt, _serializerOptions);
55-
56-
return new AuthenticationData()
57-
{
58-
AccessToken = jwt.Token,
59-
RefreshToken = Guid.NewGuid().ToString(format: null, CultureInfo.InvariantCulture),
60-
Expiration = jwt.Payload.Expiration,
61-
};
52+
return CreateAuthenticationData(token: unauthorizedToken.AccessToken.Payload);
6253
}
6354

64-
private string CreateJsonWebToken(AuthenticationToken token = null, TimeSpan? timeToLive = null)
55+
/// <summary>
56+
/// Creates a JSON Web Token.
57+
/// </summary>
58+
/// <remarks>
59+
/// This function has been made public and static for testing purposes.
60+
/// </remarks>
61+
/// <param name="token">The token to use.</param>
62+
/// <param name="email">The email or unique name to store in the token.</param>
63+
/// <param name="now">The current date and time to use for the authentication token.</param>
64+
/// <param name="serializerOptions">The serializer options to use for the token serialization.</param>
65+
/// <returns>The JSON Web token.</returns>
66+
public static string CreateJsonWebToken(AuthenticationToken token = null, string email = null, DateTimeOffset? now = null, JsonSerializerOptions serializerOptions = null)
6567
{
6668
const string header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; // alg=HS256, type=JWT
6769
const string signature = "QWqnPP8W6ymexz74P6quP-oG-wxr7vMGqrEL8y_tV6M"; // dummy stuff
6870

69-
var now = DateTimeOffset.Now;
71+
now ??= DateTimeOffset.Now;
7072

71-
token = token ?? new AuthenticationToken(default, DateTimeOffset.MinValue, DateTimeOffset.MinValue);
73+
token ??= new AuthenticationToken()
74+
{
75+
Email = email,
76+
Expiration = now.Value.AddSeconds(TokenExpirationSeconds),
77+
IssuedAt = now.Value,
78+
};
7279

7380
string payload;
7481
using (var stream = new MemoryStream())
7582
{
76-
JsonSerializer.Serialize(stream, token, _serializerOptions);
83+
var test = JsonSerializer.Serialize(token, serializerOptions);
84+
85+
JsonSerializer.Serialize(stream, token, serializerOptions);
7786
payload = Convert.ToBase64String(stream.ToArray());
7887
}
7988

8089
return header + '.' + payload + '.' + signature;
8190
}
8291

92+
private AuthenticationData CreateAuthenticationData(AuthenticationToken token = null, string email = null)
93+
{
94+
var now = _timeProvider.GetLocalNow();
95+
var encodedJwt = CreateJsonWebToken(token, email, now, _serializerOptions);
96+
97+
return new AuthenticationData()
98+
{
99+
AccessToken = new JwtData<AuthenticationToken>(encodedJwt, _serializerOptions),
100+
RefreshToken = Guid.NewGuid().ToString(format: null, CultureInfo.InvariantCulture),
101+
};
102+
}
103+
83104
private async Task SimulateDelay(CancellationToken ct)
84105
{
85106
if (_mockOptionsMonitor.CurrentValue.IsDelayForSimulatedApiCallsEnabled)

src/app/ApplicationTemplate.Access/ApiClients/Authentication/AuthenticationData.cs

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/app/ApplicationTemplate.Access/ApiClients/Authentication/AuthenticationToken.cs

Lines changed: 0 additions & 28 deletions
This file was deleted.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Text.Json.Serialization;
3+
using MallardMessageHandlers;
4+
5+
namespace ApplicationTemplate.DataAccess;
6+
7+
public sealed class AuthenticationData : IAuthenticationToken
8+
{
9+
[JsonPropertyName("access_token")]
10+
[JsonConverter(typeof(JwtDataJsonConverter<AuthenticationToken>))]
11+
public JwtData<AuthenticationToken> AccessToken { get; init; }
12+
13+
[JsonPropertyName("refresh_token")]
14+
public string RefreshToken { get; init; }
15+
16+
[JsonIgnore]
17+
string IAuthenticationToken.AccessToken => AccessToken?.Token;
18+
19+
[JsonIgnore]
20+
public bool CanBeRefreshed => !string.IsNullOrEmpty(RefreshToken);
21+
22+
[JsonIgnore]
23+
public string Email => AccessToken?.Payload?.Email;
24+
25+
[JsonIgnore]
26+
public DateTimeOffset? Expiration => AccessToken?.Payload?.Expiration;
27+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Text.Json.Serialization;
3+
4+
namespace ApplicationTemplate.DataAccess;
5+
6+
public sealed class AuthenticationToken
7+
{
8+
[JsonPropertyName("unique_name")]
9+
public string Email { get; init; }
10+
11+
[JsonPropertyName("exp")]
12+
[JsonConverter(typeof(UnixTimestampJsonConverter))]
13+
public DateTimeOffset Expiration { get; init; }
14+
15+
[JsonPropertyName("iat")]
16+
[JsonConverter(typeof(UnixTimestampJsonConverter))]
17+
public DateTimeOffset IssuedAt { get; init; }
18+
}

src/app/ApplicationTemplate.Access/ApiClients/Authentication/IAuthenticationApiClient.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Text;
4-
using System.Threading;
1+
using System.Threading;
52
using System.Threading.Tasks;
63

74
namespace ApplicationTemplate.DataAccess;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace ApplicationTemplate.DataAccess;
2+
3+
public sealed class ErrorData
4+
{
5+
}

src/app/ApplicationTemplate.Access/ApiClients/Posts/PostData.cs renamed to src/app/ApplicationTemplate.Access/ApiClients/Posts/Data/PostData.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Text;
4-
using System.Text.Json.Serialization;
1+
using System.Text.Json.Serialization;
52

63
namespace ApplicationTemplate.DataAccess;
74

8-
public record PostData
5+
public sealed record PostData
96
{
107
public PostData(long id, string title, string body, long userIdentifier)
118
{
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace ApplicationTemplate.DataAccess;
2+
3+
public sealed class PostErrorResponse
4+
{
5+
public PostData Data { get; }
6+
7+
public ErrorData Error { get; }
8+
}

src/app/ApplicationTemplate.Access/ApiClients/Posts/ErrorData.cs

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/app/ApplicationTemplate.Access/ApiClients/Posts/IPostRepository.cs renamed to src/app/ApplicationTemplate.Access/ApiClients/Posts/IPostsApiClient.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Text;
4-
using System.Threading;
1+
using System.Threading;
52
using System.Threading.Tasks;
63
using Refit;
74

@@ -11,7 +8,7 @@ namespace ApplicationTemplate.DataAccess;
118
/// Provides access to the posts API.
129
/// </summary>
1310
[Headers("Authorization: Bearer")]
14-
public interface IPostsRepository
11+
public interface IPostsApiClient
1512
{
1613
/// <summary>
1714
/// Gets the list of all posts.

src/app/ApplicationTemplate.Access/ApiClients/Posts/PostErrorResponse.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/app/ApplicationTemplate.Access/ApiClients/Posts/PostRepositoryException.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System;
2+
3+
namespace ApplicationTemplate.DataAccess;
4+
5+
public sealed class PostsApiClientException : Exception
6+
{
7+
public PostsApiClientException()
8+
{
9+
}
10+
11+
public PostsApiClientException(string message)
12+
: base(message)
13+
{
14+
}
15+
16+
public PostsApiClientException(string message, Exception innerException)
17+
: base(message, innerException)
18+
{
19+
}
20+
21+
public PostsApiClientException(PostErrorResponse errorResponse)
22+
{
23+
}
24+
}

src/app/ApplicationTemplate.Access/ApiClients/Posts/PostsRepositoryMock.cs renamed to src/app/ApplicationTemplate.Access/ApiClients/Posts/PostsApiClientMock.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Text;
4-
using System.Text.Json;
1+
using System.Text.Json;
52
using System.Threading;
63
using System.Threading.Tasks;
74
using Refit;
85

96
namespace ApplicationTemplate.DataAccess;
107

11-
public class PostsRepositoryMock : BaseMock, IPostsRepository
8+
public sealed class PostsApiClientMock : BaseMock, IPostsApiClient
129
{
13-
public PostsRepositoryMock(JsonSerializerOptions serializerOptions)
10+
public PostsApiClientMock(JsonSerializerOptions serializerOptions)
1411
: base(serializerOptions)
1512
{
1613
}

0 commit comments

Comments
 (0)