11using Microsoft . Extensions . Caching . Memory ;
2+ using Microsoft . Extensions . Hosting ;
23using Microsoft . Extensions . Logging ;
34using Microsoft . Extensions . Options ;
45using System . Text . Json ;
@@ -12,6 +13,7 @@ public class SessionizeApiClient
1213 private readonly SessionizeOptions _options ;
1314 private readonly IMemoryCache _cache ;
1415 private readonly ILogger < SessionizeApiClient > _logger ;
16+ private readonly string _cacheFilePath ;
1517
1618 private static readonly JsonSerializerOptions JsonOptions = new ( )
1719 {
@@ -22,12 +24,17 @@ public SessionizeApiClient(
2224 IHttpClientFactory httpClientFactory ,
2325 IOptions < SessionizeOptions > options ,
2426 IMemoryCache cache ,
27+ IHostEnvironment hostEnvironment ,
2528 ILogger < SessionizeApiClient > logger )
2629 {
2730 _httpClient = httpClientFactory . CreateClient ( "Sessionize" ) ;
2831 _options = options . Value ;
2932 _cache = cache ;
3033 _logger = logger ;
34+
35+ var cacheDir = Path . Combine ( hostEnvironment . ContentRootPath , "umbraco" , "Data" , "TEMP" , "SessionizeCache" ) ;
36+ Directory . CreateDirectory ( cacheDir ) ;
37+ _cacheFilePath = Path . Combine ( cacheDir , $ "sessionize_{ _options . EventId } .json") ;
3138 }
3239
3340 /// <summary>
@@ -76,11 +83,22 @@ public async Task<SessionizeAllData> GetAllDataAsync(CancellationToken cancellat
7683 "Fetched Sessionize data: {SessionCount} sessions, {SpeakerCount} speakers, {CategoryCount} categories" ,
7784 allData . Sessions . Count , allData . Speakers . Count , allData . Categories . Count ) ;
7885
86+ await WriteCacheFileAsync ( json , cancellationToken ) ;
87+
7988 return allData ;
8089 }
8190 catch ( Exception ex )
8291 {
8392 _logger . LogError ( ex , "Error fetching data from Sessionize for event {EventId}" , _options . EventId ) ;
93+
94+ var stale = await TryReadCacheFileAsync ( cancellationToken ) ;
95+ if ( stale != null )
96+ {
97+ _logger . LogWarning ( "Returning stale Sessionize data from disk cache for event {EventId}" , _options . EventId ) ;
98+ _cache . Set ( cacheKey , stale , TimeSpan . FromMinutes ( _options . CacheDurationInMinutes ) ) ;
99+ return stale ;
100+ }
101+
84102 throw ;
85103 }
86104 }
@@ -288,6 +306,43 @@ public void ClearCache()
288306 _logger . LogInformation ( "Cleared Sessionize cache for event {EventId}" , _options . EventId ) ;
289307 }
290308
309+ private async Task WriteCacheFileAsync ( string json , CancellationToken cancellationToken )
310+ {
311+ try
312+ {
313+ await File . WriteAllTextAsync ( _cacheFilePath , json , cancellationToken ) ;
314+ }
315+ catch ( Exception ex )
316+ {
317+ _logger . LogWarning ( ex , "Failed to write Sessionize disk cache to {Path}" , _cacheFilePath ) ;
318+ }
319+ }
320+
321+ private async Task < SessionizeAllData ? > TryReadCacheFileAsync ( CancellationToken cancellationToken )
322+ {
323+ try
324+ {
325+ if ( ! File . Exists ( _cacheFilePath ) )
326+ return null ;
327+
328+ var json = await File . ReadAllTextAsync ( _cacheFilePath , cancellationToken ) ;
329+ var allData = JsonSerializer . Deserialize < SessionizeAllData > ( json , JsonOptions ) ;
330+ if ( allData != null )
331+ {
332+ allData . PronounsQuestionId = allData . Questions
333+ . FirstOrDefault ( q => q . Question . Contains ( "Pronouns" , StringComparison . OrdinalIgnoreCase ) )
334+ ? . Id ;
335+ }
336+
337+ return allData ;
338+ }
339+ catch ( Exception ex )
340+ {
341+ _logger . LogWarning ( ex , "Failed to read Sessionize disk cache from {Path}" , _cacheFilePath ) ;
342+ return null ;
343+ }
344+ }
345+
291346 private static SessionizeSpeaker MapToSpeaker ( SessionizeSpeakerRaw raw , Dictionary < string , SessionizeSessionRaw > sessionLookup , int ? pronounsQuestionId )
292347 {
293348 var pronouns = pronounsQuestionId . HasValue
0 commit comments