Skip to content

Conversation

@gabynevada
Copy link
Contributor

@gabynevada gabynevada commented Aug 26, 2025

Summary

  • Adds support for using IValueProvider (including EndpointReference) in Dapr component metadata configuration
  • Enables dynamic endpoint resolution for Dapr components at runtime
  • Maintains backward compatibility with existing string and ParameterResource overloads

Changes

  • Added new WithMetadata overload accepting IValueProvider for flexible metadata configuration
  • Implemented deferred value resolution through environment variables and secretKeyRef mechanism
  • Added DaprComponentValueProviderAnnotation to track value providers requiring resolution
  • Updated lifecycle hook to resolve and inject endpoint values at runtime
  • Enhanced DaprComponentSchema with async value resolution capabilities

Test Plan

  • Added unit tests for EndpointReference metadata configuration
  • Added tests for value provider resolution in component schema
  • Added tests for multiple endpoint references
  • Updated example to demonstrate Redis endpoint usage with the new feature
  • Verified backward compatibility with existing metadata configuration methods

Example Usage

var redis = builder.AddRedis("redis");

var redisHost= redis.Resource.PrimaryEndpoint.Property(EndpointProperty.Host);
var redisTargetPort = redis.Resource.PrimaryEndpoint.Property(EndpointProperty.TargetPort);

var pubsub = builder
  .AddDaprPubSub("pubsub")
  .WithMetadata(
    "redisHost",
    ReferenceExpression.Create(
      $"{redisHost}:{redisTargetPort}"
    )
  );

This allows Dapr components to dynamically reference Aspire resource endpoints that are only known at runtime.

Closes #604

gabynevada and others added 9 commits August 25, 2025 20:16
Implements IResourceWithWaitSupport for IDaprSidecarResource to enable proper
service startup sequencing. This allows Dapr sidecars to wait for dependent
services (like Redis for pubsub) to be ready before starting, preventing
component loading failures during application startup.

Fixes CommunityToolkit#604

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Added new WithMetadata overload that accepts EndpointReference parameter
- Extracts URL from EndpointReference for metadata configuration
- Added comprehensive tests for the new functionality

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 26, 2025 05:14
@gabynevada gabynevada changed the title feat: add IValueProvider support for Dapr component metadata feat(dapr): add IValueProvider support for component metadata configuration Aug 26, 2025

This comment was marked as outdated.

@gabynevada gabynevada closed this Aug 26, 2025
gabynevada and others added 5 commits August 26, 2025 01:30
…tern

- Removed placeholder values from DaprDistributedApplicationLifecycleHook
- Replaced with cleaner hasValueProviders flag for tracking value providers
- Consolidated tests from EndpointReferenceTests into ResourceBuilderExtensionsTests
- Deleted redundant EndpointReferenceTests.cs file
- All tests pass with cleaner separation of concerns

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
/// Represents a Dapr sidecar resource.
/// </summary>
public interface IDaprSidecarResource : IResource
public interface IDaprSidecarResource : IResource, IResourceWithWaitSupport
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you implement the waiting? To make this work, the resource needs a lifecycle, see https://github.com/dotnet/aspire/blob/main/docs/specs/appmodel.md#built-in-resources--lifecycle.

When you implement IResourceWithWaitSupport, at some point you have to raise https://github.com/dotnet/aspire/blob/364382724b4cd3590ec75010525ce36ef0b5e769/src/Aspire.Hosting/ConnectionStringBuilderExtensions.cs#L92

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented it, don't know if it's the correct approach but I followed the examples. Also implemented on the IDaprComponentResource as it seems it was missing support

gabynevada and others added 5 commits August 26, 2025 10:16
…ation

- Add comprehensive XML documentation for WithMetadata method explaining runtime value resolution
- Implement DaprSidecarAvailableEvent for sidecar lifecycle tracking
- Add automatic dependency detection and wait logic for value provider resources
- Ensure sidecars wait for dependent resources before starting
- Add proper state management for sidecar resources (NotStarted -> Starting -> Running)
- Improve error handling with FailedToStart state on initialization errors

This improves reliability when using dynamic endpoint references and ensures proper initialization order.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
…ifecycle

- Add SetupComponentLifecycle method to configure WaitAnnotations for Dapr components
- Extract resource dependencies from value provider annotations
- Ensure components wait for their dependencies before becoming ready
- Follow the same pattern as SetupSidecarLifecycle for consistency

This ensures proper dependency ordering when Dapr components reference other resources
through value providers, preventing initialization failures.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@gabynevada gabynevada requested a review from Copilot August 26, 2025 17:24
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for using IValueProvider (including EndpointReference) in Dapr component metadata configuration, enabling dynamic endpoint resolution at runtime while maintaining backward compatibility.

