From e68d33cd3ceb848ca9d167e6ce462ebf49927b10 Mon Sep 17 00:00:00 2001 From: Kurt Monge Date: Fri, 29 Nov 2024 15:14:33 +0100 Subject: [PATCH] Code moved from api to server project --- .../Controllers/WeatherForecastController.cs | 33 --- .../Altinn/AltinnAuthHandler.cs | 76 ++++++ .../Infrastructure/Altinn/AltinnClient.cs | 65 +++++ .../Altinn/AltinnJsonSerializer.cs | 29 +++ .../Infrastructure/Altinn/AltinnSettings.cs | 9 + .../Altinn/MaskinportenAuthHandler.cs | 76 ++++++ .../Altinn/Models/AltinnInstancesResponse.cs | 21 ++ .../Altinn/ServiceCollectionExtensions.cs | 19 ++ .../Infrastructure/TestdataStore/DaData.cs | 117 +++++++++ .../TestdataStore/ITestdataStore.cs | 7 + .../ServiceCollectionExtensions.cs | 9 + .../TestdataStore/TestdataFileStore.cs | 48 ++++ .../Models/StatusNotification.cs | 14 ++ src/oed-testdata.Server/Models/oed.cs | 230 ++++++++++++++++++ src/oed-testdata.Server/Models/oed.xsd | 87 +++++++ .../Oed/InstanceEndpoints.cs | 42 ++++ src/oed-testdata.Server/Program.cs | 33 ++- .../Properties/launchSettings.json | 4 +- .../Services/TestService.cs | 25 ++ .../Testdata/Estate/EstateDto.cs | 14 ++ .../Testdata/Estate/EstateEndpoints.cs | 34 +++ .../Testdata/Estate/EstateMapper.cs | 21 ++ .../Json/Estate/24817296595-Aktuell_Make.json | 76 ++++++ .../oed-testdata.Server.csproj | 43 ++-- 24 files changed, 1067 insertions(+), 65 deletions(-) delete mode 100644 src/oed-testdata.Server/Controllers/WeatherForecastController.cs create mode 100644 src/oed-testdata.Server/Infrastructure/Altinn/AltinnAuthHandler.cs create mode 100644 src/oed-testdata.Server/Infrastructure/Altinn/AltinnClient.cs create mode 100644 src/oed-testdata.Server/Infrastructure/Altinn/AltinnJsonSerializer.cs create mode 100644 src/oed-testdata.Server/Infrastructure/Altinn/AltinnSettings.cs create mode 100644 src/oed-testdata.Server/Infrastructure/Altinn/MaskinportenAuthHandler.cs create mode 100644 src/oed-testdata.Server/Infrastructure/Altinn/Models/AltinnInstancesResponse.cs create mode 100644 src/oed-testdata.Server/Infrastructure/Altinn/ServiceCollectionExtensions.cs create mode 100644 src/oed-testdata.Server/Infrastructure/TestdataStore/DaData.cs create mode 100644 src/oed-testdata.Server/Infrastructure/TestdataStore/ITestdataStore.cs create mode 100644 src/oed-testdata.Server/Infrastructure/TestdataStore/ServiceCollectionExtensions.cs create mode 100644 src/oed-testdata.Server/Infrastructure/TestdataStore/TestdataFileStore.cs create mode 100644 src/oed-testdata.Server/Models/StatusNotification.cs create mode 100644 src/oed-testdata.Server/Models/oed.cs create mode 100644 src/oed-testdata.Server/Models/oed.xsd create mode 100644 src/oed-testdata.Server/Oed/InstanceEndpoints.cs create mode 100644 src/oed-testdata.Server/Services/TestService.cs create mode 100644 src/oed-testdata.Server/Testdata/Estate/EstateDto.cs create mode 100644 src/oed-testdata.Server/Testdata/Estate/EstateEndpoints.cs create mode 100644 src/oed-testdata.Server/Testdata/Estate/EstateMapper.cs create mode 100644 src/oed-testdata.Server/Testdata/Json/Estate/24817296595-Aktuell_Make.json diff --git a/src/oed-testdata.Server/Controllers/WeatherForecastController.cs b/src/oed-testdata.Server/Controllers/WeatherForecastController.cs deleted file mode 100644 index c9a309a..0000000 --- a/src/oed-testdata.Server/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace oed_testdata.Server.Controllers -{ - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - } - } -} diff --git a/src/oed-testdata.Server/Infrastructure/Altinn/AltinnAuthHandler.cs b/src/oed-testdata.Server/Infrastructure/Altinn/AltinnAuthHandler.cs new file mode 100644 index 0000000..2ce244f --- /dev/null +++ b/src/oed-testdata.Server/Infrastructure/Altinn/AltinnAuthHandler.cs @@ -0,0 +1,76 @@ +using System.Net.Http.Headers; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; + +namespace oed_testdata.Server.Infrastructure.Altinn +{ + public class AltinnAuthHandler( + IHttpClientFactory factory, + IOptionsMonitor options, + IMemoryCache cache) + : DelegatingHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (request.Headers.Authorization is not null && request.Headers.Authorization.Scheme == "Bearer") + { + return await base.SendAsync(request, cancellationToken); + } + + // Do auth, add auth header and try again + var token = await GetCachedEnterpriseToken(); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); + return await base.SendAsync(request, cancellationToken); + } + + private async Task GetCachedEnterpriseToken() + { + // TODO: Build key from params + var cacheKey = "key"; + + var token = await cache.GetOrCreateAsync(cacheKey, async (entry) => + { + return await GetEnterpriseToken(); + }, + new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(86300) + }); + + return token; + } + + private async Task GetEnterpriseToken() + { + var scopes = "altinn:serviceowner/instances.read altinn:serviceowner/instances.write altinn:lookup"; + + var queryParams = new Dictionary + { + { "env", "tt02" }, + { "orgNo", "991825827" }, + { "org", "digdir" }, + { "ttl", "86400" }, + //{ "partyId", "50552094" }, + { "scopes", scopes } + }; + + var baseUri = new Uri(options.CurrentValue.TokenGeneratorUrl, UriKind.Absolute); + var pathWithQuery = QueryHelpers.AddQueryString("/api/GetEnterpriseToken", queryParams); + var requestUri = new Uri(baseUri, pathWithQuery); + + var auth = $"{options.CurrentValue.Username}:{options.CurrentValue.Password}"; + var encodedAuth = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(auth)); + + var request = new HttpRequestMessage(HttpMethod.Get, requestUri); + request.Headers.Authorization = new AuthenticationHeaderValue("Basic", encodedAuth); + + var httpClient = factory.CreateClient(nameof(AltinnAuthHandler)); + var response = await httpClient.SendAsync(request); + + var token = await response.Content.ReadAsStringAsync(); + + return token; + } + } +} diff --git a/src/oed-testdata.Server/Infrastructure/Altinn/AltinnClient.cs b/src/oed-testdata.Server/Infrastructure/Altinn/AltinnClient.cs new file mode 100644 index 0000000..10e67bb --- /dev/null +++ b/src/oed-testdata.Server/Infrastructure/Altinn/AltinnClient.cs @@ -0,0 +1,65 @@ +using Altinn.Platform.Storage.Interface.Models; +using Microsoft.Extensions.Options; +using oed_testdata.Server.Infrastructure.Altinn.Models; + +namespace oed_testdata.Server.Infrastructure.Altinn; + +public interface IAltinnClient +{ + public Task> GetOedInstances(); + public Task> GetOedInstancesByDeceasedNin(string deceasedNin); + + public Task GetOedInstanceData(string instanceId, string instanceDataId); +} + +public class AltinnClient( + HttpClient httpClient, + IOptionsMonitor options) + : IAltinnClient +{ + public async Task> GetOedInstances() + { + var baseUri = new Uri(options.CurrentValue.PlatformUrl, UriKind.Absolute); + var requestUri = new Uri(baseUri, "/storage/api/v1/instances?org=digdir&appId=digdir/oed&status.isHardDeleted=false&status.isSoftDeleted=false&size=50"); + + var request = new HttpRequestMessage(HttpMethod.Get, requestUri); + var response = await httpClient.SendAsync(request); + + var s = response.Content.ReadAsStringAsync(); + + await using var contentStream = await response.Content.ReadAsStreamAsync(); + var altinnResponse = await AltinnJsonSerializer.Deserialize(contentStream); + + return altinnResponse.Instances; + } + + public async Task> GetOedInstancesByDeceasedNin(string deceasedNin) + { + var baseUri = new Uri(options.CurrentValue.PlatformUrl, UriKind.Absolute); + var requestUri = new Uri(baseUri, "/storage/api/v1/instances?org=digdir&appId=digdir/oed&status.isHardDeleted=false&status.isSoftDeleted=false"); + + var request = new HttpRequestMessage(HttpMethod.Get, requestUri); + request.Headers.TryAddWithoutValidation("X-Ai-InstanceOwnerIdentifier", $"person:{deceasedNin}"); + var response = await httpClient.SendAsync(request); + + await using var contentStream = await response.Content.ReadAsStreamAsync(); + var altinnResponse = await AltinnJsonSerializer.Deserialize(contentStream); + + return altinnResponse.Instances; + } + + public async Task GetOedInstanceData(string instanceId, string instanceDataId) + { + var baseUri = new Uri(options.CurrentValue.PlatformUrl, UriKind.Absolute); + var requestUri = new Uri(baseUri, $"https://platform.tt02.altinn.no/storage/api/v1/instances/{instanceId}/data/{instanceDataId}"); + + var request = new HttpRequestMessage(HttpMethod.Get, requestUri); + var response = await httpClient.SendAsync(request); + + await using var contentStream = await response.Content.ReadAsStreamAsync(); + var data = AltinnXmlSerializer.Deserialize(contentStream); + + return data; + } + +} \ No newline at end of file diff --git a/src/oed-testdata.Server/Infrastructure/Altinn/AltinnJsonSerializer.cs b/src/oed-testdata.Server/Infrastructure/Altinn/AltinnJsonSerializer.cs new file mode 100644 index 0000000..7ebdd9e --- /dev/null +++ b/src/oed-testdata.Server/Infrastructure/Altinn/AltinnJsonSerializer.cs @@ -0,0 +1,29 @@ +using System.Xml.Serialization; +using Newtonsoft.Json; + +namespace oed_testdata.Server.Infrastructure.Altinn +{ + public static class AltinnJsonSerializer + { + public static async Task Deserialize(Stream contentStream) + { + var serializer = new JsonSerializer(); + + using var sr = new StreamReader(contentStream); + await using var jsonTextReader = new JsonTextReader(sr); + return serializer.Deserialize(jsonTextReader)!; + } + } + + public static class AltinnXmlSerializer + { + public static T Deserialize(Stream contentStream) + { + var serializer = new XmlSerializer(typeof(T)); + var dataObject = serializer.Deserialize(contentStream); + + var data = (T)dataObject!; + return data; + } + } +} diff --git a/src/oed-testdata.Server/Infrastructure/Altinn/AltinnSettings.cs b/src/oed-testdata.Server/Infrastructure/Altinn/AltinnSettings.cs new file mode 100644 index 0000000..a390245 --- /dev/null +++ b/src/oed-testdata.Server/Infrastructure/Altinn/AltinnSettings.cs @@ -0,0 +1,9 @@ +namespace oed_testdata.Server.Infrastructure.Altinn; + +public class AltinnSettings +{ + public required string TokenGeneratorUrl { get; set; } + public required string PlatformUrl { get; set; } + public string? Username { get; set; } + public string? Password { get; set; } +} \ No newline at end of file diff --git a/src/oed-testdata.Server/Infrastructure/Altinn/MaskinportenAuthHandler.cs b/src/oed-testdata.Server/Infrastructure/Altinn/MaskinportenAuthHandler.cs new file mode 100644 index 0000000..b133791 --- /dev/null +++ b/src/oed-testdata.Server/Infrastructure/Altinn/MaskinportenAuthHandler.cs @@ -0,0 +1,76 @@ +using System.Net.Http.Headers; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; + +namespace oed_testdata.Server.Infrastructure.Altinn +{ + public class MaskinportenAuthHandler( + IHttpClientFactory factory, + IOptionsMonitor options, + IMemoryCache cache) + : DelegatingHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (request.Headers.Authorization is not null && request.Headers.Authorization.Scheme == "Bearer") + { + return await base.SendAsync(request, cancellationToken); + } + + // Do auth, add auth header and try again + var token = await GetCachedEnterpriseToken(); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); + return await base.SendAsync(request, cancellationToken); + } + + private async Task GetCachedEnterpriseToken() + { + // TODO: Build key from params + var cacheKey = "key"; + + var token = await cache.GetOrCreateAsync(cacheKey, async (entry) => + { + return await GetEnterpriseToken(); + }, + new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(86300) + }); + + return token; + } + + private async Task GetEnterpriseToken() + { + var scopes = "altinn:serviceowner/instances.read altinn:serviceowner/instances.write altinn:lookup"; + + var queryParams = new Dictionary + { + { "env", "tt02" }, + { "orgNo", "991825827" }, + { "org", "digdir" }, + { "ttl", "86400" }, + //{ "partyId", "50552094" }, + { "scopes", scopes } + }; + + var baseUri = new Uri(options.CurrentValue.TokenGeneratorUrl, UriKind.Absolute); + var pathWithQuery = QueryHelpers.AddQueryString("/api/GetEnterpriseToken", queryParams); + var requestUri = new Uri(baseUri, pathWithQuery); + + var auth = $"{options.CurrentValue.Username}:{options.CurrentValue.Password}"; + var encodedAuth = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(auth)); + + var request = new HttpRequestMessage(HttpMethod.Get, requestUri); + request.Headers.Authorization = new AuthenticationHeaderValue("Basic", encodedAuth); + + var httpClient = factory.CreateClient(nameof(MaskinportenAuthHandler)); + var response = await httpClient.SendAsync(request); + + var token = await response.Content.ReadAsStringAsync(); + + return token; + } + } +} diff --git a/src/oed-testdata.Server/Infrastructure/Altinn/Models/AltinnInstancesResponse.cs b/src/oed-testdata.Server/Infrastructure/Altinn/Models/AltinnInstancesResponse.cs new file mode 100644 index 0000000..a08ae6b --- /dev/null +++ b/src/oed-testdata.Server/Infrastructure/Altinn/Models/AltinnInstancesResponse.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; +using Altinn.Platform.Storage.Interface.Models; +using Newtonsoft.Json; + +namespace oed_testdata.Server.Infrastructure.Altinn.Models +{ + public class AltinnInstancesResponse + { + [JsonProperty("count")] + public int Count { get; set; } + + [JsonPropertyName("self")] + public string Self { get; set; } + + [JsonPropertyName("next")] + public string Next { get; set; } + + [JsonPropertyName("instances")] + public List Instances { get; set; } + } +} diff --git a/src/oed-testdata.Server/Infrastructure/Altinn/ServiceCollectionExtensions.cs b/src/oed-testdata.Server/Infrastructure/Altinn/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..cef3981 --- /dev/null +++ b/src/oed-testdata.Server/Infrastructure/Altinn/ServiceCollectionExtensions.cs @@ -0,0 +1,19 @@ +namespace oed_testdata.Server.Infrastructure.Altinn; + +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddAltinnClient( + this IServiceCollection services, + IConfiguration configuration) + { + services.Configure(configuration.GetSection("AltinnSettings")); + + services + .AddMemoryCache() + .AddTransient() + .AddHttpClient() + .AddHttpMessageHandler(); + + return services; + } +} \ No newline at end of file diff --git a/src/oed-testdata.Server/Infrastructure/TestdataStore/DaData.cs b/src/oed-testdata.Server/Infrastructure/TestdataStore/DaData.cs new file mode 100644 index 0000000..2d23f9c --- /dev/null +++ b/src/oed-testdata.Server/Infrastructure/TestdataStore/DaData.cs @@ -0,0 +1,117 @@ +using System.Text.Json.Serialization; + +namespace oed_testdata.Server.Infrastructure.TestdataStore; +public class DaData +{ + [JsonPropertyName("DaEventList")] + public required DaEvent[][] DaEventList { get; set; } + + [JsonPropertyName("DaCaseList")] + public required DaCase[] DaCaseList { get; set; } + + + public void UpdateTimestamps(DateTimeOffset timestamp) + { + DaEventList.First().First().Time = timestamp; + + var daCase = DaCaseList.First(); + daCase.ReceivedDate = timestamp; + daCase.DeadlineDate = timestamp.AddDays(60); + } +} + +public class DaEvent +{ + [JsonPropertyName("specversion")] + public required string Specversion { get; set; } + + [JsonPropertyName("id")] + public required string Id { get; set; } + + [JsonPropertyName("source")] + public required string Source { get; set; } + + [JsonPropertyName("type")] + public required string Type { get; set; } + + [JsonPropertyName("datacontenttype")] + public required string DataContentType { get; set; } + + [JsonPropertyName("time")] + public required DateTimeOffset Time { get; set; } + + [JsonPropertyName("data")] + public required Data Data { get; set; } +} + +public class Data +{ + [JsonPropertyName("@id")] + public string Id { get; set; } +} + +public class DaCase +{ + [JsonPropertyName("sakId")] + public required string SakId { get; set; } + + [JsonPropertyName("avdode")] + public required string Avdode { get; set; } + + [JsonPropertyName("embete")] + public required string Embete { get; set; } + + [JsonPropertyName("status")] + public required string Status { get; set; } + + [JsonPropertyName("parter")] + public required Parter[] Parter { get; set; } + + [JsonPropertyName("receivedDate")] + public required DateTimeOffset ReceivedDate { get; set; } + + [JsonPropertyName("deadlineDate")] + public required DateTimeOffset DeadlineDate { get; set; } + + [JsonPropertyName("resultatType")] + public string? ResultatType { get; set; } + + [JsonPropertyName("skifteattest")] + public Skifteattest? Skifteattest { get; set; } +} + +public class Skifteattest +{ + [JsonPropertyName("arvinger")] + public required string[] Arvinger { get; set; } + + [JsonPropertyName("arvingerSomPaatarSegGjeldsansvar")] + public required string[] ArvingerSomPaatarSegGjeldsansvar { get; set; } + + [JsonPropertyName("resultat")] + public required string Resultat { get; set; } +} + +public class Parter +{ + [JsonPropertyName("nin")] + public required string Nin { get; set; } + + [JsonPropertyName("role")] + public required string Role { get; set; } + + [JsonPropertyName("formuesfullmakt")] + public required bool Formuesfullmakt { get; set; } + + [JsonPropertyName("signertDato")] + public DateTimeOffset? SignertDato { get; set; } + + [JsonPropertyName("onsketSkifteform")] + public string? OnsketSkifteform { get; set; } + + [JsonPropertyName("paatarGjeldsansvar")] + public required bool PaatarGjeldsansvar { get; set; } + + [JsonPropertyName("godkjennerSkifteattest")] + public required bool GodkjennerSkifteattest { get; set; } +} \ No newline at end of file diff --git a/src/oed-testdata.Server/Infrastructure/TestdataStore/ITestdataStore.cs b/src/oed-testdata.Server/Infrastructure/TestdataStore/ITestdataStore.cs new file mode 100644 index 0000000..d7cf448 --- /dev/null +++ b/src/oed-testdata.Server/Infrastructure/TestdataStore/ITestdataStore.cs @@ -0,0 +1,7 @@ +namespace oed_testdata.Server.Infrastructure.TestdataStore; + +public interface ITestdataStore +{ + public Task> ListAll(); + public Task GetByEstateSsn(string estateSsn); +} \ No newline at end of file diff --git a/src/oed-testdata.Server/Infrastructure/TestdataStore/ServiceCollectionExtensions.cs b/src/oed-testdata.Server/Infrastructure/TestdataStore/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..89f5ad2 --- /dev/null +++ b/src/oed-testdata.Server/Infrastructure/TestdataStore/ServiceCollectionExtensions.cs @@ -0,0 +1,9 @@ +namespace oed_testdata.Server.Infrastructure.TestdataStore; + +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddTestdataStore(this IServiceCollection services) + { + return services.AddTransient(); + } +} \ No newline at end of file diff --git a/src/oed-testdata.Server/Infrastructure/TestdataStore/TestdataFileStore.cs b/src/oed-testdata.Server/Infrastructure/TestdataStore/TestdataFileStore.cs new file mode 100644 index 0000000..9cd98dd --- /dev/null +++ b/src/oed-testdata.Server/Infrastructure/TestdataStore/TestdataFileStore.cs @@ -0,0 +1,48 @@ +using System.Text.Json; + +namespace oed_testdata.Server.Infrastructure.TestdataStore; + +public class TestdataFileStore : ITestdataStore +{ + private const string EstatePath = "./Testdata/Json/Estate"; + + public async Task> ListAll() + { + EnsureDirectory(); + + var daDataList = new List(); + foreach (var file in Directory.EnumerateFiles(EstatePath)) + { + await using var filestream = File.OpenRead(file); + var daData = await JsonSerializer.DeserializeAsync(filestream); + + daData!.UpdateTimestamps(DateTimeOffset.UtcNow); + daDataList.Add(daData); + } + + return daDataList; + } + + public async Task GetByEstateSsn(string estateSsn) + { + EnsureDirectory(); + + var files = Directory.EnumerateFiles(EstatePath); + var file = files.SingleOrDefault(f => f.Contains(estateSsn)); + + if (file is null) throw new Exception("File not found"); + + await using var filestream = File.OpenRead(file); + var daData = await JsonSerializer.DeserializeAsync(filestream); + + daData!.UpdateTimestamps(DateTimeOffset.UtcNow); + + return daData; + } + + private static void EnsureDirectory() + { + if (!Directory.Exists(EstatePath)) + throw new Exception("No path"); + } +} \ No newline at end of file diff --git a/src/oed-testdata.Server/Models/StatusNotification.cs b/src/oed-testdata.Server/Models/StatusNotification.cs new file mode 100644 index 0000000..a66040f --- /dev/null +++ b/src/oed-testdata.Server/Models/StatusNotification.cs @@ -0,0 +1,14 @@ +namespace oed_testdata.Server.Models +{ + public enum StatusNotificationType + { + OpenApp + } + + public class StatusNotification + { + public int PartyID { get; set; } + public DateTime Timestamp { get; set; } + public StatusNotificationType StatusNotificationType { get; set; } + } +} diff --git a/src/oed-testdata.Server/Models/oed.cs b/src/oed-testdata.Server/Models/oed.cs new file mode 100644 index 0000000..980bd74 --- /dev/null +++ b/src/oed-testdata.Server/Models/oed.cs @@ -0,0 +1,230 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using System.Xml.Serialization; +using Newtonsoft.Json; + +namespace oed_testdata.Server.Models +{ + [XmlRoot(ElementName = "oed")] + public class OED_M + { + [XmlElement("CaseId")] + [JsonProperty("caseId")] + [JsonPropertyName("caseId")] + public string CaseId { get; set; } + + [XmlElement("DeceasedInfo")] + [JsonProperty("deceasedInfo")] + [JsonPropertyName("deceasedInfo")] + public DeceasedInfo DeceasedInfo { get; set; } + + [XmlElement("Heirs")] + [JsonProperty("heirs")] + [JsonPropertyName("heirs")] + public List Heirs { get; set; } + + [XmlElement("StatusNotifications")] + [JsonProperty("statusNotifications")] + [JsonPropertyName("statusNotifications")] + public List StatusNotifications { get; set; } + + [XmlElement("MarriagePactsFromRegister")] + [JsonProperty("marriagePactsFromRegister")] + [JsonPropertyName("marriagePactsFromRegister")] + public string MarriagePactsFromRegister { get; set; } + + [XmlElement("PropertyRights")] + [JsonProperty("propertyRights")] + [JsonPropertyName("propertyRights")] + public string PropertyRights { get; set; } + + [XmlElement("ProbateDeadline")] + [JsonProperty("probateDeadline")] + [JsonPropertyName("probateDeadline")] + public string ProbateDeadline { get; set; } + + [XmlElement("DistrictCourtName")] + [JsonProperty("districtCourtName")] + [JsonPropertyName("districtCourtName")] + public string DistrictCourtName { get; set; } + + [XmlElement("ProbateResult")] + [JsonProperty("probateResult")] + [JsonPropertyName("probateResult")] + public ProbateResult ProbateResult { get; set; } + + [XmlElement("CaseStatus")] + [JsonProperty("caseStatus")] + [JsonPropertyName("caseStatus")] + public string CaseStatus { get; set; } + + [XmlElement("Version")] + [JsonProperty("version")] + [JsonPropertyName("version")] + public string Version { get; set; } + + [XmlElement("EstateRevoked")] + [JsonProperty("estateRevoked")] + [JsonPropertyName("estateRevoked")] + public bool? EstateRevoked { get; set; } + + [XmlElement("MyTaxUri")] + [JsonProperty("myTaxUri")] + [JsonPropertyName("myTaxUri")] + public string MyTaxUri { get; set; } + + } + + public class ProbateResult + { + [XmlElement("Result")] + [JsonProperty("result")] + [JsonPropertyName("result")] + public string Result { get; set; } + + [XmlElement("Heirs")] + [JsonProperty("heirs")] + [JsonPropertyName("heirs")] + public List Heirs { get; set; } + + [XmlElement("AcceptsDebtHeirs")] + [JsonProperty("acceptsDebtHeirs")] + [JsonPropertyName("acceptsDebtHeirs")] + public List AcceptsDebtHeirs { get; set; } + + [XmlElement("Received")] + [JsonProperty("received")] + [JsonPropertyName("received")] + public string Received { get; set; } + } + + public class DeceasedInfo + { + [XmlElement("Deceased")] + [JsonProperty("deceased")] + [JsonPropertyName("deceased")] + public Person Deceased { get; set; } + + [XmlElement("DateOfDeath")] + [JsonProperty("dateOfDeath")] + [JsonPropertyName("dateOfDeath")] + public string DateOfDeath { get; set; } + + [XmlElement("MaritalStatus")] + [JsonProperty("maritalStatus")] + [JsonPropertyName("maritalStatus")] + public int MaritalStatus { get; set; } + + [XmlElement("ContactPerson")] + [JsonProperty("contactPerson")] + [JsonPropertyName("contactPerson")] + public Person ContactPerson { get; set; } + } + + public class Person + { + [Range(Int32.MinValue, Int32.MaxValue)] + [XmlElement("UserId")] + [JsonProperty("userid")] + [JsonPropertyName("userid")] + public int? UserId { get; set; } + + [Range(Int32.MinValue, Int32.MaxValue)] + [XmlElement("PartyId")] + [JsonProperty("partyId")] + [JsonPropertyName("partyId")] + public int PartyId { get; set; } + + [XmlElement("Birthday")] + [JsonProperty("birthday")] + [JsonPropertyName("birthday")] + public string Birthday { get; set; } + + [XmlElement("Nin")] + [JsonProperty("nin")] + [JsonPropertyName("nin")] + public string Nin { get; set; } + + [XmlElement("FirstName")] + [JsonProperty("firstName")] + [JsonPropertyName("firstName")] + public string FirstName { get; set; } + + [XmlElement("MiddleName")] + [JsonProperty("middleName")] + [JsonPropertyName("middleName")] + public string MiddleName { get; set; } + + [XmlElement("LastName")] + [JsonProperty("lastName")] + [JsonPropertyName("lastName")] + public string LastName { get; set; } + + [XmlElement("Address")] + [JsonProperty("address")] + [JsonPropertyName("address")] + public Address Address { get; set; } + + [XmlElement("Role")] + [JsonProperty("role")] + [JsonPropertyName("role")] + public string Role { get; set; } + + } + + public class Address + { + [XmlElement("StreetAddress")] + [JsonProperty("streetAddress")] + [JsonPropertyName("streetAddress")] + public string StreetAddress { get; set; } + + [XmlElement("PostalCode")] + [JsonProperty("postalCode")] + [JsonPropertyName("postalCode")] + public string PostalCode { get; set; } + + [XmlElement("City")] + [JsonProperty("city")] + [JsonPropertyName("city")] + public string City { get; set; } + } + + public class HeirInfo + { + [XmlElement("Heir")] + [JsonProperty("heir")] + [JsonPropertyName("heir")] + public Person Heir { get; set; } + + [XmlElement("CorrespondenceReceived")] + [JsonProperty("correspondenceReceived")] + [JsonPropertyName("correspondenceReceived")] + public bool EstateReadyCorrespondenceSent { get; set; } + + [XmlElement("Waiver60DayPeriod")] + [JsonProperty("waiver60DayPeriod")] + [JsonPropertyName("waiver60DayPeriod")] + public bool Waiver60DayPeriod { get; set; } + + [XmlElement("WillingToAssumeDebt")] + [JsonProperty("willingToAssumeDebt")] + [JsonPropertyName("willingToAssumeDebt")] + public bool WillingToAssumeDebt { get; set; } + + [XmlElement("SignedDate")] + [JsonProperty("signedDate")] + [JsonPropertyName("signedDate")] + public DateTimeOffset? SignedDate { get; set; } + + [XmlElement("PreferredSettlementProcedure")] + [JsonProperty("preferredSettlementProcedure")] + [JsonPropertyName("preferredSettlementProcedure")] + public string PreferredSettlementProcedure { get; set; } + + [XmlElement("ProbateIssuedCorrespondenceSent")] + [JsonProperty("probateIssuedCorrespondenceSent")] + [JsonPropertyName("probateIssuedCorrespondenceSent")] + public bool ProbateIssuedCorrespondenceSent { get; set; } + } +} diff --git a/src/oed-testdata.Server/Models/oed.xsd b/src/oed-testdata.Server/Models/oed.xsd new file mode 100644 index 0000000..70a60f5 --- /dev/null +++ b/src/oed-testdata.Server/Models/oed.xsd @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/oed-testdata.Server/Oed/InstanceEndpoints.cs b/src/oed-testdata.Server/Oed/InstanceEndpoints.cs new file mode 100644 index 0000000..0c7b70a --- /dev/null +++ b/src/oed-testdata.Server/Oed/InstanceEndpoints.cs @@ -0,0 +1,42 @@ +using Altinn.Platform.Storage.Interface.Models; +using Microsoft.AspNetCore.Http.HttpResults; +using oed_testdata.Server.Infrastructure.Altinn; +using oed_testdata.Server.Models; + +namespace oed_testdata.Server.Oed +{ + public static class InstanceEndpoints + { + public static void MapOedInstanceEndpoints(this WebApplication app) + { + app.MapGroup("/api/oed/instance").MapEndpoints(); + //.RequireAuthorization(); + } + + private static RouteGroupBuilder MapEndpoints(this RouteGroupBuilder group) + { + group.MapGet("/{estateSsn}", GetSingleByEstateSsn); + group.MapGet("/{estateSsn}/data", GetInstanceDataByEstateSsn); + + return group; + } + + private static async Task>> GetSingleByEstateSsn(IAltinnClient altinnClient, string estateSsn) + { + var instances = await altinnClient.GetOedInstancesByDeceasedNin(estateSsn); + return TypedResults.Ok(instances); + } + + private static async Task> GetInstanceDataByEstateSsn(IAltinnClient altinnClient, string estateSsn) + { + var instances = await altinnClient.GetOedInstancesByDeceasedNin(estateSsn); + + var instanceId = instances.First().Id; + var instanceDataId = instances.First().Data.First(data => data.ContentType == "application/xml").Id; + + var data = await altinnClient.GetOedInstanceData(instanceId, instanceDataId); + + return TypedResults.Ok(data); + } + } +} diff --git a/src/oed-testdata.Server/Program.cs b/src/oed-testdata.Server/Program.cs index 1da74a0..70bdb54 100644 --- a/src/oed-testdata.Server/Program.cs +++ b/src/oed-testdata.Server/Program.cs @@ -1,11 +1,18 @@ +using Microsoft.AspNetCore.Mvc; +using oed_testdata.Server.Infrastructure.Altinn; +using oed_testdata.Server.Infrastructure.TestdataStore; +using oed_testdata.Server.Oed; +using oed_testdata.Server.Services; +using oed_testdata.Server.Testdata.Estate; +using Scalar.AspNetCore; + var builder = WebApplication.CreateBuilder(args); -// Add services to the container. +builder.Services.AddOpenApi(); -builder.Services.AddControllers(); -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +builder.Services.AddTransient(); +builder.Services.AddAltinnClient(builder.Configuration); +builder.Services.AddTestdataStore(); var app = builder.Build(); @@ -15,16 +22,22 @@ // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(); + app.MapOpenApi(); + app.MapScalarApiReference(); } +app.MapEstateEndpoints(); +app.MapOedInstanceEndpoints(); + app.UseHttpsRedirection(); -app.UseAuthorization(); -app.MapControllers(); +app.MapGet("/test", async ([FromServices] ITestService testService) => +{ + var instanceData = await testService.Test(); + return TypedResults.Ok(instanceData); +}).WithName("Test"); app.MapFallbackToFile("/index.html"); -app.Run(); +app.Run(); \ No newline at end of file diff --git a/src/oed-testdata.Server/Properties/launchSettings.json b/src/oed-testdata.Server/Properties/launchSettings.json index 63174cd..d5df30a 100644 --- a/src/oed-testdata.Server/Properties/launchSettings.json +++ b/src/oed-testdata.Server/Properties/launchSettings.json @@ -24,7 +24,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "launchUrl": "swagger", + "launchUrl": "scalar/v1", "applicationUrl": "https://localhost:7280;http://localhost:5149", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", @@ -34,7 +34,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "swagger", + "launchUrl": "scalar/v1", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy" diff --git a/src/oed-testdata.Server/Services/TestService.cs b/src/oed-testdata.Server/Services/TestService.cs new file mode 100644 index 0000000..11f36b4 --- /dev/null +++ b/src/oed-testdata.Server/Services/TestService.cs @@ -0,0 +1,25 @@ +using oed_testdata.Server.Infrastructure.Altinn; +using oed_testdata.Server.Infrastructure.TestdataStore; +using oed_testdata.Server.Models; + +namespace oed_testdata.Server.Services +{ + public interface ITestService + { + public Task Test(); + } + public class TestService(IAltinnClient altinnClient, ITestdataStore store) : ITestService + { + public async Task Test() + { + var instances = await altinnClient.GetOedInstancesByDeceasedNin("24817296595"); + + var instanceId = instances.First().Id; + var instanceDataId = instances.First().Data.First().Id; + + var data = await altinnClient.GetOedInstanceData(instanceId, instanceDataId); + + return data; + } + } +} diff --git a/src/oed-testdata.Server/Testdata/Estate/EstateDto.cs b/src/oed-testdata.Server/Testdata/Estate/EstateDto.cs new file mode 100644 index 0000000..df30fc0 --- /dev/null +++ b/src/oed-testdata.Server/Testdata/Estate/EstateDto.cs @@ -0,0 +1,14 @@ +namespace oed_testdata.Server.Testdata.Estate +{ + public class EstateDto + { + public required string EstateSsn { get; init; } + public required IEnumerable Heirs { get; init; } + } + + public class Heir + { + public required string Ssn { get; init; } + public required string Relation { get; init; } + } +} diff --git a/src/oed-testdata.Server/Testdata/Estate/EstateEndpoints.cs b/src/oed-testdata.Server/Testdata/Estate/EstateEndpoints.cs new file mode 100644 index 0000000..7f0c76b --- /dev/null +++ b/src/oed-testdata.Server/Testdata/Estate/EstateEndpoints.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Http.HttpResults; +using oed_testdata.Server.Infrastructure.TestdataStore; + +namespace oed_testdata.Server.Testdata.Estate +{ + public static class EstateEndpoints + { + public static void MapEstateEndpoints(this WebApplication app) + { + app.MapGroup("/api/testdata/estate").MapEndpoints(); + //.RequireAuthorization(); + } + + private static RouteGroupBuilder MapEndpoints(this RouteGroupBuilder group) + { + group.MapGet("/", GetAll); + group.MapGet("/{estateSsn}", GetSingleByEstateSsn); + + return group; + } + + private static async Task>> GetAll(ITestdataStore store) + { + var data = await store.ListAll(); + return TypedResults.Ok(data.Select(EstateMapper.Map)); + } + + private static async Task> GetSingleByEstateSsn(ITestdataStore store, string estateSsn) + { + var data = await store.GetByEstateSsn(estateSsn); + return TypedResults.Ok(EstateMapper.Map(data)); + } + } +} diff --git a/src/oed-testdata.Server/Testdata/Estate/EstateMapper.cs b/src/oed-testdata.Server/Testdata/Estate/EstateMapper.cs new file mode 100644 index 0000000..7e49e75 --- /dev/null +++ b/src/oed-testdata.Server/Testdata/Estate/EstateMapper.cs @@ -0,0 +1,21 @@ +using oed_testdata.Server.Infrastructure.TestdataStore; + +namespace oed_testdata.Server.Testdata.Estate; + +public static class EstateMapper +{ + public static EstateDto Map(DaData daData) + { + return daData.DaCaseList + .Select(da => new EstateDto + { + EstateSsn = da.Avdode, + Heirs = da.Parter.Select(p => new Heir + { + Ssn = p.Nin, + Relation = p.Role + }) + }) + .Single(); + } +} \ No newline at end of file diff --git a/src/oed-testdata.Server/Testdata/Json/Estate/24817296595-Aktuell_Make.json b/src/oed-testdata.Server/Testdata/Json/Estate/24817296595-Aktuell_Make.json new file mode 100644 index 0000000..40ab147 --- /dev/null +++ b/src/oed-testdata.Server/Testdata/Json/Estate/24817296595-Aktuell_Make.json @@ -0,0 +1,76 @@ +{ + "DaEventList": [ + [ + { + "specversion": "1.0", + "id": "a7a7d8b9-7956-4031-922e-b9c02f4a3643", + "source": "https://domstol.no", + "type": "DODSFALLSAK-STATUS_OPPDATERT", + "datacontenttype": "application/json", + "time": "1900-01-01T00:00:00Z", + "data": { + "@id": "https://hendelsesliste.test.domstol.no/api/objects/a7a7d8b9-7956-4031-922e-b9c02f4a3643" + } + } + ] + ], + "DaCaseList": [ + { + "sakId": "a7a7d8b9-7956-4031-922e-b9c02f4a3643", + "avdode": "24817296595", + "embete": "Oslo Tingrett", + "status": "MOTTATT", + "parter": [ + { + "nin": "05844498863", + "role": "MOR", + "formuesfullmakt": true, + "signertDato": null, + "onsketSkifteform": null, + "paatarGjeldsansvar": false, + "godkjennerSkifteattest": false + }, + { + "nin": "13814497164", + "role": "FAR", + "formuesfullmakt": true, + "signertDato": null, + "onsketSkifteform": null, + "paatarGjeldsansvar": false, + "godkjennerSkifteattest": false + }, + { + "nin": "13837297278", + "role": "GJENLEV_PARTNER", + "formuesfullmakt": true, + "signertDato": null, + "onsketSkifteform": null, + "paatarGjeldsansvar": false, + "godkjennerSkifteattest": false + }, + { + "nin": "15870897739", + "role": "BARN", + "formuesfullmakt": true, + "signertDato": null, + "onsketSkifteform": null, + "paatarGjeldsansvar": false, + "godkjennerSkifteattest": false + }, + { + "nin": "10839998622", + "role": "BARN", + "formuesfullmakt": true, + "signertDato": null, + "onsketSkifteform": null, + "paatarGjeldsansvar": false, + "godkjennerSkifteattest": false + } + ], + "receivedDate": "1900-01-01T00:00:00Z", + "deadlineDate": "1900-01-01T00:00:00Z", + "resultatType": null, + "skifteattest": null + } + ] +} \ No newline at end of file diff --git a/src/oed-testdata.Server/oed-testdata.Server.csproj b/src/oed-testdata.Server/oed-testdata.Server.csproj index a3e2c02..9124a22 100644 --- a/src/oed-testdata.Server/oed-testdata.Server.csproj +++ b/src/oed-testdata.Server/oed-testdata.Server.csproj @@ -1,26 +1,29 @@ - - net8.0 - enable - enable - oed_testdata.Server - ..\oed-testdata.client - npm run dev - https://localhost:5173 - + + net9.0 + enable + enable + oed_testdata.Server + ..\oed-testdata.client + npm run dev + https://localhost:5173 + - - - 8.*-* - - - + + + + + + 8.*-* + + + - - - false - - + + + false + +