Skip to content

Commit a433391

Browse files
Merge pull request #1572 from TechnologyEnhancedLearning/Develop/Fixes/TD-6638-Investigate-and-optimise-three-high-frequency-API-requests-causing-potential-performance-issues-on-LH/ELFH-platforms
Develop/fixes/td 6638 investigate and optimise three high frequency api requests causing potential performance issues on lh/elfh platforms
2 parents 3132358 + 5b26855 commit a433391

File tree

5 files changed

+99
-36
lines changed

5 files changed

+99
-36
lines changed

.github/dependabot.yml

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,45 @@
44
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
55

66
version: 2
7+
registries:
8+
learninghubfeed:
9+
type: nuget-feed
10+
url: https://pkgs.dev.azure.com/e-LfH/_packaging/LearningHubFeed/nuget/v3/index.json
11+
username: "kevin.whittaker"
12+
password: ${{ secrets.AZURE_DEVOPS_PAT }}
13+
nuget.org:
14+
type: nuget-feed
15+
url: "https://api.nuget.org/v3/index.json"
716
updates:
817
- package-ecosystem: "nuget"
918
directory: "/" # Location of package manifests
1019
schedule:
1120
interval: "daily"
12-
open-pull-requests-limit: 10
21+
open-pull-requests-limit: 5
22+
registries:
23+
- learninghubfeed
24+
- nuget.org
1325
target-branch: "Automatic_version_update_dependabot"
1426
ignore:
1527
# Ignore updates to packages that start with 'Wildcards'
1628
- dependency-name: "Microsoft.FeatureManagement.AspNetCore*"
1729
- dependency-name: "LearningHub.Nhs.Models*"
1830
- dependency-name: "LearningHub.Nhs.Caching*"
19-
- dependency-name: "elfhHub.Nhs.Models*"
20-
- dependency-name: "linqtotwitter*"
21-
# Ignore some updates to the package
22-
- dependency-name: "Microsoft.VisualStudio.Web.CodeGeneration.Design"
23-
versions: [">7.0.0"]
24-
- dependency-name: "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
25-
versions: [">7.0.0"]
26-
- dependency-name: "Microsoft.AspNetCore.Mvc.Testing"
27-
versions: [">7.0.0"]
31+
- dependency-name: "elfhHub.Nhs.Models*"
32+
- dependency-name: "UK.NHS.CookieBanner"
33+
- dependency-name: "GDS.MultiPageFormData"
34+
- dependency-name: "linqtotwitter*"
35+
# Ignore some updates to the package
36+
- dependency-name: "Azure.Storage.Files.Shares"
37+
versions: [">12.11.0"]
38+
- dependency-name: "FluentAssertions"
39+
versions: [">6.12.0"]
40+
- dependency-name: "HtmlSanitizer"
41+
versions: [">6.0.453"]
42+
- dependency-name: "xunit"
43+
versions: [">2.4.1"]
44+
- dependency-name: "xunit.runner.visualstudio"
45+
versions: [">2.4.3"]
2846
- dependency-name: "Selenium.WebDriver.ChromeDriver"
2947
versions: ">=113.0.5672.1278" # Recommended version
3048
# For all packages, ignore all patch updates

