Skip to content

Add MailDev and MailKit custom integration sample - #1891

Open
David Pine (IEvangelist) wants to merge 2 commits into
mainfrom
dapine/add-maildev-mailkit-sample
Open

Add MailDev and MailKit custom integration sample#1891
David Pine (IEvangelist) wants to merge 2 commits into
mainfrom
dapine/add-maildev-mailkit-sample

Conversation

@IEvangelist

Copy link
Copy Markdown
Member

Summary

  • add an Aspire 13.5 custom MailDev hosting integration exported through ATS
  • add a MailKit client integration with health checks, tracing, metrics, and secure credentials
  • provide a runnable TypeScript AppHost plus a compile-validated C# equivalent
  • add a newsletter API and focused hosting/client tests

Validation

  • MailDev hosting tests: 4 passed
  • MailKit client tests: 8 passed
  • TypeScript AppHost compile and lint passed
  • end-to-end subscribe/unsubscribe flow verified against MailDev
  • vulnerable package checks found no known vulnerabilities

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a new Aspire sample (samples/maildev-mailkit) demonstrating a custom MailDev hosting integration exported via the Aspire Type System (ATS) and a MailKit client integration consumed by a newsletter API, with both TypeScript and C# AppHost options.

Changes:

  • Introduces MailDev hosting integration (AddMailDev, MailDevResource) and a MailKit client integration with health checks, tracing, and metrics.
  • Adds a runnable TypeScript AppHost plus a compile-validated C# AppHost equivalent, along with sample newsletter endpoints.
  • Adds focused unit tests for the hosting/client integrations and documents setup/run/test steps.

Reviewed changes

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

