From 5610117139769de7fb7ec1ca757e12201b52d57f Mon Sep 17 00:00:00 2001 From: Stephanie Buadu <47737608+acn-sbuad@users.noreply.github.com> Date: Wed, 15 May 2024 12:35:59 +0200 Subject: [PATCH] new endpoint for unit contact points (#151) * new endpoint for unit contact points * added UnitProfileClientTest * test implemented, but not working * fixed unit test for unitclient * fixed code smell * still fixing code smells * fixed final code smell * Update src/Altinn.Profile.Integrations/SblBridge/UnitProfileClient.cs * Fixed pr comments --- .../Integrations/IUnitProfileClient.cs | 17 +++ .../ServiceCollectionExtensions.cs | 4 +- .../Unit.ContactPoints/IUnitContactPoints.cs | 16 +++ .../UnitContactPointLookup.cs | 18 +++ .../UnitContactPointService.cs | 28 ++++ .../Unit.ContactPoints/UnitContactPoints.cs | 36 ++++++ .../User.ContactPoints/UserContactPoints.cs | 2 +- .../PartyNotificationContactPoints.cs | 75 +++++++++++ .../SblBridge/UnitProfileClient.cs | 70 ++++++++++ .../ServiceCollectionExtensions.cs | 6 +- .../Controllers/UnitContactPointController.cs | 45 +++++++ .../Controllers/UserContactPointController.cs | 1 + .../Models}/UserContactPointLookup.cs | 4 +- src/Altinn.Profile/appsettings.json | 2 +- .../UnitContactPointControllerTests.cs | 122 ++++++++++++++++++ .../UserContactPointControllerTests.cs | 1 + .../Utils/WebApplicationFactorySetup.cs | 8 ++ .../User/UserProfileCachingDecoratorTest.cs | 0 .../PartyNotificationContactPointsTests.cs | 74 +++++++++++ .../SblBridge/UnitProfileClientTest.cs | 118 +++++++++++++++++ 20 files changed, 640 insertions(+), 7 deletions(-) create mode 100644 src/Altinn.Profile.Core/Integrations/IUnitProfileClient.cs create mode 100644 src/Altinn.Profile.Core/Unit.ContactPoints/IUnitContactPoints.cs create mode 100644 src/Altinn.Profile.Core/Unit.ContactPoints/UnitContactPointLookup.cs create mode 100644 src/Altinn.Profile.Core/Unit.ContactPoints/UnitContactPointService.cs create mode 100644 src/Altinn.Profile.Core/Unit.ContactPoints/UnitContactPoints.cs create mode 100644 src/Altinn.Profile.Integrations/SblBridge/PartyNotificationContactPoints.cs create mode 100644 src/Altinn.Profile.Integrations/SblBridge/UnitProfileClient.cs create mode 100644 src/Altinn.Profile/Controllers/UnitContactPointController.cs rename src/{Altinn.Profile.Core/User.ContactPoints => Altinn.Profile/Models}/UserContactPointLookup.cs (82%) create mode 100644 test/Altinn.Profile.Tests/IntegrationTests/API/Controllers/UnitContactPointControllerTests.cs rename test/Altinn.Profile.Tests/{Core => Profile.Core}/User/UserProfileCachingDecoratorTest.cs (100%) create mode 100644 test/Altinn.Profile.Tests/Profile.Integrations/SblBridge/PartyNotificationContactPointsTests.cs create mode 100644 test/Altinn.Profile.Tests/Profile.Integrations/SblBridge/UnitProfileClientTest.cs diff --git a/src/Altinn.Profile.Core/Integrations/IUnitProfileClient.cs b/src/Altinn.Profile.Core/Integrations/IUnitProfileClient.cs new file mode 100644 index 0000000..500783f --- /dev/null +++ b/src/Altinn.Profile.Core/Integrations/IUnitProfileClient.cs @@ -0,0 +1,17 @@ +using Altinn.Profile.Core.Unit.ContactPoints; + +namespace Altinn.Profile.Core.Integrations +{ + /// + /// Interface describing a client for the user profile service + /// + public interface IUnitProfileClient + { + /// + /// Provides a list of user registered contact points based on the lookup criteria + /// + /// Lookup object containing a list of organizations and a resource + /// A list of unit contact points + Task> GetUserRegisteredContactPoints(UnitContactPointLookup lookup); + } +} diff --git a/src/Altinn.Profile.Core/ServiceCollectionExtensions.cs b/src/Altinn.Profile.Core/ServiceCollectionExtensions.cs index 0b3d3af..da1f54d 100644 --- a/src/Altinn.Profile.Core/ServiceCollectionExtensions.cs +++ b/src/Altinn.Profile.Core/ServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using Altinn.Profile.Core; +using Altinn.Profile.Core.Unit.ContactPoints; using Altinn.Profile.Core.User; using Altinn.Profile.Core.User.ContactPoints; @@ -24,6 +25,7 @@ public static void AddCoreServices(this IServiceCollection services, IConfigurat .AddMemoryCache() .AddSingleton() .AddSingleton() - .Decorate(); + .Decorate() + .AddSingleton(); } } diff --git a/src/Altinn.Profile.Core/Unit.ContactPoints/IUnitContactPoints.cs b/src/Altinn.Profile.Core/Unit.ContactPoints/IUnitContactPoints.cs new file mode 100644 index 0000000..3aec836 --- /dev/null +++ b/src/Altinn.Profile.Core/Unit.ContactPoints/IUnitContactPoints.cs @@ -0,0 +1,16 @@ +using Altinn.Profile.Core.Unit.ContactPoints; + +namespace Altinn.Profile.Core.User.ContactPoints; + +/// +/// Class describing the methods required for user contact point service +/// +public interface IUnitContactPoints +{ + /// + /// Method for retriveing user registered contact points for a unit + /// + /// A lookup object containing a list of organisation numbers and the resource to lookup contact points for + /// The users' contact points and reservation status or a boolean if failure. + Task> GetUserRegisteredContactPoints(UnitContactPointLookup lookup); +} diff --git a/src/Altinn.Profile.Core/Unit.ContactPoints/UnitContactPointLookup.cs b/src/Altinn.Profile.Core/Unit.ContactPoints/UnitContactPointLookup.cs new file mode 100644 index 0000000..4ca1d51 --- /dev/null +++ b/src/Altinn.Profile.Core/Unit.ContactPoints/UnitContactPointLookup.cs @@ -0,0 +1,18 @@ +namespace Altinn.Profile.Core.Unit.ContactPoints +{ + /// + /// A class describing the query model for contact points for units + /// + public class UnitContactPointLookup + { + /// + /// Gets or sets the list of organisation numbers to lookup contact points for + /// + public List OrganizationNumbers { get; set; } = []; + + /// + /// Gets or sets the resource id to filter the contact points by + /// + public string ResourceId { get; set; } = string.Empty; + } +} diff --git a/src/Altinn.Profile.Core/Unit.ContactPoints/UnitContactPointService.cs b/src/Altinn.Profile.Core/Unit.ContactPoints/UnitContactPointService.cs new file mode 100644 index 0000000..a16ca10 --- /dev/null +++ b/src/Altinn.Profile.Core/Unit.ContactPoints/UnitContactPointService.cs @@ -0,0 +1,28 @@ +using Altinn.Profile.Core.Integrations; +using Altinn.Profile.Core.User.ContactPoints; + +namespace Altinn.Profile.Core.Unit.ContactPoints +{ + /// + /// Implementation of the interface using a REST client to retrieve profile data "/> + /// + public class UnitContactPointService : IUnitContactPoints + { + private readonly IUnitProfileClient _unitClient; + + /// + /// Initializes a new instance of the class. + /// + public UnitContactPointService(IUnitProfileClient unitClient) + { + _unitClient = unitClient; + } + + /// + public async Task> GetUserRegisteredContactPoints(UnitContactPointLookup lookup) + { + Result result = await _unitClient.GetUserRegisteredContactPoints(lookup); + return result; + } + } +} diff --git a/src/Altinn.Profile.Core/Unit.ContactPoints/UnitContactPoints.cs b/src/Altinn.Profile.Core/Unit.ContactPoints/UnitContactPoints.cs new file mode 100644 index 0000000..9233d25 --- /dev/null +++ b/src/Altinn.Profile.Core/Unit.ContactPoints/UnitContactPoints.cs @@ -0,0 +1,36 @@ +using Altinn.Profile.Core.User.ContactPoints; + +namespace Altinn.Profile.Core.Unit.ContactPoints +{ + /// + /// Class describing the user registered contact points for a unit + /// + public class UnitContactPoints + { + /// + /// Gets or sets the organization number of the organization. + /// + public string OrganizationNumber { get; set; } = string.Empty; + + /// + /// Gets or sets the party id of the organization. + /// + public int PartyId { get; set; } + + /// + /// Gets or sets a list of multiple contanct points associated with the organisation. + /// + public List UserContactPoints { get; set; } = []; + } + + /// + /// A list representation of + /// + public class UnitContactPointsList + { + /// + /// A list containing contact points for users + /// + public List ContactPointsList { get; set; } = []; + } +} diff --git a/src/Altinn.Profile.Core/User.ContactPoints/UserContactPoints.cs b/src/Altinn.Profile.Core/User.ContactPoints/UserContactPoints.cs index e7e335c..f705bfe 100644 --- a/src/Altinn.Profile.Core/User.ContactPoints/UserContactPoints.cs +++ b/src/Altinn.Profile.Core/User.ContactPoints/UserContactPoints.cs @@ -1,7 +1,7 @@ namespace Altinn.Profile.Core.User.ContactPoints; /// -/// Class describing the availability of contact points for a user +/// Class describing the contact points for a user /// public class UserContactPoints { diff --git a/src/Altinn.Profile.Integrations/SblBridge/PartyNotificationContactPoints.cs b/src/Altinn.Profile.Integrations/SblBridge/PartyNotificationContactPoints.cs new file mode 100644 index 0000000..a74643e --- /dev/null +++ b/src/Altinn.Profile.Integrations/SblBridge/PartyNotificationContactPoints.cs @@ -0,0 +1,75 @@ +using Altinn.Profile.Core.Unit.ContactPoints; +using Altinn.Profile.Core.User.ContactPoints; + +namespace Altinn.Profile.Integrations.SblBridge +{ + /// + /// Model describing a container for a list of contact points. + /// + public class PartyNotificationContactPoints + { + /// + /// Gets or sets the party id of the organisation. + /// + public Guid? PartyId { get; set; } + + /// + /// Gets or sets the legacy id of the organisation. + /// + public int LegacyPartyId { get; set; } + + /// + /// Gets or sets the organization number of the organisation. + /// + public string OrganizationNumber { get; set; } = string.Empty; + + /// + /// Gets or sets a list of multiple contanct points associated with the organisation. + /// + public List ContactPoints { get; set; } = new List(); + + /// + /// Maps a list of to a list of . + /// + public static UnitContactPointsList MapToUnitContactPoints(List source) + { + List contactPoints = source.Select(partyNotificationEndpoint => new UnitContactPoints + { + OrganizationNumber = partyNotificationEndpoint.OrganizationNumber, + PartyId = partyNotificationEndpoint.LegacyPartyId, + UserContactPoints = partyNotificationEndpoint.ContactPoints.Select(contactPoint => new UserContactPoints + { + UserId = contactPoint.LegacyUserId, + Email = contactPoint.Email, + MobileNumber = contactPoint.MobileNumber + }).ToList() + }).ToList(); + + return new UnitContactPointsList() { ContactPointsList = contactPoints }; + } + } + + /// + /// Model describing the contact information that a user has associated with a party they can represent. + /// + public class UserRegisteredContactPoint + { + /// + /// Gets or sets the legacy user id for the owner of this party notification endpoint. + /// + /// + /// This was named as legacy for consistency. Property for UUID will probably never be added. + /// + public int LegacyUserId { get; set; } + + /// + /// Gets or sets the email address for this contact point. + /// + public string Email { get; set; } = string.Empty; + + /// + /// Gets or sets the mobile number for this contact point. + /// + public string MobileNumber { get; set; } = string.Empty; + } +} diff --git a/src/Altinn.Profile.Integrations/SblBridge/UnitProfileClient.cs b/src/Altinn.Profile.Integrations/SblBridge/UnitProfileClient.cs new file mode 100644 index 0000000..a8d77cb --- /dev/null +++ b/src/Altinn.Profile.Integrations/SblBridge/UnitProfileClient.cs @@ -0,0 +1,70 @@ +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +using Altinn.Profile.Core; +using Altinn.Profile.Core.Integrations; +using Altinn.Profile.Core.Unit.ContactPoints; + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Altinn.Profile.Integrations.SblBridge; + +/// +/// Represents an implementation of using SBLBridge to obtain unit profile information. +/// +public class UnitProfileClient : IUnitProfileClient +{ + private readonly ILogger _logger; + private readonly HttpClient _client; + private readonly JsonSerializerOptions _serializerOptions; + + /// + /// Initializes a new instance of the class + /// + /// HttpClient from default http client factory + /// the logger + /// the sbl bridge settings + public UnitProfileClient( + HttpClient httpClient, + ILogger logger, + IOptions settings) + { + _logger = logger; + _client = httpClient; + _client.BaseAddress = new Uri(settings.Value.ApiProfileEndpoint); + + _serializerOptions = new JsonSerializerOptions + { + WriteIndented = true, + PropertyNameCaseInsensitive = true, + Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) } + }; + } + + /// + public async Task> GetUserRegisteredContactPoints(UnitContactPointLookup lookup) + { + string endpoint = $"units/contactpointslookup"; + + StringContent requestBody = new StringContent(JsonSerializer.Serialize(lookup), Encoding.UTF8, "application/json"); + + HttpResponseMessage response = await _client.PostAsync(endpoint, requestBody); + + if (!response.IsSuccessStatusCode) + { + _logger.LogError( + "// UnitClient // GetUserRegisteredContactPoints // Unexpected response. Failed with {StatusCode} and message {Message}", + response.StatusCode, + await response.Content.ReadAsStringAsync()); + + return false; + } + + string content = await response.Content.ReadAsStringAsync(); + List partyNotificationEndpoints = JsonSerializer.Deserialize>(content, _serializerOptions)!; + + return PartyNotificationContactPoints.MapToUnitContactPoints(partyNotificationEndpoints); + } +} diff --git a/src/Altinn.Profile.Integrations/ServiceCollectionExtensions.cs b/src/Altinn.Profile.Integrations/ServiceCollectionExtensions.cs index f1ee636..703128c 100644 --- a/src/Altinn.Profile.Integrations/ServiceCollectionExtensions.cs +++ b/src/Altinn.Profile.Integrations/ServiceCollectionExtensions.cs @@ -22,8 +22,8 @@ public static void AddSblBridgeClients(this IServiceCollection services, IConfig .Get() ?? throw new ArgumentNullException(nameof(config), "Required SblBridgeSettings is missing from application configuration"); - services - .Configure(config.GetSection(nameof(SblBridgeSettings))) - .AddHttpClient(); + services.Configure(config.GetSection(nameof(SblBridgeSettings))); + services.AddHttpClient(); + services.AddHttpClient(); } } diff --git a/src/Altinn.Profile/Controllers/UnitContactPointController.cs b/src/Altinn.Profile/Controllers/UnitContactPointController.cs new file mode 100644 index 0000000..59887a2 --- /dev/null +++ b/src/Altinn.Profile/Controllers/UnitContactPointController.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; + +using Altinn.Profile.Core; +using Altinn.Profile.Core.Unit.ContactPoints; +using Altinn.Profile.Core.User.ContactPoints; + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Altinn.Profile.Controllers +{ + /// + /// Controller for unit profile contact point API endpoints for internal consumption (e.g. Notifications) requiring neither authenticated user token nor access token authorization. + /// + [Route("profile/api/v1/units/contactpoint")] + [ApiExplorerSettings(IgnoreApi = true)] + [Consumes("application/json")] + [Produces("application/json")] + public class UnitContactPointController : ControllerBase + { + private readonly IUnitContactPoints _contactPointService; + + /// + /// Initializes a new instance of the class. + /// + public UnitContactPointController(IUnitContactPoints contactPointsService) + { + _contactPointService = contactPointsService; + } + + /// + /// Endpoint looking up the contact points for the units provided in the lookup object in the request body + /// + /// Returns an overview of the user registered contact points for the provided units and given resource + [HttpPost("lookup")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> PostLookup([FromBody] UnitContactPointLookup unitContactPointLookup) + { + Result result = await _contactPointService.GetUserRegisteredContactPoints(unitContactPointLookup); + return result.Match>( + success => Ok(success), + _ => Problem("Could not retrieve contact points")); + } + } +} diff --git a/src/Altinn.Profile/Controllers/UserContactPointController.cs b/src/Altinn.Profile/Controllers/UserContactPointController.cs index c11722f..d45d328 100644 --- a/src/Altinn.Profile/Controllers/UserContactPointController.cs +++ b/src/Altinn.Profile/Controllers/UserContactPointController.cs @@ -2,6 +2,7 @@ using Altinn.Profile.Core; using Altinn.Profile.Core.User.ContactPoints; +using Altinn.Profile.Models; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/src/Altinn.Profile.Core/User.ContactPoints/UserContactPointLookup.cs b/src/Altinn.Profile/Models/UserContactPointLookup.cs similarity index 82% rename from src/Altinn.Profile.Core/User.ContactPoints/UserContactPointLookup.cs rename to src/Altinn.Profile/Models/UserContactPointLookup.cs index f3db92f..1451e30 100644 --- a/src/Altinn.Profile.Core/User.ContactPoints/UserContactPointLookup.cs +++ b/src/Altinn.Profile/Models/UserContactPointLookup.cs @@ -1,4 +1,6 @@ -namespace Altinn.Profile.Core.User.ContactPoints; +using System.Collections.Generic; + +namespace Altinn.Profile.Models; /// /// A class respresenting a user contact point lookup object diff --git a/src/Altinn.Profile/appsettings.json b/src/Altinn.Profile/appsettings.json index 2eb678f..d2b678e 100644 --- a/src/Altinn.Profile/appsettings.json +++ b/src/Altinn.Profile/appsettings.json @@ -4,7 +4,7 @@ "JwtCookieName": "AltinnStudioRuntime" }, "SblBridgeSettings": { - "ApiProfileEndpoint": "https://at22.altinn.cloud/sblbridge/profile/api/" + "ApiProfileEndpoint": "https://at24.altinn.cloud/sblbridge/profile/api/" }, "CoreSettings": { "ProfileCacheLifetimeSeconds": 600 diff --git a/test/Altinn.Profile.Tests/IntegrationTests/API/Controllers/UnitContactPointControllerTests.cs b/test/Altinn.Profile.Tests/IntegrationTests/API/Controllers/UnitContactPointControllerTests.cs new file mode 100644 index 0000000..fa664c5 --- /dev/null +++ b/test/Altinn.Profile.Tests/IntegrationTests/API/Controllers/UnitContactPointControllerTests.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Net.Http.Json; +using System.Text.Json; +using System.Threading.Tasks; + +using Altinn.Profile.Controllers; +using Altinn.Profile.Core.Unit.ContactPoints; +using Altinn.Profile.Integrations.SblBridge; +using Altinn.Profile.Tests.IntegrationTests.Mocks; +using Altinn.Profile.Tests.IntegrationTests.Utils; + +using Microsoft.AspNetCore.Mvc.Testing; + +using Xunit; + +namespace Altinn.Profile.Tests.IntegrationTests.API.Controllers +{ + public class UnitContactPointControllerTests : IClassFixture> + { + private readonly WebApplicationFactorySetup _webApplicationFactorySetup; + + private readonly JsonSerializerOptions _serializerOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true + }; + + public UnitContactPointControllerTests(WebApplicationFactory factory) + { + _webApplicationFactorySetup = new WebApplicationFactorySetup(factory); + + _webApplicationFactorySetup.SblBridgeHttpMessageHandler = new DelegatingHandlerStub(async (request, token) => + { + string requestString = await request.Content.ReadAsStringAsync(token); + UnitContactPointLookup lookup = JsonSerializer.Deserialize(requestString, _serializerOptions); + return GetSBlResponseFromSBL(lookup.OrganizationNumbers[0]); + }); + + SblBridgeSettings sblBrideSettings = new() { ApiProfileEndpoint = "http://localhost/" }; + _webApplicationFactorySetup.SblBridgeSettingsOptions.Setup(s => s.Value).Returns(sblBrideSettings); + } + + [Fact] + public async Task PostLookup_SuccessResult_ReturnsOk() + { + // Arrange + UnitContactPointLookup input = new() + { + OrganizationNumbers = ["123456789"], + ResourceId = "app_ttd_apps-test" + }; + + HttpClient client = _webApplicationFactorySetup.GetTestServerClient(); + HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, "/profile/api/v1/units/contactpoint/lookup") + { + Content = new StringContent(JsonSerializer.Serialize(input, _serializerOptions), System.Text.Encoding.UTF8, "application/json") + }; + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + string responseContent = await response.Content.ReadAsStringAsync(); + var actual = JsonSerializer.Deserialize(responseContent, _serializerOptions); + Assert.Single(actual.ContactPointsList); + } + + [Fact] + public async Task PostLookup_ErrorResult_ReturnsProblemDetails() + { + // Arrange + UnitContactPointLookup input = new() + { + OrganizationNumbers = ["error-org"], + ResourceId = "app_ttd_apps-test" + }; + + HttpClient client = _webApplicationFactorySetup.GetTestServerClient(); + HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, "/profile/api/v1/units/contactpoint/lookup") + { + Content = new StringContent(JsonSerializer.Serialize(input, _serializerOptions), System.Text.Encoding.UTF8, "application/json") + }; + + // Act + HttpResponseMessage response = await client.SendAsync(httpRequestMessage); + + // Assert + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + } + + private HttpResponseMessage GetSBlResponseFromSBL(string orgNo) + { + switch (orgNo) + { + case "123456789": + var input = new List() + { + new PartyNotificationContactPoints() + { + ContactPoints = [new UserRegisteredContactPoint() + { + LegacyUserId = 20001, + Email = "user@email.com" + } + ], + LegacyPartyId = 50001, + OrganizationNumber = "123456789", + PartyId = Guid.NewGuid() + } + }; + + return new HttpResponseMessage() { Content = JsonContent.Create(input, options: _serializerOptions), StatusCode = HttpStatusCode.OK }; + default: + return new HttpResponseMessage() { StatusCode = HttpStatusCode.ServiceUnavailable }; + } + } + } +} diff --git a/test/Altinn.Profile.Tests/IntegrationTests/API/Controllers/UserContactPointControllerTests.cs b/test/Altinn.Profile.Tests/IntegrationTests/API/Controllers/UserContactPointControllerTests.cs index beea3ee..4fd4299 100644 --- a/test/Altinn.Profile.Tests/IntegrationTests/API/Controllers/UserContactPointControllerTests.cs +++ b/test/Altinn.Profile.Tests/IntegrationTests/API/Controllers/UserContactPointControllerTests.cs @@ -9,6 +9,7 @@ using Altinn.Profile.Controllers; using Altinn.Profile.Core.User.ContactPoints; using Altinn.Profile.Integrations.SblBridge; +using Altinn.Profile.Models; using Altinn.Profile.Tests.IntegrationTests.Mocks; using Altinn.Profile.Tests.IntegrationTests.Utils; using Altinn.Profile.Tests.Testdata; diff --git a/test/Altinn.Profile.Tests/IntegrationTests/Utils/WebApplicationFactorySetup.cs b/test/Altinn.Profile.Tests/IntegrationTests/Utils/WebApplicationFactorySetup.cs index cb8cd38..d437dd2 100644 --- a/test/Altinn.Profile.Tests/IntegrationTests/Utils/WebApplicationFactorySetup.cs +++ b/test/Altinn.Profile.Tests/IntegrationTests/Utils/WebApplicationFactorySetup.cs @@ -30,6 +30,8 @@ public WebApplicationFactorySetup(WebApplicationFactory webApplicationFactory public Mock> UserProfileClientLogger { get; set; } = new(); + public Mock> UnitProfileClientLogger { get; set; } = new(); + public Mock> SblBridgeSettingsOptions { get; set; } = new(); public HttpMessageHandler SblBridgeHttpMessageHandler { get; set; } = new DelegatingHandlerStub(); @@ -59,6 +61,12 @@ public HttpClient GetTestServerClient() new HttpClient(SblBridgeHttpMessageHandler), UserProfileClientLogger.Object, SblBridgeSettingsOptions.Object)); + + services.AddSingleton( + new UnitProfileClient( + new HttpClient(SblBridgeHttpMessageHandler), + UnitProfileClientLogger.Object, + SblBridgeSettingsOptions.Object)); }); }).CreateClient(); } diff --git a/test/Altinn.Profile.Tests/Core/User/UserProfileCachingDecoratorTest.cs b/test/Altinn.Profile.Tests/Profile.Core/User/UserProfileCachingDecoratorTest.cs similarity index 100% rename from test/Altinn.Profile.Tests/Core/User/UserProfileCachingDecoratorTest.cs rename to test/Altinn.Profile.Tests/Profile.Core/User/UserProfileCachingDecoratorTest.cs diff --git a/test/Altinn.Profile.Tests/Profile.Integrations/SblBridge/PartyNotificationContactPointsTests.cs b/test/Altinn.Profile.Tests/Profile.Integrations/SblBridge/PartyNotificationContactPointsTests.cs new file mode 100644 index 0000000..e1216b1 --- /dev/null +++ b/test/Altinn.Profile.Tests/Profile.Integrations/SblBridge/PartyNotificationContactPointsTests.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; + +using Altinn.Profile.Core.Unit.ContactPoints; +using Altinn.Profile.Core.User.ContactPoints; +using Altinn.Profile.Integrations.SblBridge; + +using Xunit; + +namespace Altinn.Profile.Tests.Profile.Integrations.SblBridge +{ + public class PartyNotificationContactPointsTests + { + [Fact] + public void MapToUnitContactPoints_EmptyListProvided_EmptyListReturned() + { + // Act + UnitContactPointsList actual = PartyNotificationContactPoints.MapToUnitContactPoints([]); + + // Assert + Assert.Empty(actual.ContactPointsList); + } + + [Fact] + public void MapToUnitContactPoints_LegacyPartyIdMappedToPartyId() + { + // Arrange + List input = new List + { + new PartyNotificationContactPoints + { + PartyId = Guid.NewGuid(), + LegacyPartyId = 512345, + OrganizationNumber = "123456789", + ContactPoints = new List + { + new UserRegisteredContactPoint + { + LegacyUserId = 212345, + Email = "user@domain.com", + MobileNumber = "12345678" + } + } + } + }; + + UnitContactPointsList expected = new() + { + ContactPointsList = [new UnitContactPoints() + { + OrganizationNumber = "123456789", + PartyId = 512345, + UserContactPoints = [ + new UserContactPoints() + { + UserId = 212345, + Email = "user@domain.com", + MobileNumber = "12345678", + IsReserved = false, + NationalIdentityNumber = string.Empty + } + ] + } + ] + }; + + // Act + var actual = PartyNotificationContactPoints.MapToUnitContactPoints(input); + + // Assert + Assert.Equivalent(expected.ContactPointsList, actual.ContactPointsList); + } + } +} diff --git a/test/Altinn.Profile.Tests/Profile.Integrations/SblBridge/UnitProfileClientTest.cs b/test/Altinn.Profile.Tests/Profile.Integrations/SblBridge/UnitProfileClientTest.cs new file mode 100644 index 0000000..40a9d27 --- /dev/null +++ b/test/Altinn.Profile.Tests/Profile.Integrations/SblBridge/UnitProfileClientTest.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading.Tasks; + +using Altinn.Profile.Core; +using Altinn.Profile.Core.Unit.ContactPoints; +using Altinn.Profile.Integrations.SblBridge; +using Altinn.Profile.Tests.IntegrationTests.Mocks; + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +using Moq; + +using Xunit; + +namespace Altinn.Profile.Tests.Profile.Integrations.SblBridge +{ + public class UnitProfileClientTest + { + [Fact] + public async Task GetUserRegisteredContactPoints_BridgeRespondsWithOk_UnitContactPointsListReturned() + { + // Arrange + UnitContactPointLookup lookup = new() + { + OrganizationNumbers = ["123456789"], + ResourceId = "app_ttd_apps-test" + }; + + var sblBridgeHttpMessageHandler = new DelegatingHandlerStub(async (request, token) => + { + if (request!.RequestUri!.AbsolutePath.EndsWith("units/contactpointslookup")) + { + var contentData = new List(); + JsonContent content = JsonContent.Create(contentData); + + return await Task.FromResult(new HttpResponseMessage() + { + StatusCode = HttpStatusCode.OK, + Content = content + }); + } + + return new HttpResponseMessage(HttpStatusCode.NotFound); + }); + + SblBridgeSettings settings = new() + { + ApiProfileEndpoint = "https://platform.at22.altinn.cloud/profile/api/v1/" + }; + + var sut = new UnitProfileClient( + new HttpClient(sblBridgeHttpMessageHandler), + Mock.Of>(), + Options.Create(settings)); + + // Act + Result result = await sut.GetUserRegisteredContactPoints(lookup); + + // Assert + result.Match( + success => + { + Assert.IsType(success); + }, + error => throw new Exception("No error value should be returned if SBL client respons with 200 OK.")) +; + } + + [Fact] + public async Task GetUserRegisteredContactPoints_BridgeRespondsWithServiceUnavailable_ErrorReturned_UnitContactPointsListReturned() + { + // Arrange + UnitContactPointLookup lookup = new() + { + OrganizationNumbers = ["123456789"], + ResourceId = "app_ttd_apps-test" + }; + + var sblBridgeHttpMessageHandler = new DelegatingHandlerStub((request, token) => + { + if (request!.RequestUri!.AbsolutePath.EndsWith("units/contactpointslookup")) + { + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.ServiceUnavailable)); + } + + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound)); + }); + + SblBridgeSettings settings = new() + { + ApiProfileEndpoint = "https://platform.at22.altinn.cloud/profile/api/v1/" + }; + + var sut = new UnitProfileClient( + new HttpClient(sblBridgeHttpMessageHandler), + Mock.Of>(), + Options.Create(settings)); + + // Act + Result result = await sut.GetUserRegisteredContactPoints(lookup); + + // Assert + result.Match( + success => throw new Exception("No success value should be returned if SBL client respons with 5xx."), + error => + { + Assert.IsType(error); + Assert.False(error); + }) +; + } + } +}