< Summary

Information
Class: FirebaseAdapter.FirebaseContextRepository
Assembly: FirebaseAdapter
File(s): /home/runner/work/KicktippAi/KicktippAi/src/FirebaseAdapter/FirebaseContextRepository.cs
Line coverage
80%
Covered lines: 95
Uncovered lines: 23
Coverable lines: 118
Total lines: 228
Line coverage: 80.5%
Branch coverage
100%
Covered branches: 22
Total branches: 22
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%44100%
SaveContextDocumentAsync()100%6685.19%
GetLatestContextDocumentAsync()100%2275%
GetContextDocumentAsync()100%2266.67%
GetContextDocumentNamesAsync()100%2278.57%
GetContextDocumentVersionsAsync()100%4475%
UpdateContextDocumentVersionAsync()100%2278.95%
ConvertToContextDocument(...)100%11100%

File(s)

/home/runner/work/KicktippAi/KicktippAi/src/FirebaseAdapter/FirebaseContextRepository.cs

#LineLine coverage
 1using EHonda.KicktippAi.Core;
 2using FirebaseAdapter.Models;
 3using Google.Cloud.Firestore;
 4using Microsoft.Extensions.Logging;
 5using NodaTime;
 6
 7namespace FirebaseAdapter;
 8
 9/// <summary>
 10/// Firebase Firestore implementation of the context repository.
 11/// </summary>
 12public class FirebaseContextRepository : IContextRepository
 13{
 14    private readonly FirestoreDb _firestoreDb;
 15    private readonly ILogger<FirebaseContextRepository> _logger;
 16    private readonly string _contextDocumentsCollection;
 17    private readonly string _competition;
 18
 119    public FirebaseContextRepository(FirestoreDb firestoreDb, ILogger<FirebaseContextRepository> logger)
 20    {
 121        _firestoreDb = firestoreDb ?? throw new ArgumentNullException(nameof(firestoreDb));
 122        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
 23
 124        _contextDocumentsCollection = "context-documents";
 125        _competition = "bundesliga-2025-26";
 26
 127        _logger.LogInformation("Firebase context repository initialized");
 128    }
 29
 30    public async Task<int?> SaveContextDocumentAsync(string documentName, string content, string communityContext, Cance
 31    {
 32        try
 33        {
 34            // Get the latest version to check if content differs
 135            var latestDocument = await GetLatestContextDocumentAsync(documentName, communityContext, cancellationToken);
 36
 37            // If content is the same, don't save a new version
 138            if (latestDocument != null && latestDocument.Content == content)
 39            {
 140                _logger.LogInformation("Context document {DocumentName} content unchanged, skipping save", documentName)
 141                return null;
 42            }
 43
 44            // Determine the next version number
 145            var nextVersion = latestDocument?.Version + 1 ?? 0;
 46
 147            var now = Timestamp.GetCurrentTimestamp();
 148            var documentId = $"{documentName}_{communityContext}_{nextVersion}";
 49
 150            var firestoreDocument = new FirestoreContextDocument
 151            {
 152                Id = documentId,
 153                DocumentName = documentName,
 154                Content = content,
 155                Version = nextVersion,
 156                CreatedAt = now,
 157                Competition = _competition,
 158                CommunityContext = communityContext
 159            };
 60
 161            var docRef = _firestoreDb.Collection(_contextDocumentsCollection).Document(documentId);
 162            await docRef.SetAsync(firestoreDocument, cancellationToken: cancellationToken);
 63
 164            _logger.LogInformation("Saved context document {DocumentName} version {Version} for community {CommunityCont
 165                documentName, nextVersion, communityContext);
 66
 167            return nextVersion;
 68        }
 069        catch (Exception ex)
 70        {
 071            _logger.LogError(ex, "Failed to save context document {DocumentName} for community {CommunityContext}",
 072                documentName, communityContext);
 073            throw;
 74        }
 175    }
 76
 77    public async Task<ContextDocument?> GetLatestContextDocumentAsync(string documentName, string communityContext, Canc
 78    {
 79        try
 80        {
 181            var query = _firestoreDb.Collection(_contextDocumentsCollection)
 182                .WhereEqualTo("documentName", documentName)
 183                .WhereEqualTo("communityContext", communityContext)
 184                .WhereEqualTo("competition", _competition)
 185                .OrderByDescending("version")
 186                .Limit(1);
 87
 188            var snapshot = await query.GetSnapshotAsync(cancellationToken);
 89
 190            if (!snapshot.Documents.Any())
 91            {
 192                return null;
 93            }
 94
 195            var firestoreDoc = snapshot.Documents.First().ConvertTo<FirestoreContextDocument>();
 196            return ConvertToContextDocument(firestoreDoc);
 97        }
 098        catch (Exception ex)
 99        {
 0100            _logger.LogError(ex, "Failed to retrieve latest context document {DocumentName} for community {CommunityCont
 0101                documentName, communityContext);
 0102            throw;
 103        }
 1104    }
 105
 106    public async Task<ContextDocument?> GetContextDocumentAsync(string documentName, int version, string communityContex
 107    {
 108        try
 109        {
 1110            var documentId = $"{documentName}_{communityContext}_{version}";
 1111            var docRef = _firestoreDb.Collection(_contextDocumentsCollection).Document(documentId);
 1112            var snapshot = await docRef.GetSnapshotAsync(cancellationToken);
 113
 1114            if (!snapshot.Exists)
 115            {
 1116                return null;
 117            }
 118
 1119            var firestoreDoc = snapshot.ConvertTo<FirestoreContextDocument>();
 1120            return ConvertToContextDocument(firestoreDoc);
 121        }
 0122        catch (Exception ex)
 123        {
 0124            _logger.LogError(ex, "Failed to retrieve context document {DocumentName} version {Version} for community {Co
 0125                documentName, version, communityContext);
 0126            throw;
 127        }
 1128    }
 129
 130    public async Task<IReadOnlyList<string>> GetContextDocumentNamesAsync(string communityContext, CancellationToken can
 131    {
 132        try
 133        {
 1134            var query = _firestoreDb.Collection(_contextDocumentsCollection)
 1135                .WhereEqualTo("communityContext", communityContext)
 1136                .WhereEqualTo("competition", _competition)
 1137                .Select("documentName");
 138
 1139            var snapshot = await query.GetSnapshotAsync(cancellationToken);
 140
 1141            var documentNames = snapshot.Documents
 1142                .Select(doc => doc.GetValue<string>("documentName"))
 1143                .Distinct()
 1144                .ToList()
 1145                .AsReadOnly();
 146
 1147            return documentNames;
 148        }
 0149        catch (Exception ex)
 150        {
 0151            _logger.LogError(ex, "Failed to retrieve context document names for community {CommunityContext}", community
 0152            throw;
 153        }
 1154    }
 155
 156    public async Task<IReadOnlyList<ContextDocument>> GetContextDocumentVersionsAsync(string documentName, string commun
 157    {
 158        try
 159        {
 1160            var query = _firestoreDb.Collection(_contextDocumentsCollection)
 1161                .WhereEqualTo("documentName", documentName)
 1162                .WhereEqualTo("communityContext", communityContext)
 1163                .WhereEqualTo("competition", _competition)
 1164                .OrderBy("version");
 165
 1166            var snapshot = await query.GetSnapshotAsync(cancellationToken);
 167
 1168            var documents = snapshot.Documents
 1169                .Select(doc => doc.ConvertTo<FirestoreContextDocument>())
 1170                .Select(ConvertToContextDocument)
 1171                .ToList()
 1172                .AsReadOnly();
 173
 1174            return documents;
 175        }
 0176        catch (Exception ex)
 177        {
 0178            _logger.LogError(ex, "Failed to retrieve context document versions for {DocumentName} in community {Communit
 0179                documentName, communityContext);
 0180            throw;
 181        }
 1182    }
 183
 184    public async Task<bool> UpdateContextDocumentVersionAsync(string documentName, int version, string newContent, strin
 185    {
 186        try
 187        {
 1188            var documentId = $"{documentName}_{communityContext}_{version}";
 1189            var docRef = _firestoreDb.Collection(_contextDocumentsCollection).Document(documentId);
 190
 191            // Check if document exists
 1192            var snapshot = await docRef.GetSnapshotAsync(cancellationToken);
 1193            if (!snapshot.Exists)
 194            {
 1195                _logger.LogWarning("Cannot update non-existent document {DocumentId}", documentId);
 1196                return false;
 197            }
 198
 199            // Update only the content field
 1200            var updates = new Dictionary<string, object>
 1201            {
 1202                ["content"] = newContent
 1203            };
 204
 1205            await docRef.UpdateAsync(updates, cancellationToken: cancellationToken);
 206
 1207            _logger.LogInformation("Updated content for context document {DocumentName} version {Version} in community {
 1208                documentName, version, communityContext);
 209
 1210            return true;
 211        }
 0212        catch (Exception ex)
 213        {
 0214            _logger.LogError(ex, "Failed to update context document {DocumentName} version {Version} in community {Commu
 0215                documentName, version, communityContext);
 0216            throw;
 217        }
 1218    }
 219
 220    private static ContextDocument ConvertToContextDocument(FirestoreContextDocument firestoreDoc)
 221    {
 1222        return new ContextDocument(
 1223            firestoreDoc.DocumentName,
 1224            firestoreDoc.Content,
 1225            firestoreDoc.Version,
 1226            firestoreDoc.CreatedAt.ToDateTimeOffset());
 227    }
 228}