Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added endpoint and service supporting contactpoint lookup #135

Merged
merged 9 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 20 additions & 41 deletions .github/workflows/build-and-analyze.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,28 @@ name: Code test and analysis
on:
push:
branches: [ main ]
paths-ignore:
- "test/k6/**"
- ".github/**"
pull_request:
branches: [ main ]
types: [opened, synchronize, reopened]
workflow_dispatch:
jobs:
build-and-test:
name: Build and Test
build-test-analyze:
name: Build, test & analyze
if: ((github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) || github.event_name == 'push') && github.repository_owner == 'Altinn' && github.actor != 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
- name: Build & Test
run: |
dotnet build Altinn.Profile.sln -v m
dotnet test Altinn.Profile.sln -v m
analyze:
if: ((github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) || github.event_name == 'push') && github.repository_owner == 'Altinn' && github.actor != 'dependabot[bot]'
name: Analyze
runs-on: windows-latest
steps:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
- name: Set up JDK 17
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'microsoft'
distribution: 'temurin'
java-version: 17
- uses: actions/checkout@v4
with:
Expand All @@ -46,40 +34,31 @@ jobs:
path: ~\sonar\cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache SonarCloud scanner
id: cache-sonar-scanner
uses: actions/cache@v4
with:
path: .\.sonar\scanner
key: ${{ runner.os }}-sonar-scanner
restore-keys: ${{ runner.os }}-sonar-scanner
- name: Install SonarCloud scanner
if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
shell: powershell
- name: Install SonarCloud scanners
run: |
New-Item -Path .\.sonar\scanner -ItemType Directory
dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner
- name: Analyze
dotnet tool install --global dotnet-sonarscanner
- name: Build & Test
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
shell: powershell
run: |
.\.sonar\scanner\dotnet-sonarscanner begin /k:"Altinn_altinn-profile" /o:"altinn" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vstest.reportsPaths="**/*.trx" /d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml" /d:sonar.coverage.exclusions="src/**/Program.cs"
run: |
dotnet-sonarscanner begin /k:"Altinn_altinn-profile" /o:"altinn" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vstest.reportsPaths="**/*.trx" /d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml" /d:sonar.coverage.exclusions="src/**/Program.cs"

dotnet build Altinn.Profile.sln -v q

dotnet build Altinn.Profile.sln
dotnet test Altinn.Profile.sln `
--no-build `
--results-directory TestResults/ `
--collect:"XPlat Code Coverage" `
dotnet test Altinn.Profile.sln \
-v q \
--results-directory TestResults/ \
--collect:"XPlat Code Coverage" \
--configuration release \
-- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover
- name: Complete sonar analysis
if: always()
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
.\.sonar\scanner\dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"
dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
- name: Process .NET test result
if: always()
uses: NasAmin/[email protected]
Expand Down
56 changes: 56 additions & 0 deletions src/Altinn.Profile/Controllers/UserContactPointController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.Threading.Tasks;

using Altinn.Profile.Models;
using Altinn.Profile.Services.Interfaces;

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Altinn.Profile.Controllers;

/// <summary>
/// Controller for user profile contact point API endpoints for internal consumption (e.g. Notifications) requiring neither authenticated user token nor access token authorization.
/// </summary>
[Route("profile/api/v1/users/contactpoint")]
[ApiExplorerSettings(IgnoreApi = true)]
[Consumes("application/json")]
[Produces("application/json")]
public class UserContactPointController : Controller
{
private readonly IUserContactPoints _contactPointService;

/// <summary>
/// Initializes a new instance of the <see cref="UserContactPointController"/> class.
/// </summary>
public UserContactPointController(IUserContactPoints contactPointService)
{
_contactPointService = contactPointService;
}

/// <summary>
/// Endpoint looking up the availability of contact points for the provideded national identity number in the request body
/// </summary>
/// <returns>Returns an overview of the availability of various contact points for the user</returns>
[HttpPost("availability")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<UserContactPointAvailabilityList>> PostAvailabilityLookup([FromBody] UserContactPointLookup userContactPointLookup)
{
if (userContactPointLookup.NationalIdentityNumbers.Count == 0)
{
return new UserContactPointAvailabilityList();
}

return await _contactPointService.GetContactPointAvailability(userContactPointLookup.NationalIdentityNumbers);
}

/// <summary>
/// Endpoint looking up the contact points for the user connected to the provideded national identity number in the request body
/// </summary>
/// <returns>Returns an overview of the contact points for the user</returns>
[HttpPost("lookup")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<UserContactPointsList>> PostLookup([FromBody] UserContactPointLookup userContactPointLookup)
{
return await _contactPointService.GetContactPoints(userContactPointLookup.NationalIdentityNumbers);
}
}
46 changes: 46 additions & 0 deletions src/Altinn.Profile/Models/UserContactPointAvailability.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Collections.Generic;

namespace Altinn.Profile.Models
{
/// <summary>
/// Class describing the contact points of a user
/// </summary>
public class UserContactPointAvailability
{
/// <summary>
/// Gets or sets the ID of the user
/// </summary>
public int UserId { get; set; }

/// <summary>
/// Gets or sets the national identityt number of the user
/// </summary>
public string NationalIdentityNumber { get; set; }

/// <summary>
/// Gets or sets a boolean indicating whether the user has reserved themselves from electronic communication
/// </summary>
public bool IsReserved { get; set; }

/// <summary>
/// Gets or sets a boolean indicating whether the user has registered a mobile number
/// </summary>
public bool MobileNumberRegistered { get; set; }

/// <summary>
/// Gets or sets a boolean indicating whether the user has registered an email address
/// </summary>
public bool EmailRegistered { get; set; }
}

/// <summary>
/// A list representation of <see cref="UserContactPointAvailability"/>
/// </summary>
public class UserContactPointAvailabilityList
{
/// <summary>
/// A list containing contact point availabiliy for users
/// </summary>
public List<UserContactPointAvailability> AvailabilityList { get; set; } = [];
}
}
15 changes: 15 additions & 0 deletions src/Altinn.Profile/Models/UserContactPointLookup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Generic;

namespace Altinn.Profile.Models
{
/// <summary>
/// A class respresenting a user contact point lookup object
/// </summary>
public class UserContactPointLookup
{
/// <summary>
/// A list of national identity numbers to look up contact points or contact point availability for
/// </summary>
public List<string> NationalIdentityNumbers { get; set; } = [];
}
}
46 changes: 46 additions & 0 deletions src/Altinn.Profile/Models/UserContactPoints.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Collections.Generic;

namespace Altinn.Profile.Models
{
/// <summary>
/// Class describing the availability of contact points for a user
/// </summary>
public class UserContactPoints
{
/// <summary>
/// Gets or sets the ID of the user
/// </summary>
public int UserId { get; set; }

/// <summary>
/// Gets or sets the national identityt number of the user
/// </summary>
public string NationalIdentityNumber { get; set; }

/// <summary>
/// Gets or sets a boolean indicating whether the user has reserved themselves from electronic communication
/// </summary>
public bool IsReserved { get; set; }

/// <summary>
/// Gets or sets the mobile number
/// </summary>
public string MobileNumber { get; set; }

/// <summary>
/// Gets or sets the email address
/// </summary>
public string Email { get; set; }
}

/// <summary>
/// A list representation of <see cref="UserContactPoints"/>
/// </summary>
public class UserContactPointsList
{
/// <summary>
/// A list containing contact points for users
/// </summary>
public List<UserContactPoints> ContactPointList { get; set; } = [];
}
}
1 change: 1 addition & 0 deletions src/Altinn.Profile/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ void ConfigureServices(IServiceCollection services, IConfiguration config)
});

