Skip to content

Commit

Permalink
Implement Access Token creation logic (#690)
Browse files Browse the repository at this point in the history
* Install the Altinn.Common.AccessTokenClient package

* Update Confluent.Kafka package

* Fix an issue when retrieving data from the Register API.

* Mock the IAccessTokenGenerator to use with unit tests

* Add a new unit test to check the null response.

* Cover two more conditions

* Add a unit test to ensures that the GetPartyDetails method correctly handles the scenario where the access token is null or an empty string.

* Add a test to ensure that the GetPartyDetails method correctly adds the PlatformAccessToken header when the access token is not empty,

* Mock the JSON deserialization behavior

* Code refactoring.
Make the GetPartyDetails usable for only one set of registeration numbers.

* Implement a new test to ensure that the RegisterClient constructor correctly handles the scenario where the HttpClient parameter is null

* Remove the implementation of IHttpContextAccessor

* Code refactoring

* Update the GetPartyDetails method to make sure it handles invalid JSON responses gracefully by returning an empty list.

* Remove a try catch block

* Remove an extra async keyword

* Remove a test that is already covered by the logic in the RegisterClient class.

* Create a unit test that ensures the deserialization of PartyDetailsLookupResult returns null.

* Update docs

* Improve code readability and remove one unit test

* Code refactoring

* Remove a redundant test.

* Remove unused case.

* Fix a bug introduced in the previous commit.

* Test retrieving data for organizations and persons using one request.

* Remove a redundant test.

* Change the name notification to notifications

* Remove an unused using statement.

* Remove the default values null
  • Loading branch information
Ahmed-Ghanam authored Jan 14, 2025
1 parent 440d8d2 commit 93f9375
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ public interface IRegisterClient
/// A task that represents the asynchronous operation.
/// The task result contains a list of <see cref="PartyDetails"/> representing the details of the specified individuals and organizations.
/// </returns>
Task<List<PartyDetails>> GetPartyDetails(List<string> organizationNumbers, List<string> socialSecurityNumbers);
Task<List<PartyDetails>> GetPartyDetails(List<string>? organizationNumbers, List<string>? socialSecurityNumbers);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ public interface IKeywordsService
/// <summary>
/// Replaces placeholder keywords in a collection of <see cref="SmsRecipient"/> with actual values.
/// </summary>
/// <param name="smsRecipients">The collection of <see cref="SmsRecipient"/> to process.</param>
/// <param name="recipients">The collection of <see cref="SmsRecipient"/> to process.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the collection of <see cref="SmsRecipient"/> with the placeholder keywords replaced by actual values.</returns>
Task<IEnumerable<SmsRecipient>> ReplaceKeywordsAsync(IEnumerable<SmsRecipient> smsRecipients);
Task<IEnumerable<SmsRecipient>> ReplaceKeywordsAsync(IEnumerable<SmsRecipient> recipients);

/// <summary>
/// Replaces placeholder keywords in a collection of <see cref="EmailRecipient"/> with actual values.
/// </summary>
/// <param name="emailRecipients">The collection of <see cref="EmailRecipient"/> to process.</param>
/// <param name="recipients">The collection of <see cref="EmailRecipient"/> to process.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the collection of <see cref="EmailRecipient"/> with the placeholder keywords replaced by actual values.</returns>
Task<IEnumerable<EmailRecipient>> ReplaceKeywordsAsync(IEnumerable<EmailRecipient> emailRecipients);
Task<IEnumerable<EmailRecipient>> ReplaceKeywordsAsync(IEnumerable<EmailRecipient> recipients);
}
26 changes: 13 additions & 13 deletions src/Altinn.Notifications.Core/Services/KeywordsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,53 +33,53 @@ public bool ContainsRecipientNumberPlaceholder(string? value) =>
!string.IsNullOrWhiteSpace(value) && value.Contains(_recipientNumberPlaceholder);

