diff --git a/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI.Core/Extensions/Caching/CacheKeys.cs b/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI.Core/Extensions/Caching/CacheKeys.cs
index 19e0465..869b346 100644
--- a/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI.Core/Extensions/Caching/CacheKeys.cs
+++ b/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI.Core/Extensions/Caching/CacheKeys.cs
@@ -68,6 +68,20 @@ public static class CacheKeys
///
public const string EventsPattern = "thrive:events:*";
+ // ============================================
+ // Transcript Cache Keys
+ // ============================================
+
+ ///
+ /// Sermon transcript blob. Format: thrive:transcripts:blob:{messageId}
+ ///
+ public const string TranscriptBlob = "thrive:transcripts:blob:{0}";
+
+ ///
+ /// Pattern for invalidating all transcript caches
+ ///
+ public const string TranscriptsPattern = "thrive:transcripts:*";
+
// ============================================
// Bible Passage Cache Keys
// ============================================
diff --git a/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI.Services/Services/TranscriptService.cs b/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI.Services/Services/TranscriptService.cs
index 70ed293..1a43df2 100644
--- a/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI.Services/Services/TranscriptService.cs
+++ b/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI.Services/Services/TranscriptService.cs
@@ -15,14 +15,23 @@ namespace ThriveChurchOfficialAPI.Services
public class TranscriptService : ITranscriptService
{
private readonly BlobContainerClient _containerClient;
+ private readonly ICacheService _cache;
+
+ ///
+ /// Cache expiration for transcript data (365 days - transcripts are immutable once created)
+ ///
+ private static readonly TimeSpan CacheExpiration = TimeSpan.FromDays(365);
///
/// Constructor for TranscriptService
///
/// Azure Storage connection string
/// Name of the blob container storing transcripts
- public TranscriptService(string connectionString, string containerName)
+ /// Cache service instance
+ public TranscriptService(string connectionString, string containerName, ICacheService cache)
{
+ _cache = cache ?? throw new ArgumentNullException(nameof(cache));
+
if (string.IsNullOrEmpty(connectionString))
{
Log.Warning("TranscriptService initialized with empty connection string");
@@ -39,9 +48,11 @@ public TranscriptService(string connectionString, string containerName)
/// Constructor for testing - allows injecting a mock container client
///
/// Mock blob container client for testing
- public TranscriptService(BlobContainerClient containerClient)
+ /// Cache service instance
+ public TranscriptService(BlobContainerClient containerClient, ICacheService cache)
{
_containerClient = containerClient;
+ _cache = cache ?? throw new ArgumentNullException(nameof(cache));
Log.Information("TranscriptService initialized with injected container client");
}
@@ -212,10 +223,20 @@ public async Task> GetStudyGuideAsync(string
#region Private Helper Methods
///
- /// Downloads and parses the transcript blob
+ /// Downloads and parses the transcript blob with caching
///
private async Task DownloadBlobAsync(string messageId)
{
+ // Check cache first
+ var cacheKey = string.Format(CacheKeys.TranscriptBlob, messageId.ToLowerInvariant());
+ var cachedBlob = _cache.ReadFromCache(cacheKey);
+ if (cachedBlob != null)
+ {
+ Log.Debug("Cache hit for transcript blob: {MessageId}", messageId);
+ return cachedBlob;
+ }
+
+ // Cache miss - download from Azure Blob Storage
var blobName = $"{messageId}.json";
var blobClient = _containerClient.GetBlobClient(blobName);
@@ -227,7 +248,16 @@ private async Task DownloadBlobAsync(string messageId)
var downloadResponse = await blobClient.DownloadContentAsync();
var content = downloadResponse.Value.Content.ToString();
- return JsonConvert.DeserializeObject(content);
+ var blob = JsonConvert.DeserializeObject(content);
+
+ // Cache the result
+ if (blob != null)
+ {
+ _cache.InsertIntoCache(cacheKey, blob, CacheExpiration);
+ Log.Debug("Cached transcript blob for message: {MessageId}", messageId);
+ }
+
+ return blob;
}
///
diff --git a/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI.Tests/Services/TranscriptServiceTests.cs b/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI.Tests/Services/TranscriptServiceTests.cs
index 26b1ac3..d41e740 100644
--- a/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI.Tests/Services/TranscriptServiceTests.cs
+++ b/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI.Tests/Services/TranscriptServiceTests.cs
@@ -1,5 +1,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
using System.Threading.Tasks;
+using ThriveChurchOfficialAPI.Core;
using ThriveChurchOfficialAPI.Services;
namespace ThriveChurchOfficialAPI.Tests.Services
@@ -12,13 +14,21 @@ namespace ThriveChurchOfficialAPI.Tests.Services
[TestClass]
public class TranscriptServiceTests
{
+ private Mock _mockCache;
+
+ [TestInitialize]
+ public void Setup()
+ {
+ _mockCache = new Mock();
+ }
+
#region Constructor Tests
[TestMethod]
public void Constructor_WithNullConnectionString_CreatesServiceWithWarning()
{
// Act - Using connection string constructor with null
- var service = new TranscriptService(null, "transcripts");
+ var service = new TranscriptService(null, "transcripts", _mockCache.Object);
// Assert - Service should be created but will return errors when used
Assert.IsNotNull(service);
@@ -28,7 +38,7 @@ public void Constructor_WithNullConnectionString_CreatesServiceWithWarning()
public void Constructor_WithEmptyConnectionString_CreatesServiceWithWarning()
{
// Act
- var service = new TranscriptService(string.Empty, "transcripts");
+ var service = new TranscriptService(string.Empty, "transcripts", _mockCache.Object);
// Assert
Assert.IsNotNull(service);
@@ -42,7 +52,7 @@ public void Constructor_WithEmptyConnectionString_CreatesServiceWithWarning()
public async Task GetTranscriptAsync_NullMessageId_ReturnsError()
{
// Arrange
- var service = new TranscriptService(null, "transcripts");
+ var service = new TranscriptService(null, "transcripts", _mockCache.Object);
// Act
var result = await service.GetTranscriptAsync(null);
@@ -56,7 +66,7 @@ public async Task GetTranscriptAsync_NullMessageId_ReturnsError()
public async Task GetTranscriptAsync_EmptyMessageId_ReturnsError()
{
// Arrange
- var service = new TranscriptService(null, "transcripts");
+ var service = new TranscriptService(null, "transcripts", _mockCache.Object);
// Act
var result = await service.GetTranscriptAsync(string.Empty);
@@ -70,7 +80,7 @@ public async Task GetTranscriptAsync_EmptyMessageId_ReturnsError()
public async Task GetTranscriptAsync_WhitespaceMessageId_ReturnsError()
{
// Arrange
- var service = new TranscriptService(null, "transcripts");
+ var service = new TranscriptService(null, "transcripts", _mockCache.Object);
// Act
var result = await service.GetTranscriptAsync(" ");
@@ -87,7 +97,7 @@ public async Task GetTranscriptAsync_WhitespaceMessageId_ReturnsError()
public async Task GetTranscriptAsync_ServiceNotConfigured_ReturnsConfigurationError()
{
// Arrange - Create service with null connection string
- var unconfiguredService = new TranscriptService(null, "transcripts");
+ var unconfiguredService = new TranscriptService(null, "transcripts", _mockCache.Object);
var messageId = "507f1f77bcf86cd799439011";
// Act
@@ -102,7 +112,7 @@ public async Task GetTranscriptAsync_ServiceNotConfigured_ReturnsConfigurationEr
public async Task GetTranscriptAsync_EmptyConnectionString_ReturnsConfigurationError()
{
// Arrange
- var service = new TranscriptService(string.Empty, "transcripts");
+ var service = new TranscriptService(string.Empty, "transcripts", _mockCache.Object);
var messageId = "507f1f77bcf86cd799439011";
// Act
@@ -121,7 +131,7 @@ public async Task GetTranscriptAsync_EmptyConnectionString_ReturnsConfigurationE
public async Task GetSermonNotesAsync_NullMessageId_ReturnsError()
{
// Arrange
- var service = new TranscriptService(null, "transcripts");
+ var service = new TranscriptService(null, "transcripts", _mockCache.Object);
// Act
var result = await service.GetSermonNotesAsync(null);
@@ -135,7 +145,7 @@ public async Task GetSermonNotesAsync_NullMessageId_ReturnsError()
public async Task GetSermonNotesAsync_EmptyMessageId_ReturnsError()
{
// Arrange
- var service = new TranscriptService(null, "transcripts");
+ var service = new TranscriptService(null, "transcripts", _mockCache.Object);
// Act
var result = await service.GetSermonNotesAsync(string.Empty);
@@ -149,7 +159,7 @@ public async Task GetSermonNotesAsync_EmptyMessageId_ReturnsError()
public async Task GetSermonNotesAsync_WhitespaceMessageId_ReturnsConfigurationError()
{
// Arrange
- var service = new TranscriptService(null, "transcripts");
+ var service = new TranscriptService(null, "transcripts", _mockCache.Object);
// Act
var result = await service.GetSermonNotesAsync(" ");
@@ -167,7 +177,7 @@ public async Task GetSermonNotesAsync_WhitespaceMessageId_ReturnsConfigurationEr
public async Task GetSermonNotesAsync_ServiceNotConfigured_ReturnsConfigurationError()
{
// Arrange - Create service with null connection string
- var unconfiguredService = new TranscriptService(null, "transcripts");
+ var unconfiguredService = new TranscriptService(null, "transcripts", _mockCache.Object);
var messageId = "507f1f77bcf86cd799439011";
// Act
@@ -182,7 +192,7 @@ public async Task GetSermonNotesAsync_ServiceNotConfigured_ReturnsConfigurationE
public async Task GetSermonNotesAsync_EmptyConnectionString_ReturnsConfigurationError()
{
// Arrange
- var service = new TranscriptService(string.Empty, "transcripts");
+ var service = new TranscriptService(string.Empty, "transcripts", _mockCache.Object);
var messageId = "507f1f77bcf86cd799439011";
// Act
@@ -201,7 +211,7 @@ public async Task GetSermonNotesAsync_EmptyConnectionString_ReturnsConfiguration
public async Task GetStudyGuideAsync_NullMessageId_ReturnsError()
{
// Arrange
- var service = new TranscriptService(null, "transcripts");
+ var service = new TranscriptService(null, "transcripts", _mockCache.Object);
// Act
var result = await service.GetStudyGuideAsync(null);
@@ -215,7 +225,7 @@ public async Task GetStudyGuideAsync_NullMessageId_ReturnsError()
public async Task GetStudyGuideAsync_EmptyMessageId_ReturnsError()
{
// Arrange
- var service = new TranscriptService(null, "transcripts");
+ var service = new TranscriptService(null, "transcripts", _mockCache.Object);
// Act
var result = await service.GetStudyGuideAsync(string.Empty);
@@ -229,7 +239,7 @@ public async Task GetStudyGuideAsync_EmptyMessageId_ReturnsError()
public async Task GetStudyGuideAsync_WhitespaceMessageId_ReturnsConfigurationError()
{
// Arrange
- var service = new TranscriptService(null, "transcripts");
+ var service = new TranscriptService(null, "transcripts", _mockCache.Object);
// Act
var result = await service.GetStudyGuideAsync(" ");
@@ -247,7 +257,7 @@ public async Task GetStudyGuideAsync_WhitespaceMessageId_ReturnsConfigurationErr
public async Task GetStudyGuideAsync_ServiceNotConfigured_ReturnsConfigurationError()
{
// Arrange - Create service with null connection string
- var unconfiguredService = new TranscriptService(null, "transcripts");
+ var unconfiguredService = new TranscriptService(null, "transcripts", _mockCache.Object);
var messageId = "507f1f77bcf86cd799439011";
// Act
@@ -262,7 +272,7 @@ public async Task GetStudyGuideAsync_ServiceNotConfigured_ReturnsConfigurationEr
public async Task GetStudyGuideAsync_EmptyConnectionString_ReturnsConfigurationError()
{
// Arrange
- var service = new TranscriptService(string.Empty, "transcripts");
+ var service = new TranscriptService(string.Empty, "transcripts", _mockCache.Object);
var messageId = "507f1f77bcf86cd799439011";
// Act
diff --git a/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI/Startup.cs b/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI/Startup.cs
index 93cc4be..243e840 100644
--- a/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI/Startup.cs
+++ b/API/ThriveChurchOfficialAPI/ThriveChurchOfficialAPI/Startup.cs
@@ -390,7 +390,10 @@ public void ConfigureServices(IServiceCollection services)
// Azure Blob Storage services (for transcripts)
var azureStorageConnectionString = Configuration["AzureStorageConnectionString"];
services.AddSingleton(sp =>
- new TranscriptService(azureStorageConnectionString, "transcripts"));
+ new TranscriptService(
+ azureStorageConnectionString,
+ "transcripts",
+ sp.GetRequiredService()));
Log.Information("Services configured.");
}