File: Model\MetricsHelpers.cs
Web Access
Project: src\src\Aspire.Dashboard\Aspire.Dashboard.csproj (Aspire.Dashboard)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Globalization;
using Aspire.Dashboard.Otlp.Model;
using Aspire.Dashboard.Resources;
using Aspire.Dashboard.Utils;
using Microsoft.Extensions.Localization;
using Microsoft.FluentUI.AspNetCore.Components;
 
namespace Aspire.Dashboard.Model;
 
public static class MetricsHelpers
{
    public static async Task<bool> WaitForSpanToBeAvailableAsync(
        string traceId,
        string spanId,
        Func<string, string, OtlpSpan?> getSpan,
        IDialogService dialogService,
        Func<Func<Task>, Task> dispatcher,
        IStringLocalizer<Dialogs> loc,
        CancellationToken cancellationToken)
    {
        var span = getSpan(traceId, spanId);
 
        // Exemplar span isn't loaded yet. Display a dialog until the data is ready or the user cancels the dialog.
        if (span == null)
        {
            using var cts = new CancellationTokenSource();
            using var registration = cancellationToken.Register(cts.Cancel);
 
            var reference = await dialogService.ShowMessageBoxAsync(new DialogParameters<MessageBoxContent>()
            {
                Content = new MessageBoxContent
                {
                    Intent = MessageBoxIntent.Info,
                    Icon = new Icons.Filled.Size24.Info(),
                    IconColor = Color.Info,
                    Message = string.Format(CultureInfo.InvariantCulture, loc[nameof(Dialogs.OpenTraceDialogMessage)], OtlpHelpers.ToShortenedId(traceId)),
                },
                DialogType = DialogType.MessageBox,
                PrimaryAction = string.Empty,
                SecondaryAction = loc[nameof(Dialogs.OpenTraceDialogCancelButtonText)]
            }).ConfigureAwait(false);
 
            // Task that polls for the span to be available.
            var waitForTraceTask = Task.Run(async () =>
            {
                while (!cts.IsCancellationRequested)
                {
                    span = getSpan(traceId, spanId);
                    if (span != null)
                    {
                        await dispatcher(async () =>
                        {
                            await reference.CloseAsync(DialogResult.Ok<bool>(true)).ConfigureAwait(false);
                        }).ConfigureAwait(false);
                    }
                    else
                    {
                        await Task.Delay(TimeSpan.FromSeconds(0.5), cts.Token).ConfigureAwait(false);
                    }
                }
            }, cts.Token);
 
            var result = await reference.Result.ConfigureAwait(false);
            cts.Cancel();
 
            await TaskHelpers.WaitIgnoreCancelAsync(waitForTraceTask).ConfigureAwait(false);
 
            if (result.Cancelled)
            {
                // Dialog was canceled before span was ready. Exit without navigating.
                return false;
            }
        }
 
        return true;
    }
}