/// <inheritdoc/>
public async Task<IEnumerable<SmsRecipient>> ReplaceKeywordsAsync(IEnumerable<SmsRecipient> smsRecipients)
public async Task<IEnumerable<SmsRecipient>> ReplaceKeywordsAsync(IEnumerable<SmsRecipient> recipients)
{
ArgumentNullException.ThrowIfNull(smsRecipients);
ArgumentNullException.ThrowIfNull(recipients);

var organizationNumbers = smsRecipients
var organizationNumbers = recipients
.Where(e => !string.IsNullOrWhiteSpace(e.CustomizedBody))
.Where(e => !string.IsNullOrWhiteSpace(e.OrganizationNumber))
.Select(e => e.OrganizationNumber!)
.ToList();

var nationalIdentityNumbers = smsRecipients
var nationalIdentityNumbers = recipients
.Where(e => !string.IsNullOrWhiteSpace(e.CustomizedBody))
.Where(e => !string.IsNullOrWhiteSpace(e.NationalIdentityNumber))
.Select(e => e.NationalIdentityNumber!)
.ToList();

var (personDetails, organizationDetails) = await FetchPartyDetailsAsync(organizationNumbers, nationalIdentityNumbers);

foreach (var smsRecipient in smsRecipients)
foreach (var smsRecipient in recipients)
{
smsRecipient.CustomizedBody =
ReplacePlaceholders(smsRecipient.CustomizedBody, smsRecipient.OrganizationNumber, smsRecipient.NationalIdentityNumber, organizationDetails, personDetails);
}

return smsRecipients;
return recipients;
}

