Editor/SyncToolWindow.CollectionVerificationRetry.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
public partial class SyncToolWindow
{
private async Task RetryCollectionVerificationMismatches(
Dictionary<string, string> localColByName,
Dictionary<string, string> localColSourceTextByName,
string publishTarget )
{
var collectionMismatches = _syncLog
.Where( e => e.Type == "Collection" && e.Detail != null && e.Detail.Contains( "Mismatch" ) )
.Select( e => e.Name.EndsWith( ".collection.yml", StringComparison.OrdinalIgnoreCase )
? e.Name[..^".collection.yml".Length]
: e.Name )
.Where( name => !string.IsNullOrWhiteSpace( name ) )
.Distinct( StringComparer.OrdinalIgnoreCase )
.ToList();
if ( collectionMismatches.Count == 0 )
return;
var delaysMs = new[] { 1000, 2000, 3000, 5000, 8000, 13000 };
foreach ( var delayMs in delaysMs )
{
_status = $"Waiting for remote readback ({collectionMismatches.Count} collection mismatch(es))...";
Update();
await Task.Delay( delayMs );
var remoteCols = await SyncToolApi.GetCollectionsForPublishTarget( publishTarget );
if ( !remoteCols.HasValue )
continue;
var data = remoteCols.Value;
if ( data.TryGetProperty( "data", out var d ) ) data = d;
if ( data.ValueKind != JsonValueKind.Array )
continue;
var remaining = new List<string>();
foreach ( var colName in collectionMismatches )
{
var remoteCollection = data.EnumerateArray().FirstOrDefault( col => string.Equals( GetRemoteCollectionName( col ), colName, StringComparison.OrdinalIgnoreCase ) );
if ( remoteCollection.ValueKind != JsonValueKind.Object )
{
remaining.Add( colName );
continue;
}
if ( CollectionVerificationMatches( colName, remoteCollection, localColByName, localColSourceTextByName ) )
{
MarkCollectionMismatchVerified( colName );
}
else
{
remaining.Add( colName );
}
}
collectionMismatches = remaining;
if ( collectionMismatches.Count == 0 )
return;
}
}
private static string GetRemoteCollectionName( JsonElement collection )
{
return collection.ValueKind == JsonValueKind.Object && collection.TryGetProperty( "name", out var name )
? name.GetString()
: null;
}
private static bool CollectionVerificationMatches(
string colName,
JsonElement remoteCollection,
Dictionary<string, string> localColByName,
Dictionary<string, string> localColSourceTextByName )
{
var sourceTextMatches = localColSourceTextByName.TryGetValue( colName, out var localSourceText )
&& SyncToolTransforms.TryGetSourceText( remoteCollection, out var remoteSourceText )
&& localSourceText == NormalizeSourceTextForVerification( remoteSourceText );
if ( sourceTextMatches )
return true;
var remoteLocal = SyncToolTransforms.ServerCollectionToLocal( remoteCollection );
var remoteNorm = NormalizeJson( JsonSerializer.Serialize( remoteLocal ) );
return localColByName.TryGetValue( colName, out var localNorm )
&& (localNorm == remoteNorm || CollectionSemanticsMatch( localNorm, remoteNorm ));
}
private void MarkCollectionMismatchVerified( string colName )
{
var logIdx = _syncLog.FindIndex( e => e.Name == $"{colName}.collection.yml" && e.Type == "Collection" );
if ( logIdx >= 0 )
{
var entry = _syncLog[logIdx];
entry.Ok = true;
entry.Detail = "Verified source ✓ (after readback retry)";
_syncLog[logIdx] = entry;
}
SetItemState( $"col_{colName}", remoteDiffers: false, diffSummary: "", status: SyncStatus.InSync );
}
}