Skip to content

Commit

Permalink
Added new internal api for fetching list of user profiles. (#130)
Browse files Browse the repository at this point in the history
* Adde bew internal api for fetching list of user profiles.

* Fixed logging to not trigger on loging user input

* Tries to fix a encoding problem in Assert comparer where strings in source code is encoded wrong on build server but correct on dev machine.

* Removed double byte characters from utf8 file to remove encoding error in assert.

* Fixed all entries of dual byte character

* Fixed code issue from an ekstra blank line

* Changed test name due to incorect description pointed out in review.
  • Loading branch information
lovoll authored Feb 2, 2024
1 parent ca6525b commit 113ee9f
Show file tree
Hide file tree
Showing 7 changed files with 370 additions and 3 deletions.
24 changes: 24 additions & 0 deletions src/Altinn.Profile/Controllers/UserProfileInternalController.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Altinn.Platform.Profile.Models;
using Altinn.Profile.Models;
Expand Down Expand Up @@ -71,5 +73,27 @@ public async Task<ActionResult<UserProfile>> Get([FromBody] UserProfileLookup us

return Ok(result);
}

/// <summary>
/// Gets a list of user profiles for a list of of users identified by userUuid.
/// </summary>
/// <param name="userUuidList">List of uuid identifying the users profiles to return</param>
/// <returns>List of user profiles</returns>
[HttpPost]
[Route("listbyuuid")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[Consumes("application/json")]
[Produces("application/json")]
public async Task<ActionResult<List<UserProfile>>> GetList([FromBody] List<Guid> userUuidList)
{
if (userUuidList == null || userUuidList.Count == 0)
{
return BadRequest();
}

List<UserProfile> result = await _userProfilesWrapper.GetUserListByUuid(userUuidList);
return Ok(result);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using Altinn.Platform.Profile.Models;
Expand Down Expand Up @@ -99,6 +100,39 @@ public async Task<UserProfile> GetUserByUuid(Guid userUuid)
return user;
}

/// <inheritdoc />
public async Task<List<UserProfile>> GetUserListByUuid(List<Guid> userUuidList)
{
List<Guid> userUuidListNotInCache = new List<Guid>();
List<UserProfile> result = new List<UserProfile>();

foreach (Guid userUuid in userUuidList)
{
string uniqueCacheKey = $"User:UserUuid:{userUuid}";
if (_memoryCache.TryGetValue(uniqueCacheKey, out UserProfile user))
{
result.Add(user);
}
else
{
userUuidListNotInCache.Add(userUuid);
}
}

if (userUuidListNotInCache.Count > 0)
{
List<UserProfile> usersToCache = await _decoratedService.GetUserListByUuid(userUuidListNotInCache);
foreach (UserProfile user in usersToCache)
{
string uniqueCacheKey = $"User:UserUuid:{user.UserUuid}";
_memoryCache.Set(uniqueCacheKey, user, _cacheOptions);
result.Add(user);
}
}

return result;
}

/// <inheritdoc/>
public async Task<UserProfile> GetUserByUsername(string username)
{
Expand Down
21 changes: 21 additions & 0 deletions src/Altinn.Profile/Services/Implementation/UserProfilesWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Text.Json;
Expand Down Expand Up @@ -107,6 +108,26 @@ public async Task<UserProfile> GetUserByUuid(Guid userUuid)
return user;
}

/// <inheritdoc />
public async Task<List<UserProfile>> GetUserListByUuid(List<Guid> userUuidList)
{
Uri endpointUrl = new Uri($"{_generalSettings.BridgeApiEndpoint}users/byuuid");
StringContent requestBody = new StringContent(JsonSerializer.Serialize(userUuidList), Encoding.UTF8, "application/json");

HttpResponseMessage response = await _client.PostAsync(endpointUrl, requestBody);

if (!response.IsSuccessStatusCode)
{
_logger.LogError("Getting users failed with {statusCode}", response.StatusCode);
return new List<UserProfile>();
}

string content = await response.Content.ReadAsStringAsync();
List<UserProfile> users = JsonSerializer.Deserialize<List<UserProfile>>(content, _serializerOptions);

return users;
}

/// <inheritdoc />
public async Task<UserProfile> GetUserByUsername(string username)
{
Expand Down
10 changes: 9 additions & 1 deletion src/Altinn.Profile/Services/Interfaces/IUserProfiles.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using Altinn.Platform.Profile.Models;
Expand Down Expand Up @@ -28,9 +29,16 @@ public interface IUserProfiles
/// Method that fetches a user based on a user uuid
/// </summary>
/// <param name="userUuid">The user uuid</param>
/// <returns>User profile with given user id.</returns>
/// <returns>User profile with given user uuid.</returns>
Task<UserProfile> GetUserByUuid(Guid userUuid);

/// <summary>
/// Method that fetches a list of users based on a list of user uuid
/// </summary>
/// <param name="userUuidList">The list of user uuids</param>
/// <returns>List of User profiles with given user uuids</returns>
Task<List<UserProfile>> GetUserListByUuid(List<Guid> userUuidList);

/// <summary>
/// Method that fetches a user based on username.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
Expand All @@ -12,7 +13,6 @@
using Altinn.Profile.Tests.IntegrationTests.Utils;
using Altinn.Profile.Tests.Mocks;
using Altinn.Profile.Tests.Testdata;

using Microsoft.AspNetCore.Mvc.Testing;

using Xunit;
Expand Down Expand Up @@ -117,6 +117,112 @@ public async Task GetUserByUuid_SblBridgeFindsProfile_ResponseOk_ReturnsUserProf
Assert.Equal("nb", actualUser.ProfileSettingPreference.Language);
}

[Fact]
public async Task GetUserListByUuid_SblBridgeFindsProfile_ResponseOk_ReturnsUserProfileList()
{
// Arrange
List<Guid> userUuids = new List<Guid> { new("cc86d2c7-1695-44b0-8e82-e633243fdf31"), new("4c3b4909-eb17-45d5-bde1-256e065e196a") };

HttpRequestMessage sblRequest = null;
DelegatingHandlerStub messageHandler = new(async (request, token) =>
{
sblRequest = request;
List<UserProfile> userProfiles = new()
{
await TestDataLoader.Load<UserProfile>(userUuids[0].ToString()),
await TestDataLoader.Load<UserProfile>(userUuids[1].ToString())
};
return new HttpResponseMessage() { Content = JsonContent.Create(userProfiles) };
});
_webApplicationFactorySetup.SblBridgeHttpMessageHandler = messageHandler;

HttpRequestMessage httpRequestMessage = CreatePostRequest($"/profile/api/v1/internal/user/listbyuuid", userUuids);

HttpClient client = _webApplicationFactorySetup.GetTestServerClient();

// Act
HttpResponseMessage response = await client.SendAsync(httpRequestMessage);

// Assert
Assert.NotNull(sblRequest);
Assert.Equal(HttpMethod.Post, sblRequest.Method);
Assert.EndsWith($"sblbridge/profile/api/users/byuuid", sblRequest.RequestUri.ToString());

string responseContent = await response.Content.ReadAsStringAsync();

List<UserProfile> actualUsers = JsonSerializer.Deserialize<List<UserProfile>>(
responseContent, serializerOptionsCamelCase);

// These asserts check that deserializing with camel casing was successful.
Assert.Equal(userUuids[0], actualUsers[0].UserUuid);
Assert.Equal("LEO WILHELMSEN", actualUsers[0].Party.Name);
Assert.Equal("LEO", actualUsers[0].Party.Person.FirstName);
Assert.Equal("nb", actualUsers[0].ProfileSettingPreference.Language);

Assert.Equal(userUuids[1], actualUsers[1].UserUuid);
Assert.Equal("ELENA FJAR", actualUsers[1].Party.Name);
Assert.Equal("ELENA", actualUsers[1].Party.Person.FirstName);
Assert.Equal("nn", actualUsers[1].ProfileSettingPreference.Language);
}

[Fact]
public async Task GetUserListByUuid_SblBridgeFindsNoProfile_ResponseOk_ReturnsEmptyProfileList()
{
// Arrange
List<Guid> userUuids = new List<Guid> { new("cc86d2c7-1695-44b0-8e82-e633243fdf31"), new("4c3b4909-eb17-45d5-bde1-256e065e196a") };

HttpRequestMessage sblRequest = null;
DelegatingHandlerStub messageHandler = new((request, token) =>
{
sblRequest = request;
List<UserProfile> userProfiles = new List<UserProfile>();
return Task.FromResult(new HttpResponseMessage() { Content = JsonContent.Create(userProfiles) });
});
_webApplicationFactorySetup.SblBridgeHttpMessageHandler = messageHandler;

HttpRequestMessage httpRequestMessage = CreatePostRequest($"/profile/api/v1/internal/user/listbyuuid", userUuids);

HttpClient client = _webApplicationFactorySetup.GetTestServerClient();

// Act
HttpResponseMessage response = await client.SendAsync(httpRequestMessage);

// Assert
Assert.NotNull(sblRequest);
Assert.Equal(HttpMethod.Post, sblRequest.Method);
Assert.EndsWith($"sblbridge/profile/api/users/byuuid", sblRequest.RequestUri.ToString());

string responseContent = await response.Content.ReadAsStringAsync();

List<UserProfile> actualUsers = JsonSerializer.Deserialize<List<UserProfile>>(
responseContent, serializerOptionsCamelCase);

// These asserts check that deserializing with camel casing was successful.
Assert.NotNull(actualUsers);
Assert.Empty(actualUsers);
}

[Fact]
public async Task GetUserListByUuid_EmptyInput_ResponseBadRequest_ReturnsBadRequest()
{
// Arrange
List<Guid> userUuids = new List<Guid>();

HttpRequestMessage httpRequestMessage = CreatePostRequest($"/profile/api/v1/internal/user/listbyuuid", userUuids);

HttpClient client = _webApplicationFactorySetup.GetTestServerClient();

// Act
HttpResponseMessage response = await client.SendAsync(httpRequestMessage);

// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}

[Fact]
public async Task GetUserById_SblBridgeReturnsNotFound_ResponseNotFound()
{
Expand Down Expand Up @@ -488,5 +594,12 @@ private static HttpRequestMessage CreatePostRequest(string requestUri, UserProfi
httpRequestMessage.Content = new StringContent(JsonSerializer.Serialize(lookupRequest), Encoding.UTF8, "application/json");
return httpRequestMessage;
}

private static HttpRequestMessage CreatePostRequest(string requestUri, List<Guid> listRequest)
{
HttpRequestMessage httpRequestMessage = new(HttpMethod.Post, requestUri);
httpRequestMessage.Content = new StringContent(JsonSerializer.Serialize(listRequest), Encoding.UTF8, "application/json");
return httpRequestMessage;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"UserId": 20000006,
"UserUUID": "4c3b4909-eb17-45d5-bde1-256e065e196a",
"UserType": 1,
"UserName": "",
"ExternalIdentity": "",
"PhoneNumber": null,
"Email": null,
"PartyId": 50002114,
"Party": {
"PartyTypeName": 1,
"SSN": "01025161013",
"OrgNumber": "",
"Person": {
"SSN": "01025161013",
"Name": "ELENA FJAR",
"FirstName": "ELENA",
"MiddleName": "",
"LastName": "FJAR",
"TelephoneNumber": "",
"MobileNumber": "",
"MailingAddress": " Søreidåsen 3 5252 SØREIDGREND",
"MailingPostalCode": "5252",
"MailingPostalCity": "SØREIDGREND",
"AddressMunicipalNumber": "",
"AddressMunicipalName": "",
"AddressStreetName": "",
"AddressHouseNumber": "",
"AddressHouseLetter": "",
"AddressPostalCode": "5252",
"AddressCity": "SØREIDGREND",
"DateOfDeath": null
},
"Organization": null,
"PartyId": 50002114,
"PartyUUID": "4c3b4909-eb17-45d5-bde1-256e065e196a",
"UnitType": null,
"Name": "ELENA FJAR",
"IsDeleted": false,
"OnlyHierarchyElementWithNoAccess": false,
"ChildParties": null
},
"ProfileSettingPreference": {
"Language": "nn",
"PreSelectedPartyId": 0,
"DoNotPromptForParty": false
}
}
Loading

0 comments on commit 113ee9f

Please sign in to comment.