Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/Aspire.Hosting/ApplicationModel/ResourceStartedEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Hosting.Eventing;

namespace Aspire.Hosting.ApplicationModel;

/// <summary>
/// Event that is raised when a resource has started and is in a running state, but before any health checks have run.
/// </summary>
/// <param name="resource">The resource that has started.</param>
/// <param name="services">The service provider for the app host.</param>
/// <remarks>
/// This event is fired after a resource transitions to a running state but before health checks begin.
/// It fills the gap between <see cref="BeforeResourceStartedEvent"/> and <see cref="ResourceReadyEvent"/>.
/// </remarks>
public class ResourceStartedEvent(IResource resource, IServiceProvider services) : IDistributedApplicationResourceEvent
{
/// <summary>
/// The resource that has started.
/// </summary>
public IResource Resource => resource;

/// <summary>
/// The service provider for the app host.
/// </summary>
public IServiceProvider Services => services;
}
15 changes: 15 additions & 0 deletions src/Aspire.Hosting/DistributedApplicationEventingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,21 @@ public static IResourceBuilder<T> OnBeforeResourceStarted<T>(this IResourceBuild
where T : IResource
=> builder.OnEvent(callback);

/// <summary>
/// Subscribes a callback to the <see cref="ResourceStartedEvent"/> event within the AppHost.
/// </summary>
/// <typeparam name="T">The resource type.</typeparam>
/// <param name="builder">The resource builder.</param>
/// <param name="callback">A callback to handle the event.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
/// <remarks>
/// This event is fired after a resource has started and is in a running state, but before any health checks have run.
/// It fills the gap between <see cref="BeforeResourceStartedEvent"/> and <see cref="ResourceReadyEvent"/>.
/// </remarks>
public static IResourceBuilder<T> OnResourceStarted<T>(this IResourceBuilder<T> builder, Func<T, ResourceStartedEvent, CancellationToken, Task> callback)
where T : IResource
=> builder.OnEvent(callback);

/// <summary>
/// Subscribes a callback to the <see cref="ResourceStoppedEvent"/> event within the AppHost.
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions src/Aspire.Hosting/Health/ResourceHealthCheckService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ private async Task MonitorResourceHealthAsync(ResourceMonitorState state)
var cancellationToken = state.CancellationToken;
var resource = state.LatestEvent.Resource;

// Fire the ResourceStartedEvent before health check monitoring begins
var resourceStartedEvent = new ResourceStartedEvent(resource, services);
await eventing.PublishAsync(resourceStartedEvent, cancellationToken).ConfigureAwait(false);

if (!resource.TryGetAnnotationsIncludingAncestorsOfType<HealthCheckAnnotation>(out var annotations))
{
// NOTE: If there are no health check annotations then there
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,22 @@ await app.ResourceNotifications.PublishUpdateAsync(resource.Resource, s => s wit
await app.StopAsync().TimeoutAfter(TestConstants.LongTimeoutTimeSpan);
}

[Fact]
public void ResourceStartedEventHasCorrectProperties()
{
// Arrange
var resource = new ParentResource("test-resource");
var serviceProvider = new ServiceCollection().BuildServiceProvider();

// Act
var resourceStartedEvent = new ResourceStartedEvent(resource, serviceProvider);

// Assert
Assert.Equal(resource, resourceStartedEvent.Resource);
Assert.Equal(serviceProvider, resourceStartedEvent.Services);
Assert.Equal("test-resource", resourceStartedEvent.Resource.Name);
}

private sealed class ParentResource(string name) : Resource(name)
{
}
Expand Down