AdminUI/LearningHub.Nhs.AdminUI/Services/UserService.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ public async Task<LearningHubValidationResult> SendAdminPasswordResetEmail(int u
337337
public async Task<LearningHubValidationResult> ClearUserCachedPermissions(int userId)
338338
{
339339
await this.cacheService.RemoveAsync($"{userId}:AllRolesWithPermissions");
340+
await this.cacheService.RemoveAsync($"{userId}:UserHasPublishedResources");
340341
return new LearningHubValidationResult(true);
341342
}
342343

LearningHub.Nhs.WebUI/Services/ResourceService.cs

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@ namespace LearningHub.Nhs.WebUI.Services
66
using System.Net.Http;
77
using System.Text;
88
using System.Threading.Tasks;
9+
using LearningHub.Nhs.Caching;
910
using LearningHub.Nhs.Models.Common;
1011
using LearningHub.Nhs.Models.Entities.Activity;
1112
using LearningHub.Nhs.Models.Entities.Resource;
1213
using LearningHub.Nhs.Models.Enums;
14+
using LearningHub.Nhs.Models.Extensions;
1315
using LearningHub.Nhs.Models.Hierarchy;
1416
using LearningHub.Nhs.Models.Resource;
1517
using LearningHub.Nhs.Models.Resource.Contribute;
1618
using LearningHub.Nhs.Models.Resource.ResourceDisplay;
1719
using LearningHub.Nhs.Models.Validation;
1820
using LearningHub.Nhs.WebUI.Interfaces;
1921
using LearningHub.Nhs.WebUI.Models;
22+
using Microsoft.AspNetCore.Http;
2023
using Microsoft.Extensions.Logging;
2124
using Microsoft.Extensions.Options;
2225
using Newtonsoft.Json;
@@ -29,20 +32,26 @@ public class ResourceService : BaseService<ResourceService>, IResourceService
2932
{
3033
private readonly Settings settings;
3134
private readonly IAzureMediaService azureMediaService;
35+
private readonly IHttpContextAccessor contextAccessor;
36+
private readonly ICacheService cacheService;
3237

3338
/// <summary>
3439
/// Initializes a new instance of the <see cref="ResourceService"/> class.
3540
/// </summary>
3641
/// <param name="learningHubHttpClient">Learning hub http client.</param>
3742
/// <param name="openApiHttpClient">The Open Api Http Client.</param>
3843
/// <param name="azureMediaService">Azure media services.</param>
44+
/// <param name="contextAccessor">The http context accessor.</param>
45+
/// <param name="cacheService">The cacheService.</param>
3946
/// <param name="logger">Logger.</param>
4047
/// <param name="settings">Settings.</param>
41-
public ResourceService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, IAzureMediaService azureMediaService, ILogger<ResourceService> logger, IOptions<Settings> settings)
48+
public ResourceService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, IAzureMediaService azureMediaService, IHttpContextAccessor contextAccessor, ICacheService cacheService, ILogger<ResourceService> logger, IOptions<Settings> settings)
4249
: base(learningHubHttpClient, openApiHttpClient, logger)
4350
{
4451
this.settings = settings.Value;
4552
this.azureMediaService = azureMediaService;
53+
this.contextAccessor = contextAccessor;
54+
this.cacheService = cacheService;
4655
}
4756

4857
/// <summary>
@@ -885,24 +894,8 @@ public async Task<LearningHubValidationResult> UnpublishResourceVersionAsync(int
885894
/// <returns>The <see cref="bool"/>.</returns>
886895
public async Task<bool> UserHasPublishedResourcesAsync()
887896
{
888-
var client = await this.OpenApiHttpClient.GetClientAsync();
889-
890-
var request = $"Resource/HasPublishedResources";
891-
var response = await client.GetAsync(request).ConfigureAwait(false);
892-
bool hasResources = false;
893-
if (response.IsSuccessStatusCode)
894-
{
895-
var result = response.Content.ReadAsStringAsync().Result;
896-
hasResources = bool.Parse(result);
897-
}
898-
else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized
899-
||
900-
response.StatusCode == System.Net.HttpStatusCode.Forbidden)
901-
{
902-
throw new Exception("AccessDenied");
903-
}
904-
905-
return hasResources;
897+
var cacheKey = $"{this.contextAccessor.HttpContext.User.Identity.GetCurrentUserId()}:UserHasPublishedResources";
898+
return await this.cacheService.GetOrFetchAsync("UserHasPublishedResources", () => this.HasPublishedResources());
906899
}
907900

908901
/// <summary>
@@ -1340,5 +1333,32 @@ public async Task<List<string>> GetObsoleteResourceFile(int resourceVersionId, b
13401333

13411334
return filePaths;
13421335
}
1336+
1337+
/// <summary>
1338+
/// Check if the user has published resources.
1339+
/// </summary>
1340+
/// <returns>The bool.</returns>
1341+
/// <exception cref="Exception"></exception>
1342+
private async Task<bool> HasPublishedResources()
1343+
{
1344+
var client = await this.OpenApiHttpClient.GetClientAsync();
1345+
1346+
var request = $"Resource/HasPublishedResources";
1347+
var response = await client.GetAsync(request).ConfigureAwait(false);
1348+
bool hasResources = false;
1349+
if (response.IsSuccessStatusCode)
1350+
{
1351+
var result = response.Content.ReadAsStringAsync().Result;
1352+
hasResources = bool.Parse(result);
1353+
}
1354+
else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized
1355+
||
1356+
response.StatusCode == System.Net.HttpStatusCode.Forbidden)
1357+
{
1358+
throw new Exception("AccessDenied");
1359+
}
1360+
1361+
return hasResources;
1362+
}
13431363
}
13441364
}

