diff --git a/src/frontend/src/assets/integrations/custom-integrations/maildev-details-light.png b/src/frontend/src/assets/integrations/custom-integrations/maildev-details-light.png index 254a8e746..b75446d7c 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/maildev-details-light.png and b/src/frontend/src/assets/integrations/custom-integrations/maildev-details-light.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/maildev-details.png b/src/frontend/src/assets/integrations/custom-integrations/maildev-details.png index 690bed7a7..d8a45571b 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/maildev-details.png and b/src/frontend/src/assets/integrations/custom-integrations/maildev-details.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/maildev-envvar-light.png b/src/frontend/src/assets/integrations/custom-integrations/maildev-envvar-light.png index 476ae487e..e6c44b830 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/maildev-envvar-light.png and b/src/frontend/src/assets/integrations/custom-integrations/maildev-envvar-light.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/maildev-envvar.png b/src/frontend/src/assets/integrations/custom-integrations/maildev-envvar.png index 48e686d3d..a7d352929 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/maildev-envvar.png and b/src/frontend/src/assets/integrations/custom-integrations/maildev-envvar.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/maildev-in-aspire-dashboard-light.png b/src/frontend/src/assets/integrations/custom-integrations/maildev-in-aspire-dashboard-light.png index f270b5516..5eb001c9b 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/maildev-in-aspire-dashboard-light.png and b/src/frontend/src/assets/integrations/custom-integrations/maildev-in-aspire-dashboard-light.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/maildev-in-aspire-dashboard.png b/src/frontend/src/assets/integrations/custom-integrations/maildev-in-aspire-dashboard.png index a0cd11d5e..beb99cdd9 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/maildev-in-aspire-dashboard.png and b/src/frontend/src/assets/integrations/custom-integrations/maildev-in-aspire-dashboard.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/maildev-with-newsletterservice-dashboard-light.png b/src/frontend/src/assets/integrations/custom-integrations/maildev-with-newsletterservice-dashboard-light.png index da457b70e..ae5ada5e1 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/maildev-with-newsletterservice-dashboard-light.png and b/src/frontend/src/assets/integrations/custom-integrations/maildev-with-newsletterservice-dashboard-light.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/maildev-with-newsletterservice-dashboard.png b/src/frontend/src/assets/integrations/custom-integrations/maildev-with-newsletterservice-dashboard.png index fedbf6942..9b5ceb534 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/maildev-with-newsletterservice-dashboard.png and b/src/frontend/src/assets/integrations/custom-integrations/maildev-with-newsletterservice-dashboard.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/maildevresource-empty-dashboard-light.png b/src/frontend/src/assets/integrations/custom-integrations/maildevresource-empty-dashboard-light.png index cd6379913..dbea50169 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/maildevresource-empty-dashboard-light.png and b/src/frontend/src/assets/integrations/custom-integrations/maildevresource-empty-dashboard-light.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/maildevresource-empty-dashboard.png b/src/frontend/src/assets/integrations/custom-integrations/maildevresource-empty-dashboard.png index 45393fa5f..3ecf739f1 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/maildevresource-empty-dashboard.png and b/src/frontend/src/assets/integrations/custom-integrations/maildevresource-empty-dashboard.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-dashboard-light.png b/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-dashboard-light.png index a67b2271b..3e0d38ff9 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-dashboard-light.png and b/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-dashboard-light.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-dashboard.png b/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-dashboard.png index eb2953d58..3fa56697a 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-dashboard.png and b/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-dashboard.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-graph-dashboard-light.png b/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-graph-dashboard-light.png index ee1031c92..2ae2df7a4 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-graph-dashboard-light.png and b/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-graph-dashboard-light.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-graph-dashboard.png b/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-graph-dashboard.png index 27c8bf529..16825d024 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-graph-dashboard.png and b/src/frontend/src/assets/integrations/custom-integrations/mailkit-metrics-graph-dashboard.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/newsletter-details-light.png b/src/frontend/src/assets/integrations/custom-integrations/newsletter-details-light.png index 9e319de68..4db7b71eb 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/newsletter-details-light.png and b/src/frontend/src/assets/integrations/custom-integrations/newsletter-details-light.png differ diff --git a/src/frontend/src/assets/integrations/custom-integrations/newsletter-details.png b/src/frontend/src/assets/integrations/custom-integrations/newsletter-details.png index ae19d5cbf..b49228376 100644 Binary files a/src/frontend/src/assets/integrations/custom-integrations/newsletter-details.png and b/src/frontend/src/assets/integrations/custom-integrations/newsletter-details.png differ diff --git a/src/frontend/src/content/docs/integrations/custom-integrations/client-integrations.mdx b/src/frontend/src/content/docs/integrations/custom-integrations/client-integrations.mdx index 0e6ae212c..7212e4972 100644 --- a/src/frontend/src/content/docs/integrations/custom-integrations/client-integrations.mdx +++ b/src/frontend/src/content/docs/integrations/custom-integrations/client-integrations.mdx @@ -21,6 +21,8 @@ import mailkitMetricsGraphDashboardLight from '@assets/integrations/custom-integ This article is a continuation of the [Create custom hosting integrations](/integrations/custom-integrations/hosting-integrations/) article. It guides you through creating an Aspire client integration that uses [MailKit](https://github.com/jstedfast/MailKit) to send emails. This integration is then added into the Newsletter app you previously built. The previous example omitted the creation of a client integration and instead relied on the existing .NET `SmtpClient`. It's best to use MailKit's `SmtpClient` over the official .NET `SmtpClient` for sending emails, as it's more modern and supports more features/protocols. For more information, see [.NET SmtpClient: Remarks](https://learn.microsoft.com/dotnet/api/system.net.mail.smtpclient#remarks). +Aspire Type System (ATS) generates TypeScript APIs for the hosting integration. The MailKit client integration remains standard .NET code and doesn't need ATS annotations. To see both integrations used from a TypeScript AppHost, explore the [MailDev and MailKit custom integrations sample](https://github.com/microsoft/aspire-samples/tree/main/samples/maildev-mailkit). For details about exporting hosting integration APIs, see [Multi-language integrations](/extensibility/multi-language-integration-authoring/). + ## Prerequisites If you're following along, you should have a Newsletter app from the steps in the [Create custom hosting integrations](/integrations/custom-integrations/hosting-integrations/) article. @@ -59,12 +61,12 @@ The next step is to add the following NuGet packages that the integration relies + -+ ++ + + + + -+ ++ + diff --git a/src/frontend/src/content/docs/integrations/custom-integrations/hosting-integrations.mdx b/src/frontend/src/content/docs/integrations/custom-integrations/hosting-integrations.mdx index 30d9571ea..ce1adc985 100644 --- a/src/frontend/src/content/docs/integrations/custom-integrations/hosting-integrations.mdx +++ b/src/frontend/src/content/docs/integrations/custom-integrations/hosting-integrations.mdx @@ -57,12 +57,16 @@ Building a custom resource in Aspire requires the following: 2. An extension method for `IDistributedApplicationBuilder` named `Add{CustomResource}` where `{CustomResource}` is the name of the custom resource. +To use a C# hosting integration from a TypeScript AppHost, mark the resource types and extension methods that you want to expose with `[AspireExport]`. The Aspire CLI uses this metadata to generate a typed TypeScript API that calls the C# implementation. For export rules and analyzer guidance, see [Multi-language integrations](/extensibility/multi-language-integration-authoring/). + When custom resource requires optional configuration, developers may wish to implement `With*` suffixed extension methods to make these configuration options discoverable using the _builder pattern_. ## A practical example: MailDev To help understand how to develop custom resources, this article shows an example of how to build a custom resource for [MailDev](https://maildev.github.io/maildev/). MailDev is an open-source tool which provides a local mail server designed to allow developers to test e-mail sending behaviors within their app. For more information, see [the MailDev GitHub repository](https://github.com/maildev/maildev). +For a runnable version with TypeScript and C# AppHost examples, see the [MailDev and MailKit custom integrations sample](https://github.com/microsoft/aspire-samples/tree/main/samples/maildev-mailkit). + In this example you create a new Aspire project as a test environment for the MailDev resource that you create. While you can create custom resources in existing Aspire projects it's a good idea to consider whether the custom resource might be used across multiple Aspire-based solutions and should be developed as a reusable integration. ## Set up the starter project @@ -80,7 +84,7 @@ dotnet new aspire -o MailDevResource 1. When prompted to select a template, choose the **AppHost and service defaults** template — use the and keys to navigate the options. Press to select the template. 1. Enter a project name, in this example use `MailDevResource`, then press to continue. -1. Finally, use the and keys to navigate to the desired template version. This example uses 13.1.0. +1. Finally, use the and keys to navigate to the desired template version. This example uses 13.5.0. ```bash title="Change directory" @@ -140,7 +144,7 @@ Aspire resources are just classes and methods contained within a class library t 2. Add `Aspire.Hosting` to the class library as a package reference. ```bash title=".NET CLI" - dotnet add ./MailDev.Hosting/MailDev.Hosting.csproj package Aspire.Hosting --version 13.1.0 + dotnet add ./MailDev.Hosting/MailDev.Hosting.csproj package Aspire.Hosting --version 13.5.0 ``` :::note @@ -200,6 +204,7 @@ Replace the contents of the `Class1.cs` file in the `MailDev.Hosting` project, a // an alternative namespace. namespace Aspire.Hosting.ApplicationModel; +[AspireExport] public sealed class MailDevResource([ResourceName] string name) : ContainerResource(name), IResourceWithConnectionString { @@ -256,6 +261,7 @@ public static class MailDevResourceBuilderExtensions /// An instance that /// represents the added MailDev resource. /// + [AspireExport] public static IResourceBuilder AddMailDev( this IDistributedApplicationBuilder builder, [ResourceName] string name, @@ -301,7 +307,12 @@ This example uses MailDev version 2.2.1. Be sure to check the [MailDev Docker Hu ## Validate custom integration inside the AppHost -Now that the basic structure for the custom resource is complete it's time to test it in a real AppHost project. Open the `AppHost.cs` file in the `MailDevResource.AppHost` project and update it with the following code: +Now that the basic structure for the custom resource is complete, test it in an AppHost. The `[AspireExport]` attributes make the same C# implementation available to both AppHost languages. + + + + +Open the `AppHost.cs` file in the `MailDevResource.AppHost` project and update it with the following code: ```csharp title="AppHost.cs" var builder = DistributedApplication.CreateBuilder(args); @@ -311,6 +322,36 @@ var maildev = builder.AddMailDev("maildev"); builder.Build().Run(); ``` + + + +Reference the local integration project from `aspire.config.json`. The key is the integration assembly name, and the project path is relative to the configuration file: + +```json title="aspire.config.json" +{ + "packages": { + "MailDev.Hosting": "MailDev.Hosting/MailDev.Hosting.csproj" + } +} +``` + +Run `aspire restore` to generate the typed API, then use `addMailDev` in the AppHost: + +```typescript title="apphost.mts" +import { createBuilder } from './.aspire/modules/aspire.mjs'; + +const builder = await createBuilder(); + +const maildev = await builder.addMailDev('maildev'); + +await builder.build().run(); +``` + +Files under `.aspire/modules` are generated and shouldn't be edited. + + + + After updating the `AppHost.cs` file, launch the AppHost project and open the dashboard: ```bash title="Aspire CLI — run" diff --git a/src/frontend/src/content/docs/integrations/custom-integrations/secure-communication.mdx b/src/frontend/src/content/docs/integrations/custom-integrations/secure-communication.mdx index 45965184e..8eef2c61c 100644 --- a/src/frontend/src/content/docs/integrations/custom-integrations/secure-communication.mdx +++ b/src/frontend/src/content/docs/integrations/custom-integrations/secure-communication.mdx @@ -3,7 +3,7 @@ title: Secure communication between integrations description: Learn how to secure communication between Aspire hosting and client integrations with HTTPS, mutual TLS, secret-backed parameters, and managed identities in production. --- -import { Aside, Steps } from '@astrojs/starlight/components'; +import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components'; import { Kbd } from 'starlight-kbd/components'; import ThemeImage from '@components/ThemeImage.astro'; import maildevDetails from '@assets/integrations/custom-integrations/maildev-details.png'; @@ -13,6 +13,8 @@ import newsletterDetailsLight from '@assets/integrations/custom-integrations/new This article is a continuation of two previous articles demonstrating the creation of [custom hosting integrations](/integrations/custom-integrations/hosting-integrations/) and [custom client integrations](/integrations/custom-integrations/client-integrations/). +The [MailDev and MailKit custom integrations sample](https://github.com/microsoft/aspire-samples/tree/main/samples/maildev-mailkit) shows the complete secure flow with both TypeScript and C# AppHost examples. The Aspire CLI uses [Aspire Type System (ATS)](/extensibility/multi-language-integration-authoring/) metadata from the C# hosting integration to generate its TypeScript API. + One of the primary benefits to Aspire is how it simplifies the configurability of resources and consuming clients (or integrations). This article demonstrates how to share authentication credentials from a custom resource in a hosting integration, to the consuming client in a custom client integration. The custom resource is a MailDev container that allows for either incoming or outgoing credentials. The custom client integration is a MailKit client that sends emails. ## Prerequisites @@ -51,8 +53,9 @@ The MailDev container supports basic authentication for both incoming and outgoi // an alternative namespace. namespace Aspire.Hosting.ApplicationModel; +[AspireExport] public sealed class MailDevResource( - string name, + [ResourceName] string name, ParameterResource? username, ParameterResource password) : ContainerResource(name), IResourceWithConnectionString @@ -123,9 +126,10 @@ public static class MailDevResourceBuilderExtensions /// An instance that /// represents the added MailDev resource. /// + [AspireExport] public static IResourceBuilder AddMailDev( this IDistributedApplicationBuilder builder, - string name, + [ResourceName] string name, int? httpPort = null, int? smtpPort = null, IResourceBuilder? username = null, @@ -174,35 +178,63 @@ internal static class MailDevContainerImageTags } ``` -The preceding code updates the `AddMailDev` extension method to include the `userName` and `password` parameters. The `WithEnvironment` method is updated to include the `UserEnvVarName` and `PasswordEnvVarName` environment variables. These environment variables are used to set the MailDev username and password. +The preceding code updates the `AddMailDev` extension method to include the `username` and `password` parameters. The `WithEnvironment` method is updated to include the `UserEnvVarName` and `PasswordEnvVarName` environment variables. These environment variables are used to set the MailDev username and password. ## Update the AppHost -Now that the resource is updated to include the username and password parameters, you need to update the AppHost to include these parameters. Update the `AppHost.cs` file in the `MailDevResource.AppHost` project with the following C# code: +Now that the resource includes username and password parameters, update the AppHost to provide them. Mark the password parameter as secret so tooling and deployment environments can handle it appropriately. + + + -```csharp {3-4,6-9} title="AppHost.cs" +```csharp title="AppHost.cs" var builder = DistributedApplication.CreateBuilder(args); var mailDevUsername = builder.AddParameter("maildev-username"); -var mailDevPassword = builder.AddParameter("maildev-password"); +var mailDevPassword = builder.AddParameter("maildev-password", secret: true); var maildev = builder.AddMailDev( name: "maildev", - userName: mailDevUsername, + username: mailDevUsername, password: mailDevPassword); builder.AddProject("newsletterservice") - .WithUrlForEndpoint("https", e => - { - e.DisplayText = "Scalar"; - e.Url += "/scalar"; - }) .WithReference(maildev); builder.Build().Run(); ``` -The preceding code adds two parameters for the MailDev username and password. It assigns these parameters to the MailDev resource. The `AddMailDev` method has two chained calls to `WithEnvironment` which includes these environment variables. + + + +After referencing the hosting integration in `aspire.config.json`, run `aspire restore`. The Aspire CLI generates the `addMailDev` method and its typed options. + +```typescript title="apphost.mts" +import { createBuilder } from './.aspire/modules/aspire.mjs'; + +const builder = await createBuilder(); + +const mailDevUsername = await builder.addParameter('maildev-username'); +const mailDevPassword = await builder.addParameter( + 'maildev-password', + { secret: true } +); + +const maildev = await builder.addMailDev('maildev', { + username: mailDevUsername, + password: mailDevPassword +}); + +await builder.addCSharpApp('newsletterservice', './NewsletterService') + .withReference(maildev); + +await builder.build().run(); +``` + + + + +The preceding code adds parameters for the MailDev username and password, then passes them to the MailDev resource. The resource provides the credentials to the container through environment variables. Next, configure the secrets for these parameters. Right-click on the `MailDevResource.AppHost` project and select `Manage User Secrets` (from within Visual Studio Code or Visual Studio). Add the following JSON to the `secrets.json` file: