diff --git a/src/Dfe.ContentSupport.Web/Controllers/CacheController.cs b/src/Dfe.ContentSupport.Web/Controllers/CacheController.cs new file mode 100644 index 0000000..64d5ac6 --- /dev/null +++ b/src/Dfe.ContentSupport.Web/Controllers/CacheController.cs @@ -0,0 +1,19 @@ +using Dfe.ContentSupport.Web.Models.Mapped; +using Dfe.ContentSupport.Web.Services; +using Microsoft.AspNetCore.Mvc; + +namespace Dfe.ContentSupport.Web.Controllers; + +[Route("api/[controller]")] +[ApiController] +public class CacheController(ICacheService> cache) : Controller +{ + [HttpGet] + [Route("clear")] + public IActionResult Clear() + { + cache.ClearCache(); + + return Ok(); + } +} \ No newline at end of file diff --git a/src/Dfe.ContentSupport.Web/Extensions/WebApplicationBuilderExtensions.cs b/src/Dfe.ContentSupport.Web/Extensions/WebApplicationBuilderExtensions.cs index ec77f42..e50e736 100644 --- a/src/Dfe.ContentSupport.Web/Extensions/WebApplicationBuilderExtensions.cs +++ b/src/Dfe.ContentSupport.Web/Extensions/WebApplicationBuilderExtensions.cs @@ -13,6 +13,9 @@ public static void InitDependencyInjection(this WebApplicationBuilder app) app.Configuration.GetSection("Contentful").Bind(contentfulOptions); app.Services.AddSingleton(contentfulOptions); + + + app.Services .AddTransient>, CsPagesCacheService>(); app.Services.AddTransient(); diff --git a/src/Dfe.ContentSupport.Web/Program.cs b/src/Dfe.ContentSupport.Web/Program.cs index f1793ae..40316f8 100644 --- a/src/Dfe.ContentSupport.Web/Program.cs +++ b/src/Dfe.ContentSupport.Web/Program.cs @@ -19,7 +19,7 @@ public static void Main(string[] args) var azureCredentials = new DefaultAzureCredential(); builder.Configuration.AddAzureKeyVault(new Uri(keyVaultUri), azureCredentials); - + builder.Services.AddControllers(); builder.Services.AddControllersWithViews(); builder.Services.AddApplicationInsightsTelemetry(); @@ -51,12 +51,18 @@ public static void Main(string[] args) new { controller = "Sitemap", action = "Index" } ); + + app.MapControllerRoute( + "clearCache", + pattern: "{controller=Cache}/{action=Clear}" + ); + app.MapControllerRoute( name: "home", pattern: "{controller=Home}/{action=Home}"); app.MapControllerRoute( - name: "slug", + name: "slug", pattern: "{slug}", defaults: new { controller = "Home", action = "Index" }); diff --git a/src/Dfe.ContentSupport.Web/Services/ContentService.cs b/src/Dfe.ContentSupport.Web/Services/ContentService.cs index ccbc1a0..b56a921 100644 --- a/src/Dfe.ContentSupport.Web/Services/ContentService.cs +++ b/src/Dfe.ContentSupport.Web/Services/ContentService.cs @@ -35,10 +35,10 @@ from url in resp return sitemap.ToString(); } - public async Task> GetCsPages() + public async Task> GetCsPages(bool isPreview = true) { var pages = - await GetContentSupportPages(nameof(ContentSupportPage.IsSitemap), "true", true); + await GetContentSupportPages(nameof(ContentSupportPage.IsSitemap), "true", isPreview); return pages.ToList(); } @@ -46,18 +46,26 @@ public async Task> GetContentSupportPages( string field, string value, bool isPreview) { var key = $"{field}_{value}"; - var fromCache = cache.GetFromCache(key); - if (fromCache is not null) + if (isPreview is false) { - return fromCache; + var fromCache = cache.GetFromCache(key); + if (fromCache is not null) + { + return fromCache; + } } + var builder = QueryBuilder.New.ContentTypeIs(nameof(ContentSupportPage)) .FieldEquals($"fields.{field}", value); var result = await contentfulService.ContentfulClient(isPreview).Query(builder); var pages = result.Select(page => new CsPage(page)).ToList(); - cache.AddToCache(key, pages); + if (isPreview is false) + { + cache.AddToCache(key, pages); + } + return pages; } } \ No newline at end of file diff --git a/src/Dfe.ContentSupport.Web/Services/CsPagesCacheService.cs b/src/Dfe.ContentSupport.Web/Services/CsPagesCacheService.cs index 4313318..0826f40 100644 --- a/src/Dfe.ContentSupport.Web/Services/CsPagesCacheService.cs +++ b/src/Dfe.ContentSupport.Web/Services/CsPagesCacheService.cs @@ -23,4 +23,9 @@ public void AddToCache(string key, List item) { return cache.Get>(key); } + + public void ClearCache() + { + (cache as MemoryCache)?.Clear(); + } } \ No newline at end of file diff --git a/src/Dfe.ContentSupport.Web/Services/ICacheService.cs b/src/Dfe.ContentSupport.Web/Services/ICacheService.cs index 9ddb8dc..2752a6d 100644 --- a/src/Dfe.ContentSupport.Web/Services/ICacheService.cs +++ b/src/Dfe.ContentSupport.Web/Services/ICacheService.cs @@ -4,4 +4,5 @@ public interface ICacheService { void AddToCache(string key,T item); T? GetFromCache(string key); + void ClearCache(); } \ No newline at end of file diff --git a/src/Dfe.ContentSupport.Web/Services/IContentService.cs b/src/Dfe.ContentSupport.Web/Services/IContentService.cs index 355592a..eab77c3 100644 --- a/src/Dfe.ContentSupport.Web/Services/IContentService.cs +++ b/src/Dfe.ContentSupport.Web/Services/IContentService.cs @@ -6,5 +6,5 @@ public interface IContentService { Task GetContent(string slug, bool isPreview); Task GenerateSitemap(string baseUrl); - Task> GetCsPages(); + Task> GetCsPages(bool isPreview = true); } \ No newline at end of file diff --git a/tests/Dfe.ContentSupport.Web.Tests/Controllers/CacheControllerTests.cs b/tests/Dfe.ContentSupport.Web.Tests/Controllers/CacheControllerTests.cs new file mode 100644 index 0000000..1681478 --- /dev/null +++ b/tests/Dfe.ContentSupport.Web.Tests/Controllers/CacheControllerTests.cs @@ -0,0 +1,17 @@ +using Dfe.ContentSupport.Web.Controllers; +using Dfe.ContentSupport.Web.Models.Mapped; + +namespace Dfe.ContentSupport.Web.Tests.Controllers; + +public class CacheControllerTests +{ + [Fact] + public void Clear_Calls_CacheClear() + { + var cacheServiceMock = new Mock>>(); + var sut = new CacheController(cacheServiceMock.Object); + sut.Clear(); + + cacheServiceMock.Verify(o => o.ClearCache(), Times.Once); + } +} \ No newline at end of file diff --git a/tests/Dfe.ContentSupport.Web.Tests/Controllers/HomeControllerTests.cs b/tests/Dfe.ContentSupport.Web.Tests/Controllers/HomeControllerTests.cs index b3c8ba1..cf55041 100644 --- a/tests/Dfe.ContentSupport.Web.Tests/Controllers/HomeControllerTests.cs +++ b/tests/Dfe.ContentSupport.Web.Tests/Controllers/HomeControllerTests.cs @@ -18,7 +18,7 @@ private HomeController GetController() [Fact] public async void Home_Returns_View() { - _contentServiceMock.Setup(o => o.GetCsPages()).ReturnsAsync([]); + _contentServiceMock.Setup(o => o.GetCsPages(It.IsAny())).ReturnsAsync([]); var sut = GetController(); var result = await sut.Home(); diff --git a/tests/Dfe.ContentSupport.Web.Tests/Services/ContentServiceTests.cs b/tests/Dfe.ContentSupport.Web.Tests/Services/ContentServiceTests.cs index 0dffc83..18c0b58 100644 --- a/tests/Dfe.ContentSupport.Web.Tests/Services/ContentServiceTests.cs +++ b/tests/Dfe.ContentSupport.Web.Tests/Services/ContentServiceTests.cs @@ -107,16 +107,48 @@ public async void GetCsPages_Calls_Client_Once() } [Fact] - public async void GetCsPages_Calls_Cache_Correct_Key() + public async void GetCsPages_NotPreview_Calls_Cache_Correct_Key() + { + const string expectedKey = "IsSitemap_true"; + SetupResponse(); + var sut = GetService(); + await sut.GetCsPages(false); + + _cacheMock.Verify(o => o.GetFromCache(expectedKey), Times.Once); + } + + [Fact] + public async void GetCsPages_Preview_Calls_Cache_Correct_Key() { const string expectedKey = "IsSitemap_true"; SetupResponse(); var sut = GetService(); await sut.GetCsPages(); - _cacheMock.Verify(o => o.GetFromCache(expectedKey)); + _cacheMock.Verify(o => o.GetFromCache(expectedKey), Times.Never); + } + + [Fact] + public async void GetCsPages_NotPreview_Calls_AddCache_Correct_Key() + { + const string expectedKey = "IsSitemap_true"; + SetupResponse(); + var sut = GetService(); + await sut.GetCsPages(false); + + _cacheMock.Verify(o => o.AddToCache(expectedKey, It.IsAny>()), Times.Once); } + [Fact] + public async void GetCsPages_Preview_Calls_AddCache_Correct_Key() + { + const string expectedKey = "IsSitemap_true"; + SetupResponse(); + var sut = GetService(); + await sut.GetCsPages(); + + _cacheMock.Verify(o => o.AddToCache(expectedKey, It.IsAny>()), Times.Never); + } [Fact] public async void GetContent_Calls_Cache_Correct_Key()