/// <inheritdoc/>
public async Task<IEnumerable<EmailRecipient>> ReplaceKeywordsAsync(IEnumerable<EmailRecipient> emailRecipients)
public async Task<IEnumerable<EmailRecipient>> ReplaceKeywordsAsync(IEnumerable<EmailRecipient> recipients)
{
ArgumentNullException.ThrowIfNull(emailRecipients);
ArgumentNullException.ThrowIfNull(recipients);

var organizationNumbers = emailRecipients
var organizationNumbers = recipients
.Where(e => !string.IsNullOrWhiteSpace(e.OrganizationNumber))
.Where(e => !string.IsNullOrWhiteSpace(e.CustomizedBody) || !string.IsNullOrWhiteSpace(e.CustomizedSubject))
.Select(e => e.OrganizationNumber!)
.ToList();

var nationalIdentityNumbers = emailRecipients
var nationalIdentityNumbers = recipients
.Where(e => !string.IsNullOrWhiteSpace(e.NationalIdentityNumber))
.Where(e => !string.IsNullOrWhiteSpace(e.CustomizedBody) || !string.IsNullOrWhiteSpace(e.CustomizedSubject))
.Select(e => e.NationalIdentityNumber!)
.ToList();

var (personDetails, organizationDetails) = await FetchPartyDetailsAsync(organizationNumbers, nationalIdentityNumbers);

foreach (var emailRecipient in emailRecipients)
foreach (var emailRecipient in recipients)
{
emailRecipient.CustomizedBody =
ReplacePlaceholders(emailRecipient.CustomizedBody, emailRecipient.OrganizationNumber, emailRecipient.NationalIdentityNumber, organizationDetails, personDetails);
Expand All @@ -88,7 +88,7 @@ public async Task<IEnumerable<EmailRecipient>> ReplaceKeywordsAsync(IEnumerable<
ReplacePlaceholders(emailRecipient.CustomizedSubject, emailRecipient.OrganizationNumber, emailRecipient.NationalIdentityNumber, organizationDetails, personDetails);
}

return emailRecipients;
return recipients;
}

/// <summary>
Expand All @@ -102,7 +102,7 @@ public async Task<IEnumerable<EmailRecipient>> ReplaceKeywordsAsync(IEnumerable<
List<string> organizationNumbers,
List<string> nationalIdentityNumbers)
{
var partyDetails = await _registerClient.GetPartyDetails(nationalIdentityNumbers, organizationNumbers);
var partyDetails = await _registerClient.GetPartyDetails(organizationNumbers, nationalIdentityNumbers);

var organizationDetails = partyDetails
.Where(e => !string.IsNullOrWhiteSpace(e.OrganizationNumber) && organizationNumbers.Contains(e.OrganizationNumber))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@

<ItemGroup>
<PackageReference Include="Altinn.ApiClients.Maskinporten" Version="9.2.1" />
<PackageReference Include="Altinn.Common.AccessTokenClient" Version="3.0.11" />
<PackageReference Include="Altinn.Common.PEP" Version="4.1.2" />
<PackageReference Include="Confluent.Kafka" Version="2.6.1" />
<PackageReference Include="Confluent.Kafka" Version="2.8.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Altinn.ApiClients.Maskinporten.Extensions;
using Altinn.ApiClients.Maskinporten.Services;
using Altinn.Common.AccessTokenClient.Services;
using Altinn.Common.PEP.Clients;
using Altinn.Common.PEP.Implementation;
using Altinn.Common.PEP.Interfaces;
Expand Down Expand Up @@ -70,14 +71,15 @@ public static void AddAuthorizationService(this IServiceCollection services, ICo
services.Configure<Common.PEP.Configuration.PlatformSettings>(config.GetSection("PlatformSettings"));
services.AddHttpClient<AuthorizationApiClient>();
services.AddSingleton<IPDP, PDPAppSI>();
services.AddTransient<IAccessTokenGenerator, AccessTokenGenerator>();
services.AddSingleton<IAuthorizationService, AuthorizationService>();

services.AddMaskinportenHttpClient<SettingsJwkClientDefinition, IConditionClient, SendConditionClient>(
config.GetSection("SendConditionClient:MaskinportenSettings"));
}

/// <summary>
/// Adds kafka health checks
/// Adds Kafka health checks
/// </summary>
/// <param name="services">service collection.</param>
/// <param name="config">the configuration collection</param>
Expand Down
31 changes: 23 additions & 8 deletions src/Altinn.Notifications.Integrations/Register/RegisterClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Text;
using System.Text.Json;

using Altinn.Common.AccessTokenClient.Services;
using Altinn.Notifications.Core.Integrations;
using Altinn.Notifications.Core.Models.ContactPoints;
using Altinn.Notifications.Core.Models.Parties;
Expand All @@ -13,7 +14,7 @@
namespace Altinn.Notifications.Integrations.Register;

/// <summary>
/// Implementation of the <see cref="IRegisterClient"/> to retrieve information for organizations and individuals.
/// A client implementation of <see cref="IRegisterClient"/> for retrieving information about organizations and individuals.
/// </summary>
public class RegisterClient : IRegisterClient
{
Expand All @@ -22,15 +23,21 @@ public class RegisterClient : IRegisterClient
private readonly string _contactPointLookupEndpoint = "organizations/contactpoint/lookup";
private readonly string _nameComponentsLookupEndpoint = "parties/nameslookup";

private readonly IAccessTokenGenerator _accessTokenGenerator;

/// <summary>
/// Initializes a new instance of the <see cref="RegisterClient"/> class.
/// </summary>
/// <param name="client">The HTTP client used to make requests to the register service.</param>
/// <param name="settings">The platform settings containing the API endpoints.</param>
public RegisterClient(HttpClient client, IOptions<PlatformSettings> settings)
/// <param name="accessTokenGenerator">The access token generator.</param>
public RegisterClient(HttpClient client, IOptions<PlatformSettings> settings, IAccessTokenGenerator accessTokenGenerator)
{
_client = client;
_client.BaseAddress = new Uri(settings.Value.ApiRegisterEndpoint);

_accessTokenGenerator = accessTokenGenerator;

_jsonSerializerOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
}

Expand Down Expand Up @@ -62,27 +69,35 @@ public async Task<List<OrganizationContactPoints>> GetOrganizationContactPoints(
}

/// <inheritdoc/>
public async Task<List<PartyDetails>> GetPartyDetails(List<string> organizationNumbers, List<string> socialSecurityNumbers)
public async Task<List<PartyDetails>> GetPartyDetails(List<string>? organizationNumbers, List<string>? socialSecurityNumbers)
{
if ((organizationNumbers?.Count ?? 0) == 0 && (socialSecurityNumbers?.Count ?? 0) == 0)
{
return [];
}

var partyDetailsLookupBatch = new PartyDetailsLookupBatch(organizationNumbers, socialSecurityNumbers);
var content = CreateJsonContent(partyDetailsLookupBatch);
HttpRequestMessage requestMessage = new(HttpMethod.Post, _nameComponentsLookupEndpoint)
{
Content = CreateJsonContent(new PartyDetailsLookupBatch(organizationNumbers, socialSecurityNumbers))
};

var accessToken = _accessTokenGenerator.GenerateAccessToken("platform", "notifications");
if (!string.IsNullOrEmpty(accessToken))
{
requestMessage.Headers.Add("PlatformAccessToken", accessToken);
}

var response = await _client.PostAsync(_nameComponentsLookupEndpoint, content);
var response = await _client.SendAsync(requestMessage, CancellationToken.None);

if (!response.IsSuccessStatusCode)
{
throw await PlatformHttpException.CreateAsync(response);
}

var responseContent = await response.Content.ReadAsStringAsync();
var partyDetailsList = JsonSerializer.Deserialize<PartyDetailsLookupResult>(responseContent, _jsonSerializerOptions)?.PartyDetailsList;

return partyDetailsList ?? [];
var deserializeResponse = JsonSerializer.Deserialize<PartyDetailsLookupResult>(responseContent, _jsonSerializerOptions);
return deserializeResponse?.PartyDetailsList ?? [];
}

/// <summary>
Expand Down
Loading

0 comments on commit 93f9375

Please sign in to comment.