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();