Show a summary per file
File Description
samples/maildev-mailkit/tsconfig.apphost.json TypeScript compiler config for the sample AppHost build output.
samples/maildev-mailkit/ServiceDefaults/ServiceDefaults.csproj New ServiceDefaults project to share health check + OpenTelemetry wiring.
samples/maildev-mailkit/ServiceDefaults/Extensions.cs Implements ServiceDefaults extensions and endpoint mapping.
samples/maildev-mailkit/README.md Documentation for ATS exports, credentials flow, running and testing the sample.
samples/maildev-mailkit/package.json Node package scripts/deps for building and linting the TypeScript AppHost.
samples/maildev-mailkit/package-lock.json Locks Node dependencies for reproducible installs.
samples/maildev-mailkit/NewsletterService/Properties/launchSettings.json Local run profiles for the newsletter API.
samples/maildev-mailkit/NewsletterService/Program.cs Newsletter subscribe/unsubscribe endpoints sending SMTP mail via MailKit integration.
samples/maildev-mailkit/NewsletterService/NewsletterService.csproj Newsletter API project wiring (OpenAPI/Scalar + project references).
samples/maildev-mailkit/MailKit.Client/Properties/AssemblyInfo.cs Exposes internals to MailKit client tests.
samples/maildev-mailkit/MailKit.Client/MailKitHealthCheck.cs Health check that validates SMTP connectivity via the factory.
samples/maildev-mailkit/MailKit.Client/MailKitExtensions.cs Builder extensions registering MailKit factory + OTel + health checks.
samples/maildev-mailkit/MailKit.Client/MailKitClientSettings.cs Connection string parsing and configuration settings for SMTP.
samples/maildev-mailkit/MailKit.Client/MailKitClientFactory.cs Scoped SMTP client factory with connection caching per scope.
samples/maildev-mailkit/MailKit.Client/MailKit.Client.csproj MailKit client integration package references/TFM.
samples/maildev-mailkit/MailKit.Client.Tests/MailKitExtensionsTests.cs Unit tests for registration behavior (scoped/keyed/validation).
samples/maildev-mailkit/MailKit.Client.Tests/MailKitClientSettingsTests.cs Unit tests for connection string parsing behavior.
samples/maildev-mailkit/MailKit.Client.Tests/MailKit.Client.Tests.csproj Test project setup for MailKit client integration.
samples/maildev-mailkit/MailDev.Hosting/MailDevResourceBuilderExtensions.cs Adds the MailDev container resource and exports it for ATS.
samples/maildev-mailkit/MailDev.Hosting/MailDevResource.cs Defines MailDev resource model + deferred connection string expression.
samples/maildev-mailkit/MailDev.Hosting/MailDev.Hosting.csproj Hosting integration project for MailDev (Aspire.Hosting dependency).
samples/maildev-mailkit/MailDev.Hosting.Tests/MailDevResourceTests.cs Unit tests validating container config, ATS exports, and connection string expression.
samples/maildev-mailkit/MailDev.Hosting.Tests/MailDev.Hosting.Tests.csproj Test project setup for MailDev hosting integration.
samples/maildev-mailkit/eslint.config.mjs ESLint configuration for the TypeScript AppHost file.
samples/maildev-mailkit/CSharpAppHost/Program.cs C# equivalent AppHost wiring MailDev + newsletter service.
samples/maildev-mailkit/CSharpAppHost/CSharpAppHost.csproj C# AppHost project setup referencing Aspire.Hosting + MailDev.Hosting.
samples/maildev-mailkit/aspire.config.json Configures the TypeScript AppHost and ATS package export for restore.
samples/maildev-mailkit/apphost.mts Runnable TypeScript AppHost consuming ATS-generated APIs.
samples/maildev-mailkit/.gitignore Ignores .aspire/ and build outputs for the sample.
README.md Adds the new sample entry to the repository’s samples table.
Files not reviewed (1)
  • samples/maildev-mailkit/package-lock.json: Generated file

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread samples/maildev-mailkit/ServiceDefaults/Extensions.cs
Comment thread samples/maildev-mailkit/package-lock.json
if (!settings.DisableHealthChecks)
{
builder.Services.AddHealthChecks().Add(new HealthCheckRegistration(
serviceKey is null ? "MailKit" : $"MailKit_{connectionName}",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for fixing the duplicate names. I think this is only half fixed: both unkeyed checks still call GetRequiredService<MailKitClientFactory>(), so the last registration wins. A focused test resolves both MailKit_primary and MailKit_secondary to smtp://localhost:1026/. Could we bind each check to its connection and assert the endpoint here, rather than just the label?

@IEvangelist
David Pine (IEvangelist) force-pushed the dapine/add-maildev-mailkit-sample branch from 6a6bbcd to ad93e11 Compare August 14, 2026 16:48
@IEvangelist
David Pine (IEvangelist) force-pushed the dapine/add-maildev-mailkit-sample branch from ad93e11 to f42053e Compare August 14, 2026 16:56
builder, $"{name}-password");
var resource = new MailDevResource(name, username?.Resource, passwordParameter);

return builder.AddResource(resource)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since this sample is marked run-only, could we exclude MailDev from the manifest? I confirmed the pinned head publishes it as container.v0 with its SMTP credential parameter, which teaches the wrong lifecycle for a development mail sink. .ExcludeFromManifest() plus a manifest assertion should cover it.

.WithHttpEndpoint(
targetPort: 1080,
port: httpPort,
name: MailDevResource.HttpEndpointName)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

WaitFor(maildev) only waits for the container to reach Running here because the resource has no health check. MailDev exposes /healthz; could we add an HTTP health check so the newsletter service waits for actual readiness rather than racing SMTP startup?

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we add a .slnx for this sample containing the workload, integrations, AppHost, and tests? Repo CI discovers solutions or AppHosts, and this TypeScript sample path only restores ATS + runs npm run aspire:build; NewsletterService, ServiceDefaults, MailKit.Client, the C# AppHost, and the test projects are otherwise not compiled/tested by CI.

{
if (settings.Endpoint is null)
{
throw new InvalidOperationException("The MailKit SMTP endpoint is not configured.");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we avoid throwing while constructing the health check dependency? Running NewsletterService without ConnectionStrings:maildev, GET /health returns an unhandled 500 from this line before MailKitHealthCheck.CheckHealthAsync can convert it to Unhealthy. The existing endpoint validation in GetSmtpClientAsync lets the health check report correctly.

{
var message = new MimeMessage();
message.From.Add(new MailboxAddress("Aspire Newsletter", "newsletter@example.com"));
message.To.Add(MailboxAddress.Parse(email));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could we validate with MailboxAddress.TryParse and return 400? I sent {"email":"bad@@example.com"} against the pinned service and this throws MimeKit.ParseException, returning an empty 500; {} and an empty value do the same.

/// <inheritdoc />
public ReferenceExpression ConnectionStringExpression =>
ReferenceExpression.Create(
$"Endpoint=smtp://{SmtpEndpoint.Property(EndpointProperty.HostAndPort)};Username={UsernameReference};Password={PasswordParameter}");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since this is an integration-authoring sample, could we implement GetConnectionProperties() for Host, Port, Username, Password, and Uri? The current default is empty, so generated/polyglot consumers only see the opaque connection string and miss the structured pattern we are trying to teach.

<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EnableAspireIntegrationAnalyzers>true</EnableAspireIntegrationAnalyzers>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These XML comments do not reach the generated ATS SDK because this project does not emit MailDev.Hosting.xml (confirmed in the build output). Could we enable GenerateDocumentationFile and inspect or assert the generated .d.ts JSDoc?

@adamint Adam Ratzman (adamint) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed pinned head 7ff86d2d6fd5ba479889cfa5a789f641ba434596. The overall shape is sound and these are fixable in place, so I am approving. I think the must-fix items are keeping MailDev run-only, adding real readiness, making repo CI compile/test the .NET projects, and returning Unhealthy instead of an unhandled 500 for missing MailKit configuration. I also left comments on request validation, connection properties, and ATS docs.

The PR head advanced while this review ran; this decision is intentionally attached to the requested pinned commit.

@adamint Adam Ratzman (adamint) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Incremental review of f42053e: the MailKit naming fix is incomplete, so I reopened that thread with a failing focused test. Ubuntu is failing in the unrelated Angular sample (typescript@7.0.2 vs @angular-devkit/build-angular requiring <6.1), which #1866 already fixed on main. This head is still conflicting with main and red, so I’m leaving a comment rather than approving. Please merge main, address the MailKit thread, and rerun CI.

@adamint Adam Ratzman (adamint) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approving this as the remaining issue is fixable in place. The health-check thread is still must-fix before merge, and this branch needs updating from main to clear the unrelated Angular failure and workflow conflict.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants