Add sample Temporal integration for Aspire#127
Conversation
|
|
| var connectOptions = ClientEnvConfig.LoadClientConnectOptions(); | ||
| Console.WriteLine("\nAttempting to connect client to temporal server..."); | ||
|
|
||
| var client = await TemporalClient.ConnectAsync(connectOptions); |
There was a problem hiding this comment.
Aren't you supposed to be able to use Aspire to get client connectivity information too? Even if it is powered by env vars under the hood, one might expect a type-safe Aspire way to get a client or client options for the given "temporal" reference from other project.
There was a problem hiding this comment.
Aspire only does that through environment variables. There isn't anything type-safe configuration support specifically from aspire. Typically library authors would create their own using Microsoft.Extensions.Configuration bindings
There was a problem hiding this comment.
I mean from a caller POV. For instance, stuff like https://aspire.dev/integrations/databases/postgres/postgres-get-started/?lang=csharp#use-the-integration-in-client-projects. I would expect that you can just put a client on the DI container w/ a single Aspire helper. Sure you can always still manually create clients.
There was a problem hiding this comment.
I mean we could create an extension method that makes that a little nicer, but that would be a client specific library/package. From an aspire API perspective, they just inject configuration properties that get consumed .
There was a problem hiding this comment.
They inject configuration properties for those not wanting to use the helper, but they also provide a helper. I figure the nice aspect of "Aspire helper to make server, Aspire helper to make client" is valuable even though they can use the env vars directly of course, same as Postgres there.
There was a problem hiding this comment.
@cecilphillip I think what you have is idiomatic to Temporal and Aspire and what you'd want to do if a customer would actually deploy this from their Aspire setup.
There was a problem hiding this comment.
@jmaeagle99 What's currently in this PR covers a hosting integration. We could also include a client integration at some point later on.
Co-authored-by: Chad Retz <chad@temporal.io>
There was a problem hiding this comment.
Pull request overview
This PR adds a new Aspire integration sample set for Temporal and introduces a Temporal.Extensions.Aspire.Hosting library that defines Temporal resources (local dev server, Docker container, and Temporal CLI) with health checks and environment variable wiring for dependent projects.
Changes:
- Added an Aspire sample AppHost + client/worker + workflow projects under
src/AspireIntegrations. - Introduced
Temporal.Extensions.Aspire.Hostingwith custom Temporal resources, options, lifecycle subscriber, and health check extensions. - Added documentation and Aspire settings for running the new sample.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| TemporalioSamples.sln | Adds the new Aspire integration projects into the solution under a new folder. |
| src/AspireIntegrations/TemporalioSamples.SampleWorkflow/TemporalioSamples.SampleWorkflow.csproj | New workflow project for the Aspire sample. |
| src/AspireIntegrations/TemporalioSamples.SampleWorkflow/SimpleWorkflow.cs | Sample workflow definition. |
| src/AspireIntegrations/TemporalioSamples.SampleWorkflow/SimpleActivities.cs | Sample activity definition. |
| src/AspireIntegrations/TemporalioSamples.SampleWorker/TemporalioSamples.SampleWorker.csproj | New worker project for the Aspire sample. |
| src/AspireIntegrations/TemporalioSamples.SampleWorker/Program.cs | Configures and runs a hosted Temporal worker via env-configured connectivity. |
| src/AspireIntegrations/TemporalioSamples.SampleClient/TemporalioSamples.SampleClient.csproj | New client project for the Aspire sample. |
| src/AspireIntegrations/TemporalioSamples.SampleClient/Program.cs | Connects to Temporal and starts the sample workflow. |
| src/AspireIntegrations/TemporalioSamples.SampleAppHost/TemporalioSamples.SampleAppHost.csproj | New Aspire AppHost project referencing the Temporal Aspire hosting library and sample projects. |
| src/AspireIntegrations/TemporalioSamples.SampleAppHost/Properties/launchSettings.json | Launch profiles for the AppHost. |
| src/AspireIntegrations/TemporalioSamples.SampleAppHost/appsettings.json | Logging configuration for AppHost. |
| src/AspireIntegrations/TemporalioSamples.SampleAppHost/appsettings.Development.json | Development logging configuration for AppHost. |
| src/AspireIntegrations/TemporalioSamples.SampleAppHost/AppHost.cs | Wires up the Temporal local dev server resource and references it from worker/client. |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalResourceOptions.cs | Adds Temporal resource options layered on Temporal’s local environment options. |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalResourceConstants.cs | Defines endpoint names, default ports, and image constants. |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalLocalResourceSubscriber.cs | Manages local dev server lifecycle (start on init, shutdown on stop). |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalLocalResourceExtensions.cs | Adds AddTemporalLocalDevServer, endpoints, health checks, and dashboard commands. |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalLocalResource.cs | Resource model for the local dev server. |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalHealthCheckBuilderExtensions.cs | Adds health check registration helper for Temporal connectivity. |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalHealthCheck.cs | Implements the Temporal connectivity health check. |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalContainerResource.cs | Container-based Temporal resource + connection string expression. |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalContainerOptions.cs | Container-specific options (image tag) for Temporal. |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalContainerBuilderExtensions.cs | Adds Docker-based Temporal dev server resource and wiring helpers. |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalCliServerResourceExtensions.cs | Adds Temporal CLI-based server resource and wiring helpers. |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/TemporalCliServerResource.cs | Executable-based Temporal resource + connection string expression. |
| src/AspireIntegrations/Temporal.Extensions.Aspire.Hosting/Temporal.Extensions.Aspire.Hosting.csproj | New hosting library project definition and dependencies. |
| src/AspireIntegrations/README.md | Adds documentation for running/using the new Aspire integration and configuration options. |
| src/AspireIntegrations/.aspire/settings.json | Points Aspire tooling at the new AppHost project. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net10.0</TargetFramework> |
| public new List<string> AdditionalNamespaces | ||
| { | ||
| get => additionalNamespaces.Count > 0 ? additionalNamespaces : [Namespace]; | ||
| set => additionalNamespaces = value ?? []; |
| if (resource.WorkflowEnvironment != null) | ||
| { | ||
| resourceLogger.LogInformation("Shutting down Temporal test server '{ResourceName}'...", resource.Name); | ||
| await resource.WorkflowEnvironment.ShutdownAsync(); | ||
| resource.WorkflowEnvironment = null; | ||
| resourceLogger.LogInformation("Temporal test server '{ResourceName}' shut down successfully.", resource.Name); | ||
| } | ||
|
|
||
| // Publish ResourceStoppedEvent to trigger subscriber cleanup and keep _environments dictionary in sync | ||
| var resourceEvent = new ResourceEvent(resource, resource.Name, new CustomResourceSnapshot | ||
| { | ||
| ResourceType = "temporal-local", | ||
| CreationTimeStamp = DateTime.UtcNow, | ||
| State = KnownResourceStates.Exited, | ||
| Properties = [] | ||
| }); | ||
| var stoppedEvent = new ResourceStoppedEvent(resource, context.ServiceProvider, resourceEvent); | ||
| await eventing.PublishAsync(stoppedEvent, context.CancellationToken); | ||
|
|
| { | ||
| try | ||
| { | ||
| await TemporalClient.ConnectAsync(clientConnectOptions); |
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net10.0</TargetFramework> |
There was a problem hiding this comment.
Let's move these down to net8.0 since that is what the samples repo is currently using; I believe that is supported even with the most recent versions of Aspire.
|
|
||
| return builder.AddResource(resource) | ||
| .WithArgs(BuildContainerArgs(resource.Options)) | ||
| .ExcludeFromManifest().WithEndpoint( |
There was a problem hiding this comment.
nit: put .WithEndpoint( on its own line
| var connectOptions = ClientEnvConfig.LoadClientConnectOptions(); | ||
| Console.WriteLine("\nAttempting to connect client to temporal server..."); | ||
|
|
||
| var client = await TemporalClient.ConnectAsync(connectOptions); |
There was a problem hiding this comment.
@cecilphillip I think what you have is idiomatic to Temporal and Aspire and what you'd want to do if a customer would actually deploy this from their Aspire setup.
|
|
||
| // Add a worker project that depends on Temporal | ||
| builder.AddProject<Projects.SampleWorker>("worker") | ||
| .WaitFor(temporal) |
There was a problem hiding this comment.
Should we do this automatically for them in the .WithReference calls?
There was a problem hiding this comment.
that's an Apsire pattern that I've wondered about in general. It should be fine
…dd Aspire integration tests
…ssing dashboard URLs - Switch AppHost sample from AddTemporalLocalDevServer to AddTemporalCliServer - Add TemporalCliLocator with PATH guard so AddTemporalCliServer throws immediately with an actionable error when the temporal CLI is not installed - Fix temporal-local dashboard URLs by supplying explicit port values on WithHttpEndpoint for the UI and metrics endpoints - Fix temporal-cli-server FailedToStart caused by ConnectionStringAvailableEvent callback throwing when the CLI process hasn't bound its port yet; replace with non-throwing retry loop (up to 30 x 500ms attempts) - Refactor health-check client accessor from Func<ITemporalClient?> to Func<CancellationToken, Task<ITemporalClient?>> so each probe can lazily reconnect; Unhealthy resources recover to Healthy once the server is ready without requiring a manual restart - Add 28 unit tests covering TemporalHealthCheck, TemporalHealthCheckHelper, TemporalCliLocator, TemporalResourceOptions, TemporalEnvironmentHelper, and TemporalCliServerResourceExtensions
…y guard - Add explicit port to all three WithEndpoint/WithHttpEndpoint calls in AddTemporalCliServer so the Aspire dashboard renders clickable URLs for the CLI resource (parity with local and container resources) - Add isProxied: false to all three endpoint registrations in AddTemporalDevContainer; without it Aspire's DCP proxy intercepts ports including gRPC on 7233, preventing the Temporal client from connecting and keeping the resource permanently Unhealthy - Add internal AddTemporalCliServer overload with injectable isTemporalCliAvailable seam so tests can exercise the public extension method path without depending on temporal being on the test machine PATH - Replace AddTemporalCliServer_Throws_WhenTemporalCliNotOnPath test: now calls the public extension method via builder rather than the helper directly - Add AddTemporalCliServer_RegistersExplicitPortsOnAllEndpoints regression test - Fix missing trailing newline in aspire.config.json - Switch AppHost sample to AddTemporalDevContainer
- Replace nullable 'as' casts with explicit casts for type safety - Remove conflicting VersionOverride from csproj (use central package management) - Fix StyleCop spacing violation in SimpleWorkflow - Correct README examples: AddTemporalCliServer → AddTemporalLocalDevServer - Update README project references: Projects.SampleWorker → Projects.TemporalioSamples_SampleWorker - Fix container metrics port to use constant instead of variable
Expand CLI-based deployment documentation with advanced configuration options for ports, namespaces, search attributes, and dynamic config values. This brings the CLI-based section to feature parity with Local Server and Container-based sections.
Add XML documentation comments for: - All public types (resources, extension classes, options) - All public and internal methods and properties - All method parameters and return values - All public constants with their descriptions Includes documentation for: - Resource classes (TemporalCliServerResource, TemporalContainerResource, TemporalLocalResource) - Extension methods for AddTemporalCliServer, AddTemporalDevContainer, AddTemporalLocalDevServer - Helper classes (TemporalArgsBuilder, TemporalEnvironmentHelper, TemporalHealthCheck) - Configuration classes (TemporalResourceOptions, TemporalContainerOptions) - Event subscribers and lifecycle management This addresses StyleCop rules SA1611 (missing parameter docs) and SA1615 (missing return value docs).
What was changed
This PR adds comprehensive .NET Aspire integration for Temporal, including:
Custom Aspire resource definitions for Temporal server deployment with three deployment models:
Temporalio.Testing.WorkflowEnvironmentTemporal.Extensions.Aspire.Hostinglibrary with:Sample Aspire application demonstrating the integration:
TemporalioSamples.SampleAppHost- Aspire app host configuring Temporal resourcesTemporalioSamples.SampleWorker- Worker service consuming workflows/activitiesTemporalioSamples.SampleClient- Client triggering workflow executionTemporalioSamples.SampleWorkflow- Simple workflow and activity definitions