Skip to content

Commit b0bad35

Browse files
authored
Merge pull request #98 from Virtual-Finland-Development/feat/codesets-service-timeouts-handling
Service timeouts handling for codesets
2 parents 158bae0 + fce8278 commit b0bad35

14 files changed

Lines changed: 149 additions & 175 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using VirtualFinland.UserAPI.Helpers.Configurations;
2+
using VirtualFinland.UserAPI.Helpers.Services;
3+
4+
namespace VirtualFinland.UserAPI.Helpers;
5+
6+
public abstract class CodesetsResourceRepository<T>
7+
{
8+
private readonly CodesetsService _codesetsService;
9+
private T? _resourceCache; // @TODO: Add caching suitable for AWS Lambda
10+
protected CodesetsResource? _resource;
11+
12+
public CodesetsResourceRepository(CodesetsService codesetsService)
13+
{
14+
_codesetsService = codesetsService;
15+
}
16+
17+
public async Task<T> GetResource(CodesetsResource? resource = null)
18+
{
19+
if (_resourceCache is not null)
20+
{
21+
return _resourceCache;
22+
}
23+
24+
var resourceToGet = resource ?? _resource ?? throw new Exception("Resource not defined");
25+
_resourceCache = await _codesetsService.GetResource<T>(resourceToGet);
26+
return _resourceCache;
27+
}
28+
}
Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,16 @@
1-
using System.Text.Json;
2-
using Microsoft.Extensions.Options;
1+
using VirtualFinland.UserAPI.Helpers;
2+
using VirtualFinland.UserAPI.Helpers.Configurations;
3+
using VirtualFinland.UserAPI.Helpers.Services;
34
using VirtualFinland.UserAPI.Models.Repositories;
45

56
namespace VirtualFinland.UserAPI.Data.Repositories;
67

7-
public class CountriesRepository : ICountriesRepository
8+
public class CountriesRepository : CodesetsResourceRepository<List<Country>>, ICountriesRepository
89
{
9-
private readonly IHttpClientFactory _httpClientFactory;
10-
private readonly string _iso3166CountriesUrl;
11-
private List<Country>? _countries;
12-
13-
public CountriesRepository(IOptions<CodesetConfig> settings, IHttpClientFactory httpClientFactory)
10+
public CountriesRepository(CodesetsService codesetsService) : base(codesetsService)
1411
{
15-
_httpClientFactory = httpClientFactory;
16-
_iso3166CountriesUrl = settings.Value.IsoCountriesUrl;
12+
_resource = CodesetsResource.Countries;
1713
}
1814

19-
public async Task<List<Country>> GetAllCountries()
20-
{
21-
if (_countries is not null)
22-
{
23-
return _countries;
24-
}
25-
26-
var httpClient = _httpClientFactory.CreateClient();
27-
var httpResponseMessage = await httpClient.GetAsync(_iso3166CountriesUrl);
28-
29-
if (httpResponseMessage.IsSuccessStatusCode)
30-
{
31-
var countries = JsonSerializer.Deserialize<List<Country>>(await httpResponseMessage.Content.ReadAsStringAsync());
32-
33-
if (countries is not null)
34-
{
35-
_countries = countries;
36-
return _countries;
37-
}
38-
}
39-
40-
return new List<Country>();
41-
}
15+
public Task<List<Country>> GetAllCountries() => GetResource();
4216
}
Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,16 @@
1-
using System.Text.Json;
2-
using Microsoft.Extensions.Options;
1+
using VirtualFinland.UserAPI.Helpers;
2+
using VirtualFinland.UserAPI.Helpers.Configurations;
3+
using VirtualFinland.UserAPI.Helpers.Services;
34
using VirtualFinland.UserAPI.Models.Repositories;
45

56
namespace VirtualFinland.UserAPI.Data.Repositories;
67

7-
public class LanguageRepository : ILanguageRepository
8+
public class LanguageRepository : CodesetsResourceRepository<List<Language>>, ILanguageRepository
89
{
9-
private readonly IHttpClientFactory _httpClientFactory;
10-
private readonly string _languagesUrl;
11-
private List<Language>? _languages;
12-
13-
14-
public LanguageRepository(IOptions<CodesetConfig> settings, IHttpClientFactory httpClientFactory)
10+
public LanguageRepository(CodesetsService codesetsService) : base(codesetsService)
1511
{
16-
_httpClientFactory = httpClientFactory;
17-
_languagesUrl = settings.Value.IsoLanguages;
12+
_resource = CodesetsResource.Languages;
1813
}
1914

20-
public async Task<List<Language>> GetAllLanguages()
21-
{
22-
if (_languages is not null)
23-
{
24-
return _languages;
25-
}
26-
27-
var httpClient = _httpClientFactory.CreateClient();
28-
var httpResponseMessage = await httpClient.GetAsync(_languagesUrl);
29-
30-
if (httpResponseMessage.IsSuccessStatusCode)
31-
{
32-
var languages = JsonSerializer.Deserialize<List<Language>>(await httpResponseMessage.Content.ReadAsStringAsync());
33-
34-
if (languages is not null)
35-
{
36-
_languages = languages;
37-
return languages;
38-
}
39-
}
40-
41-
return new List<Language>();
42-
}
15+
public Task<List<Language>> GetAllLanguages() => GetResource();
4316
}
Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,16 @@
1-
using System.Text.Json;
2-
using Microsoft.Extensions.Options;
1+
using VirtualFinland.UserAPI.Helpers;
2+
using VirtualFinland.UserAPI.Helpers.Configurations;
3+
using VirtualFinland.UserAPI.Helpers.Services;
34
using VirtualFinland.UserAPI.Models.Repositories;
45