Key changes include:

  • Added new WithMetadata overload accepting IValueProvider for flexible metadata configuration
  • Implemented deferred value resolution through environment variables and secretKeyRef mechanism
  • Enhanced lifecycle management with proper dependency tracking and state management for Dapr sidecars

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Shared/Dapr/Core/DaprMetadataResourceBuilderExtensions.cs Added new WithMetadata overload for IValueProvider with environment variable generation and secretKeyRef configuration
src/Shared/Dapr/Core/DaprDistributedApplicationLifecycleHook.cs Enhanced lifecycle hook with value provider resolution, dependency management, and async environment variable setup
src/Shared/Dapr/Core/DaprComponentValueProviderAnnotation.cs New annotation class to track value providers requiring deferred resolution
src/Shared/Dapr/Core/DaprComponentSchema.cs Added async value resolution capabilities and new metadata provider type
src/Shared/Dapr/Core/IDistributedApplicationComponentBuilderExtensions.cs Enhanced sidecar lifecycle with dependency tracking, state management, and proper event publishing
src/Shared/Dapr/Core/IDaprSidecarResource.cs Added IResourceWithWaitSupport interface for proper dependency management
src/Shared/Dapr/Core/DaprSidecarEvents.cs New event class for Dapr sidecar availability notifications
src/Shared/Dapr/Core/DaprComponentMetadataAnnotation.cs Updated annotation to support async configuration with cancellation tokens
tests/dapr-shared/ResourceBuilderExtensionsTests.cs Added comprehensive tests for EndpointReference metadata configuration and value provider resolution
tests/CommunityToolkit.Aspire.Hosting.Dapr.Tests/WithDaprSidecarTests.cs Added test verifying sidecar WaitFor functionality
examples/dapr/CommunityToolkit.Aspire.Hosting.Dapr.AppHost/Program.cs Updated example to demonstrate Redis endpoint usage with the new IValueProvider feature

Properties = [],
ResourceType = "DaprSidecar",
IsHidden = true,
State = KnownResourceStates.NotStarted
Copy link

Copilot AI Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The State property assignment should be moved to after the resource is created but before the configureSidecar call, as the configureSidecar callback might expect the resource to have an initial state set.

Copilot uses AI. Check for mistakes.
Comment on lines +145 to +157
daprSidecar.Annotations.Add(new EnvironmentCallbackAnnotation(async context =>
{
foreach (var secret in secrets)
{
context.EnvironmentVariables.TryAdd(secret.Key, secret.Value);
}


// Add value provider references
foreach (var (envVarName, valueProvider) in endpointEnvironmentVars)
{
var value = await valueProvider.GetValueAsync(context.CancellationToken);
context.EnvironmentVariables.TryAdd(envVarName, value ?? string.Empty);
}
Copy link

Copilot AI Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The EnvironmentCallbackAnnotation constructor expects a synchronous delegate, but an async lambda is being passed. This could lead to fire-and-forget behavior where exceptions are swallowed. Consider using a synchronous approach or ensure proper exception handling.

Suggested change
daprSidecar.Annotations.Add(new EnvironmentCallbackAnnotation(async context =>
{
foreach (var secret in secrets)
{
context.EnvironmentVariables.TryAdd(secret.Key, secret.Value);
}
// Add value provider references
foreach (var (envVarName, valueProvider) in endpointEnvironmentVars)
{
var value = await valueProvider.GetValueAsync(context.CancellationToken);
context.EnvironmentVariables.TryAdd(envVarName, value ?? string.Empty);
}
// Precompute all value provider results asynchronously
var precomputedEnvVars = new Dictionary<string, string>();
foreach (var secret in secrets)
{
precomputedEnvVars[secret.Key] = secret.Value;
}
foreach (var (envVarName, valueProvider) in endpointEnvironmentVars)
{
// Synchronously block here to get the value
// (since we're already in an async method, this is safe)
var value = valueProvider.GetValueAsync(cancellationToken).GetAwaiter().GetResult();
precomputedEnvVars[envVarName] = value ?? string.Empty;
}
daprSidecar.Annotations.Add(new EnvironmentCallbackAnnotation(context =>
{
foreach (var kvp in precomputedEnvVars)
{
context.EnvironmentVariables.TryAdd(kvp.Key, kvp.Value);
}

Copilot uses AI. Check for mistakes.
[YamlMember(Order = 2)]
public required string Value { get; set; }
}

Copy link

Copilot AI Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This internal class lacks XML documentation comments. As it's a key component for value provider functionality, it should have proper documentation explaining its purpose and usage.

Suggested change
/// <summary>
/// Represents a Dapr component spec metadata item whose value is provided by an <see cref="IValueProvider"/> and can be resolved asynchronously.
/// </summary>

Copilot uses AI. Check for mistakes.
@gabynevada
Copy link
Contributor Author

Here's the example:

WaitForRecording.mov

@FullStackChef
Copy link
Contributor

@gabynevada just checking the state of the PR - are you wanting this re-reviewed? is it ready to merge?

gabynevada and others added 2 commits August 30, 2025 14:01
…ations

Instead of passing WaitAnnotations from components to the Dapr CLI, components now:
- Manage their own lifecycle state (NotStarted -> Starting -> Running)
- Wait directly for their dependencies via WaitAnnotations
- CLI resources wait for component resources to be ready

This provides clearer dependency semantics where resources wait for configuration
resources, which in turn wait for their actual dependencies. The refactoring
follows the established SetupSidecarLifecycle pattern for consistency.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Components are long-lived configuration resources that should reach
"Running" state and stay there. Using WaitForCompletion was incorrect
as it expected components to reach "Completed" state.

Now using WaitType.WaitUntilHealthy which properly waits for resources
to be in a healthy/running state, matching the component lifecycle.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@gabynevada
Copy link
Contributor Author

gabynevada commented Aug 30, 2025

@FullStackChef was occupied on work so could not do the final changes. Pushed the final changes so that the dapr component resources have a lifecycle and thus other resources can wait for them to get to the running state.

Let me know if anything needs to be changed!

@github-actions github-actions bot added the Stale label Sep 5, 2025
@FullStackChef FullStackChef merged commit 63e4202 into CommunityToolkit:main Sep 6, 2025
100 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dapr sidecar WaitFor

3 participants