LearningHub.Nhs.WebUI/ViewComponents/NavigationItemsViewComponent.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,19 @@ public async Task<IViewComponentResult> InvokeAsync(string navView = "Default",
5757
var userId = this.User.Identity.GetCurrentUserId();
5858

5959
var (cacheExists, _) = await this.cacheService.TryGetAsync<string>($"{userId}:LoginWizard");
60-
6160
model = await this.permissionService.GetNavigationModelAsync(this.User, !cacheExists, controllerName);
6261

63-
model.NotificationCount = await this.notificationService.GetUserUnreadNotificationCountAsync(userId);
62+
// Check if NotificationCount is already stored for this request
63+
if (this.HttpContext.Items.TryGetValue("NotificationCount", out var cachedCount))
64+
{
65+
model.NotificationCount = (int)cachedCount;
66+
}
67+
else
68+
{
69+
var count = await this.notificationService.GetUserUnreadNotificationCountAsync(userId);
70+
this.HttpContext.Items["NotificationCount"] = count;
71+
model.NotificationCount = count;
72+
}
6473
}
6574

6675
return await Task.FromResult<IViewComponentResult>(this.View(navView, model));

OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ namespace LearningHub.Nhs.OpenApi.Services.Services
2727
using LearningHub.Nhs.Models.Resource.Contribute;
2828
using LearningHub.Nhs.Models.Resource.Files;
2929
using LearningHub.Nhs.Models.Resource.ResourceDisplay;
30+
using LearningHub.Nhs.Models.User;
3031
using LearningHub.Nhs.Models.Validation;
3132
using LearningHub.Nhs.Models.ViewModels.Helpers;
3233
using LearningHub.Nhs.OpenApi.Models.Configuration;
@@ -170,7 +171,7 @@ public class ResourceService : IResourceService
170171
/// <param name="assessmentResourceActivityMatchQuestionRepository">The repository for assessment activity-question matches.</param>
171172
/// <param name="resourceVersionKeywordRepository">The repository for resource version keywords.</param>
172173
/// <param name="resourceVersionValidationResultRepository">The repository for validation results of resource versions.</param>
173-
174+
174175