56
namespace VirtualFinland.UserAPI.Data.Repositories;
67

7-
public class OccupationsFlatRepository : IOccupationsFlatRepository
8+
public class OccupationsFlatRepository : CodesetsResourceRepository<List<OccupationFlatRoot.Occupation>>, IOccupationsFlatRepository
89
{
9-
private readonly IHttpClientFactory _httpClientFactory;
10-
private readonly string _occupationsFlatUrl;
11-
private List<OccupationFlatRoot.Occupation>? _occupationsFlat;
12-
13-
public OccupationsFlatRepository(IOptions<CodesetConfig> settings, IHttpClientFactory httpClientFactory)
10+
public OccupationsFlatRepository(CodesetsService codesetsService) : base(codesetsService)
1411
{
15-
_httpClientFactory = httpClientFactory;
16-
_occupationsFlatUrl = settings.Value.OccupationsFlatUrl;
12+
_resource = CodesetsResource.OccupationsFlat;
1713
}
1814

19-
public async Task<List<OccupationFlatRoot.Occupation>> GetAllOccupationsFlat()
20-
{
21-
// TODO: Better cache control, maybe use .NET Core 6 In-Memory Cache. Fastest solution at the moment.
22-
if (_occupationsFlat is not null)
23-
{
24-
return _occupationsFlat;
25-
}
26-
27-
var httpClient = _httpClientFactory.CreateClient();
28-
var httpResponseMessage = await httpClient.GetAsync(_occupationsFlatUrl);
29-
30-
if (httpResponseMessage.IsSuccessStatusCode)
31-
{
32-
var rootOccupationFlatData = JsonSerializer.Deserialize<List<OccupationFlatRoot.Occupation>>(await httpResponseMessage.Content.ReadAsStringAsync());
33-
34-
if (rootOccupationFlatData is not null)
35-
{
36-
_occupationsFlat = rootOccupationFlatData;
37-
return rootOccupationFlatData;
38-
}
39-
}
40-
41-
return new List<OccupationFlatRoot.Occupation>();
42-
}
43-
}
15+
public Task<List<OccupationFlatRoot.Occupation>> GetAllOccupationsFlat() => GetResource();
16+
}
Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,16 @@
1-
using System.Text.Json;
2-
using Microsoft.Extensions.Options;
1+
using VirtualFinland.UserAPI.Helpers;
2+
using VirtualFinland.UserAPI.Helpers.Configurations;
3+
using VirtualFinland.UserAPI.Helpers.Services;
34
using VirtualFinland.UserAPI.Models.Repositories;
45

56
namespace VirtualFinland.UserAPI.Data.Repositories;
67

