121 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Linq;
 | |
| using MongoDB.Bson;
 | |
| 
 | |
| namespace StellaOps.Concelier.Connector.Kisa.Internal;
 | |
| 
 | |
| internal sealed record KisaCursor(
 | |
|     IReadOnlyCollection<Guid> PendingDocuments,
 | |
|     IReadOnlyCollection<Guid> PendingMappings,
 | |
|     IReadOnlyCollection<string> KnownIds,
 | |
|     DateTimeOffset? LastPublished,
 | |
|     DateTimeOffset? LastFetchAt)
 | |
| {
 | |
|     private static readonly IReadOnlyCollection<Guid> EmptyGuids = Array.Empty<Guid>();
 | |
|     private static readonly IReadOnlyCollection<string> EmptyStrings = Array.Empty<string>();
 | |
| 
 | |
|     public static KisaCursor Empty { get; } = new(EmptyGuids, EmptyGuids, EmptyStrings, null, null);
 | |
| 
 | |
|     public KisaCursor WithPendingDocuments(IEnumerable<Guid> documents)
 | |
|         => this with { PendingDocuments = Distinct(documents) };
 | |
| 
 | |
|     public KisaCursor WithPendingMappings(IEnumerable<Guid> mappings)
 | |
|         => this with { PendingMappings = Distinct(mappings) };
 | |
| 
 | |
|     public KisaCursor WithKnownIds(IEnumerable<string> ids)
 | |
|         => this with { KnownIds = ids?.Distinct(StringComparer.OrdinalIgnoreCase).ToArray() ?? EmptyStrings };
 | |
| 
 | |
|     public KisaCursor WithLastPublished(DateTimeOffset? published)
 | |
|         => this with { LastPublished = published };
 | |
| 
 | |
|     public KisaCursor WithLastFetch(DateTimeOffset? timestamp)
 | |
|         => this with { LastFetchAt = timestamp };
 | |
| 
 | |
|     public BsonDocument ToBsonDocument()
 | |
|     {
 | |
|         var document = new BsonDocument
 | |
|         {
 | |
|             ["pendingDocuments"] = new BsonArray(PendingDocuments.Select(id => id.ToString())),
 | |
|             ["pendingMappings"] = new BsonArray(PendingMappings.Select(id => id.ToString())),
 | |
|             ["knownIds"] = new BsonArray(KnownIds),
 | |
|         };
 | |
| 
 | |
|         if (LastPublished.HasValue)
 | |
|         {
 | |
|             document["lastPublished"] = LastPublished.Value.UtcDateTime;
 | |
|         }
 | |
| 
 | |
|         if (LastFetchAt.HasValue)
 | |
|         {
 | |
|             document["lastFetchAt"] = LastFetchAt.Value.UtcDateTime;
 | |
|         }
 | |
| 
 | |
|         return document;
 | |
|     }
 | |
| 
 | |
|     public static KisaCursor FromBson(BsonDocument? document)
 | |
|     {
 | |
|         if (document is null || document.ElementCount == 0)
 | |
|         {
 | |
|             return Empty;
 | |
|         }
 | |
| 
 | |
|         var pendingDocuments = ReadGuidArray(document, "pendingDocuments");
 | |
|         var pendingMappings = ReadGuidArray(document, "pendingMappings");
 | |
|         var knownIds = ReadStringArray(document, "knownIds");
 | |
|         var lastPublished = document.TryGetValue("lastPublished", out var publishedValue)
 | |
|             ? ParseDate(publishedValue)
 | |
|             : null;
 | |
|         var lastFetch = document.TryGetValue("lastFetchAt", out var fetchValue)
 | |
|             ? ParseDate(fetchValue)
 | |
|             : null;
 | |
| 
 | |
|         return new KisaCursor(pendingDocuments, pendingMappings, knownIds, lastPublished, lastFetch);
 | |
|     }
 | |
| 
 | |
|     private static IReadOnlyCollection<Guid> Distinct(IEnumerable<Guid>? values)
 | |
|         => values?.Distinct().ToArray() ?? EmptyGuids;
 | |
| 
 | |
|     private static IReadOnlyCollection<Guid> ReadGuidArray(BsonDocument document, string field)
 | |
|     {
 | |
|         if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
 | |
|         {
 | |
|             return EmptyGuids;
 | |
|         }
 | |
| 
 | |
|         var items = new List<Guid>(array.Count);
 | |
|         foreach (var element in array)
 | |
|         {
 | |
|             if (Guid.TryParse(element?.ToString(), out var id))
 | |
|             {
 | |
|                 items.Add(id);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return items;
 | |
|     }
 | |
| 
 | |
|     private static IReadOnlyCollection<string> ReadStringArray(BsonDocument document, string field)
 | |
|     {
 | |
|         if (!document.TryGetValue(field, out var value) || value is not BsonArray array)
 | |
|         {
 | |
|             return EmptyStrings;
 | |
|         }
 | |
| 
 | |
|         return array
 | |
|             .Select(element => element?.ToString() ?? string.Empty)
 | |
|             .Where(static s => !string.IsNullOrWhiteSpace(s))
 | |
|             .Distinct(StringComparer.OrdinalIgnoreCase)
 | |
|             .ToArray();
 | |
|     }
 | |
| 
 | |
|     private static DateTimeOffset? ParseDate(BsonValue value)
 | |
|         => value.BsonType switch
 | |
|         {
 | |
|             BsonType.DateTime => DateTime.SpecifyKind(value.ToUniversalTime(), DateTimeKind.Utc),
 | |
|             BsonType.String when DateTimeOffset.TryParse(value.AsString, out var parsed) => parsed.ToUniversalTime(),
 | |
|             _ => null,
 | |
|         };
 | |
| }
 |