Files
launcher-next/Launcher.Next.Application/PackageResolutionUseCase.cs
T

51 lines
3.8 KiB
C#

using Launcher.Next.Core;
namespace Launcher.Next.Application;
public sealed class PackageResolutionUseCase(IPackageCatalogPackageAcquirer catalog, IPackageSynchronizer synchronizer, IPackageRequirementCacheBinder binder, ILastKnownGoodPackageResolver cache, ILauncherTelemetry telemetry)
{
public async Task<PackageResolutionResult> ResolveAsync(PackageRequirement requirement, CancellationToken cancellationToken)
{
var evidence = new List<PackageResolutionEvidence> { new("PACKAGE_REQUIREMENT_RECEIVED", requirement.PackageId) };
telemetry.Write("PackageResolutionStarted", new Dictionary<string, object?> { ["packageId"] = requirement.PackageId, ["product"] = requirement.Product, ["channel"] = requirement.Channel, ["offlineAllowed"] = requirement.AllowOfflineFallback });
try
{
var online = await catalog.AcquireAsync(requirement, cancellationToken);
if (online is not null && Matches(requirement, online.Manifest))
{
await synchronizer.SynchronizeAsync(online.Manifest, online.SourceRoot, cancellationToken);
await binder.BindAsync(requirement, online.Manifest.Version, cancellationToken);
var selected = await cache.ResolveAsync(requirement, cancellationToken);
if (selected is not null && !selected.IsOfflineFallback)
{
evidence.Add(new("ONLINE_PACKAGE_SELECTED", online.Manifest.Version));
telemetry.Write("PackageResolutionSelected", new Dictionary<string, object?> { ["code"] = "ONLINE_PACKAGE_SELECTED", ["packageId"] = selected.PackageId, ["version"] = selected.Version });
return new(selected, true, evidence);
}
evidence.Add(new("ONLINE_PACKAGE_REJECTED", "LOCAL_REVALIDATION_FAILED"));
}
else evidence.Add(new("CATALOG_UNAVAILABLE", online is null ? "NO_CATALOG_PACKAGE" : "REQUIREMENT_MISMATCH"));
}
catch (Exception ex) when (ex is not OperationCanceledException) { evidence.Add(new("CATALOG_UNAVAILABLE", "ACQUISITION_OR_STAGING_FAILED")); }
if (!requirement.AllowOfflineFallback)
{
evidence.Add(new("OFFLINE_FALLBACK_DISABLED", "POLICY"));
evidence.Add(new("NO_SAFE_PACKAGE_AVAILABLE", "BLOCKED"));
telemetry.Write("PackageResolutionBlocked", new Dictionary<string, object?> { ["code"] = "OFFLINE_FALLBACK_DISABLED", ["packageId"] = requirement.PackageId });
return new(null, false, evidence);
}
var cached = await cache.ResolveAsync(requirement, cancellationToken);
if (cached is null)
{
evidence.Add(new("NO_SAFE_PACKAGE_AVAILABLE", "CACHE_REJECTED"));
telemetry.Write("PackageResolutionBlocked", new Dictionary<string, object?> { ["code"] = "NO_SAFE_PACKAGE_AVAILABLE", ["packageId"] = requirement.PackageId });
return new(null, false, evidence);
}
var code = cached.IsOfflineFallback ? "PREVIOUS_LKG_SELECTED" : "VALID_CURRENT_CACHE_SELECTED";
evidence.Add(new(code, cached.Version));
telemetry.Write("PackageResolutionSelected", new Dictionary<string, object?> { ["code"] = code, ["packageId"] = cached.PackageId, ["version"] = cached.Version });
return new(cached, false, evidence);
}
private static bool Matches(PackageRequirement r, PackageManifest m) => string.Equals(r.PackageId, m.PackageId, StringComparison.Ordinal) && string.Equals(r.Product, m.Product, StringComparison.Ordinal) && r.ProductYear == m.ProductYear && string.Equals(r.Architecture, m.Architecture, StringComparison.OrdinalIgnoreCase) && string.Equals(r.Channel, m.RolloutChannel, StringComparison.OrdinalIgnoreCase) && string.Equals(r.RequiredVersion, m.Version, StringComparison.Ordinal);
}