Skip to content

Commit

Permalink
- added caching on RegisterService both runtime and integrationtest m…
Browse files Browse the repository at this point in the history
…ock service
  • Loading branch information
Jon Kjetil Øye committed Sep 13, 2024
1 parent 1b7a06d commit 872ef00
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 47 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net.Http;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using Altinn.Common.AccessTokenClient.Services;
using Altinn.Platform.Authorization.Configuration;
Expand All @@ -9,6 +10,7 @@ namespace Altinn.Authorization.Services;
/// <summary>
/// Sets up an access token generator for development environment using the web based Altinn test token generator.
/// </summary>
[ExcludeFromCodeCoverage]
public class DevAccessTokenGenerator : IAccessTokenGenerator
{
private readonly TokenGeneratorSettings _settings;
Expand Down
102 changes: 72 additions & 30 deletions src/Authorization/Services/Implementation/RegisterService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
Expand All @@ -12,6 +13,7 @@
using Altinn.Platform.Register.Models;
using AltinnCore.Authentication.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

Expand All @@ -20,13 +22,15 @@ namespace Altinn.Platform.Authorization.Services
/// <summary>
/// Handles register service
/// </summary>
[ExcludeFromCodeCoverage]
public class RegisterService : IRegisterService
{
private readonly HttpClient _client;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly GeneralSettings _generalSettings;
private readonly IAccessTokenGenerator _accessTokenGenerator;
private readonly ILogger<IRegisterService> _logger;
private readonly IMemoryCache _memoryCache;
private readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };

/// <summary>
Expand All @@ -38,7 +42,8 @@ public RegisterService(
IAccessTokenGenerator accessTokenGenerator,
IOptions<GeneralSettings> generalSettings,
IOptions<PlatformSettings> platformSettings,
ILogger<IRegisterService> logger)
ILogger<IRegisterService> logger,
IMemoryCache memoryCache)
{
httpClient.BaseAddress = new Uri(platformSettings.Value.ApiRegisterEndpoint);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Expand All @@ -47,26 +52,31 @@ public RegisterService(
_generalSettings = generalSettings.Value;
_accessTokenGenerator = accessTokenGenerator;
_logger = logger;
_memoryCache = memoryCache;
}

/// <inheritdoc/>
public async Task<Party> GetParty(int partyId)
{
Party party = null;
string cacheKey = $"p:{partyId}";
if (!_memoryCache.TryGetValue(cacheKey, out Party party))
{
string endpointUrl = $"parties/{partyId}";
string token = JwtTokenUtil.GetTokenFromContext(_httpContextAccessor.HttpContext, _generalSettings.RuntimeCookieName);
string accessToken = _accessTokenGenerator.GenerateAccessToken("platform", "authorization");

string endpointUrl = $"parties/{partyId}";
string token = JwtTokenUtil.GetTokenFromContext(_httpContextAccessor.HttpContext, _generalSettings.RuntimeCookieName);
string accessToken = _accessTokenGenerator.GenerateAccessToken("platform", "authorization");
HttpResponseMessage response = await _client.GetAsync(token, endpointUrl, accessToken);

HttpResponseMessage response = await _client.GetAsync(token, endpointUrl, accessToken);
if (response.StatusCode == HttpStatusCode.OK)
{
string responseContent = await response.Content.ReadAsStringAsync();
party = JsonSerializer.Deserialize<Party>(responseContent, _serializerOptions);
}
else
{
_logger.LogError("// Getting party with partyID {partyId} failed with statuscode {response.StatusCode}", partyId, response.StatusCode);
if (response.StatusCode == HttpStatusCode.OK)
{
string responseContent = await response.Content.ReadAsStringAsync();
party = JsonSerializer.Deserialize<Party>(responseContent, _serializerOptions);
PutInCache(cacheKey, 10, party);
}
else
{
_logger.LogError("// Getting party with partyID {partyId} failed with statuscode {response.StatusCode}", partyId, response.StatusCode);
}
}

return party;
Expand All @@ -75,29 +85,61 @@ public async Task<Party> GetParty(int partyId)
/// <inheritdoc/>
public async Task<Party> PartyLookup(string orgNo, string person)
{
string endpointUrl = "parties/lookup";

PartyLookup partyLookup = new PartyLookup() { Ssn = person, OrgNo = orgNo };
string cacheKey;
PartyLookup partyLookup;

string bearerToken = JwtTokenUtil.GetTokenFromContext(_httpContextAccessor.HttpContext, _generalSettings.RuntimeCookieName);
string accessToken = _accessTokenGenerator.GenerateAccessToken("platform", "authorization");

StringContent content = new StringContent(JsonSerializer.Serialize(partyLookup));
content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");

HttpResponseMessage response = await _client.PostAsync(endpointUrl, content, bearerToken, accessToken);
if (response.StatusCode == HttpStatusCode.OK)
if (!string.IsNullOrWhiteSpace(orgNo))
{
cacheKey = $"org:{orgNo}";
partyLookup = new PartyLookup { OrgNo = orgNo };
}
else if (!string.IsNullOrWhiteSpace(person))
{
string responseContent = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<Party>(responseContent, _serializerOptions);
cacheKey = $"fnr:{person}";
partyLookup = new PartyLookup { Ssn = person };
}
else
{
string reason = await response.Content.ReadAsStringAsync();
_logger.LogError("// RegisterService // PartyLookup // Failed to lookup party in platform register. Response {response}. \n Reason {reason}.", response, reason);
return null;
}

throw await PlatformHttpException.CreateAsync(response);
if (!_memoryCache.TryGetValue(cacheKey, out Party party))
{
string endpointUrl = "parties/lookup";

string bearerToken = JwtTokenUtil.GetTokenFromContext(_httpContextAccessor.HttpContext, _generalSettings.RuntimeCookieName);
string accessToken = _accessTokenGenerator.GenerateAccessToken("platform", "authorization");

StringContent content = new StringContent(JsonSerializer.Serialize(partyLookup));
content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");

HttpResponseMessage response = await _client.PostAsync(endpointUrl, content, bearerToken, accessToken);

if (response.StatusCode == HttpStatusCode.OK)
{
string responseContent = await response.Content.ReadAsStringAsync();
party = JsonSerializer.Deserialize<Party>(responseContent, _serializerOptions);
PutInCache(cacheKey, 10, party);
}
else
{
string reason = await response.Content.ReadAsStringAsync();
_logger.LogError("// RegisterService // PartyLookup // Failed to lookup party in platform register. Response {response}. \n Reason {reason}.", response, reason);

throw await PlatformHttpException.CreateAsync(response);
}
}

return party;
}

private void PutInCache(string cachekey, int cacheTimeout, object cacheObject)
{
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetPriority(CacheItemPriority.High)
.SetAbsoluteExpiration(new TimeSpan(0, cacheTimeout, 0));

_memoryCache.Set(cachekey, cacheObject, cacheEntryOptions);
}
}
}
75 changes: 59 additions & 16 deletions test/IntegrationTests/MockServices/RegisterServiceMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
using Altinn.Platform.Authorization.Exceptions;
using Altinn.Platform.Authorization.Services.Interfaces;
using Altinn.Platform.Register.Models;

using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;

namespace Altinn.Platform.Events.Tests.Mocks
{
public class RegisterServiceMock : IRegisterService
{
private readonly int _partiesCollection;
private IMemoryCache _memoryCache = new MemoryCache(new MemoryCacheOptions());

public RegisterServiceMock(int partiesCollection = 1)
{
Expand All @@ -23,39 +24,72 @@ public RegisterServiceMock(int partiesCollection = 1)

public async Task<Party> GetParty(int partyId)
{
Party party = null;
string partyPath = GetPartyPath(partyId);
if (File.Exists(partyPath))
string cacheKey = $"p:{partyId}";
if (!_memoryCache.TryGetValue(cacheKey, out Party party))
{
string content = File.ReadAllText(partyPath);
party = JsonConvert.DeserializeObject<Party>(content);
string partyPath = GetPartyPath(partyId);
if (File.Exists(partyPath))
{
string content = File.ReadAllText(partyPath);
party = JsonConvert.DeserializeObject<Party>(content);
}

if (party != null)
{
PutInCache(cacheKey, 10, party);
}
}

return await Task.FromResult(party);
}

public async Task<Party> PartyLookup(string orgNo, string person)
{
string eventsPath = Path.Combine(GetPartiesPath(), $@"{_partiesCollection}.json");
Party party = null;
string cacheKey;
PartyLookup partyLookup;

if (File.Exists(eventsPath))
if (!string.IsNullOrWhiteSpace(orgNo))
{
cacheKey = $"org:{orgNo}";
partyLookup = new PartyLookup { OrgNo = orgNo };
}
else if (!string.IsNullOrWhiteSpace(person))
{
cacheKey = $"fnr:{person}";
partyLookup = new PartyLookup { Ssn = person };
}
else
{
string content = File.ReadAllText(eventsPath);
List<Party> parties = JsonConvert.DeserializeObject<List<Party>>(content);
return null;
}

if (!_memoryCache.TryGetValue(cacheKey, out Party party))
{
string eventsPath = Path.Combine(GetPartiesPath(), $@"{_partiesCollection}.json");

if (!string.IsNullOrEmpty(orgNo))
if (File.Exists(eventsPath))
{
party = parties.Where(p => p.OrgNumber != null && p.OrgNumber.Equals(orgNo)).FirstOrDefault();
string content = File.ReadAllText(eventsPath);
List<Party> parties = JsonConvert.DeserializeObject<List<Party>>(content);

if (!string.IsNullOrEmpty(orgNo))
{
party = parties.Where(p => p.OrgNumber != null && p.OrgNumber.Equals(orgNo)).FirstOrDefault();
}
else
{
party = parties.Where(p => p.SSN != null && p.SSN.Equals(person)).FirstOrDefault();
}
}
else

if (party != null)
{
party = parties.Where(p => p.SSN != null && p.SSN.Equals(person)).FirstOrDefault();
PutInCache(cacheKey, 10, party);
}
}

return party != null
? party
? await Task.FromResult(party)
: throw await PlatformHttpException.CreateAsync(new HttpResponseMessage
{ Content = new StringContent(string.Empty), StatusCode = System.Net.HttpStatusCode.NotFound });
}
Expand All @@ -71,5 +105,14 @@ private static string GetPartyPath(int partyId)
string unitTestFolder = Path.GetDirectoryName(new Uri(typeof(RegisterServiceMock).Assembly.Location).LocalPath);
return Path.Combine(unitTestFolder, "..", "..", "..", "Data", "Register", partyId.ToString() + ".json");
}

private void PutInCache(string cachekey, int cacheTimeout, object cacheObject)
{
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetPriority(CacheItemPriority.High)
.SetAbsoluteExpiration(new TimeSpan(0, cacheTimeout, 0));

_memoryCache.Set(cachekey, cacheObject, cacheEntryOptions);
}
}
}

0 comments on commit 872ef00

Please sign in to comment.