From 8435b879bf1bc69159cd902544d789bc77a2f5c5 Mon Sep 17 00:00:00 2001 From: Ivar Nesje Date: Mon, 2 Jan 2023 17:33:04 +0100 Subject: [PATCH] TestData rewrite for LocalTest (#9342) * Consolidate reading of TestData in Localtest This is in preparation of adding multiple data sources for registry data. Also enable for localtest and gradually enable nullable reference types on single files while I touch them. * Add indentation to json responses from LocalTest * Add new simplified test data model, and a few debug endpoints on HomeController * Add functionality to fetch an alternate user database from the app itself it is stored in `app/wwwroot/testData.json` --- src/Controllers/HomeController.cs | 80 ++--- src/LocalTest.csproj | 1 + .../Implementation/ClaimsService.cs | 36 +- .../Implementation/PartiesService.cs | 35 +- .../Implementation/RolesWrapper.cs | 44 +-- .../Authorization/Interface/IClaims.cs | 3 +- .../Authorization/Interface/IParties.cs | 5 +- .../LocalApp/Implementation/LocalAppFile.cs | 22 +- .../LocalApp/Implementation/LocalAppHttp.cs | 61 +++- src/Services/LocalApp/Interface/ILocalApp.cs | 8 +- .../Implementation/UserProfilesWrapper.cs | 28 +- .../Profile/Interface/IUserProfiles.cs | 4 +- .../Implementation/OrganizationsWrapper.cs | 26 +- .../Register/Implementation/PartiesWrapper.cs | 58 +--- .../Register/Implementation/PersonsWrapper.cs | 27 +- .../Register/Interface/IOrganizations.cs | 4 +- src/Services/Register/Interface/IParties.cs | 6 +- src/Services/Register/Interface/IPersons.cs | 4 +- src/Services/TestData/AppTestDataModel.cs | 322 ++++++++++++++++++ src/Services/TestData/TestDataDiskReader.cs | 61 ++++ src/Services/TestData/TestDataModel.cs | 38 +++ src/Services/TestData/TestDataService.cs | 48 +++ src/Startup.cs | 4 +- 23 files changed, 666 insertions(+), 259 deletions(-) create mode 100644 src/Services/TestData/AppTestDataModel.cs create mode 100644 src/Services/TestData/TestDataDiskReader.cs create mode 100644 src/Services/TestData/TestDataModel.cs create mode 100644 src/Services/TestData/TestDataService.cs diff --git a/src/Controllers/HomeController.cs b/src/Controllers/HomeController.cs index aae1c814..2154745e 100644 --- a/src/Controllers/HomeController.cs +++ b/src/Controllers/HomeController.cs @@ -1,11 +1,5 @@ -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net.Http; using System.Text.Json; -using System.Threading.Tasks; using System.Security.Claims; using System.Xml; @@ -27,8 +21,8 @@ using LocalTest.Services.Profile.Interface; using LocalTest.Services.LocalApp.Interface; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Authentication.Cookies; +using LocalTest.Services.TestData; namespace LocalTest.Controllers { @@ -41,6 +35,7 @@ public class HomeController : Controller private readonly IApplicationRepository _applicationRepository; private readonly IClaims _claimsService; private readonly ILocalApp _localApp; + private readonly TestDataService _testDataService; public HomeController( IOptions generalSettings, @@ -49,7 +44,8 @@ public HomeController( IAuthentication authenticationService, IApplicationRepository applicationRepository, IClaims claimsService, - ILocalApp localApp) + ILocalApp localApp, + TestDataService testDataService) { _generalSettings = generalSettings.Value; _localPlatformSettings = localPlatformSettings.Value; @@ -58,6 +54,33 @@ public HomeController( _applicationRepository = applicationRepository; _claimsService = claimsService; _localApp = localApp; + _testDataService = testDataService; + } + + [AllowAnonymous] + public async Task LocalTestUsersRaw() + { + var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); + + return Json(localData); + } + + [AllowAnonymous] + public async Task LocalTestUsers() + { + var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); + var constructedAppData = AppTestDataModel.FromTestDataModel(localData); + + return Json(constructedAppData); + } + + [AllowAnonymous] + public async Task LocalTestUsersRoundTrip() + { + var localData = await TestDataDiskReader.ReadFromDisk(_localPlatformSettings.LocalTestingStaticTestDataPath); + var constructedAppData = AppTestDataModel.FromTestDataModel(localData); + + return Json(constructedAppData.GetTestDataModel()); } [AllowAnonymous] @@ -67,19 +90,19 @@ public async Task Index() try { model.TestApps = await GetAppsList(); + model.TestUsers = await GetTestUsersForList(); + var defaultAuthLevel = _localPlatformSettings.LocalAppMode == "http" ? await GetAppAuthLevel(model.TestApps) : 2; + model.AuthenticationLevels = GetAuthenticationLevels(defaultAuthLevel); } catch (HttpRequestException e) { model.HttpException = e; } - model.TestUsers = await GetTestUsersForList(); model.AppPath = _localPlatformSettings.AppRepositoryBasePath; model.StaticTestDataPath = _localPlatformSettings.LocalTestingStaticTestDataPath; model.LocalAppUrl = _localPlatformSettings.LocalAppUrl; - var defaultAuthLevel = _localPlatformSettings.LocalAppMode == "http" ? await GetAppAuthLevel(model.TestApps) : 2; model.AppModeIsHttp = _localPlatformSettings.LocalAppMode == "http"; - model.AuthenticationLevels = GetAuthenticationLevels(defaultAuthLevel); model.LocalFrontendUrl = HttpContext.Request.Cookies[FRONTEND_URL_COOKIE_NAME]; if (!model.TestApps?.Any() ?? true) @@ -270,42 +293,18 @@ public ActionResult FrontendVersion(FrontendVersion frontendVersion) return RedirectToAction("Index"); } - - private async Task> GetTestUsers() - { - List users = new List(); - string path = this._localPlatformSettings.LocalTestingStaticTestDataPath + "Profile/User/"; - - if (!Directory.Exists(path)) - { - return users; - } - - string[] files = Directory.GetFiles(path, "*.json"); - - foreach (string file in files) - { - if (int.TryParse(Path.GetFileNameWithoutExtension(file), out int userId)) - { - users.Add(await _userProfileService.GetUser(userId)); - } - } - - return users; - } - private async Task> GetTestUsersForList() { - List users = await GetTestUsers(); - + var data = await _testDataService.GetTestData(); List userItems = new List(); - foreach (UserProfile profile in users) + foreach (UserProfile profile in data.Profile.User.Values) { + var properProfile = await _userProfileService.GetUser(profile.UserId); SelectListItem item = new SelectListItem() { - Value = profile.UserId.ToString(), - Text = profile.Party.Person.Name + Value = properProfile.UserId.ToString(), + Text = properProfile.Party.Person.Name }; userItems.Add(item); @@ -313,6 +312,7 @@ private async Task> GetTestUsersForList() return userItems; } + private async Task GetAppAuthLevel(IEnumerable testApps) { try diff --git a/src/LocalTest.csproj b/src/LocalTest.csproj index 19e1e33f..cf53a07a 100644 --- a/src/LocalTest.csproj +++ b/src/LocalTest.csproj @@ -3,6 +3,7 @@ net6.0 56f36ce2-b44b-415e-a8a5-f399a76e35b9 + enable diff --git a/src/Services/Authorization/Implementation/ClaimsService.cs b/src/Services/Authorization/Implementation/ClaimsService.cs index 5436b7a9..279e05b2 100644 --- a/src/Services/Authorization/Implementation/ClaimsService.cs +++ b/src/Services/Authorization/Implementation/ClaimsService.cs @@ -1,44 +1,28 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; +#nullable enable using System.Security.Claims; -using System.Threading.Tasks; -using Altinn.Platform.Authentication.Model; using Altinn.Platform.Authorization.Services.Interface; -using LocalTest.Configuration; -using Microsoft.Extensions.Options; -using Newtonsoft.Json; +using LocalTest.Services.TestData; namespace LocalTest.Services.Authorization.Implementation { public class ClaimsService : IClaims { - private readonly LocalPlatformSettings _localPlatformSettings; + private readonly TestDataService _testDataService; - public ClaimsService(IOptions localPlatformSettings) + public ClaimsService(TestDataService testDataService) { - _localPlatformSettings = localPlatformSettings.Value; + _testDataService = testDataService; } - public Task> GetCustomClaims(int userId, string issuer) + public async Task> GetCustomClaims(int userId, string issuer) { - var path = GetCustomClaimsPath(userId); - - if (File.Exists(path)) + var data = await _testDataService.GetTestData(); + if(data.Authorization.Claims.TryGetValue(userId.ToString(), out var customClaims)) { - var content = File.ReadAllText(path); - var claims = JsonConvert.DeserializeObject>(content) ?? new List(); - return Task.FromResult(claims.Select(c => new Claim(c.Type, c.Value, c.ValueType, issuer)).ToList()); + return customClaims.Select(c => new Claim(c.Type, c.Value, c.ValueType, issuer)).ToList(); } - return Task.FromResult(new List()); - } - - private string GetCustomClaimsPath(int userId) - { - return _localPlatformSettings.LocalTestingStaticTestDataPath + - _localPlatformSettings.AuthorizationDataFolder + _localPlatformSettings.ClaimsFolder + userId + - ".json"; + return new List(); } } } diff --git a/src/Services/Authorization/Implementation/PartiesService.cs b/src/Services/Authorization/Implementation/PartiesService.cs index b2c89d97..60e02b25 100644 --- a/src/Services/Authorization/Implementation/PartiesService.cs +++ b/src/Services/Authorization/Implementation/PartiesService.cs @@ -1,37 +1,23 @@ +#nullable enable using Altinn.Platform.Authorization.Services.Interface; using Altinn.Platform.Register.Models; -using LocalTest.Configuration; -using Microsoft.Extensions.Options; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; +using LocalTest.Services.TestData; namespace LocalTest.Services.Authorization.Implementation { public class PartiesService : IParties { - private readonly LocalPlatformSettings _localPlatformSettings; + private readonly TestDataService _testDataService; - public PartiesService(IOptions localPlatformSettings) + public PartiesService(TestDataService testDataService) { - _localPlatformSettings = localPlatformSettings.Value; + _testDataService = testDataService; } - public Task> GetParties(int userId) + public async Task?> GetParties(int userId) { - string path = GetPartyListPath(userId); - - if (File.Exists(path)) - { - string content = System.IO.File.ReadAllText(path); - List instance = (List)JsonConvert.DeserializeObject(content, typeof(List)); - return Task.FromResult(instance); - } - - return null; + var data = await _testDataService.GetTestData(); + return data.Authorization.PartyList.TryGetValue(userId.ToString(), out var result) ? result : null; } public Task ValidateSelectedParty(int userId, int partyId) @@ -39,10 +25,5 @@ public Task ValidateSelectedParty(int userId, int partyId) return Task.FromResult(true); } - - private string GetPartyListPath(int userId) - { - return _localPlatformSettings.LocalTestingStaticTestDataPath + _localPlatformSettings.AuthorizationDataFolder + _localPlatformSettings.PartyListFolder + userId + ".json"; - } } } diff --git a/src/Services/Authorization/Implementation/RolesWrapper.cs b/src/Services/Authorization/Implementation/RolesWrapper.cs index 17dba2a4..1cdcaab5 100644 --- a/src/Services/Authorization/Implementation/RolesWrapper.cs +++ b/src/Services/Authorization/Implementation/RolesWrapper.cs @@ -1,11 +1,7 @@ -using System.Collections.Generic; -using System.IO; -using System.Net.Http; -using System.Threading.Tasks; +#nullable enable using Altinn.Platform.Authorization.Services.Interface; using Authorization.Interface.Models; -using LocalTest.Configuration; -using Microsoft.Extensions.Logging; +using LocalTest.Services.TestData; using Microsoft.Extensions.Options; using Newtonsoft.Json; @@ -16,44 +12,30 @@ namespace Altinn.Platform.Authorization.Services.Implementation /// public class RolesWrapper : IRoles { - private readonly LocalPlatformSettings _localPlatformSettings; + private readonly TestDataService _testDataService; /// /// Initializes a new instance of the class /// - /// the client handler for roles api - public RolesWrapper(IOptions localPlatformSettings) + /// Service to fetch test data + public RolesWrapper(TestDataService testDataService) { - this._localPlatformSettings = localPlatformSettings.Value; + this._testDataService = testDataService; } /// public async Task> GetDecisionPointRolesForUser(int coveredByUserId, int offeredByPartyId) { - string rolesPath = GetRolesPath(coveredByUserId, offeredByPartyId); - - List roles = new List(); - - if (File.Exists(rolesPath)) + var data = await _testDataService.GetTestData(); + if(data.Authorization.Roles.TryGetValue(coveredByUserId.ToString(), out var user)) { - string content = System.IO.File.ReadAllText(rolesPath); - roles = (List)JsonConvert.DeserializeObject(content, typeof(List)); + if(user.TryGetValue(offeredByPartyId.ToString(), out var roles)) + { + return roles; + } } - return await Task.FromResult(roles); - } - - private string GetRolesPath(int userId, int resourcePartyId) - { - string[] pathArray = new string[] { - this._localPlatformSettings.LocalTestingStaticTestDataPath, - this._localPlatformSettings.AuthorizationDataFolder, - this._localPlatformSettings.RolesFolder, - $"User_{userId}/", - $"party_{resourcePartyId}/", - "roles.json" - }; - return Path.Combine(pathArray); + return new List(); } } } diff --git a/src/Services/Authorization/Interface/IClaims.cs b/src/Services/Authorization/Interface/IClaims.cs index 13dcdb4e..5b98ba35 100644 --- a/src/Services/Authorization/Interface/IClaims.cs +++ b/src/Services/Authorization/Interface/IClaims.cs @@ -1,6 +1,5 @@ -using System.Collections.Generic; +#nullable enable using System.Security.Claims; -using System.Threading.Tasks; namespace Altinn.Platform.Authorization.Services.Interface { diff --git a/src/Services/Authorization/Interface/IParties.cs b/src/Services/Authorization/Interface/IParties.cs index e23b2888..6ce64d16 100644 --- a/src/Services/Authorization/Interface/IParties.cs +++ b/src/Services/Authorization/Interface/IParties.cs @@ -1,6 +1,5 @@ +#nullable enable using Altinn.Platform.Register.Models; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Altinn.Platform.Authorization.Services.Interface { @@ -14,7 +13,7 @@ public interface IParties /// /// The user id /// list of parties that the logged in user can represent - Task> GetParties(int userId); + Task?> GetParties(int userId); /// /// Verifies that the selected party is contained in the user's party list diff --git a/src/Services/LocalApp/Implementation/LocalAppFile.cs b/src/Services/LocalApp/Implementation/LocalAppFile.cs index 96dd9296..6b715ae7 100644 --- a/src/Services/LocalApp/Implementation/LocalAppFile.cs +++ b/src/Services/LocalApp/Implementation/LocalAppFile.cs @@ -1,10 +1,7 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.Net; +using System.Net.Http; using System.Text; -using System.Threading.Tasks; using Microsoft.Extensions.Options; using Newtonsoft.Json; @@ -13,8 +10,7 @@ using LocalTest.Configuration; using LocalTest.Helpers; using LocalTest.Services.LocalApp.Interface; -using System.Net.Http; -using System.Net; +using LocalTest.Services.TestData; namespace LocalTest.Services.LocalApp.Implementation { @@ -31,8 +27,13 @@ public LocalAppFile(IOptions localPlatformSettings) return await File.ReadAllTextAsync(Path.Join(GetAppPath(appId), $"App/config/authorization/policy.xml")); } - public async Task GetApplicationMetadata(string appId) + public async Task GetApplicationMetadata(string? appId) { + if(appId is null) + { + throw new ArgumentNullException(nameof(appId), "AppMode = file does not support null as appId"); + } + var filename = Path.Join(GetAppPath(appId), $"App/config/applicationmetadata.json"); if (File.Exists(filename)) { @@ -142,5 +143,10 @@ private async Task GetAccessManagment() } } + public Task GetTestData() + { + // Not implemented, but empty result is vald, so better than NotImplementedException + return Task.FromResult(null); + } } } diff --git a/src/Services/LocalApp/Implementation/LocalAppHttp.cs b/src/Services/LocalApp/Implementation/LocalAppHttp.cs index 79013f03..b1a2b3ab 100644 --- a/src/Services/LocalApp/Implementation/LocalAppHttp.cs +++ b/src/Services/LocalApp/Implementation/LocalAppHttp.cs @@ -1,33 +1,36 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Security.Claims; + using System.Text.Json; using System.Text.Json.Serialization; -using System.Threading.Tasks; using Microsoft.Extensions.Options; using Microsoft.Extensions.Caching.Memory; using Altinn.Platform.Storage.Interface.Models; using LocalTest.Services.Authentication.Interface; -using AltinnCore.Authentication.Constants; using LocalTest.Configuration; using LocalTest.Services.LocalApp.Interface; +using LocalTest.Services.TestData; namespace LocalTest.Services.LocalApp.Implementation { public class LocalAppHttp : ILocalApp { + public static readonly JsonSerializerOptions JSON_OPTIONS = new JsonSerializerOptions(JsonSerializerDefaults.Web) + { + Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() } + }; public const string XACML_CACHE_KEY = "/api/v1/meta/authorizationpolicy/"; public const string APPLICATION_METADATA_CACHE_KEY = "/api/v1/applicationmetadata?checkOrgApp=false"; public const string TEXT_RESOURCE_CACE_KEY = "/api/v1/texts"; + public const string TEST_DATA_CACHE_KEY = "TEST_DATA_CACHE_KEY"; private readonly GeneralSettings _generalSettings; private readonly IAuthentication _authenticationService; private readonly HttpClient _httpClient; private readonly IMemoryCache _cache; - public LocalAppHttp(IOptions generalSettings, IAuthentication authenticationService, IHttpClientFactory httpClientFactory, IOptions localPlatformSettings, IMemoryCache cache) + private readonly ILogger _logger; + + public LocalAppHttp(IOptions generalSettings, IAuthentication authenticationService, IHttpClientFactory httpClientFactory, IOptions localPlatformSettings, IMemoryCache cache, ILogger logger) { _generalSettings = generalSettings.Value; _authenticationService = authenticationService; @@ -35,6 +38,7 @@ public LocalAppHttp(IOptions generalSettings, IAuthentication a _httpClient.BaseAddress = new Uri(localPlatformSettings.Value.LocalAppUrl); _httpClient.Timeout = TimeSpan.FromHours(1); _cache = cache; + _logger = logger; } public async Task GetXACMLPolicy(string appId) { @@ -45,15 +49,17 @@ public LocalAppHttp(IOptions generalSettings, IAuthentication a return await _httpClient.GetStringAsync($"{appId}/api/v1/meta/authorizationpolicy"); }); } - public async Task GetApplicationMetadata(string appId) + public async Task GetApplicationMetadata(string? appId) { + appId = appId ?? "dummyOrg/dummyApp"; var content = await _cache.GetOrCreateAsync(APPLICATION_METADATA_CACHE_KEY + appId, async cacheEntry => { // Cache with very short duration to not slow down page load, where this file can be accessed many many times cacheEntry.SetSlidingExpiration(TimeSpan.FromSeconds(5)); return await _httpClient.GetStringAsync($"{appId}/api/v1/applicationmetadata?checkOrgApp=false"); }); - return JsonSerializer.Deserialize(content, new JsonSerializerOptions{PropertyNameCaseInsensitive = true} ); + + return JsonSerializer.Deserialize(content, JSON_OPTIONS); } public async Task GetTextResource(string org, string app, string language) @@ -64,7 +70,7 @@ public LocalAppHttp(IOptions generalSettings, IAuthentication a return await _httpClient.GetStringAsync($"{org}/{app}/api/v1/texts/{language}"); }); - var textResource = JsonSerializer.Deserialize(content, new JsonSerializerOptions{PropertyNameCaseInsensitive = true}); + var textResource = JsonSerializer.Deserialize(content, JSON_OPTIONS); if (textResource != null) { textResource.Id = $"{org}-{app}-{language}"; @@ -80,7 +86,7 @@ public async Task> GetApplications() { var ret = new Dictionary(); // Return a single element as only one app can run on port 5005 - var app = await GetApplicationMetadata("dummyOrg/dummyApp"); + var app = await GetApplicationMetadata(null); if (app != null) { ret.Add(app.Id, app); @@ -113,5 +119,38 @@ public async Task> GetApplications() return JsonSerializer.Deserialize(stringResponse, new JsonSerializerOptions{PropertyNameCaseInsensitive = true}); } + + public async Task GetTestData() + { + return await _cache.GetOrCreateAsync(TEST_DATA_CACHE_KEY, async (cacheEntry) => + { + cacheEntry.SetSlidingExpiration(TimeSpan.FromSeconds(5)); + + try + { + var applicationmetadata = await GetApplicationMetadata(null); + if (applicationmetadata is null) + { + return null; + } + + var response = await _httpClient.GetAsync($"{applicationmetadata.Id}/testData.json"); + if (response.StatusCode == System.Net.HttpStatusCode.NotFound) + { + _logger.LogInformation("No custom www/testData.json found. Using default test users"); + return null; + } + + response.EnsureSuccessStatusCode(); + return JsonSerializer.Deserialize(await response.Content.ReadAsByteArrayAsync(), JSON_OPTIONS); + } + catch (HttpRequestException e) + { + _logger.LogCritical(e, "Failed to get Test data"); + return null; + } + + }); + } } } diff --git a/src/Services/LocalApp/Interface/ILocalApp.cs b/src/Services/LocalApp/Interface/ILocalApp.cs index a6615830..ad747426 100644 --- a/src/Services/LocalApp/Interface/ILocalApp.cs +++ b/src/Services/LocalApp/Interface/ILocalApp.cs @@ -1,8 +1,6 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.Threading.Tasks; using Altinn.Platform.Storage.Interface.Models; +using LocalTest.Services.TestData; namespace LocalTest.Services.LocalApp.Interface { @@ -10,12 +8,14 @@ public interface ILocalApp { Task GetXACMLPolicy(string appId); - Task GetApplicationMetadata(string appId); + Task GetApplicationMetadata(string? appId); Task> GetApplications(); Task GetTextResource(string org, string app, string language); Task Instantiate(string appId, Instance instance, string xmlPrefill, string xmlDataId); + + Task GetTestData(); } } diff --git a/src/Services/Profile/Implementation/UserProfilesWrapper.cs b/src/Services/Profile/Implementation/UserProfilesWrapper.cs index 516701e3..b3f7728d 100644 --- a/src/Services/Profile/Implementation/UserProfilesWrapper.cs +++ b/src/Services/Profile/Implementation/UserProfilesWrapper.cs @@ -1,11 +1,7 @@ -using System.IO; -using System.Threading.Tasks; -using Altinn.Platform.Authorization.Services.Interface; +#nullable enable using Altinn.Platform.Profile.Models; -using LocalTest.Configuration; using LocalTest.Services.Profile.Interface; -using Microsoft.Extensions.Options; -using Newtonsoft.Json; +using LocalTest.Services.TestData; namespace LocalTest.Services.Profile.Implementation { @@ -14,29 +10,27 @@ namespace LocalTest.Services.Profile.Implementation /// public class UserProfilesWrapper : IUserProfiles { - private readonly LocalPlatformSettings _localPlatformSettings; + private readonly TestDataService _testDataService; private readonly LocalTest.Services.Register.Interface.IParties _partiesService; - public UserProfilesWrapper(IOptions localPlatformSettings, LocalTest.Services.Register.Interface.IParties partiesService) + public UserProfilesWrapper(TestDataService testDataService, LocalTest.Services.Register.Interface.IParties partiesService) { - _localPlatformSettings = localPlatformSettings.Value; + _testDataService = testDataService; _partiesService = partiesService; } /// - public async Task GetUser(int userId) + public async Task GetUser(int userId) { - UserProfile user = null; - string path = this._localPlatformSettings.LocalTestingStaticTestDataPath + "Profile/User/" + userId + ".json"; - if (File.Exists(path)) + var data = await _testDataService.GetTestData(); + if (data.Profile.User.TryGetValue(userId.ToString(), out UserProfile? user)) { - string content = File.ReadAllText(path); - user = (UserProfile)JsonConvert.DeserializeObject(content, typeof(UserProfile)); + user.Party = await _partiesService.GetParty(user.PartyId); + return user; } - user.Party = await _partiesService.GetParty(user.PartyId); - return user; + return null; } } } diff --git a/src/Services/Profile/Interface/IUserProfiles.cs b/src/Services/Profile/Interface/IUserProfiles.cs index ed33b0be..7e6c8d23 100644 --- a/src/Services/Profile/Interface/IUserProfiles.cs +++ b/src/Services/Profile/Interface/IUserProfiles.cs @@ -1,5 +1,5 @@ +#nullable enable using Altinn.Platform.Profile.Models; -using System.Threading.Tasks; namespace LocalTest.Services.Profile.Interface { @@ -13,6 +13,6 @@ public interface IUserProfiles /// /// The user id /// - Task GetUser(int userId); + Task GetUser(int userId); } } diff --git a/src/Services/Register/Implementation/OrganizationsWrapper.cs b/src/Services/Register/Implementation/OrganizationsWrapper.cs index 5a739c80..8ea4ff38 100644 --- a/src/Services/Register/Implementation/OrganizationsWrapper.cs +++ b/src/Services/Register/Implementation/OrganizationsWrapper.cs @@ -1,10 +1,7 @@ -using System.IO; -using System.Threading.Tasks; +#nullable enable using Altinn.Platform.Register.Models; -using LocalTest.Configuration; using LocalTest.Services.Register.Interface; -using Microsoft.Extensions.Options; -using Newtonsoft.Json; +using LocalTest.Services.TestData; namespace LocalTest.Services.Register.Implementation { @@ -13,25 +10,18 @@ namespace LocalTest.Services.Register.Implementation /// public class OrganizationsWrapper : IOrganizations { - private readonly LocalPlatformSettings _localPlatformSettings; + private readonly TestDataService _testDataService; - public OrganizationsWrapper(IOptions localPlatformSettings) + public OrganizationsWrapper(TestDataService testDataService) { - _localPlatformSettings = localPlatformSettings.Value; + _testDataService = testDataService; } /// - public async Task GetOrganization(string orgNr) + public async Task GetOrganization(string orgNr) { - Organization org = null; - string path = this._localPlatformSettings.LocalTestingStaticTestDataPath + "Register/Org/" + orgNr + ".json"; - if (File.Exists(path)) // lgtm [cs/path-injection] - { - string content = File.ReadAllText(path); // lgtm [cs/path-injection] - org = (Organization)JsonConvert.DeserializeObject(content, typeof(Organization)); - } - - return await Task.FromResult(org); + var data = await _testDataService.GetTestData(); + return data.Register.Org.TryGetValue(orgNr, out var value) ? value : null; } } } diff --git a/src/Services/Register/Implementation/PartiesWrapper.cs b/src/Services/Register/Implementation/PartiesWrapper.cs index 61c33991..6043cec5 100644 --- a/src/Services/Register/Implementation/PartiesWrapper.cs +++ b/src/Services/Register/Implementation/PartiesWrapper.cs @@ -1,44 +1,33 @@ -using System.IO; -using System.Threading.Tasks; - +#nullable enable using Altinn.Platform.Register.Enums; using Altinn.Platform.Register.Models; -using LocalTest.Configuration; using LocalTest.Services.Register.Interface; - -using Microsoft.Extensions.Options; - -using Newtonsoft.Json; +using LocalTest.Services.TestData; namespace LocalTest.Services.Register.Implementation { public class PartiesWrapper : IParties { - private readonly LocalPlatformSettings _localPlatformSettings; + private readonly TestDataService _testDataService; private readonly IPersons _personService; private readonly IOrganizations _organizationService; public PartiesWrapper( - IOptions localPlatformSettings, + TestDataService testDataService, IPersons personsService, IOrganizations organizationService) { - _localPlatformSettings = localPlatformSettings.Value; + _testDataService = testDataService; _organizationService = organizationService; _personService = personsService; } /// - public async Task GetParty(int partyId) + public async Task GetParty(int partyId) { - Party party = null; - string path = _localPlatformSettings.LocalTestingStaticTestDataPath + "Register/Party/" + partyId + ".json"; - if (File.Exists(path)) - { - string content = File.ReadAllText(path); - party = (Party)JsonConvert.DeserializeObject(content, typeof(Party)); - } + var data = await _testDataService.GetTestData(); + Party? party = data.Register.Party.TryGetValue(partyId.ToString()!, out var value) ? value : null; await AddPersonOrOrganization(party); @@ -46,9 +35,9 @@ public async Task GetParty(int partyId) } /// - public async Task LookupPartyBySSNOrOrgNo(string lookupValue) + public async Task LookupPartyBySSNOrOrgNo(string lookupValue) { - Party party = FindParty(lookupValue); + Party? party = await FindParty(lookupValue); await AddPersonOrOrganization(party); @@ -58,35 +47,18 @@ public async Task LookupPartyBySSNOrOrgNo(string lookupValue) /// public async Task LookupPartyIdBySSNOrOrgNo(string lookupValue) { - await Task.CompletedTask; - - Party party = FindParty(lookupValue); + Party? party = await FindParty(lookupValue); return party?.PartyId ?? -1; } - private Party FindParty(string lookupValue) + private async Task FindParty(string lookupValue) { - string path = _localPlatformSettings.LocalTestingStaticTestDataPath + "Register/Party"; - string[] allPathsToParties = Directory.GetFiles(path); - - foreach (string partyPath in allPathsToParties) - { - string content = File.ReadAllText(partyPath); - - string targetOrgNbr = $"\"orgNumber\": \"{lookupValue}\""; - string targetSsn = $"\"ssn\": \"{lookupValue}\""; - - if (content.Contains(targetOrgNbr) || content.Contains(targetSsn)) - { - return (Party)JsonConvert.DeserializeObject(content, typeof(Party)); - } - } - - return null; + var data = await _testDataService.GetTestData(); + return data.Register.Party.Values.FirstOrDefault((party)=> party.OrgNumber == lookupValue || party.SSN == lookupValue); } - private async Task AddPersonOrOrganization(Party party) + private async Task AddPersonOrOrganization(Party? party) { if (party is null) { diff --git a/src/Services/Register/Implementation/PersonsWrapper.cs b/src/Services/Register/Implementation/PersonsWrapper.cs index 0abfb05f..cc8998bb 100644 --- a/src/Services/Register/Implementation/PersonsWrapper.cs +++ b/src/Services/Register/Implementation/PersonsWrapper.cs @@ -1,11 +1,7 @@ -using System; -using System.IO; -using System.Threading.Tasks; +#nullable enable using Altinn.Platform.Register.Models; -using LocalTest.Configuration; using LocalTest.Services.Register.Interface; -using Microsoft.Extensions.Options; -using Newtonsoft.Json; +using LocalTest.Services.TestData; namespace LocalTest.Services.Register.Implementation { @@ -14,25 +10,18 @@ namespace LocalTest.Services.Register.Implementation /// public class PersonsWrapper : IPersons { - private readonly LocalPlatformSettings _localPlatformSettings; + private readonly TestDataService _testDataService; - public PersonsWrapper(IOptions localPlatformSettings) + public PersonsWrapper(TestDataService testDataService) { - _localPlatformSettings = localPlatformSettings.Value; + _testDataService = testDataService; } /// - public async Task GetPerson(string ssn) + public async Task GetPerson(string ssn) { - Person person = null; - string path = this._localPlatformSettings.LocalTestingStaticTestDataPath + "Register/Person/" + ssn + ".json"; - if (File.Exists(path)) - { - string content = File.ReadAllText(path); - person = (Person)JsonConvert.DeserializeObject(content, typeof(Person)); - } - - return await Task.FromResult(person); + var data = await _testDataService.GetTestData(); + return data.Register.Person.TryGetValue(ssn, out var value) ? value : null; } } } diff --git a/src/Services/Register/Interface/IOrganizations.cs b/src/Services/Register/Interface/IOrganizations.cs index c7928631..98237033 100644 --- a/src/Services/Register/Interface/IOrganizations.cs +++ b/src/Services/Register/Interface/IOrganizations.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +#nullable enable using Altinn.Platform.Register.Models; namespace LocalTest.Services.Register.Interface @@ -13,6 +13,6 @@ public interface IOrganizations /// /// The organization number /// - Task GetOrganization(string orgNr); + Task GetOrganization(string orgNr); } } diff --git a/src/Services/Register/Interface/IParties.cs b/src/Services/Register/Interface/IParties.cs index 49ae98cf..c09b1dd3 100644 --- a/src/Services/Register/Interface/IParties.cs +++ b/src/Services/Register/Interface/IParties.cs @@ -1,5 +1,5 @@ +#nullable enable using Altinn.Platform.Register.Models; -using System.Threading.Tasks; namespace LocalTest.Services.Register.Interface { @@ -13,7 +13,7 @@ public interface IParties /// /// The party id /// - Task GetParty(int partyId); + Task GetParty(int partyId); /// /// Method that looks up a party id based on social security number or organisation number. @@ -27,6 +27,6 @@ public interface IParties /// /// SSN or org number /// - Task LookupPartyBySSNOrOrgNo(string lookupValue); + Task LookupPartyBySSNOrOrgNo(string lookupValue); } } diff --git a/src/Services/Register/Interface/IPersons.cs b/src/Services/Register/Interface/IPersons.cs index 8cb970ef..074ecf48 100644 --- a/src/Services/Register/Interface/IPersons.cs +++ b/src/Services/Register/Interface/IPersons.cs @@ -1,5 +1,5 @@ +#nullable enable using Altinn.Platform.Register.Models; -using System.Threading.Tasks; namespace LocalTest.Services.Register.Interface { @@ -13,6 +13,6 @@ public interface IPersons /// /// The persons ssn /// - Task GetPerson(string ssn); + Task GetPerson(string ssn); } } diff --git a/src/Services/TestData/AppTestDataModel.cs b/src/Services/TestData/AppTestDataModel.cs new file mode 100644 index 00000000..837e6da9 --- /dev/null +++ b/src/Services/TestData/AppTestDataModel.cs @@ -0,0 +1,322 @@ +#nullable enable +using Altinn.Platform.Authentication.Model; +using Altinn.Platform.Profile.Models; +using Altinn.Platform.Register.Models; +using Authorization.Interface.Models; + +namespace LocalTest.Services.TestData; + +/// +/// Simple model that directly matches the directory structure of the TestData folder +/// Currently this is used internally for backwards compatibility reasons +/// +public class AppTestDataModel +{ + public List Persons { get; set; } = default!; + public List Orgs { get; set; } = default!; + + public TestDataModel GetTestDataModel() + { + return new TestDataModel() + { + Authorization = GetTestDataAuthorization(), + Register = GetTestDataRegister(), + Profile = GetTestDataProfile(), + }; + } + public TestDataRegister GetTestDataRegister() + { + var org = Orgs.Select(o => (Organization)o).ToDictionary(o => o.OrgNumber); + var party = GetParties(); + var person = Persons.Select(p => (Person)p).ToDictionary(p => p.SSN); + return new TestDataRegister() + { + Org = org, + Party = party, + Person = person, + }; + } + + public TestDataProfile GetTestDataProfile() + { + var user = Persons.Select(p => p.ToUserProfile()).ToDictionary(p => p.UserId.ToString()); + + return new TestDataProfile() + { + User = user, + }; + } + + public TestDataAuthorization GetTestDataAuthorization() + { + var claims = Persons.Where(p => p.CustomClaims.Count > 0).ToDictionary(p => p.UserId.ToString(), p => p.CustomClaims); + var parties = GetOrgPartiesWithChildren(); + var partyList = Persons + .ToDictionary( + p => p.UserId.ToString(), + p => { + var partiesForUser = new List{p.ToParty()}; + partiesForUser.AddRange(p.PartyRoles.Keys.Select(partyId => + parties.TryGetValue(partyId.ToString(), out var value) ? value! : null! + )); + return partiesForUser.Where(p=>p is not null).ToList(); + }); + var roles = Persons.ToDictionary(p => p.UserId.ToString(), p => p.PartyRoles.ToDictionary(r => r.Key.ToString(), r => r.Value)); + + return new TestDataAuthorization + { + Claims = claims, + PartyList = partyList, + Roles = roles, + }; + } + + private Dictionary GetParties() + { + return Enumerable.Concat( + Orgs.Select(o => o.ToParty()), + Persons.Select(p => p.ToParty())) + .ToDictionary(p => p.PartyId.ToString()); + } + private Dictionary GetOrgPartiesWithChildren() + { + return Orgs.Select(p => p.ToParty(Orgs)) + .ToDictionary(p => p.PartyId.ToString()); + } + + + public static AppTestDataModel FromTestDataModel(TestDataModel localData) + { + int negativePartyId = -1; + var constructedAppData = new AppTestDataModel() + { + Orgs = localData.Register.Org.Values.Select(org => + { + var party = localData.Register.Party.Values.FirstOrDefault(p => p.OrgNumber == org.OrgNumber); + var parentParty = localData.Authorization.PartyList.Values.SelectMany(l => l).FirstOrDefault(p => p.ChildParties?.Any(cp => cp.PartyId == party?.PartyId) == true); + return new AppTestOrg() + { + PartyId = party?.PartyId ?? negativePartyId--, + ParentPartyId = parentParty?.PartyId, + TelephoneNumber = org.TelephoneNumber, + UnitStatus = org.UnitStatus, + UnitType = party?.UnitType ?? org.UnitType, // These are inconsistent in the original TestData folder + BusinessAddress = org.BusinessAddress, + BusinessPostalCity = org.BusinessPostalCity, + BusinessPostalCode = org.BusinessPostalCode, + EMailAddress = org.EMailAddress, + FaxNumber = org.FaxNumber, + InternetAddress = org.InternetAddress, + MailingAddress = org.MailingAddress, + MailingPostalCity = org.MailingPostalCity, + MailingPostalCode = org.MailingPostalCode, + MobileNumber = org.MobileNumber, + Name = org.Name, + OrgNumber = org.OrgNumber, + }; + }).ToList(), + Persons = localData.Register.Person.Values.Select(p => + { + var party = localData.Register.Party.Values.First(party => party.SSN == p.SSN); + var profile = localData.Profile.User.Values.First(user => user.PartyId == party.PartyId); + var customClaims = localData.Authorization.Claims.TryGetValue(profile.UserId.ToString(), out var v) ? v : new(); + + var userRoles = localData.Authorization.Roles.TryGetValue(profile.UserId.ToString(), out var roles) ? roles : null; + + return new AppTestPerson() + { + + PartyId = party.PartyId, + AddressCity = p.AddressCity, + AddressHouseLetter = p.AddressHouseLetter, + AddressHouseNumber = p.AddressHouseNumber, + AddressMunicipalName = p.AddressMunicipalName, + AddressMunicipalNumber = p.AddressMunicipalNumber, + AddressPostalCode = p.AddressPostalCode, + AddressStreetName = p.AddressStreetName, + Email = profile.Email, + FirstName = p.FirstName, + MiddleName = p.MiddleName, + LastName = p.LastName, + MailingAddress = p.MailingAddress, + MailingPostalCity = p.MailingPostalCity, + MailingPostalCode = p.MailingPostalCode, + SSN = p.SSN, + TelephoneNumber = p.TelephoneNumber, + MobileNumber = p.MobileNumber, + PartyRoles = userRoles?.ToDictionary(r => int.Parse(r.Key), r => r.Value) ?? new(), + CustomClaims = customClaims, + UserId = profile.UserId, + Language = profile.ProfileSettingPreference?.Language, + UserName = profile.UserName, + }; + }).ToList(), + }; + return constructedAppData; + } +} + +public class AppTestOrg +{ + public int PartyId { get; set; } + public string OrgNumber { get; set; } = default!; + + public int? ParentPartyId { get; set; } + public string? Name { get; set; } + public string? BusinessAddress { get; set; } + public string? BusinessPostalCity { get; set; } + public string? BusinessPostalCode { get; set; } + public string? EMailAddress { get; set; } + public string? FaxNumber { get; set; } + public string? InternetAddress { get; set; } + public string? MailingAddress { get; set; } + public string? MailingPostalCity { get; set; } + public string? MailingPostalCode { get; set; } + public string? MobileNumber { get; set; } + public string? TelephoneNumber { get; set; } + public string? UnitStatus { get; set; } + public string? UnitType { get; set; } + + public Party ToParty(List? potentialChildOrgs = null) + { + List? childParties = potentialChildOrgs?.Where(o=>o.ParentPartyId == PartyId).Select(o=>o.ToParty()).ToList(); + if(childParties?.Count == 0) + { + childParties = null; + } + + return new Party() + { + PartyId = PartyId, + OrgNumber = OrgNumber, + IsDeleted = false, + PartyTypeName = Altinn.Platform.Register.Enums.PartyType.Organisation, // TODO: consider supporting bankrupt + Name = Name, + ChildParties = childParties, + //HelperFieldsSetLater + // OnlyHierarchyElementWithNoAccess = + // Organization = + //RelantForPersonOnly + // Person = + // SSN = + //Unknown field + UnitType = UnitType + }; + } + public static explicit operator Organization(AppTestOrg org) => new Organization() + { + OrgNumber = org.OrgNumber, + Name = org.Name, + BusinessAddress = org.BusinessAddress, + BusinessPostalCity = org.BusinessPostalCity, + BusinessPostalCode = org.BusinessPostalCode, + EMailAddress = org.EMailAddress, + FaxNumber = org.FaxNumber, + InternetAddress = org.InternetAddress, + MailingAddress = org.MailingAddress, + MailingPostalCity = org.MailingPostalCity, + MailingPostalCode = org.MailingPostalCode, + MobileNumber = org.MobileNumber, + TelephoneNumber = org.TelephoneNumber, + UnitStatus = org.UnitStatus, + UnitType = org.UnitType, + }; +} + +public class AppTestPerson +{ + public int PartyId { get; set; } = default!; + public string SSN { get; set; } = default!; + public string FirstName { get; set; } = default!; + public string MiddleName { get; set; } = default!; + public string LastName { get; set; } = default!; + public List CustomClaims { get; set; } = new(); + public Dictionary> PartyRoles { get; set; } = new(); + public string? AddressCity { get; set; } + public string? AddressHouseLetter { get; set; } + public string? AddressHouseNumber { get; set; } + public string? AddressMunicipalName { get; set; } + public string? AddressMunicipalNumber { get; set; } + public string? AddressPostalCode { get; set; } + public string? AddressStreetName { get; set; } + public string? MailingAddress { get; set; } + public string? MailingPostalCity { get; set; } + public string? MailingPostalCode { get; set; } + public string? MobileNumber { get; set; } + public string? TelephoneNumber { get; set; } + public string? Email { get; set; } + public int UserId { get; set; } + public string? Language { get; set; } + public string? UserName { get; set; } + + public string GetFullName() => string.IsNullOrWhiteSpace(MiddleName) ? $"{FirstName} {LastName}" : $"{FirstName} {MiddleName} {LastName}"; + + public Party ToParty(List? possibleChildParties = null) + { + List? childParties = null; + if (possibleChildParties is not null) + { + childParties = possibleChildParties.Where(o => o.ParentPartyId == PartyId).Select(o => o.ToParty()).ToList(); + if (childParties?.Count == 0) + { + childParties = null; + } + } + return new Party() + { + PartyId = PartyId, + IsDeleted = false, + SSN = SSN, + Name = GetFullName(), + PartyTypeName = Altinn.Platform.Register.Enums.PartyType.Person, // TODO: consider supporting selfIdentified + ChildParties = childParties, + //HelperFieldsSetLater + // OnlyHierarchyElementWithNoAccess = + // Person = + //RelantForOrgOnly + // Organization = + // OrgNumber = org.OrgNumber, + //Unknown field + // UnitType = + }; + } + public static explicit operator Person(AppTestPerson person) => new Person() + { + SSN = person.SSN, + Name = person.GetFullName(), + AddressCity = person.AddressCity, + AddressHouseLetter = person.AddressHouseLetter, + AddressHouseNumber = person.AddressHouseNumber, + AddressMunicipalName = person.AddressMunicipalName, + AddressMunicipalNumber = person.AddressMunicipalNumber, + AddressPostalCode = person.AddressPostalCode, + AddressStreetName = person.AddressStreetName, + FirstName = person.FirstName, + LastName = person.LastName, + MailingAddress = person.MailingAddress, + MailingPostalCity = person.MailingPostalCity, + MailingPostalCode = person.MailingPostalCode, + MiddleName = person.MiddleName, + MobileNumber = person.MobileNumber, + TelephoneNumber = person.TelephoneNumber, + }; + + public UserProfile ToUserProfile() => new UserProfile + { + PartyId = PartyId, + PhoneNumber = TelephoneNumber ?? MobileNumber, + Email = Email, + // ExternalIdentity, + Party = ToParty(), + ProfileSettingPreference = new() + { + // DoNotPromptForParty, + Language = Language, + // LanguageType, + // PreSelectedPartyId, + }, + UserId = UserId, + UserName = UserName, + // UserType, + }; +} diff --git a/src/Services/TestData/TestDataDiskReader.cs b/src/Services/TestData/TestDataDiskReader.cs new file mode 100644 index 00000000..bcb6e7b5 --- /dev/null +++ b/src/Services/TestData/TestDataDiskReader.cs @@ -0,0 +1,61 @@ +#nullable enable +using System.Text.Json; +using Authorization.Interface.Models; + +namespace LocalTest.Services.TestData; + +public static class TestDataDiskReader +{ + private static JsonSerializerOptions _options = new JsonSerializerOptions(JsonSerializerDefaults.Web) + { + Converters = {new System.Text.Json.Serialization.JsonStringEnumConverter()} + }; + + public static async Task ReadFromDisk(string testDataPath) + { + var testData = new TestDataModel(); + await ReadFolderToDictionary(Path.Join(testDataPath, "authorization", "claims"), testData.Authorization.Claims); + await ReadRoles(testDataPath, testData); + await ReadFolderToDictionary(Path.Join(testDataPath, "authorization", "partyList"), testData.Authorization.PartyList); + await ReadFolderToDictionary(Path.Join(testDataPath, "Profile", "User"), testData.Profile.User); + await ReadFolderToDictionary(Path.Join(testDataPath, "Register", "Org"), testData.Register.Org); + await ReadFolderToDictionary(Path.Join(testDataPath, "Register", "Party"), testData.Register.Party); + await ReadFolderToDictionary(Path.Join(testDataPath, "Register", "Person"), testData.Register.Person); + + return testData; + } + + private static async Task ReadRoles(string testDataPath, TestDataModel testData) + { + var rolesFolder = new DirectoryInfo(Path.Join(testDataPath, "authorization", "roles")); + foreach (var userFolder in rolesFolder.GetDirectories()) + { + var userId = userFolder.Name.Substring("User_".Length); + var roleDict = new Dictionary>(); + foreach (var partiesFolder in userFolder.GetDirectories()) + { + var party = partiesFolder.Name.Substring("party_".Length); + var fileBytes = await File.ReadAllBytesAsync(Path.Join(partiesFolder.FullName, "roles.json")); + var fileData = JsonSerializer.Deserialize>(fileBytes, _options)!; + roleDict[party] = fileData; + } + testData.Authorization.Roles[userId] = roleDict; + } + } + + private static async Task ReadFolderToDictionary(string folder, Dictionary dict) + { + var directory = new DirectoryInfo(folder); + var files = directory.GetFiles(); + var loadedFiles = await Task.WhenAll(files.Select(async file => + { + var fileBytes = await File.ReadAllBytesAsync(file.FullName); + var fileData = JsonSerializer.Deserialize(fileBytes, _options); + return KeyValuePair.Create(Path.GetFileNameWithoutExtension(file.Name), fileData!); + })); + foreach (var (filename, fileContent) in loadedFiles) + { + dict[filename] = fileContent; + } + } +} diff --git a/src/Services/TestData/TestDataModel.cs b/src/Services/TestData/TestDataModel.cs new file mode 100644 index 00000000..5e09e278 --- /dev/null +++ b/src/Services/TestData/TestDataModel.cs @@ -0,0 +1,38 @@ +#nullable enable +using Altinn.Platform.Authentication.Model; +using Altinn.Platform.Profile.Models; +using Altinn.Platform.Register.Models; +using Authorization.Interface.Models; + +namespace LocalTest.Services.TestData; + +/// +/// Simple model that directly matches the directory structure of the TestData folder +/// Currently this is used internally for backwards compatibility reasons +/// +public class TestDataModel +{ + public TestDataAuthorization Authorization { get; set; } = new(); + public TestDataProfile Profile { get; set; } = new(); + public TestDataRegister Register { get; set; } = new(); +} + +public class TestDataAuthorization +{ + public Dictionary> Claims { get; set; } = new(); + public Dictionary> PartyList { get; set; } = new(); + public Dictionary>> Roles { get; set; } = new(); + +} + +public class TestDataProfile +{ + public Dictionary User { get; set; } = new(); +} + +public class TestDataRegister +{ + public Dictionary Org { get; set; } = new(); + public Dictionary Party { get; set; } = new(); + public Dictionary Person { get; set; } = new(); +} diff --git a/src/Services/TestData/TestDataService.cs b/src/Services/TestData/TestDataService.cs new file mode 100644 index 00000000..8a4e439b --- /dev/null +++ b/src/Services/TestData/TestDataService.cs @@ -0,0 +1,48 @@ +#nullable enable +using LocalTest.Configuration; +using LocalTest.Services.LocalApp.Interface; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; + +namespace LocalTest.Services.TestData; + +public class TestDataService +{ + private readonly ILocalApp _localApp; + private readonly LocalPlatformSettings _settings; + private readonly IMemoryCache _cache; + private readonly ILogger _logger; + public TestDataService(ILocalApp localApp, IOptions settings, IMemoryCache memoryCache, ILogger logger) + { + _cache = memoryCache; + _localApp = localApp; + _settings = settings.Value; + _logger = logger; + } + + public async Task GetTestData() + { + return await _cache.GetOrCreateAsync("TEST_DATA", + async (entry) => + { + entry.SlidingExpiration = TimeSpan.FromSeconds(5); + entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30); + + try + { + var appData = await _localApp.GetTestData(); + + if (appData is not null) + { + return appData.GetTestDataModel(); + } + } + catch (HttpRequestException e) + { + _logger.LogInformation(e, "Fetching Test data from app failed."); + } + + return await TestDataDiskReader.ReadFromDisk(_settings.LocalTestingStaticTestDataPath); + }); + } +} diff --git a/src/Startup.cs b/src/Startup.cs index fcea8cd6..d56f2c37 100644 --- a/src/Startup.cs +++ b/src/Startup.cs @@ -1,4 +1,3 @@ -using System; using System.Security.Cryptography.X509Certificates; using System.Text.Json.Serialization; @@ -36,6 +35,7 @@ using LocalTest.Services.Register.Implementation; using LocalTest.Services.Register.Interface; using LocalTest.Services.Storage.Implementation; +using LocalTest.Services.TestData; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; @@ -69,6 +69,7 @@ public void ConfigureServices(IServiceCollection services) services.AddControllers().AddJsonOptions(opt => { opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + opt.JsonSerializerOptions.WriteIndented = true; }); services.Configure(Configuration.GetSection("PepSettings")); @@ -103,6 +104,7 @@ public void ConfigureServices(IServiceCollection services) services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddSingleton(); services.AddSingleton();