services.AddHttpClient<IUserProfiles, UserProfilesWrapper>();
services.AddSingleton<IUserContactPoints, UserContactPointService>();
services.Decorate<IUserProfiles, UserProfileCachingDecorator>();

if (!string.IsNullOrEmpty(applicationInsightsConnectionString))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Collections.Generic;
using System.Threading.Tasks;

using Altinn.Platform.Profile.Models;
using Altinn.Profile.Models;
using Altinn.Profile.Services.Interfaces;

namespace Altinn.Profile.Services.Implementation
{
/// <summary>
/// An implementation of <see cref="IUserContactPoints"/> that uses the <see cref="IUserProfiles"/> to obtain contact point information.
/// </summary>
public class UserContactPointService : IUserContactPoints
{
private readonly IUserProfiles _userProfiles;

/// <summary>
/// Initializes a new instance of the <see cref="UserContactPointService"/> class.
/// </summary>
public UserContactPointService(IUserProfiles userProfiles)
{
_userProfiles = userProfiles;
}

/// <inheritdoc/>
public async Task<UserContactPointAvailabilityList> GetContactPointAvailability(List<string> nationalIdentityNumbers)
{
UserContactPointAvailabilityList result = new();

foreach (var nationalIdentityNumber in nationalIdentityNumbers)
{
UserProfile profile = await _userProfiles.GetUser(nationalIdentityNumber);

if (profile == null)
{
continue;
}

result.AvailabilityList.Add(new UserContactPointAvailability()
{
UserId = profile.PartyId,
NationalIdentityNumber = profile.Party.SSN,
EmailRegistered = !string.IsNullOrEmpty(profile.Email),
MobileNumberRegistered = !string.IsNullOrEmpty(profile.PhoneNumber),
IsReserved = profile.IsReserved
});
}

return result;
}

/// <inheritdoc/>
public async Task<UserContactPointsList> GetContactPoints(List<string> nationalIdentityNumbers)
{
UserContactPointsList result = new();

foreach (var nationalIdentityNumber in nationalIdentityNumbers)
{
var profile = await _userProfiles.GetUser(nationalIdentityNumber);

if (profile == null)
{
continue;
}

result.ContactPointList.Add(
new UserContactPoints()
{
UserId = profile.PartyId,
NationalIdentityNumber = profile.Party.SSN,
Email = profile.Email,
MobileNumber = profile.PhoneNumber,
IsReserved = profile.IsReserved
});
}

return result;
}
}
}
27 changes: 27 additions & 0 deletions src/Altinn.Profile/Services/Interfaces/IUserContactPoints.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Threading.Tasks;

using Altinn.Profile.Models;

namespace Altinn.Profile.Services.Interfaces
{
/// <summary>
/// Class describing the methods required for user contact point service
/// </summary>
public interface IUserContactPoints
{
/// <summary>
/// Method for retriveing contact points for a user
/// </summary>
/// <param name="nationalIdentityNumbers">A list of national identity numbers to lookup contact points for</param>
/// <returns>The users' contact points and reservation status</returns>
Task<UserContactPointsList> GetContactPoints(List<string> nationalIdentityNumbers);

/// <summary>
/// Method for retriveing information about the availability of contact points for a user
/// </summary>
/// <param name="nationalIdentityNumbers">A list of national identity numbers to look up availability for</param>
/// <returns>Information on the existense of the users' contact points and reservation status</returns>
Task<UserContactPointAvailabilityList> GetContactPointAvailability(List<string> nationalIdentityNumbers);
}
}
Loading
Loading