7-
public class OccupationsRepository : IOccupationsRepository
8+
public class OccupationsRepository : CodesetsResourceRepository<List<OccupationRoot.Occupation>>, IOccupationsRepository
89
{
9-
private readonly IHttpClientFactory _httpClientFactory;
10-
private readonly string _occupationsUrl;
11-
private List<OccupationRoot.Occupation>? _occupations;
12-
13-
public OccupationsRepository(IOptions<CodesetConfig> settings, IHttpClientFactory httpClientFactory)
10+
public OccupationsRepository(CodesetsService codesetsService) : base(codesetsService)
1411
{
15-
_httpClientFactory = httpClientFactory;
16-
_occupationsUrl = settings.Value.OccupationsEscoUrl;
12+
_resource = CodesetsResource.Occupations;
1713
}
1814

19-
public async Task<List<OccupationRoot.Occupation>> GetAllOccupations()
20-
{
21-
// TODO: Better cache control, maybe use .NET Core 6 In-Memory Cache. Fastest solution at the moment.
22-
if (_occupations is not null)
23-
{
24-
return _occupations;
25-
}
26-
27-
var httpClient = _httpClientFactory.CreateClient();
28-
var httpResponseMessage = await httpClient.GetAsync(_occupationsUrl);
29-
30-
if (httpResponseMessage.IsSuccessStatusCode)
31-
{
32-
var rootOccupationData = JsonSerializer.Deserialize<List<OccupationRoot.Occupation>>(await httpResponseMessage.Content.ReadAsStringAsync());
33-
34-
if (rootOccupationData is not null)
35-
{
36-
_occupations = rootOccupationData;
37-
return rootOccupationData;
38-
}
39-
}
40-
41-
return new List<OccupationRoot.Occupation>();
42-
}
15+
public Task<List<OccupationRoot.Occupation>> GetAllOccupations() => GetResource();
4316
}
Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
1+
namespace VirtualFinland.UserAPI.Helpers.Configurations;
2+
13
public class CodesetConfig
24
{
5+
private readonly CodesetsServiceConfig _codesetsServiceConfig;
6+
37
public CodesetConfig(IConfiguration configuration)
48
{
5-
CodesetApiBaseUrl = configuration.GetValue<string>("CodesetApiBaseUrl");
9+
_codesetsServiceConfig = configuration.GetSection("Services:Codesets").Get<CodesetsServiceConfig>();
10+
if (string.IsNullOrEmpty(_codesetsServiceConfig.ApiEndpoint))
11+
{
12+
throw new ArgumentException("Services:Codesets.ApiEndpoint not defined");
13+
}
614
}
7-
public CodesetConfig()
15+
16+
public string GetResourceEndpoint(CodesetsResource resource)
817
{
18+
return $"{_codesetsServiceConfig.ApiEndpoint}/{resource}";
919
}
1020

11-
public string? CodesetApiBaseUrl { get; set; }
12-
public string IsoCountriesUrl => CodesetApiBaseUrl + "/ISO3166CountriesURL";
13-
public string OccupationsEscoUrl => CodesetApiBaseUrl + "/OccupationsEscoURL";
14-
public string OccupationsFlatUrl => CodesetApiBaseUrl + "/OccupationsFlatURL";
15-
public string IsoLanguages => CodesetApiBaseUrl + "/ISO639Languages";
21+
private record CodesetsServiceConfig
22+
{
23+
public string ApiEndpoint { get; init; } = string.Empty;
24+
public int ServiceRequestTimeoutInMilliseconds { get; init; } = 9000;
25+
}
1626
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
namespace VirtualFinland.UserAPI.Helpers.Configurations;
3+
4+
/// <summary>
5+
/// Enum-like class for mapping codesets resource names to codesets-endpoint paths
6+
/// </summary>
7+
public sealed class CodesetsResource
8+
{
9+
private CodesetsResource(string value) { Value = value; }
10+
11+
public string Value { get; private set; }
12+
13+
public static CodesetsResource Countries => new("ISO3166CountriesURL");
14+
public static CodesetsResource Occupations => new("OccupationsEscoURL");
15+
public static CodesetsResource OccupationsFlat => new("OccupationsFlatURL");
16+
public static CodesetsResource Languages => new("ISO639Languages");
17+
18+
public override string ToString()
19+
{
20+
return Value;
21+
}
22+
}

VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI/Helpers/Configurations/ServerConfigurationValidation.cs

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System.Text.Json;
2+
using VirtualFinland.UserAPI.Helpers.Configurations;
3+
4+
namespace VirtualFinland.UserAPI.Helpers.Services;
5+
6+
public class CodesetsService
7+
{
8+
private readonly CodesetConfig _config;
9+
private readonly HttpClient _httpClient;
10+
11+
public CodesetsService(CodesetConfig codesetConfig)
12+
{
13+
_config = codesetConfig;
14+
_httpClient = new HttpClient(
15+
new HttpRequestTimeoutHandler
16+
{
17+
DefaultTimeout = TimeSpan.FromSeconds(9),
18+
DefaultTimeoutMessage = "Codesets request timeout",
19+
InnerHandler = new HttpClientHandler()
20+
}
21+
);
22+
}
23+
24+
public async Task<T> GetResource<T>(CodesetsResource resource)
25+
{
26+
var resourceEndpoint = _config.GetResourceEndpoint(resource);
27+
28+
var httpResponseMessage = await _httpClient.GetAsync(resourceEndpoint);
29+
30+
if (!httpResponseMessage.IsSuccessStatusCode)
31+
{
32+
throw new Exception($"Failed to get resource {resource} from {resourceEndpoint}");
33+
}
34+
35+
return JsonSerializer.Deserialize<T>(await httpResponseMessage.Content.ReadAsStringAsync()) ?? throw new Exception($"Failed to deserialize resource {resource} from {resourceEndpoint}");
36+
}
37+
}

VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI/Program.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using Microsoft.AspNetCore.DataProtection;
2121
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
2222
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
23+
using Microsoft.Extensions.Options;
2324

2425
Log.Logger = new LoggerConfiguration()
2526
.WriteTo.Console()
@@ -54,9 +55,6 @@
5455
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
5556
});
5657

57-
// Validate server configuration
58-
ServerConfigurationValidation.ValidateServer(builder.Configuration);
59-
6058
//
6159
// Swagger setup
6260
//
@@ -150,7 +148,8 @@
150148
//
151149
// Other dependencies
152150
//
153-
builder.Services.Configure<CodesetConfig>(builder.Configuration);
151+
builder.Services.AddSingleton<CodesetConfig>();
152+
builder.Services.AddSingleton<CodesetsService>();
154153

155154
//
156155
// Application build

0 commit comments

Comments
 (0)