175176
public ResourceService(ILearningHubService learningHubService, IFileTypeService fileTypeService, IBlockCollectionRepository blockCollectionRepository, IInternalSystemService internalSystemService, IResourceVersionAuthorRepository resourceVersionAuthorRepository, IFileChunkDetailRepository fileChunkDetailRepository, IQueueCommunicatorService queueCommunicatorService, IResourceRepository resourceRepository, IResourceVersionProviderRepository resourceVersionProviderRepository, IProviderService providerService, IArticleResourceVersionFileRepository articleResourceVersionFileRepository, IPublicationRepository publicationRepository, IMigrationSourceRepository migrationSourceRepository, IQuestionBlockRepository questionBlockRepository, IVideoRepository videoRepository, IWholeSlideImageRepository wholeSlideImageRepository, IEmbeddedResourceVersionRepository embeddedResourceVersionRepository, IEquipmentResourceVersionRepository equipmentResourceVersionRepository, IImageResourceVersionRepository imageResourceVersionRepository, IBookmarkRepository bookmarkRepository, IAssessmentResourceActivityMatchQuestionRepository assessmentResourceActivityMatchQuestionRepository, IResourceVersionKeywordRepository resourceVersionKeywordRepository, IResourceVersionValidationResultRepository resourceVersionValidationResultRepository, ILogger<ResourceService> logger, IWebLinkResourceVersionRepository webLinkResourceVersionRepository, ICaseResourceVersionRepository caseResourceVersionRepository, IScormResourceVersionRepository scormResourceVersionRepository, IGenericFileResourceVersionRepository genericFileResourceVersionRepository, IResourceVersionRepository resourceVersionRepository, IHtmlResourceVersionRepository htmlResourceVersionRepository, IMapper mapper, IFileRepository fileRepository, IOptions<AzureConfig> azureConfig, IOptions<LearningHubConfig> learningHubConfig, IUserProfileService userProfileService, IResourceVersionFlagRepository resourceVersionFlagRepository, IArticleResourceVersionRepository articleResourceVersionRepository, IAudioResourceVersionRepository audioResourceVersionRepository, IVideoResourceVersionRepository videoResourceVersionRepository, IAssessmentResourceVersionRepository assessmentResourceVersionRepository, IResourceLicenceRepository resourceLicenceRepository, IResourceReferenceRepository resourceReferenceRepository, IResourceVersionUserAcceptanceRepository resourceVersionUserAcceptanceRepository, ICatalogueNodeVersionRepository catalogueNodeVersionRepository, ICachingService cachingService, ISearchService searchService, ICatalogueService catalogueService, INodeResourceRepository nodeResourceRepository, INodePathRepository nodePathRepository, IUserService userService, INodeRepository nodeRepository, IResourceSyncService resourceSyncService, IResourceSyncRepository resourceSyncRepository, IResourceVersionEventRepository resourceVersionEventRepository, LearningHubDbContext dbContext)
176177
{
@@ -206,8 +207,8 @@ public ResourceService(ILearningHubService learningHubService, IFileTypeService
206207
this.resourceVersionFlagRepository = resourceVersionFlagRepository;
207208
this.resourceVersionUserAcceptanceRepository = resourceVersionUserAcceptanceRepository;
208209
this.resourceVersionValidationResultRepository = resourceVersionValidationResultRepository;
209-
this.resourceVersionKeywordRepository= resourceVersionKeywordRepository;
210-
this.resourceVersionProviderRepository= resourceVersionProviderRepository;
210+
this.resourceVersionKeywordRepository = resourceVersionKeywordRepository;
211+
this.resourceVersionProviderRepository = resourceVersionProviderRepository;
211212
this.providerService = providerService;
212213
this.nodePathRepository = nodePathRepository;
213214
this.nodeResourceRepository = nodeResourceRepository;
@@ -323,7 +324,7 @@ public async Task<List<ResourceReferenceWithResourceDetailsViewModel>> GetResour
323324
List<ResourceActivityDTO> resourceActivities = new List<ResourceActivityDTO>() { };
324325
List<ResourceReferenceWithResourceDetailsViewModel> resourceReferenceWithResourceDetailsViewModelLS = new List<ResourceReferenceWithResourceDetailsViewModel>() { };
325326

326-
resourceActivities = (await this.resourceRepository.GetResourceActivityPerResourceMajorVersion(new List<int>(){ }, new List<int>(){ currentUserId }))?.ToList() ?? new List<ResourceActivityDTO>() { };
327+
resourceActivities = (await this.resourceRepository.GetResourceActivityPerResourceMajorVersion(new List<int>() { }, new List<int>() { currentUserId }))?.ToList() ?? new List<ResourceActivityDTO>() { };
327328

328329
// Removing resources that have no major versions with the required activitystatus
329330
List<int> resourceIds = resourceActivities
@@ -1332,7 +1333,21 @@ public MyContributionsTotalsViewModel GetMyContributionTotals(int catalogueId, i
13321333
/// <returns>If the user has published resources.</returns>
13331334
public async Task<bool> HasPublishedResourcesAsync(int userId)
13341335
{
1335-
return await this.resourceRepository.UserHasPublishedResourcesAsync(userId);
1336+
string cacheKey = $"{userId}:UserHasPublishedResources";
1337+
var userHasPublishedResourcesInCache = await this.cachingService.GetAsync<bool>(cacheKey);
1338+
var userHasPublishedResources = false;
1339+
1340+
if (userHasPublishedResourcesInCache.ResponseEnum == CacheReadResponseEnum.Found)
1341+
{
1342+
userHasPublishedResources = userHasPublishedResourcesInCache.Item;
1343+
}
1344+
else
1345+
{
1346+
userHasPublishedResources = await this.resourceRepository.UserHasPublishedResourcesAsync(userId);
1347+
await this.cachingService.SetAsync($"{userId}:UserHasPublishedResources", userHasPublishedResources);
1348+
}
1349+
1350+
return userHasPublishedResources;
13361351
}
13371352

13381353
/// <summary>

0 commit comments

Comments
 (0)