From 1e1baad4a37e78a4dd2f509faba1079f3981e1d3 Mon Sep 17 00:00:00 2001 From: David Pine Date: Tue, 16 Jul 2024 15:18:27 -0500 Subject: [PATCH 01/12] Minor clean up for the MailDevResource --- .../MailDevResource/MailDev.Hosting/MailDevResource.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResource.cs b/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResource.cs index 2e76605b94..b8a3e22e59 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResource.cs +++ b/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResource.cs @@ -7,10 +7,8 @@ namespace Aspire.Hosting.ApplicationModel; public sealed class MailDevResource(string name) : ContainerResource(name), IResourceWithConnectionString { // Constants used to refer to well known-endpoint names, this is specific - // for each resource type. MailDev exposes an SMTP endpoint and a HTTP - // endpoint. + // for each resource type. MailDev exposes an SMTP endpoint. internal const string SmtpEndpointName = "smtp"; - internal const string HttpEndpointName = "http"; // An EndpointReference is a core .NET Aspire type used for keeping // track of endpoint details in expressions. Simple literal values cannot From 0b923a7710d1c15741bbdc4c80d10c2950aeab34 Mon Sep 17 00:00:00 2001 From: David Pine Date: Wed, 17 Jul 2024 09:16:34 -0500 Subject: [PATCH 02/12] Address feedback --- docs/extensibility/custom-component.md | 158 ++++++++++++++++-- .../MailKit.Client/MailKitClientSettings.cs | 39 +++-- .../MailKit.Client/MailKitExtensions.cs | 6 - 3 files changed, 163 insertions(+), 40 deletions(-) diff --git a/docs/extensibility/custom-component.md b/docs/extensibility/custom-component.md index 98c563239c..5bb8b6599f 100644 --- a/docs/extensibility/custom-component.md +++ b/docs/extensibility/custom-component.md @@ -61,21 +61,20 @@ The preceding code defines the `MailKitClientSettings` class with: ### Parse connection string logic -The settings class also contains a `ParseConnectionString` method that parses the connection string into a valid `Uri`. The configuration is expected to be provided in the following format: +The settings class also contains a `ParseConnectionString` method that parses the connection string into a valid `Uri` and optionally `NetworkCredential`. The configuration is expected to be provided in the following format: - `ConnectionStrings:`: The connection string to the SMTP server. -- `MailKit:Client:Endpoint`: The connection string to the SMTP server. +- `MailKit:Client:ConnectionString`: The connection string to the SMTP server. -If neither of these values are provided, an exception is thrown. Likewise, if there's a value but it's not a valid URI, an exception is thrown. +If neither of these values are provided, an exception is thrown. -### Parse credentials logic +When a connection string is configured, the `ParseConnectionString` method expects a connection string information to be in the form of `key=value;` pairs. The following keys are supported: -The settings class also contains a `ParseCredentials` method that parses the credentials into a valid `NetworkCredential`. The configuration is expected to be provided in the following format: +- `Endpoint`: The endpoint to connect to the SMTP server. +- `Username`: The username to authenticate with the SMTP server. +- `Password`: The password to authenticate with the SMTP server. -- `MailKit:Client:Credentials:UserName`: The username to authenticate with the SMTP server. -- `MailKit:Client:Credentials:Password`: The password to authenticate with the SMTP server. - -When credentials are configured, the `ParseCredentials` method attempts to parse the username and password from the configuration. If either the username or password is missing, an exception is thrown. +When the `Endpoint` isn't a valid `Uri` and exception is thrown. If the `Username` and `Password` are provided, a `NetworkCredential` is created and the result is assigned to the setting's `Credentials` property. ## Expose component wrapper functionality @@ -197,9 +196,139 @@ Stop the application by selecting Ctrl+C in the terminal w ### Configure MailDev credentials -The MailDev container supports basic authentication for both incoming and outgoing SMTP. To configure the credentials for incoming, you need to set the `MAILDEV_INCOMING_USER` and `MAILDEV_INCOMING_PASS` environment variables. For more information, see [MailDev: Usage](https://maildev.github.io/maildev/#usage). +The MailDev container supports basic authentication for both incoming and outgoing SMTP. To configure the credentials for incoming, you need to set the `MAILDEV_INCOMING_USER` and `MAILDEV_INCOMING_PASS` environment variables. For more information, see [MailDev: Usage](https://maildev.github.io/maildev/#usage). Update the _MailDevResource.cs_ file in the `MailDev.Hosting` project, by replacing its contents with the following C# code: + +```csharp +// For ease of discovery, resource types should be placed in +// the Aspire.Hosting.ApplicationModel namespace. If there is +// likelihood of a conflict on the resource name consider using +// an alternative namespace. +namespace Aspire.Hosting.ApplicationModel; + +public sealed class MailDevResource( + string name, + ParameterResource? username, + ParameterResource password) + : ContainerResource(name), IResourceWithConnectionString +{ + // Constants used to refer to well known-endpoint names, this is specific + // for each resource type. MailDev exposes an SMTP and HTTP endpoints. + internal const string SmtpEndpointName = "smtp"; + internal const string HttpEndpointName = "http"; + + private const string DefaultUsername = "mail-dev"; + + // An EndpointReference is a core .NET Aspire type used for keeping + // track of endpoint details in expressions. Simple literal values cannot + // be used because endpoints are not known until containers are launched. + private EndpointReference? _smtpReference; + + /// + /// Gets the parameter that contains the MailDev SMTP server username. + /// + public ParameterResource? UsernameParameter { get; } = username; + + internal ReferenceExpression UserNameReference => + UsernameParameter is not null ? + ReferenceExpression.Create($"{UsernameParameter}") : + ReferenceExpression.Create($"{DefaultUsername}"); + + /// + /// Gets the parameter that contains the MailDev SMTP server password. + /// + public ParameterResource PasswordParameter { get; } = password; + + public EndpointReference SmtpEndpoint => + _smtpReference ??= new(this, SmtpEndpointName); + + // Required property on IResourceWithConnectionString. Represents a connection + // string that applications can use to access the MailDev server. In this case + // the connection string is composed of the SmtpEndpoint endpoint reference. + public ReferenceExpression ConnectionStringExpression => + ReferenceExpression.Create( + $"Endpoint=smtp://{SmtpEndpoint.Property(EndpointProperty.Host)}:{SmtpEndpoint.Property(EndpointProperty.Port)};Username={UserNameReference};Password={PasswordParameter}" + ); +} +``` + +These updates add a `UsernameParameter` and `PasswordParameter` property. These properties are used to store the parameters for the MailDev username and password. The `ConnectionStringExpression` property is updated to include the username and password parameters in the connection string. Next, update the _MailDevResourceBuilderExtensions.cs_ file in the `MailDev.Hosting` project with the following C# code: + +```csharp +using Aspire.Hosting.ApplicationModel; + +// Put extensions in the Aspire.Hosting namespace to ease discovery as referencing +// the .NET Aspire hosting package automatically adds this namespace. +namespace Aspire.Hosting; + +public static class MailDevResourceBuilderExtensions +{ + private const string UserEnvVarName = "MAILDEV_INCOMING_USER"; + private const string PasswordEnvVarName = "MAILDEV_INCOMING_PASS"; + + /// + /// Adds the to the given + /// instance. Uses the "2.0.2" tag. + /// + /// The . + /// The name of the resource. + /// The HTTP port. + /// The SMTP port. + /// + /// An instance that + /// represents the added MailDev resource. + /// + public static IResourceBuilder AddMailDev( + this IDistributedApplicationBuilder builder, + string name, + int? httpPort = null, + int? smtpPort = null, + IResourceBuilder? userName = null, + IResourceBuilder? password = null) + { + var passwordParameter = password?.Resource ?? + ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter( + builder, $"{name}-password"); + + // The AddResource method is a core API within .NET Aspire and is + // used by resource developers to wrap a custom resource in an + // IResourceBuilder instance. Extension methods to customize + // the resource (if any exist) target the builder interface. + var resource = new MailDevResource( + name, userName?.Resource, passwordParameter); + + return builder.AddResource(resource) + .WithImage(MailDevContainerImageTags.Image) + .WithImageRegistry(MailDevContainerImageTags.Registry) + .WithImageTag(MailDevContainerImageTags.Tag) + .WithHttpEndpoint( + targetPort: 1080, + port: httpPort, + name: MailDevResource.HttpEndpointName) + .WithEndpoint( + targetPort: 1025, + port: smtpPort, + name: MailDevResource.SmtpEndpointName) + .WithEnvironment(context => + { + context.EnvironmentVariables[UserEnvVarName] = resource.UserNameReference; + context.EnvironmentVariables[PasswordEnvVarName] = resource.PasswordParameter; + }); + } +} + +// This class just contains constant strings that can be updated periodically +// when new versions of the underlying container are released. +internal static class MailDevContainerImageTags +{ + internal const string Registry = "docker.io"; + + internal const string Image = "maildev/maildev"; + + internal const string Tag = "2.0.2"; +} +``` -To configure these credentials, update the _Program.cs_ file in the `MailDevResource.AppHost` project with the following code: +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. Next, update the _Program.cs_ file in the `MailDevResource.AppHost` project with the following C# code: ```csharp var builder = DistributedApplication.CreateBuilder(args); @@ -207,9 +336,10 @@ var builder = DistributedApplication.CreateBuilder(args); var mailDevUsername = builder.AddParameter("maildev-username"); var mailDevPassword = builder.AddParameter("maildev-password"); -var maildev = builder.AddMailDev("maildev") - .WithEnvironment("MAILDEV_INCOMING_USER", mailDevUsername) - .WithEnvironment("MAILDEV_INCOMING_PASS", mailDevPassword); +var maildev = builder.AddMailDev( + name: "maildev", + userName: mailDevUsername, + password: mailDevPassword); builder.AddProject("newsletterservice") .WithReference(maildev); diff --git a/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitClientSettings.cs b/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitClientSettings.cs index c75435f179..ee89028565 100644 --- a/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitClientSettings.cs +++ b/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitClientSettings.cs @@ -1,5 +1,5 @@ -using System.Net; -using Microsoft.Extensions.Configuration; +using System.Data.Common; +using System.Net; namespace MailKit.Client; @@ -63,35 +63,34 @@ configuration section. """); } - if (Uri.TryCreate(connectionString, UriKind.Absolute, out var uri) is false) + var builder = new DbConnectionStringBuilder + { + ConnectionString = connectionString + }; + + if (builder.TryGetValue("Endpoint", out var endpoint) is false) { throw new InvalidOperationException($""" The 'ConnectionStrings:' (or 'Endpoint' key in - '{DefaultConfigSectionName}') isn't a valid URI format. + '{DefaultConfigSectionName}') is missing. """); } - Endpoint = uri; - } - - internal void ParseCredentials(IConfigurationSection credentialsSection) - { - if (credentialsSection is null or { Value: null }) + if (Uri.TryCreate(endpoint.ToString(), UriKind.Absolute, out var uri) is false) { - return; + throw new InvalidOperationException($""" + The 'ConnectionStrings:' (or 'Endpoint' key in + '{DefaultConfigSectionName}') isn't a valid URI. + """); } - var username = credentialsSection["UserName"]; - var password = credentialsSection["Password"]; + Endpoint = uri; - if (username is null || password is null) + if (builder.TryGetValue("Username", out var username) && + builder.TryGetValue("Password", out var password)) { - throw new InvalidOperationException($""" - The '{DefaultConfigSectionName}:Credentials' section cannot be empty. - Either remove Credentials altogether, or provide them. - """); + Credentials = new( + username.ToString(), password.ToString()); } - - Credentials = new(username, password); } } diff --git a/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitExtensions.cs b/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitExtensions.cs index eb9639b64d..1f9006ffec 100644 --- a/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitExtensions.cs +++ b/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitExtensions.cs @@ -87,12 +87,6 @@ private static void AddMailKitClient( settings.ParseConnectionString(connectionString); } - if (builder.Configuration.GetSection( - $"{configurationSectionName}:Credentials") is { } section) - { - settings.ParseCredentials(section); - } - configureSettings?.Invoke(settings); if (serviceKey is null) From 34f219fd5c4df11227adc375ec3205e788f5a5d4 Mon Sep 17 00:00:00 2001 From: David Pine Date: Wed, 17 Jul 2024 09:48:11 -0500 Subject: [PATCH 03/12] Fix heading term --- docs/extensibility/custom-component.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/extensibility/custom-component.md b/docs/extensibility/custom-component.md index 5bb8b6599f..893402d3dc 100644 --- a/docs/extensibility/custom-component.md +++ b/docs/extensibility/custom-component.md @@ -76,7 +76,7 @@ When a connection string is configured, the `ParseConnectionString` method expec When the `Endpoint` isn't a valid `Uri` and exception is thrown. If the `Username` and `Password` are provided, a `NetworkCredential` is created and the result is assigned to the setting's `Credentials` property. -## Expose component wrapper functionality +## Expose client functionality The goal of .NET Aspire components is to expose the underlying client library to consumers through dependency injection. With MailKit and for this example, the `SmtpClient` class is what you want to expose. You're not wrapping any functionality, but rather mapping configuration settings to an `SmtpClient` class. It's common to expose both standard and keyed-service registrations for components. Standard registrations are used when there's only one instance of a service, and keyed-service registrations are used when there are multiple instances of a service. Sometimes, to achieve multiple registrations of the same type you use a factory pattern. Add the following code to the `MailKit.Client` project in a file named _MailKitClientFactory.cs_: From 8a16310daf04a787d074791e8e5dd23f02d47f63 Mon Sep 17 00:00:00 2001 From: David Pine Date: Wed, 17 Jul 2024 09:53:14 -0500 Subject: [PATCH 04/12] Remove credential settings --- docs/extensibility/custom-component.md | 17 +---------- .../MailDev.Hosting/MailDevResource.cs | 28 +++++++++++++++++-- .../MailDevResourceBuilderExtensions.cs | 21 ++++++++++++-- .../MailDevResource.AppHost/Program.cs | 8 +++++- 4 files changed, 51 insertions(+), 23 deletions(-) diff --git a/docs/extensibility/custom-component.md b/docs/extensibility/custom-component.md index 893402d3dc..d5ea4d0aba 100644 --- a/docs/extensibility/custom-component.md +++ b/docs/extensibility/custom-component.md @@ -361,22 +361,7 @@ Next, configure the secrets for these paremeters. Right-click on the `MailDevRes > [!WARNING] > These credentials are for demonstration purposes only and MailDev is intended for local development. These crednetials are fictitious and shouldn't be used in a production environment. -If you're to run the sample now, the client wouldn't be able to connect to the MailDev container. This is because the MailDev container is configured to require authentication for incoming SMTP connections. The MailKit client configuration also needs to be updated to include the credentials. - -To configure the credentials in the client, right-click on the `MailDevResource.NewsletterService` project and select `Manage User Secrets`. Add the following JSON to the `secrets.json` file: - -```json -{ - "MailKit:Client": { - "Credentials": { - "UserName": "@admin", - "Password": "t3st1ng" - } - } -} -``` - -Run the app again, and everything works as it did before, but now with authentication enabled. +Run the app again and everything works as it did before, but now with authentication enabled. ### View MailKit telemetry diff --git a/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResource.cs b/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResource.cs index b8a3e22e59..0ea11f37ce 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResource.cs +++ b/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResource.cs @@ -4,17 +4,39 @@ // an alternative namespace. namespace Aspire.Hosting.ApplicationModel; -public sealed class MailDevResource(string name) : ContainerResource(name), IResourceWithConnectionString +public sealed class MailDevResource( + string name, + ParameterResource? username, + ParameterResource password) + : ContainerResource(name), IResourceWithConnectionString { // Constants used to refer to well known-endpoint names, this is specific - // for each resource type. MailDev exposes an SMTP endpoint. + // for each resource type. MailDev exposes an SMTP and HTTP endpoints. internal const string SmtpEndpointName = "smtp"; + internal const string HttpEndpointName = "http"; + + private const string DefaultUsername = "mail-dev"; // An EndpointReference is a core .NET Aspire type used for keeping // track of endpoint details in expressions. Simple literal values cannot // be used because endpoints are not known until containers are launched. private EndpointReference? _smtpReference; + /// + /// Gets the parameter that contains the MailDev SMTP server username. + /// + public ParameterResource? UsernameParameter { get; } = username; + + internal ReferenceExpression UserNameReference => + UsernameParameter is not null ? + ReferenceExpression.Create($"{UsernameParameter}") : + ReferenceExpression.Create($"{DefaultUsername}"); + + /// + /// Gets the parameter that contains the MailDev SMTP server password. + /// + public ParameterResource PasswordParameter { get; } = password; + public EndpointReference SmtpEndpoint => _smtpReference ??= new(this, SmtpEndpointName); @@ -23,6 +45,6 @@ public sealed class MailDevResource(string name) : ContainerResource(name), IRes // the connection string is composed of the SmtpEndpoint endpoint reference. public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create( - $"smtp://{SmtpEndpoint.Property(EndpointProperty.Host)}:{SmtpEndpoint.Property(EndpointProperty.Port)}" + $"Endpoint=smtp://{SmtpEndpoint.Property(EndpointProperty.Host)}:{SmtpEndpoint.Property(EndpointProperty.Port)};Username={UserNameReference};Password={PasswordParameter}" ); } diff --git a/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResourceBuilderExtensions.cs b/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResourceBuilderExtensions.cs index c13c3269e5..99a9e81a67 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResourceBuilderExtensions.cs +++ b/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResourceBuilderExtensions.cs @@ -6,6 +6,9 @@ namespace Aspire.Hosting; public static class MailDevResourceBuilderExtensions { + private const string UserEnvVarName = "MAILDEV_INCOMING_USER"; + private const string PasswordEnvVarName = "MAILDEV_INCOMING_PASS"; + /// /// Adds the to the given /// instance. Uses the "2.0.2" tag. @@ -22,13 +25,20 @@ public static IResourceBuilder AddMailDev( this IDistributedApplicationBuilder builder, string name, int? httpPort = null, - int? smtpPort = null) + int? smtpPort = null, + IResourceBuilder? userName = null, + IResourceBuilder? password = null) { + var passwordParameter = password?.Resource ?? + ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter( + builder, $"{name}-password"); + // The AddResource method is a core API within .NET Aspire and is // used by resource developers to wrap a custom resource in an // IResourceBuilder instance. Extension methods to customize // the resource (if any exist) target the builder interface. - var resource = new MailDevResource(name); + var resource = new MailDevResource( + name, userName?.Resource, passwordParameter); return builder.AddResource(resource) .WithImage(MailDevContainerImageTags.Image) @@ -41,7 +51,12 @@ public static IResourceBuilder AddMailDev( .WithEndpoint( targetPort: 1025, port: smtpPort, - name: MailDevResource.SmtpEndpointName); + name: MailDevResource.SmtpEndpointName) + .WithEnvironment(context => + { + context.EnvironmentVariables[UserEnvVarName] = resource.UserNameReference; + context.EnvironmentVariables[PasswordEnvVarName] = resource.PasswordParameter; + }); } } diff --git a/docs/extensibility/snippets/MailDevResource/MailDevResource.AppHost/Program.cs b/docs/extensibility/snippets/MailDevResource/MailDevResource.AppHost/Program.cs index 49c9ab4df3..d291b0c15d 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDevResource.AppHost/Program.cs +++ b/docs/extensibility/snippets/MailDevResource/MailDevResource.AppHost/Program.cs @@ -1,6 +1,12 @@ var builder = DistributedApplication.CreateBuilder(args); -var maildev = builder.AddMailDev("maildev"); +var mailDevUsername = builder.AddParameter("maildev-username"); +var mailDevPassword = builder.AddParameter("maildev-password"); + +var maildev = builder.AddMailDev( + name: "maildev", + userName: mailDevUsername, + password: mailDevPassword); builder.AddProject("newsletterservice") .WithReference(maildev); From b44e1c42cd211a5980f15dc693a5a35e7e7bc845 Mon Sep 17 00:00:00 2001 From: David Pine Date: Wed, 17 Jul 2024 09:53:57 -0500 Subject: [PATCH 05/12] Revert a few bits --- .../MailDev.Hosting/MailDevResource.cs | 28 +++---------------- .../MailDevResourceBuilderExtensions.cs | 21 ++------------ .../MailDevResource.AppHost/Program.cs | 8 +----- 3 files changed, 8 insertions(+), 49 deletions(-) diff --git a/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResource.cs b/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResource.cs index 0ea11f37ce..2e76605b94 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResource.cs +++ b/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResource.cs @@ -4,39 +4,19 @@ // an alternative namespace. namespace Aspire.Hosting.ApplicationModel; -public sealed class MailDevResource( - string name, - ParameterResource? username, - ParameterResource password) - : ContainerResource(name), IResourceWithConnectionString +public sealed class MailDevResource(string name) : ContainerResource(name), IResourceWithConnectionString { // Constants used to refer to well known-endpoint names, this is specific - // for each resource type. MailDev exposes an SMTP and HTTP endpoints. + // for each resource type. MailDev exposes an SMTP endpoint and a HTTP + // endpoint. internal const string SmtpEndpointName = "smtp"; internal const string HttpEndpointName = "http"; - private const string DefaultUsername = "mail-dev"; - // An EndpointReference is a core .NET Aspire type used for keeping // track of endpoint details in expressions. Simple literal values cannot // be used because endpoints are not known until containers are launched. private EndpointReference? _smtpReference; - /// - /// Gets the parameter that contains the MailDev SMTP server username. - /// - public ParameterResource? UsernameParameter { get; } = username; - - internal ReferenceExpression UserNameReference => - UsernameParameter is not null ? - ReferenceExpression.Create($"{UsernameParameter}") : - ReferenceExpression.Create($"{DefaultUsername}"); - - /// - /// Gets the parameter that contains the MailDev SMTP server password. - /// - public ParameterResource PasswordParameter { get; } = password; - public EndpointReference SmtpEndpoint => _smtpReference ??= new(this, SmtpEndpointName); @@ -45,6 +25,6 @@ UsernameParameter is not null ? // the connection string is composed of the SmtpEndpoint endpoint reference. public ReferenceExpression ConnectionStringExpression => ReferenceExpression.Create( - $"Endpoint=smtp://{SmtpEndpoint.Property(EndpointProperty.Host)}:{SmtpEndpoint.Property(EndpointProperty.Port)};Username={UserNameReference};Password={PasswordParameter}" + $"smtp://{SmtpEndpoint.Property(EndpointProperty.Host)}:{SmtpEndpoint.Property(EndpointProperty.Port)}" ); } diff --git a/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResourceBuilderExtensions.cs b/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResourceBuilderExtensions.cs index 99a9e81a67..c13c3269e5 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResourceBuilderExtensions.cs +++ b/docs/extensibility/snippets/MailDevResource/MailDev.Hosting/MailDevResourceBuilderExtensions.cs @@ -6,9 +6,6 @@ namespace Aspire.Hosting; public static class MailDevResourceBuilderExtensions { - private const string UserEnvVarName = "MAILDEV_INCOMING_USER"; - private const string PasswordEnvVarName = "MAILDEV_INCOMING_PASS"; - /// /// Adds the to the given /// instance. Uses the "2.0.2" tag. @@ -25,20 +22,13 @@ public static IResourceBuilder AddMailDev( this IDistributedApplicationBuilder builder, string name, int? httpPort = null, - int? smtpPort = null, - IResourceBuilder? userName = null, - IResourceBuilder? password = null) + int? smtpPort = null) { - var passwordParameter = password?.Resource ?? - ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter( - builder, $"{name}-password"); - // The AddResource method is a core API within .NET Aspire and is // used by resource developers to wrap a custom resource in an // IResourceBuilder instance. Extension methods to customize // the resource (if any exist) target the builder interface. - var resource = new MailDevResource( - name, userName?.Resource, passwordParameter); + var resource = new MailDevResource(name); return builder.AddResource(resource) .WithImage(MailDevContainerImageTags.Image) @@ -51,12 +41,7 @@ public static IResourceBuilder AddMailDev( .WithEndpoint( targetPort: 1025, port: smtpPort, - name: MailDevResource.SmtpEndpointName) - .WithEnvironment(context => - { - context.EnvironmentVariables[UserEnvVarName] = resource.UserNameReference; - context.EnvironmentVariables[PasswordEnvVarName] = resource.PasswordParameter; - }); + name: MailDevResource.SmtpEndpointName); } } diff --git a/docs/extensibility/snippets/MailDevResource/MailDevResource.AppHost/Program.cs b/docs/extensibility/snippets/MailDevResource/MailDevResource.AppHost/Program.cs index d291b0c15d..49c9ab4df3 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDevResource.AppHost/Program.cs +++ b/docs/extensibility/snippets/MailDevResource/MailDevResource.AppHost/Program.cs @@ -1,12 +1,6 @@ var builder = DistributedApplication.CreateBuilder(args); -var mailDevUsername = builder.AddParameter("maildev-username"); -var mailDevPassword = builder.AddParameter("maildev-password"); - -var maildev = builder.AddMailDev( - name: "maildev", - userName: mailDevUsername, - password: mailDevPassword); +var maildev = builder.AddMailDev("maildev"); builder.AddProject("newsletterservice") .WithReference(maildev); From 30d7bb51a82550f57310a0f9b741f8aa39c9f9d5 Mon Sep 17 00:00:00 2001 From: David Pine Date: Wed, 17 Jul 2024 12:15:33 -0500 Subject: [PATCH 06/12] Split articles and use three solutions --- docs/extensibility/custom-component.md | 201 ++---------------- docs/extensibility/custom-resources.md | 37 +--- .../flow-auth-from-resource-to-component.md | 86 ++++++++ .../MailDevResource.NewsletterService.csproj | 1 - .../Program.cs | 30 +-- .../MailDevResource/MailDevResource.sln | 6 - .../MailDev.Hosting/MailDev.Hosting.csproj | 13 ++ .../MailDev.Hosting/MailDevResource.cs | 30 +++ .../MailDevResourceBuilderExtensions.cs | 57 +++++ .../MailDevResource.AppHost.csproj | 21 ++ .../MailDevResource.AppHost/Program.cs | 8 + .../Properties/launchSettings.json | 29 +++ .../appsettings.Development.json | 8 + .../MailDevResource.AppHost/appsettings.json | 9 + .../MailDevResource.NewsletterService.csproj | 21 ++ .../MailDevResource.NewsletterService.http | 6 + .../Program.cs | 54 +++++ .../Properties/launchSettings.json | 25 +++ .../appsettings.Development.json | 8 + .../appsettings.json | 9 + .../Extensions.cs | 115 ++++++++++ .../MailDevResource.ServiceDefaults.csproj | 22 ++ .../MailDevResourceAndComponent.sln | 49 +++++ .../MailKit.Client/MailKit.Client.csproj | 0 .../MailKit.Client/MailKitClientFactory.cs | 57 +++++ .../MailKit.Client/MailKitClientSettings.cs | 79 +++++++ .../MailKit.Client/MailKitExtensions.cs | 134 ++++++++++++ .../MailKit.Client/MailKitHealthCheck.cs | 0 .../MailDev.Hosting/MailDev.Hosting.csproj | 13 ++ .../MailDev.Hosting/MailDevResource.cs | 50 +++++ .../MailDevResourceBuilderExtensions.cs | 72 +++++++ .../MailDevResource.AppHost.csproj | 21 ++ .../MailDevResource.AppHost/Program.cs | 14 ++ .../Properties/launchSettings.json | 29 +++ .../appsettings.Development.json | 8 + .../MailDevResource.AppHost/appsettings.json | 9 + .../MailDevResource.NewsletterService.csproj | 21 ++ .../MailDevResource.NewsletterService.http | 6 + .../Program.cs | 54 +++++ .../Properties/launchSettings.json | 25 +++ .../appsettings.Development.json | 8 + .../appsettings.json | 9 + .../Extensions.cs | 115 ++++++++++ .../MailDevResource.ServiceDefaults.csproj | 22 ++ .../MailDevResourceWithCredentials.sln | 49 +++++ .../MailKit.Client/MailKit.Client.csproj | 18 ++ .../MailKit.Client/MailKitClientFactory.cs | 0 .../MailKit.Client/MailKitClientSettings.cs | 0 .../MailKit.Client/MailKitExtensions.cs | 0 .../MailKit.Client/MailKitHealthCheck.cs | 23 ++ .../{MailDevResource => }/global.json | 0 51 files changed, 1439 insertions(+), 242 deletions(-) create mode 100644 docs/extensibility/flow-auth-from-resource-to-component.md create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDev.Hosting.csproj create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDevResource.cs create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDevResourceBuilderExtensions.cs create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/MailDevResource.AppHost.csproj create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/Program.cs create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/Properties/launchSettings.json create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/appsettings.Development.json create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/appsettings.json create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/MailDevResource.NewsletterService.http create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/Program.cs create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/Properties/launchSettings.json create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/appsettings.Development.json create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/appsettings.json create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.ServiceDefaults/Extensions.cs create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResourceAndComponent.sln rename docs/extensibility/snippets/{MailDevResource => MailDevResourceAndComponent}/MailKit.Client/MailKit.Client.csproj (100%) create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientFactory.cs create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientSettings.cs create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitExtensions.cs rename docs/extensibility/snippets/{MailDevResource => MailDevResourceAndComponent}/MailKit.Client/MailKitHealthCheck.cs (100%) create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDev.Hosting.csproj create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResource.cs create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResourceBuilderExtensions.cs create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/MailDevResource.AppHost.csproj create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/Program.cs create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/Properties/launchSettings.json create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/appsettings.Development.json create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/appsettings.json create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/MailDevResource.NewsletterService.http create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/Program.cs create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/Properties/launchSettings.json create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/appsettings.Development.json create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/appsettings.json create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.ServiceDefaults/Extensions.cs create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResourceWithCredentials.sln create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKit.Client.csproj rename docs/extensibility/snippets/{MailDevResource => MailDevResourceWithCredentials}/MailKit.Client/MailKitClientFactory.cs (100%) rename docs/extensibility/snippets/{MailDevResource => MailDevResourceWithCredentials}/MailKit.Client/MailKitClientSettings.cs (100%) rename docs/extensibility/snippets/{MailDevResource => MailDevResourceWithCredentials}/MailKit.Client/MailKitExtensions.cs (100%) create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitHealthCheck.cs rename docs/extensibility/snippets/{MailDevResource => }/global.json (100%) diff --git a/docs/extensibility/custom-component.md b/docs/extensibility/custom-component.md index d5ea4d0aba..dec0ab887a 100644 --- a/docs/extensibility/custom-component.md +++ b/docs/extensibility/custom-component.md @@ -49,49 +49,40 @@ The next step is to add all the NuGet packages that the component relies on. Rat Whenever you're creating a .NET Aspire component, it's best to understand the client library that you're mapping to. With MailKit, you need to understand the configuration settings that are required to connect to a Simple Mail Transfer Protocol (SMTP) server. But it's also important to understand if the library has support for _health checks_, _tracing_ and _metrics_. MailKit supports _tracing_ and _metrics_, through its [`Telemetry.SmtpClient` class](https://github.com/jstedfast/MailKit/blob/master/MailKit/Telemetry.cs#L112-L189). When adding _health checks_, you should use any established or existing health checks where possible. Otherwise, you might consider implementing your own in the component. Add the following code to the `MailKit.Client` project in a file named _MailKitClientSettings.cs_: -:::code source="snippets/MailDevResource/MailKit.Client/MailKitClientSettings.cs"::: +:::code source="snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientSettings.cs"::: The preceding code defines the `MailKitClientSettings` class with: - `Endpoint` property that represents the connection string to the SMTP server. -- `Credentials` property that represents the credentials to authenticate with the SMTP server. - `DisableHealthChecks` property that determines whether health checks are enabled. - `DisableTracing` property that determines whether tracing is enabled. - `DisableMetrics` property that determines whether metrics are enabled. ### Parse connection string logic -The settings class also contains a `ParseConnectionString` method that parses the connection string into a valid `Uri` and optionally `NetworkCredential`. The configuration is expected to be provided in the following format: +The settings class also contains a `ParseConnectionString` method that parses the connection string into a valid `Uri`. The configuration is expected to be provided in the following format: - `ConnectionStrings:`: The connection string to the SMTP server. - `MailKit:Client:ConnectionString`: The connection string to the SMTP server. If neither of these values are provided, an exception is thrown. -When a connection string is configured, the `ParseConnectionString` method expects a connection string information to be in the form of `key=value;` pairs. The following keys are supported: - -- `Endpoint`: The endpoint to connect to the SMTP server. -- `Username`: The username to authenticate with the SMTP server. -- `Password`: The password to authenticate with the SMTP server. - -When the `Endpoint` isn't a valid `Uri` and exception is thrown. If the `Username` and `Password` are provided, a `NetworkCredential` is created and the result is assigned to the setting's `Credentials` property. - ## Expose client functionality The goal of .NET Aspire components is to expose the underlying client library to consumers through dependency injection. With MailKit and for this example, the `SmtpClient` class is what you want to expose. You're not wrapping any functionality, but rather mapping configuration settings to an `SmtpClient` class. It's common to expose both standard and keyed-service registrations for components. Standard registrations are used when there's only one instance of a service, and keyed-service registrations are used when there are multiple instances of a service. Sometimes, to achieve multiple registrations of the same type you use a factory pattern. Add the following code to the `MailKit.Client` project in a file named _MailKitClientFactory.cs_: -:::code source="snippets/MailDevResource/MailKit.Client/MailKitClientFactory.cs"::: +:::code source="snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientFactory.cs"::: -The `MailKitClientFactory` class is a factory that creates an `ISmtpClient` instance based on the configuration settings. It's responsible for returning an `ISmtpClient` implementation that has an active connection to a configured SMTP server and optionally authenticated. Next, you need to expose the functionality for the consumers to register this factory with the dependency injection container. Add the following code to the `MailKit.Client` project in a file named _MailKitExtensions.cs_: +The `MailKitClientFactory` class is a factory that creates an `ISmtpClient` instance based on the configuration settings. It's responsible for returning an `ISmtpClient` implementation that has an active connection to a configured SMTP server. Next, you need to expose the functionality for the consumers to register this factory with the dependency injection container. Add the following code to the `MailKit.Client` project in a file named _MailKitExtensions.cs_: -:::code source="snippets/MailDevResource/MailKit.Client/MailKitExtensions.cs"::: +:::code source="snippets/MailDevResourceAndComponent/MailKit.Client/MailKitExtensions.cs"::: The preceding code adds two extension methods on the `IHostApplicationBuilder` type, one for the standard registration of MailKit and another for keyed-registration of MailKit. > [!TIP] > Extension methods for .NET Aspire components should extend the `IHostApplicationBuilder` type and follow the `Add` naming convention where the `` is the type or functionality you're adding. For this article, the `AddMailKitClient` extension method is used to add the MailKit client. It's likely more in-line with the official guidance to use `AddMailKitSmtpClient` instead of `AddMailKitClient`, since this only registers the `SmtpClient` and not the entire MailKit library. -Both extensions ultimately rely on the private `AddMailKitClient` method to register the `MailKitClientFactory` with the dependency injection container as a [scoped service](/dotnet/core/extensions/dependency-injection#scoped). The reason for registering the `MailKitClientFactory` as a scoped service is because the connection (and authentication) operations are considered expensive and should be reused within the same scope where possible. In other words, for a single request, the same `ISmtpClient` instance should be used. The factory holds on to the instance of the `SmtpClient` that it creates and disposes of it. +Both extensions ultimately rely on the private `AddMailKitClient` method to register the `MailKitClientFactory` with the dependency injection container as a [scoped service](/dotnet/core/extensions/dependency-injection#scoped). The reason for registering the `MailKitClientFactory` as a scoped service is because the connection operations are considered expensive and should be reused within the same scope where possible. In other words, for a single request, the same `ISmtpClient` instance should be used. The factory holds on to the instance of the `SmtpClient` that it creates and disposes of it. ### Configuration binding @@ -106,7 +97,7 @@ The registration of health checks, and telemetry are described in a bit more det [Health checks](../fundamentals/health-checks.md) are a way to monitor the health of a component. With MailKit, you can check if the connection to the SMTP server is healthy. Add the following code to the `MailKit.Client` project in a file named _MailKitHealthCheck.cs_: -:::code source="snippets/MailDevResource/MailKit.Client/MailKitHealthCheck.cs"::: +:::code source="snippets/MailDevResourceAndComponent/MailKit.Client/MailKitHealthCheck.cs"::: The preceding health check implementation: @@ -164,9 +155,9 @@ With the component library created, you can now update the Newsletter service to dotnet add ./MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj reference MailKit.Client/MailKit.Client.csproj ``` -The final step is to replace the existing _Program.cs_ file in the `MailDevResource.NewsletterService` project with the following C# code: +The final step is to replace the existing _:::no-loc text="Program.cs":::_ file in the `MailDevResource.NewsletterService` project with the following C# code: -:::code source="snippets/MailDevResource/MailDevResource.NewsletterService/Program.cs"::: +:::code source="snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/Program.cs"::: The most notable changes in the preceding code are: @@ -194,175 +185,6 @@ Repeat this several times, to add multiple email addresses. You should see the e Stop the application by selecting Ctrl+C in the terminal window where the application is running, or by selecting the stop button in your IDE. -### Configure MailDev credentials - -The MailDev container supports basic authentication for both incoming and outgoing SMTP. To configure the credentials for incoming, you need to set the `MAILDEV_INCOMING_USER` and `MAILDEV_INCOMING_PASS` environment variables. For more information, see [MailDev: Usage](https://maildev.github.io/maildev/#usage). Update the _MailDevResource.cs_ file in the `MailDev.Hosting` project, by replacing its contents with the following C# code: - -```csharp -// For ease of discovery, resource types should be placed in -// the Aspire.Hosting.ApplicationModel namespace. If there is -// likelihood of a conflict on the resource name consider using -// an alternative namespace. -namespace Aspire.Hosting.ApplicationModel; - -public sealed class MailDevResource( - string name, - ParameterResource? username, - ParameterResource password) - : ContainerResource(name), IResourceWithConnectionString -{ - // Constants used to refer to well known-endpoint names, this is specific - // for each resource type. MailDev exposes an SMTP and HTTP endpoints. - internal const string SmtpEndpointName = "smtp"; - internal const string HttpEndpointName = "http"; - - private const string DefaultUsername = "mail-dev"; - - // An EndpointReference is a core .NET Aspire type used for keeping - // track of endpoint details in expressions. Simple literal values cannot - // be used because endpoints are not known until containers are launched. - private EndpointReference? _smtpReference; - - /// - /// Gets the parameter that contains the MailDev SMTP server username. - /// - public ParameterResource? UsernameParameter { get; } = username; - - internal ReferenceExpression UserNameReference => - UsernameParameter is not null ? - ReferenceExpression.Create($"{UsernameParameter}") : - ReferenceExpression.Create($"{DefaultUsername}"); - - /// - /// Gets the parameter that contains the MailDev SMTP server password. - /// - public ParameterResource PasswordParameter { get; } = password; - - public EndpointReference SmtpEndpoint => - _smtpReference ??= new(this, SmtpEndpointName); - - // Required property on IResourceWithConnectionString. Represents a connection - // string that applications can use to access the MailDev server. In this case - // the connection string is composed of the SmtpEndpoint endpoint reference. - public ReferenceExpression ConnectionStringExpression => - ReferenceExpression.Create( - $"Endpoint=smtp://{SmtpEndpoint.Property(EndpointProperty.Host)}:{SmtpEndpoint.Property(EndpointProperty.Port)};Username={UserNameReference};Password={PasswordParameter}" - ); -} -``` - -These updates add a `UsernameParameter` and `PasswordParameter` property. These properties are used to store the parameters for the MailDev username and password. The `ConnectionStringExpression` property is updated to include the username and password parameters in the connection string. Next, update the _MailDevResourceBuilderExtensions.cs_ file in the `MailDev.Hosting` project with the following C# code: - -```csharp -using Aspire.Hosting.ApplicationModel; - -// Put extensions in the Aspire.Hosting namespace to ease discovery as referencing -// the .NET Aspire hosting package automatically adds this namespace. -namespace Aspire.Hosting; - -public static class MailDevResourceBuilderExtensions -{ - private const string UserEnvVarName = "MAILDEV_INCOMING_USER"; - private const string PasswordEnvVarName = "MAILDEV_INCOMING_PASS"; - - /// - /// Adds the to the given - /// instance. Uses the "2.0.2" tag. - /// - /// The . - /// The name of the resource. - /// The HTTP port. - /// The SMTP port. - /// - /// An instance that - /// represents the added MailDev resource. - /// - public static IResourceBuilder AddMailDev( - this IDistributedApplicationBuilder builder, - string name, - int? httpPort = null, - int? smtpPort = null, - IResourceBuilder? userName = null, - IResourceBuilder? password = null) - { - var passwordParameter = password?.Resource ?? - ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter( - builder, $"{name}-password"); - - // The AddResource method is a core API within .NET Aspire and is - // used by resource developers to wrap a custom resource in an - // IResourceBuilder instance. Extension methods to customize - // the resource (if any exist) target the builder interface. - var resource = new MailDevResource( - name, userName?.Resource, passwordParameter); - - return builder.AddResource(resource) - .WithImage(MailDevContainerImageTags.Image) - .WithImageRegistry(MailDevContainerImageTags.Registry) - .WithImageTag(MailDevContainerImageTags.Tag) - .WithHttpEndpoint( - targetPort: 1080, - port: httpPort, - name: MailDevResource.HttpEndpointName) - .WithEndpoint( - targetPort: 1025, - port: smtpPort, - name: MailDevResource.SmtpEndpointName) - .WithEnvironment(context => - { - context.EnvironmentVariables[UserEnvVarName] = resource.UserNameReference; - context.EnvironmentVariables[PasswordEnvVarName] = resource.PasswordParameter; - }); - } -} - -// This class just contains constant strings that can be updated periodically -// when new versions of the underlying container are released. -internal static class MailDevContainerImageTags -{ - internal const string Registry = "docker.io"; - - internal const string Image = "maildev/maildev"; - - internal const string Tag = "2.0.2"; -} -``` - -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. Next, update the _Program.cs_ file in the `MailDevResource.AppHost` project with the following C# code: - -```csharp -var builder = DistributedApplication.CreateBuilder(args); - -var mailDevUsername = builder.AddParameter("maildev-username"); -var mailDevPassword = builder.AddParameter("maildev-password"); - -var maildev = builder.AddMailDev( - name: "maildev", - userName: mailDevUsername, - password: mailDevPassword); - -builder.AddProject("newsletterservice") - .WithReference(maildev); - -builder.Build().Run(); -``` - -The preceding code adds two parameters for the MailDev username and password. It assigns these parameters to the `MAILDEV_INCOMING_USER` and `MAILDEV_INCOMING_PASS` environment variables. The `AddMailDev` method has two chained calls to `WithEnvironment` which includes these environment variables. For more information on parameters, see [External parameters](../fundamentals/external-parameters.md). - -Next, configure the secrets for these paremeters. Right-click on the `MailDevResource.AppHost` project and select `Manage User Secrets`. Add the following JSON to the `secrets.json` file: - -```json -{ - "Parameters:maildev-username": "@admin", - "Parameters:maildev-password": "t3st1ng" -} -``` - -> [!WARNING] -> These credentials are for demonstration purposes only and MailDev is intended for local development. These crednetials are fictitious and shouldn't be used in a production environment. - -Run the app again and everything works as it did before, but now with authentication enabled. - ### View MailKit telemetry The MailKit client library exposes telemetry that can be viewed in the .NET Aspire dashboard. To view the telemetry, navigate to the .NET Aspire dashboard at [https://localhost:7251](https://localhost:7251). Select the `newsletter` resource to view the telemetry on the **Metrics** page: @@ -378,3 +200,8 @@ Open up the Swagger UI again, and make some requests to the `/subscribe` and `/u In this article, you learned how to create a .NET Aspire component that uses MailKit to send emails. You also learned how to integrate this component into the Newsletter app you previously built. You learned about the core principles of .NET Aspire components, such as exposing the underlying client library to consumers through dependency injection, and how to add health checks and telemetry to the component. You also learned how to update the Newsletter service to use the MailKit client. Go forth and build your own .NET Aspire components. If you believe that there's enough community value in the component you're building, consider publishing it as a [NuGet package](/dotnet/standard/library-guidance/nuget) for others to use. Furthermore, consider submitting a pull request to the [.NET Aspire GitHub repository](https://github.com/dotnet/aspire) for consideration to be included in the official .NET Aspire components. + +## Next steps + +> [!div class="nextstepaction"] +> [Flow auth from custom resource to component](flow-auth-from-resource-to-component.md) diff --git a/docs/extensibility/custom-resources.md b/docs/extensibility/custom-resources.md index 8c4ac58bc0..2aae26473f 100644 --- a/docs/extensibility/custom-resources.md +++ b/docs/extensibility/custom-resources.md @@ -1,7 +1,7 @@ --- title: Create custom resource types for .NET Aspire description: Learn how to create a custom resource for an existing containerized application. -ms.date: 07/15/2024 +ms.date: 07/17/2024 ms.topic: how-to --- @@ -264,45 +264,14 @@ The preceding screenshot shows the environment variables for the `newsletterserv To use the SMTP connection details that were injected into the newsletter service project, you inject an instance of into the dependency injection container as a singleton. Add the following code to the _:::no-loc text="Program.cs":::_ file in the _:::no-loc text="MailDevResource.NewsletterService":::_ project to setup the singleton service. In the `Program` class, immediately following the `// Add services to the container` comment, add the following code: -```csharp -builder.Services.AddSingleton(sp => -{ - var smtpUri = new Uri(builder.Configuration.GetConnectionString("maildev")!); - - var smtpClient = new SmtpClient(smtpUri.Host, smtpUri.Port); - - return smtpClient; -}); -``` +:::code source="snippets/MailDevResource/MailDevResource.NewsletterService/Program.cs" id="smtp"::: > [!TIP] > This code snippet relies on the official `SmtpClient`, however; this type is obsolete on some platforms and not recommended on others. This is used here to demonstrate a non-componentized approach to using the MailDev resource. For a more modern approach using [MailKit](https://github.com/jstedfast/MailKit), see [Create custom .NET Aspire component](custom-component.md). To test the client, add two simple `subscribe` and `unsubscribe` POST methods to the newsletter service. Add the following code replacing the "weatherforecast" `MapGet` call in the _:::no-loc text="Program.cs":::_ file of the _MailDevResource.NewsletterService_ project to setup the ASP.NET Core routes: -```csharp -app.MapPost("/subscribe", async (SmtpClient smtpClient, string email) => -{ - using var message = new MailMessage("newsletter@yourcompany.com", email) - { - Subject = "Welcome to our newsletter!", - Body = "Thank you for subscribing to our newsletter!" - }; - - await smtpClient.SendMailAsync(message); -}); - -app.MapPost("/unsubscribe", async (SmtpClient smtpClient, string email) => -{ - using var message = new MailMessage("newsletter@yourcompany.com", email) - { - Subject = "You are unsubscribed from our newsletter!", - Body = "Sorry to see you go. We hope you will come back soon!" - }; - - await smtpClient.SendMailAsync(message); -}); -``` +:::code source="snippets/MailDevResource/MailDevResource.NewsletterService/Program.cs" id="subs"::: > [!TIP] > Remember to reference the `System.Net.Mail` and `Microsoft.AspNetCore.Mvc` namespaces in _:::no-loc text="Program.cs":::_ if your code editor doesn't automatically add them. diff --git a/docs/extensibility/flow-auth-from-resource-to-component.md b/docs/extensibility/flow-auth-from-resource-to-component.md new file mode 100644 index 0000000000..6951847e1c --- /dev/null +++ b/docs/extensibility/flow-auth-from-resource-to-component.md @@ -0,0 +1,86 @@ +--- +title: Flow auth from custom resource to component +description: Learn how to flow authentication credentials from a custom resource to a custom component. +ms.date: 07/16/2024 +ms.topic: how-to +--- + +# Flow auth from custom resource to component + +This article is a continuation of two previous articles: + +- [Create custom resource types for .NET Aspire](custom-resources.md) +- [Create custom .NET Aspire component](custom-component.md) + +One of the primary benefits to .NET Aspire is how it simplifies the configurability of resources and consuming clients (or components). This article demonstrates how to flow authentication credentials from a custom resource to a custom component. The custom resource is a MailDev container that allows for either incoming or outgoing credentials. The custom component is a MailKit client that sends emails. + +## Prerequisites + +Since this article continues from previous content, it's expected that you've already created the resulting solution as a starting point for this article. If you haven't already, complete the following articles: + +- [Create custom resource types for .NET Aspire](custom-resources.md) +- [Create custom .NET Aspire component](custom-component.md) + +The resulting solution from these previous articles contains the following projects: + +- _MailDev.Hosting_: Contains the custom resource type for the MailDev container. +- _MailDevResource.AppHost_: The [app host](../fundamentals/app-host-overview.md) that uses the custom resource and defines it as a dependency for a Newsletter service. +- _MailDevResource.NewsletterService_: An ASP.NET Core Web API project that sends emails using the MailDev container. +- _MailDevResource.ServiceDefaults_: Contains the [default service configurations](../fundamentals/service-defaults.md) intended for sharing. +- _MailKit.Client_: Contains the custom component that exposes the MailKit `SmptClient` through a factory. + +## Update the MailDev resource + +To flow authentication credentials from the MailDev resource to the MailKit component, you need to update the MailDev resource to include the username and password parameters. + +The MailDev container supports basic authentication for both incoming and outgoing SMTP. To configure the credentials for incoming, you need to set the `MAILDEV_INCOMING_USER` and `MAILDEV_INCOMING_PASS` environment variables. For more information, see [MailDev: Usage](https://maildev.github.io/maildev/#usage). Update the _MailDevResource.cs_ file in the `MailDev.Hosting` project, by replacing its contents with the following C# code: + +:::code source="snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResource.cs"::: + +These updates add a `UsernameParameter` and `PasswordParameter` property. These properties are used to store the parameters for the MailDev username and password. The `ConnectionStringExpression` property is updated to include the username and password parameters in the connection string. Next, update the _MailDevResourceBuilderExtensions.cs_ file in the `MailDev.Hosting` project with the following C# code: + +:::code source="snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResourceBuilderExtensions.cs"::: + +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 app host + +Now that the resource is updated to include the username and password parameters, you need to update the app host to include these parameters. Update the _:::no-loc text="Program.cs":::_ file in the `MailDevResource.AppHost` project with the following C# code: + +:::code source="snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/Program.cs"::: + +The preceding code adds two parameters for the MailDev username and password. It assigns these parameters to the `MAILDEV_INCOMING_USER` and `MAILDEV_INCOMING_PASS` environment variables. The `AddMailDev` method has two chained calls to `WithEnvironment` which includes these environment variables. For more information on parameters, see [External parameters](../fundamentals/external-parameters.md). + +Next, configure the secrets for these paremeters. Right-click on the `MailDevResource.AppHost` project and select `Manage User Secrets`. Add the following JSON to the `secrets.json` file: + +```json +{ + "Parameters:maildev-username": "@admin", + "Parameters:maildev-password": "t3st1ng" +} +``` + +> [!WARNING] +> These credentials are for demonstration purposes only and MailDev is intended for local development. These crednetials are fictitious and shouldn't be used in a production environment. + +## Update the MailKit component + +It's good practice for components to expect connection strings to contain varions key/value pairs, and to parse these pairs into the appropriate properties. Update the _MailKitClientSettings.cs_ file in the `MailKit.Client` project with the following C# code: + +:::code source="snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientSettings.cs" highlight="21-28"::: + +The preceding settings class, now includes a `Credentials` property of type `NetworkCredential`. The `ParseConnectionString` method is updated to parse the `Username` and `Password` keys from the connection string. If the `Username` and `Password` keys are present, a `NetworkCredential` is created and assigned to the `Credentials` property. + +With the settings class updated to understand and populate the credentials, update the factory to conditionally use the credentials if they're configured. Update the _MailKitClientFactory.cs_ file in the `MailKit.Client` project with the following C# code: + +:::code source="snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientFactory.cs" highlight="47-51"::: + +When the factory determines that credentials have been configured, it authenticates with the SMTP server after connecting before returning the `SmtpClient`. + +## Run the sample + +Now that you've updated both the resource and corresponding component, as well as the app host, you're ready to run the sample app. To run the sample, from your IDE, select F5 or run `dotnet run` from the root directory of the solution to start the application—you should see the [.NET Aspire dashboard](../fundamentals/dashboard/overview.md). Like before validate that everything is working as expected. + +## Summary + +This article demonstrated how to flow authentication credentials from a custom resource to a custom component. The custom resource is a MailDev container that allows for either incoming or outgoing credentials. The custom component is a MailKit client that sends emails. By updating the resource to include the username and password parameters, and updating the component to parse and use these parameters, you can flow authentication credentials from the resource to the component. diff --git a/docs/extensibility/snippets/MailDevResource/MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj b/docs/extensibility/snippets/MailDevResource/MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj index 68f91ed347..6fdf0e9de2 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj +++ b/docs/extensibility/snippets/MailDevResource/MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj @@ -6,7 +6,6 @@ - diff --git a/docs/extensibility/snippets/MailDevResource/MailDevResource.NewsletterService/Program.cs b/docs/extensibility/snippets/MailDevResource/MailDevResource.NewsletterService/Program.cs index 45b2365ccd..3b99f04dcb 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDevResource.NewsletterService/Program.cs +++ b/docs/extensibility/snippets/MailDevResource/MailDevResource.NewsletterService/Program.cs @@ -1,7 +1,4 @@ using System.Net.Mail; -using MailKit.Client; -using MailKit.Net.Smtp; -using MimeKit; var builder = WebApplication.CreateBuilder(args); @@ -11,7 +8,16 @@ builder.Services.AddSwaggerGen(); // Add services to the container. -builder.AddMailKitClient("maildev"); +// +builder.Services.AddSingleton(sp => +{ + var smtpUri = new Uri(builder.Configuration.GetConnectionString("maildev")!); + + var smtpClient = new SmtpClient(smtpUri.Host, smtpUri.Port); + + return smtpClient; +}); +// var app = builder.Build(); @@ -23,32 +29,28 @@ app.UseSwaggerUI(); app.UseHttpsRedirection(); -app.MapPost("/subscribe", - async (MailKitClientFactory factory, string email) => +// +app.MapPost("/subscribe", async (SmtpClient smtpClient, string email) => { - ISmtpClient client = await factory.GetSmtpClientAsync(); - using var message = new MailMessage("newsletter@yourcompany.com", email) { Subject = "Welcome to our newsletter!", Body = "Thank you for subscribing to our newsletter!" }; - await client.SendAsync(MimeMessage.CreateFromMailMessage(message)); + await smtpClient.SendMailAsync(message); }); -app.MapPost("/unsubscribe", - async (MailKitClientFactory factory, string email) => +app.MapPost("/unsubscribe", async (SmtpClient smtpClient, string email) => { - ISmtpClient client = await factory.GetSmtpClientAsync(); - using var message = new MailMessage("newsletter@yourcompany.com", email) { Subject = "You are unsubscribed from our newsletter!", Body = "Sorry to see you go. We hope you will come back soon!" }; - await client.SendAsync(MimeMessage.CreateFromMailMessage(message)); + await smtpClient.SendMailAsync(message); }); +// app.Run(); diff --git a/docs/extensibility/snippets/MailDevResource/MailDevResource.sln b/docs/extensibility/snippets/MailDevResource/MailDevResource.sln index 88c6bd3078..932d4b427e 100644 --- a/docs/extensibility/snippets/MailDevResource/MailDevResource.sln +++ b/docs/extensibility/snippets/MailDevResource/MailDevResource.sln @@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailDev.Hosting", "MailDev. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailDevResource.NewsletterService", "MailDevResource.NewsletterService\MailDevResource.NewsletterService.csproj", "{3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailKit.Client", "MailKit.Client\MailKit.Client.csproj", "{C9A58484-13CC-4391-9D93-88FDFA7B0DDF}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -35,10 +33,6 @@ Global {3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}.Debug|Any CPU.Build.0 = Debug|Any CPU {3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}.Release|Any CPU.ActiveCfg = Release|Any CPU {3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}.Release|Any CPU.Build.0 = Release|Any CPU - {C9A58484-13CC-4391-9D93-88FDFA7B0DDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C9A58484-13CC-4391-9D93-88FDFA7B0DDF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C9A58484-13CC-4391-9D93-88FDFA7B0DDF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C9A58484-13CC-4391-9D93-88FDFA7B0DDF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDev.Hosting.csproj b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDev.Hosting.csproj new file mode 100644 index 0000000000..fc602e5bc4 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDev.Hosting.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDevResource.cs b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDevResource.cs new file mode 100644 index 0000000000..2e76605b94 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDevResource.cs @@ -0,0 +1,30 @@ +// For ease of discovery, resource types should be placed in +// the Aspire.Hosting.ApplicationModel namespace. If there is +// likelihood of a conflict on the resource name consider using +// an alternative namespace. +namespace Aspire.Hosting.ApplicationModel; + +public sealed class MailDevResource(string name) : ContainerResource(name), IResourceWithConnectionString +{ + // Constants used to refer to well known-endpoint names, this is specific + // for each resource type. MailDev exposes an SMTP endpoint and a HTTP + // endpoint. + internal const string SmtpEndpointName = "smtp"; + internal const string HttpEndpointName = "http"; + + // An EndpointReference is a core .NET Aspire type used for keeping + // track of endpoint details in expressions. Simple literal values cannot + // be used because endpoints are not known until containers are launched. + private EndpointReference? _smtpReference; + + public EndpointReference SmtpEndpoint => + _smtpReference ??= new(this, SmtpEndpointName); + + // Required property on IResourceWithConnectionString. Represents a connection + // string that applications can use to access the MailDev server. In this case + // the connection string is composed of the SmtpEndpoint endpoint reference. + public ReferenceExpression ConnectionStringExpression => + ReferenceExpression.Create( + $"smtp://{SmtpEndpoint.Property(EndpointProperty.Host)}:{SmtpEndpoint.Property(EndpointProperty.Port)}" + ); +} diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDevResourceBuilderExtensions.cs b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDevResourceBuilderExtensions.cs new file mode 100644 index 0000000000..c13c3269e5 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDev.Hosting/MailDevResourceBuilderExtensions.cs @@ -0,0 +1,57 @@ +using Aspire.Hosting.ApplicationModel; + +// Put extensions in the Aspire.Hosting namespace to ease discovery as referencing +// the .NET Aspire hosting package automatically adds this namespace. +namespace Aspire.Hosting; + +public static class MailDevResourceBuilderExtensions +{ + /// + /// Adds the to the given + /// instance. Uses the "2.0.2" tag. + /// + /// The . + /// The name of the resource. + /// The HTTP port. + /// The SMTP port. + /// + /// An instance that + /// represents the added MailDev resource. + /// + public static IResourceBuilder AddMailDev( + this IDistributedApplicationBuilder builder, + string name, + int? httpPort = null, + int? smtpPort = null) + { + // The AddResource method is a core API within .NET Aspire and is + // used by resource developers to wrap a custom resource in an + // IResourceBuilder instance. Extension methods to customize + // the resource (if any exist) target the builder interface. + var resource = new MailDevResource(name); + + return builder.AddResource(resource) + .WithImage(MailDevContainerImageTags.Image) + .WithImageRegistry(MailDevContainerImageTags.Registry) + .WithImageTag(MailDevContainerImageTags.Tag) + .WithHttpEndpoint( + targetPort: 1080, + port: httpPort, + name: MailDevResource.HttpEndpointName) + .WithEndpoint( + targetPort: 1025, + port: smtpPort, + name: MailDevResource.SmtpEndpointName); + } +} + +// This class just contains constant strings that can be updated periodically +// when new versions of the underlying container are released. +internal static class MailDevContainerImageTags +{ + internal const string Registry = "docker.io"; + + internal const string Image = "maildev/maildev"; + + internal const string Tag = "2.0.2"; +} diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/MailDevResource.AppHost.csproj b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/MailDevResource.AppHost.csproj new file mode 100644 index 0000000000..4e177d9495 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/MailDevResource.AppHost.csproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0 + enable + enable + true + 9c9bfb14-6706-4421-bc93-37cbaebe36d0 + + + + + + + + + + + + diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/Program.cs b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/Program.cs new file mode 100644 index 0000000000..49c9ab4df3 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/Program.cs @@ -0,0 +1,8 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var maildev = builder.AddMailDev("maildev"); + +builder.AddProject("newsletterservice") + .WithReference(maildev); + +builder.Build().Run(); diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/Properties/launchSettings.json b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000000..f0583ee1d3 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17251;http://localhost:15199", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21161", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22175" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15199", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19277", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20014" + } + } + } +} diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/appsettings.Development.json b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/appsettings.json b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/appsettings.json new file mode 100644 index 0000000000..31c092aa45 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj new file mode 100644 index 0000000000..68f91ed347 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + net8.0 + enable + enable + f2608916-3fcc-4bd8-9697-ce27b4156fc0 + + + diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/MailDevResource.NewsletterService.http b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/MailDevResource.NewsletterService.http new file mode 100644 index 0000000000..39958c6813 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/MailDevResource.NewsletterService.http @@ -0,0 +1,6 @@ +@MailDevResource.NewsletterService_HostAddress = http://localhost:5021 + +GET {{MailDevResource.NewsletterService_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/Program.cs b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/Program.cs new file mode 100644 index 0000000000..45b2365ccd --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/Program.cs @@ -0,0 +1,54 @@ +using System.Net.Mail; +using MailKit.Client; +using MailKit.Net.Smtp; +using MimeKit; + +var builder = WebApplication.CreateBuilder(args); + +builder.AddServiceDefaults(); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +// Add services to the container. +builder.AddMailKitClient("maildev"); + +var app = builder.Build(); + +app.MapDefaultEndpoints(); + +// Configure the HTTP request pipeline. + +app.UseSwagger(); +app.UseSwaggerUI(); +app.UseHttpsRedirection(); + +app.MapPost("/subscribe", + async (MailKitClientFactory factory, string email) => +{ + ISmtpClient client = await factory.GetSmtpClientAsync(); + + using var message = new MailMessage("newsletter@yourcompany.com", email) + { + Subject = "Welcome to our newsletter!", + Body = "Thank you for subscribing to our newsletter!" + }; + + await client.SendAsync(MimeMessage.CreateFromMailMessage(message)); +}); + +app.MapPost("/unsubscribe", + async (MailKitClientFactory factory, string email) => +{ + ISmtpClient client = await factory.GetSmtpClientAsync(); + + using var message = new MailMessage("newsletter@yourcompany.com", email) + { + Subject = "You are unsubscribed from our newsletter!", + Body = "Sorry to see you go. We hope you will come back soon!" + }; + + await client.SendAsync(MimeMessage.CreateFromMailMessage(message)); +}); + +app.Run(); diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/Properties/launchSettings.json b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/Properties/launchSettings.json new file mode 100644 index 0000000000..aed106ab5c --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5021", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7251;http://localhost:5021", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/appsettings.Development.json b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/appsettings.json b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/appsettings.json new file mode 100644 index 0000000000..10f68b8c8b --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.NewsletterService/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.ServiceDefaults/Extensions.cs b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000000..b6e80000de --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.ServiceDefaults/Extensions.cs @@ -0,0 +1,115 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +public static class Extensions +{ + public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder) + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + return builder; + } + + public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder) + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddAspNetCoreInstrumentation() + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder) + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Prometheus exporter (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) + // builder.Services.AddOpenTelemetry() + // .WithMetrics(metrics => metrics.AddPrometheusExporter()); + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder) + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks("/health"); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + + // Uncomment the following line to enable the Prometheus endpoint (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) + // app.MapPrometheusScrapingEndpoint(); + } + + return app; + } +} diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj new file mode 100644 index 0000000000..b8d847c913 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResourceAndComponent.sln b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResourceAndComponent.sln new file mode 100644 index 0000000000..88c6bd3078 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailDevResourceAndComponent.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.0.0 +MinimumVisualStudioVersion = 17.8.0.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailDevResource.AppHost", "MailDevResource.AppHost\MailDevResource.AppHost.csproj", "{70837FCE-AF39-4D07-A9FC-C52ACB32C0AF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailDevResource.ServiceDefaults", "MailDevResource.ServiceDefaults\MailDevResource.ServiceDefaults.csproj", "{9AE7DA9D-B8AD-4BA6-A358-6F352C2D7255}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailDev.Hosting", "MailDev.Hosting\MailDev.Hosting.csproj", "{896B2FA6-E580-4AFC-ACC5-383D052F9EEB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailDevResource.NewsletterService", "MailDevResource.NewsletterService\MailDevResource.NewsletterService.csproj", "{3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailKit.Client", "MailKit.Client\MailKit.Client.csproj", "{C9A58484-13CC-4391-9D93-88FDFA7B0DDF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {70837FCE-AF39-4D07-A9FC-C52ACB32C0AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70837FCE-AF39-4D07-A9FC-C52ACB32C0AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70837FCE-AF39-4D07-A9FC-C52ACB32C0AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70837FCE-AF39-4D07-A9FC-C52ACB32C0AF}.Release|Any CPU.Build.0 = Release|Any CPU + {9AE7DA9D-B8AD-4BA6-A358-6F352C2D7255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AE7DA9D-B8AD-4BA6-A358-6F352C2D7255}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AE7DA9D-B8AD-4BA6-A358-6F352C2D7255}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AE7DA9D-B8AD-4BA6-A358-6F352C2D7255}.Release|Any CPU.Build.0 = Release|Any CPU + {896B2FA6-E580-4AFC-ACC5-383D052F9EEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {896B2FA6-E580-4AFC-ACC5-383D052F9EEB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {896B2FA6-E580-4AFC-ACC5-383D052F9EEB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {896B2FA6-E580-4AFC-ACC5-383D052F9EEB}.Release|Any CPU.Build.0 = Release|Any CPU + {3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}.Release|Any CPU.Build.0 = Release|Any CPU + {C9A58484-13CC-4391-9D93-88FDFA7B0DDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C9A58484-13CC-4391-9D93-88FDFA7B0DDF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9A58484-13CC-4391-9D93-88FDFA7B0DDF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C9A58484-13CC-4391-9D93-88FDFA7B0DDF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D40F9870-8B8A-4331-BA52-CB368EDD31D6} + EndGlobalSection +EndGlobal diff --git a/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKit.Client.csproj b/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKit.Client.csproj similarity index 100% rename from docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKit.Client.csproj rename to docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKit.Client.csproj diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientFactory.cs b/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientFactory.cs new file mode 100644 index 0000000000..309dcc13c8 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientFactory.cs @@ -0,0 +1,57 @@ +using MailKit.Net.Smtp; + +namespace MailKit.Client; + +/// +/// A factory for creating instances +/// given a (and optional ). +/// +/// +/// The settings for the SMTP server +/// +public sealed class MailKitClientFactory(MailKitClientSettings settings) : IDisposable +{ + private readonly SemaphoreSlim _semaphore = new(1, 1); + + private SmtpClient? _client; + + /// + /// Gets an instance in the connected state + /// (and that's been authenticated if configured). + /// + /// Used to abort client creation and connection. + /// A connected (and authenticated) instance. + /// + /// Since both the connection and authentication are considered expensive operations, + /// the returned is intended to be used for the duration of a request + /// (registered as 'Scoped') and is automatically disposed of. + /// + public async Task GetSmtpClientAsync( + CancellationToken cancellationToken = default) + { + await _semaphore.WaitAsync(cancellationToken); + + try + { + if (_client is null) + { + _client = new SmtpClient(); + + await _client.ConnectAsync(settings.Endpoint, cancellationToken) + .ConfigureAwait(false); + } + } + finally + { + _semaphore.Release(); + } + + return _client; + } + + public void Dispose() + { + _client?.Dispose(); + _semaphore.Dispose(); + } +} diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientSettings.cs b/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientSettings.cs new file mode 100644 index 0000000000..58003223ea --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientSettings.cs @@ -0,0 +1,79 @@ +using System.Data.Common; + +namespace MailKit.Client; + +/// +/// Provides the client configuration settings for connecting MailKit to an SMTP server. +/// +public sealed class MailKitClientSettings +{ + internal const string DefaultConfigSectionName = "MailKit:Client"; + + /// + /// Gets or sets the SMTP server . + /// + /// + /// The default value is . + /// + public Uri? Endpoint { get; set; } + + /// + /// Gets or sets a boolean value that indicates whether the database health check is disabled or not. + /// + /// + /// The default value is . + /// + public bool DisableHealthChecks { get; set; } + + /// + /// Gets or sets a boolean value that indicates whether the OpenTelemetry tracing is disabled or not. + /// + /// + /// The default value is . + /// + public bool DisableTracing { get; set; } + + /// + /// Gets or sets a boolean value that indicates whether the OpenTelemetry metrics are disabled or not. + /// + /// + /// The default value is . + /// + public bool DisableMetrics { get; set; } + + internal void ParseConnectionString(string? connectionString) + { + if (string.IsNullOrWhiteSpace(connectionString)) + { + throw new InvalidOperationException($""" + ConnectionString is missing. + It should be provided in 'ConnectionStrings:' + or '{DefaultConfigSectionName}:Endpoint' key.' + configuration section. + """); + } + + var builder = new DbConnectionStringBuilder + { + ConnectionString = connectionString + }; + + if (builder.TryGetValue("Endpoint", out var endpoint) is false) + { + throw new InvalidOperationException($""" + The 'ConnectionStrings:' (or 'Endpoint' key in + '{DefaultConfigSectionName}') is missing. + """); + } + + if (Uri.TryCreate(endpoint.ToString(), UriKind.Absolute, out var uri) is false) + { + throw new InvalidOperationException($""" + The 'ConnectionStrings:' (or 'Endpoint' key in + '{DefaultConfigSectionName}') isn't a valid URI. + """); + } + + Endpoint = uri; + } +} diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitExtensions.cs b/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitExtensions.cs new file mode 100644 index 0000000000..4e5388f60f --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitExtensions.cs @@ -0,0 +1,134 @@ +using MailKit; +using MailKit.Client; +using MailKit.Net.Smtp; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.Extensions.Hosting; + +/// +/// Provides extension methods for registering a as a +/// scoped-lifetime service in the services provided by the . +/// +public static class MailKitExtensions +{ + /// + /// Registers 'Scoped' for creating + /// connected instance for sending emails. + /// + /// + /// The to read config from and add services to. + /// + /// + /// A name used to retrieve the connection string from the ConnectionStrings configuration section. + /// + /// + /// An optional delegate that can be used for customizing options. + /// It's invoked after the settings are read from the configuration. + /// + public static void AddMailKitClient( + this IHostApplicationBuilder builder, + string connectionName, + Action? configureSettings = null) => + AddMailKitClient( + builder, + MailKitClientSettings.DefaultConfigSectionName, + configureSettings, + connectionName, + serviceKey: null); + + /// + /// Registers 'Scoped' for creating + /// connected instance for sending emails. + /// + /// + /// The to read config from and add services to. + /// + /// + /// The name of the component, which is used as the of the + /// service and also to retrieve the connection string from the ConnectionStrings configuration section. + /// + /// + /// An optional method that can be used for customizing options. It's invoked after the settings are + /// read from the configuration. + /// + public static void AddKeyedMailKitClient( + this IHostApplicationBuilder builder, + string name, + Action? configureSettings = null) + { + ArgumentNullException.ThrowIfNull(name); + + AddMailKitClient( + builder, + $"{MailKitClientSettings.DefaultConfigSectionName}:{name}", + configureSettings, + connectionName: name, + serviceKey: name); + } + + private static void AddMailKitClient( + this IHostApplicationBuilder builder, + string configurationSectionName, + Action? configureSettings, + string connectionName, + object? serviceKey) + { + ArgumentNullException.ThrowIfNull(builder); + + var settings = new MailKitClientSettings(); + + builder.Configuration + .GetSection(configurationSectionName) + .Bind(settings); + + if (builder.Configuration.GetConnectionString(connectionName) is string connectionString) + { + settings.ParseConnectionString(connectionString); + } + + configureSettings?.Invoke(settings); + + if (serviceKey is null) + { + builder.Services.AddScoped(CreateMailKitClientFactory); + } + else + { + builder.Services.AddKeyedScoped(serviceKey, (sp, key) => CreateMailKitClientFactory(sp)); + } + + MailKitClientFactory CreateMailKitClientFactory(IServiceProvider _) + { + return new MailKitClientFactory(settings); + } + + if (settings.DisableHealthChecks is false) + { + builder.Services.AddHealthChecks() + .AddCheck( + name: serviceKey is null ? "MailKit" : $"MailKit_{connectionName}", + failureStatus: default, + tags: []); + } + + if (settings.DisableTracing is false) + { + builder.Services.AddOpenTelemetry() + .WithTracing( + traceBuilder => traceBuilder.AddSource( + Telemetry.SmtpClient.ActivitySourceName)); + } + + if (settings.DisableMetrics is false) + { + // Required by MailKit to enable metrics + Telemetry.SmtpClient.Configure(); + + builder.Services.AddOpenTelemetry() + .WithMetrics( + metricsBuilder => metricsBuilder.AddMeter( + Telemetry.SmtpClient.MeterName)); + } + } +} diff --git a/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitHealthCheck.cs b/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitHealthCheck.cs similarity index 100% rename from docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitHealthCheck.cs rename to docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitHealthCheck.cs diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDev.Hosting.csproj b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDev.Hosting.csproj new file mode 100644 index 0000000000..fc602e5bc4 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDev.Hosting.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResource.cs b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResource.cs new file mode 100644 index 0000000000..0ea11f37ce --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResource.cs @@ -0,0 +1,50 @@ +// For ease of discovery, resource types should be placed in +// the Aspire.Hosting.ApplicationModel namespace. If there is +// likelihood of a conflict on the resource name consider using +// an alternative namespace. +namespace Aspire.Hosting.ApplicationModel; + +public sealed class MailDevResource( + string name, + ParameterResource? username, + ParameterResource password) + : ContainerResource(name), IResourceWithConnectionString +{ + // Constants used to refer to well known-endpoint names, this is specific + // for each resource type. MailDev exposes an SMTP and HTTP endpoints. + internal const string SmtpEndpointName = "smtp"; + internal const string HttpEndpointName = "http"; + + private const string DefaultUsername = "mail-dev"; + + // An EndpointReference is a core .NET Aspire type used for keeping + // track of endpoint details in expressions. Simple literal values cannot + // be used because endpoints are not known until containers are launched. + private EndpointReference? _smtpReference; + + /// + /// Gets the parameter that contains the MailDev SMTP server username. + /// + public ParameterResource? UsernameParameter { get; } = username; + + internal ReferenceExpression UserNameReference => + UsernameParameter is not null ? + ReferenceExpression.Create($"{UsernameParameter}") : + ReferenceExpression.Create($"{DefaultUsername}"); + + /// + /// Gets the parameter that contains the MailDev SMTP server password. + /// + public ParameterResource PasswordParameter { get; } = password; + + public EndpointReference SmtpEndpoint => + _smtpReference ??= new(this, SmtpEndpointName); + + // Required property on IResourceWithConnectionString. Represents a connection + // string that applications can use to access the MailDev server. In this case + // the connection string is composed of the SmtpEndpoint endpoint reference. + public ReferenceExpression ConnectionStringExpression => + ReferenceExpression.Create( + $"Endpoint=smtp://{SmtpEndpoint.Property(EndpointProperty.Host)}:{SmtpEndpoint.Property(EndpointProperty.Port)};Username={UserNameReference};Password={PasswordParameter}" + ); +} diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResourceBuilderExtensions.cs b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResourceBuilderExtensions.cs new file mode 100644 index 0000000000..99a9e81a67 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResourceBuilderExtensions.cs @@ -0,0 +1,72 @@ +using Aspire.Hosting.ApplicationModel; + +// Put extensions in the Aspire.Hosting namespace to ease discovery as referencing +// the .NET Aspire hosting package automatically adds this namespace. +namespace Aspire.Hosting; + +public static class MailDevResourceBuilderExtensions +{ + private const string UserEnvVarName = "MAILDEV_INCOMING_USER"; + private const string PasswordEnvVarName = "MAILDEV_INCOMING_PASS"; + + /// + /// Adds the to the given + /// instance. Uses the "2.0.2" tag. + /// + /// The . + /// The name of the resource. + /// The HTTP port. + /// The SMTP port. + /// + /// An instance that + /// represents the added MailDev resource. + /// + public static IResourceBuilder AddMailDev( + this IDistributedApplicationBuilder builder, + string name, + int? httpPort = null, + int? smtpPort = null, + IResourceBuilder? userName = null, + IResourceBuilder? password = null) + { + var passwordParameter = password?.Resource ?? + ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter( + builder, $"{name}-password"); + + // The AddResource method is a core API within .NET Aspire and is + // used by resource developers to wrap a custom resource in an + // IResourceBuilder instance. Extension methods to customize + // the resource (if any exist) target the builder interface. + var resource = new MailDevResource( + name, userName?.Resource, passwordParameter); + + return builder.AddResource(resource) + .WithImage(MailDevContainerImageTags.Image) + .WithImageRegistry(MailDevContainerImageTags.Registry) + .WithImageTag(MailDevContainerImageTags.Tag) + .WithHttpEndpoint( + targetPort: 1080, + port: httpPort, + name: MailDevResource.HttpEndpointName) + .WithEndpoint( + targetPort: 1025, + port: smtpPort, + name: MailDevResource.SmtpEndpointName) + .WithEnvironment(context => + { + context.EnvironmentVariables[UserEnvVarName] = resource.UserNameReference; + context.EnvironmentVariables[PasswordEnvVarName] = resource.PasswordParameter; + }); + } +} + +// This class just contains constant strings that can be updated periodically +// when new versions of the underlying container are released. +internal static class MailDevContainerImageTags +{ + internal const string Registry = "docker.io"; + + internal const string Image = "maildev/maildev"; + + internal const string Tag = "2.0.2"; +} diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/MailDevResource.AppHost.csproj b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/MailDevResource.AppHost.csproj new file mode 100644 index 0000000000..4e177d9495 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/MailDevResource.AppHost.csproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0 + enable + enable + true + 9c9bfb14-6706-4421-bc93-37cbaebe36d0 + + + + + + + + + + + + diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/Program.cs b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/Program.cs new file mode 100644 index 0000000000..d291b0c15d --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/Program.cs @@ -0,0 +1,14 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var mailDevUsername = builder.AddParameter("maildev-username"); +var mailDevPassword = builder.AddParameter("maildev-password"); + +var maildev = builder.AddMailDev( + name: "maildev", + userName: mailDevUsername, + password: mailDevPassword); + +builder.AddProject("newsletterservice") + .WithReference(maildev); + +builder.Build().Run(); diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/Properties/launchSettings.json b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000000..f0583ee1d3 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17251;http://localhost:15199", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21161", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22175" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15199", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19277", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20014" + } + } + } +} diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/appsettings.Development.json b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/appsettings.json b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/appsettings.json new file mode 100644 index 0000000000..31c092aa45 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj new file mode 100644 index 0000000000..68f91ed347 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + net8.0 + enable + enable + f2608916-3fcc-4bd8-9697-ce27b4156fc0 + + + diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/MailDevResource.NewsletterService.http b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/MailDevResource.NewsletterService.http new file mode 100644 index 0000000000..39958c6813 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/MailDevResource.NewsletterService.http @@ -0,0 +1,6 @@ +@MailDevResource.NewsletterService_HostAddress = http://localhost:5021 + +GET {{MailDevResource.NewsletterService_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/Program.cs b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/Program.cs new file mode 100644 index 0000000000..45b2365ccd --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/Program.cs @@ -0,0 +1,54 @@ +using System.Net.Mail; +using MailKit.Client; +using MailKit.Net.Smtp; +using MimeKit; + +var builder = WebApplication.CreateBuilder(args); + +builder.AddServiceDefaults(); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +// Add services to the container. +builder.AddMailKitClient("maildev"); + +var app = builder.Build(); + +app.MapDefaultEndpoints(); + +// Configure the HTTP request pipeline. + +app.UseSwagger(); +app.UseSwaggerUI(); +app.UseHttpsRedirection(); + +app.MapPost("/subscribe", + async (MailKitClientFactory factory, string email) => +{ + ISmtpClient client = await factory.GetSmtpClientAsync(); + + using var message = new MailMessage("newsletter@yourcompany.com", email) + { + Subject = "Welcome to our newsletter!", + Body = "Thank you for subscribing to our newsletter!" + }; + + await client.SendAsync(MimeMessage.CreateFromMailMessage(message)); +}); + +app.MapPost("/unsubscribe", + async (MailKitClientFactory factory, string email) => +{ + ISmtpClient client = await factory.GetSmtpClientAsync(); + + using var message = new MailMessage("newsletter@yourcompany.com", email) + { + Subject = "You are unsubscribed from our newsletter!", + Body = "Sorry to see you go. We hope you will come back soon!" + }; + + await client.SendAsync(MimeMessage.CreateFromMailMessage(message)); +}); + +app.Run(); diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/Properties/launchSettings.json b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/Properties/launchSettings.json new file mode 100644 index 0000000000..aed106ab5c --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5021", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7251;http://localhost:5021", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/appsettings.Development.json b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/appsettings.json b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/appsettings.json new file mode 100644 index 0000000000..10f68b8c8b --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.NewsletterService/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.ServiceDefaults/Extensions.cs b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000000..b6e80000de --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.ServiceDefaults/Extensions.cs @@ -0,0 +1,115 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +public static class Extensions +{ + public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder) + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + return builder; + } + + public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder) + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddAspNetCoreInstrumentation() + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder) + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Prometheus exporter (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) + // builder.Services.AddOpenTelemetry() + // .WithMetrics(metrics => metrics.AddPrometheusExporter()); + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder) + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks("/health"); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + + // Uncomment the following line to enable the Prometheus endpoint (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) + // app.MapPrometheusScrapingEndpoint(); + } + + return app; + } +} diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj new file mode 100644 index 0000000000..b8d847c913 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResource.ServiceDefaults/MailDevResource.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResourceWithCredentials.sln b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResourceWithCredentials.sln new file mode 100644 index 0000000000..88c6bd3078 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailDevResourceWithCredentials.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.0.0 +MinimumVisualStudioVersion = 17.8.0.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailDevResource.AppHost", "MailDevResource.AppHost\MailDevResource.AppHost.csproj", "{70837FCE-AF39-4D07-A9FC-C52ACB32C0AF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailDevResource.ServiceDefaults", "MailDevResource.ServiceDefaults\MailDevResource.ServiceDefaults.csproj", "{9AE7DA9D-B8AD-4BA6-A358-6F352C2D7255}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailDev.Hosting", "MailDev.Hosting\MailDev.Hosting.csproj", "{896B2FA6-E580-4AFC-ACC5-383D052F9EEB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailDevResource.NewsletterService", "MailDevResource.NewsletterService\MailDevResource.NewsletterService.csproj", "{3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailKit.Client", "MailKit.Client\MailKit.Client.csproj", "{C9A58484-13CC-4391-9D93-88FDFA7B0DDF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {70837FCE-AF39-4D07-A9FC-C52ACB32C0AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70837FCE-AF39-4D07-A9FC-C52ACB32C0AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70837FCE-AF39-4D07-A9FC-C52ACB32C0AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70837FCE-AF39-4D07-A9FC-C52ACB32C0AF}.Release|Any CPU.Build.0 = Release|Any CPU + {9AE7DA9D-B8AD-4BA6-A358-6F352C2D7255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AE7DA9D-B8AD-4BA6-A358-6F352C2D7255}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AE7DA9D-B8AD-4BA6-A358-6F352C2D7255}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AE7DA9D-B8AD-4BA6-A358-6F352C2D7255}.Release|Any CPU.Build.0 = Release|Any CPU + {896B2FA6-E580-4AFC-ACC5-383D052F9EEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {896B2FA6-E580-4AFC-ACC5-383D052F9EEB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {896B2FA6-E580-4AFC-ACC5-383D052F9EEB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {896B2FA6-E580-4AFC-ACC5-383D052F9EEB}.Release|Any CPU.Build.0 = Release|Any CPU + {3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C023F9E-2B5D-4C48-818F-A640EDAE9E4C}.Release|Any CPU.Build.0 = Release|Any CPU + {C9A58484-13CC-4391-9D93-88FDFA7B0DDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C9A58484-13CC-4391-9D93-88FDFA7B0DDF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9A58484-13CC-4391-9D93-88FDFA7B0DDF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C9A58484-13CC-4391-9D93-88FDFA7B0DDF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D40F9870-8B8A-4331-BA52-CB368EDD31D6} + EndGlobalSection +EndGlobal diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKit.Client.csproj b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKit.Client.csproj new file mode 100644 index 0000000000..c543c8dbe9 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKit.Client.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitClientFactory.cs b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientFactory.cs similarity index 100% rename from docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitClientFactory.cs rename to docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientFactory.cs diff --git a/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitClientSettings.cs b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientSettings.cs similarity index 100% rename from docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitClientSettings.cs rename to docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientSettings.cs diff --git a/docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitExtensions.cs b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitExtensions.cs similarity index 100% rename from docs/extensibility/snippets/MailDevResource/MailKit.Client/MailKitExtensions.cs rename to docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitExtensions.cs diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitHealthCheck.cs b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitHealthCheck.cs new file mode 100644 index 0000000000..01924a130f --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitHealthCheck.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace MailKit.Client; + +internal sealed class MailKitHealthCheck(MailKitClientFactory factory) : IHealthCheck +{ + public async Task CheckHealthAsync( + HealthCheckContext context, + CancellationToken cancellationToken = default) + { + try + { + // The factory connects (and authenticates). + _ = await factory.GetSmtpClientAsync(cancellationToken); + + return HealthCheckResult.Healthy(); + } + catch (Exception ex) + { + return HealthCheckResult.Unhealthy(exception: ex); + } + } +} diff --git a/docs/extensibility/snippets/MailDevResource/global.json b/docs/extensibility/snippets/global.json similarity index 100% rename from docs/extensibility/snippets/MailDevResource/global.json rename to docs/extensibility/snippets/global.json From b5331e0695444824bd06046450358534e1797cf0 Mon Sep 17 00:00:00 2001 From: David Pine Date: Wed, 17 Jul 2024 12:47:54 -0500 Subject: [PATCH 07/12] Update TOC and add images --- .../flow-auth-from-resource-to-component.md | 10 +++++++++- docs/extensibility/media/maildev-details.png | Bin 0 -> 92561 bytes .../extensibility/media/newsletter-details.png | Bin 0 -> 99541 bytes docs/toc.yml | 17 +++++++++++------ 4 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 docs/extensibility/media/maildev-details.png create mode 100644 docs/extensibility/media/newsletter-details.png diff --git a/docs/extensibility/flow-auth-from-resource-to-component.md b/docs/extensibility/flow-auth-from-resource-to-component.md index 6951847e1c..f2f1c01dd5 100644 --- a/docs/extensibility/flow-auth-from-resource-to-component.md +++ b/docs/extensibility/flow-auth-from-resource-to-component.md @@ -79,7 +79,15 @@ When the factory determines that credentials have been configured, it authentica ## Run the sample -Now that you've updated both the resource and corresponding component, as well as the app host, you're ready to run the sample app. To run the sample, from your IDE, select F5 or run `dotnet run` from the root directory of the solution to start the application—you should see the [.NET Aspire dashboard](../fundamentals/dashboard/overview.md). Like before validate that everything is working as expected. +Now that you've updated both the resource and corresponding component, as well as the app host, you're ready to run the sample app. To run the sample, from your IDE, select F5 or run `dotnet run` from the root directory of the solution to start the application—you should see the [.NET Aspire dashboard](../fundamentals/dashboard/overview.md). Navigate to the `maildev` container resource and view the details. You should see the username and password parameters in the resource details, under the **Environment Variables** section: + +:::image type="content" source="media/maildev-details.png" lightbox="media/maildev-details.png" alt-text=".NET Aspire Dashboard: MailDev container resource details."::: + +Likewise, you should see the connection string in the `newsletterservice` resource details, under the **Environment Variables** section: + +:::image type="content" source="media/newsletter-details.png" lightbox="media/newsletter-details.png" alt-text=".NET Aspire Dashboard: Newsletter service resource details."::: + +Validate that everything is working as expected. ## Summary diff --git a/docs/extensibility/media/maildev-details.png b/docs/extensibility/media/maildev-details.png new file mode 100644 index 0000000000000000000000000000000000000000..02b3af6d71fe0668e64e53d9983670a121c98b91 GIT binary patch literal 92561 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 literal 0 HcmV?d00001 diff --git a/docs/extensibility/media/newsletter-details.png b/docs/extensibility/media/newsletter-details.png new file mode 100644 index 0000000000000000000000000000000000000000..fa8c536fb57e69e37f4b208885e2b63ef19d7cd1 GIT binary patch literal 99541 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%0DO+Ffq}@Kj8sN`S(zH(9MUq+ z-JPGd=UW>0Zh^-Q!3Q^ss-TtNGVRU*WxbJa7B#TCdV^3vv70e2BtuTY;@~gQ2`6U- zrD^F{arCIEey64**12TX>521rw)Du!7JIPEm);S^Re4A~&SmiG=!<${PZ1+}04c z8#ALAADFtBOB(&KK3QM-798d)Uvv$MEkhJ+3!ivs|j)5_ zPD_F3hnw5P&XBW(5~uDRPi`Q40QQhlz6SmT*j3eJq`y5H-$gyXW|t^mcfr(&(Md;a z2)J+PRJ9S!yHgN+x%xIBu<6yxE)YAL_FM>O7KxVyf?3vY-n6gqa@qq{u!xbZnS^hn zJgc-(?nXuM)qt6Oz+lrG8yP7<)jziCV9qzQ5dCeui4ukbm6+-hg_M9^uU?_kEGlHC z{8@gUx@Iaf9~fm(!z!DaK3=8<`n5O`6PAYP=-2uEV8H#6O&Do|72xa3G!oL{uhyWE zxJuGH>ViZCGe$N}k`5T(00M!4+4$6rE*o`#ANkj1Tqvv!D-l^iEKS5)x%dFVlrd_7 z320?`v!4d&RfbHZ>!eN4jn&>JKlWYX-(y{y`sTENac3kPw9BIeiP=7jqe6-pPPF_rHdD0yyeH*Cr5vA$t*Bu>yyT z%(kyI?TY||&Hi;rW<;*1Ckn~)6@kZKJ@Vz?yP)qKzaaE%{7VlE-8z|*ygQj|UH%sf zTiG9^SoEsZZF;5Bqb%5GRF!LZ>3vYwRX(*~`@{zs!C$I%<}uam3;E-T2hJj-=ZeJS zm#dq0rd;J7(Np1myqWwjmQDI$kY!%O1+FrZ-KKn>`wr&)__IFgdMV* zJ9uR1T!W`UUD^PI5J_bEDAt{(kp+ukj3J{tSijS@Q<-kuC=J|ZXbA1bV@5_OzWYZoR}~zv9=ZP-P=k>d$gDeGl26M-E%=%Ssh- z`tH#Ed7L@g-oc&;N}~Qu*FQmB;Mo(iXTY-}$b#NpsS?9`f(}au1|?vP;?7s!%=DkA zl$({1n|I46y)CzX@4Rg@Oj>j>xsV{|lK^mqXT$9Zt(qa9Xu3b41b}e(2*~-J&5)(0 zZDrsH|JJ%-@YP<1#7MxWb>n3Gn5Av5-H%2KayY%LyQms0cR@eprFC zfBO|^OZacL_$rnuPB#+&M(}{>C6U;_-u}N1UIM}BKP`iXJo;~pCu4hkZEY|^ET{|| zc7nFU7ZvAO1A^+$8EFYt1 zJ={YIM@XyN|B!x7+*SE@UZO?P>bZd3Wt{V0Hw6C$DgV1Po2g9b8RWq!zsJ5`H(R}) z4~GqI`_InEXA7J>c<^B5S5uIH<1bNQUavs3fBQ&TWH~=S71-V|*u-uM-b$!jF4L`D z{E@yQC{k-DdczyQ*L?g-Ic~^6mShsYOJ1+Y#o33pbiYGg;OHpxgPqk?OycGAM5zCw z2{^z|U+&^&*Yvcf7Gg4JZfrA?VY%g3Ng_4E$57Wq4!$_s??Xxuf^z}`uY%8Znyu~a zN8et<)lgBPmGxf94B>WJR$w!#*TKC*xpMfYcQWrk7=87M<;=tG#OYHSf`9T_NAZ4d zucDVOt53dQA6YxK_sSz#HC^3AUi&H1&M_-{do352av<0A@YGZ>27~$XG~Je5-8Z?j z3jt*wr@k3*UyAV$kqzeW%vLTXYs=O7XRNX!UvRkWTksf)h@ERQu8pP(Ls5{kBLSv? zJ((hYV_j%6T%UYbXQXcnX(PyMX7%!Qf`Mm^F6WlBtkhSlzSHd$4}=49U6A)p-)U%9 z4>}LXjaQhFAqv!sxplfN1k9P`$&>!>L@qRtEYIQ+)>xBB?xj3u z;?x>_4q=2MjrptVpt66wFxZAH@IV<92HyIal+6Qm(OJ~L&m=Pe2a9ZOHi<>A`PZ;8 z>I{+uGR@ZQGXd+^h1T1@fjvklqL)RS78_oI$q1Qq_S^1FC*3zP99i%e0|@{5(BVDV z=|rQ7H&JO`%ZVRA%JoY${>QJu``ug<-<-1VPz!yD-weqQhtSOIXN{gCn{v8XdkBfjBl=uT>`TLkPJGNX8Iu?$ZgZS+;v|&eyzS70f%0ELrA|(TS+4Ur&h_q zH^)lpa+gYs!0P!*BY>0xQZ$$i)fq!a?HAs@>ymHaq6U}*(~{42@Q1rZ^?=}^Fx9AT z?eX@**53LPhoz`RheuzpHK)Lzj(Az3)@ahdu|NLqGBaL7)bQ45vQ$j-!U@qUD5#Bm z*xlKxBGDQ_#oBRmCZ{v4=^AKN{2#q^9T8gBH%f9*$yNE;%wK0dj^_=LtNgjzVS0GT zKa_N*F?X%*T+B_cAm9I(jo24MlXUo%P36G=s?ED!=Wrf7y>Vm;W8EOMKCkX_^KjLQ zZYJg1OwUO?p|KgF;_6mlaH)sw4BfG;x%fPC)VlRjE+h2=KLbOfZX13dK}hYL>SGMiXjbn<>5RI?VVpXFIn{+)^h_753iQ^fn5GOFz_NDwDVI* zgs>zF2S@ZlW|ii}kM{G@G@6L&U~jApP_lBh3huKA*(EMqTu*Y2R~6V|3U|G@;tf<3 z{4H^0>S!u95)7m|s-9(QOwM7$BO1KL?PrSf9E=K;jS>s2JQRj21pPBC3=8*Y&g+&h zso(TGsRzbe0*%nwH2!L>WE6BPWjb_|W%a5`IvtI>#7`IH0`(eAmhu=B}+ddT9; z+79gVF_AC4)ZD(rh~@ahGMt=l^E~XNK8Aj2;DFkvRl%XQ>Q%h|(W(0gmXmU^@c48) zM6TKoC0!j%U9Qt*v=`h35Qit2BMYU!galBa=G~|{(_;kPd|8z~dg9}sxwd9)XOl2I zQvm#<2^@|!)o?8$qlLmop_)Y+)5n?YPuP*#9H9o``HSAGahgyVEP~=!N9Ve!@V;{N z$?j+1{K91mI5=IsXop~DDHtYwEj}+TBjThXDlR`Vy2Qcg+*@jskTFi@(Uq-Q6T6e; zxFn}?*LBGDkll&)T68i4cG$Bw?tX7H!VV#n&E8Kb>lVFyHcg*OHkDs(g1P!r^>~|w znkKnYy?W2NhQ@lJn*&?7d^X(8h#9<2HG{r3Nov;>UQL~t;bIVdpQBc)G;WFk1hJ+Yg}cOL3ryE@||g5p9YYlR4*K9tvaf8^4_jlU=?7O7>5WUmod?-eMX<{&2% z1-8TJ!!z+ob{6e{*@sr$)T~@93Wb}Jc7ntEyIkof-O7jOuK&a==dUZ9Rx!OPD#iTE zbW(*?^kO7jr+FuF1!>?$LUd@3ixfw!Z@4$#n{&Z~6zs%)XPba+7qxS`U;M#pNND+c z+j}^{k2z!TKDm|3V-{6xzq0I{ij3tQuE%67)|^v;HA%~#C>%!HAWr>!6mIbr+3Hux zmrDWx$QXL7>A1PJOz6e$`ygdoxkRl2l9 zWz!*{Luf%Mp+^WUgd}$b+~4<|d&)icKj%OHIAi>4WH&iW3&?x;a_$@F~x0BTr#V_M}!o0Ox2a*FX$xye2;HW3P?*X2o0wz24KQ zs(^{kK)MqbTYF{fd^VDi_|omQKNiXv^p7E=Zk>i-etr74NtSM~E_DmKmOU&~5nRxq zY(}{oulw2*HftA8Ht2~DJC=O}5~lsWz5?;;C?0K_@M5G`4-|!qc6>@=-8vt?y^7iX z(!jI6zjbyJ%BbpF>pGwZS6rVksv6mWeMz2~t@D|xub|hKw)57uPCU*rsX}hnwNQdSU*uc~Lc$xkL zP5pGbCQL&?w(BNq;`4&Z9WN}2LJdX}HsQ*mJrq>})D+El%~E_qKu>y2pm{4S-2VDa zOB22WB)=tBj<~l2K_=-ye2UPyp<;4}3_%yjR2&G0Qvq~yh4+!?E%*BMfj;Mopd}X@ zzN!_@=$PdI@1#llEjq1v2Qe))%}ed4>`ZkrxN-1D=w<+%hbs$2?PBB5Bc-Yc#-`cy zDik5&q7PD_ky(CI3@_#a$etj9Q}dQe?>>I$Ugr`nc4l+ucNX}1$GO7@^%OY|tQ3t7 z;y-NG1}2tmiMwMjD@{r^mqip@~dQQ zdI0C5p4!IY))9L5aGc8lHw2m+o}=t-oA9h{8}*tPRvC2pD^ISK zHD#a3gipttt+t63+XKN%0f1wi+oHw|_|d$|-HP2uqk#*BM#j?_jNa7gsRB|B}obDurvlErOhkPw~45)yxLjI1*OePvzIo-FFRi#Huy9$Hl=u6 zF2P(UmK@njXv5XdllV)<_&l&^2LjE>DXP`*wox7+3ct2QAZ10rVp3oIshwbJ2JiU2#4f*E>+dv;4h0fnEG*y}P-}+4} znqS2)=y$=GCT}-(-9L+d`>ZB)LB((JlXXn^VZdX=zUJTTv0X*s3!ipQ-c)mRbo>Z} zqym>F`9ALc6KB9e@NBDK!nXl0HT*C6B6iJ1zOQRAHWT-3!Gq)#&nHffj*1z$*uW3P zhM27TS1(j{zTNg5^BuHGUt9>geJNfW+~;z8mvB$S-NzUkVvMB$w#B3T=9hbio~1kW z<}A@EMculoQz`6Mf6&;FPCg$Fr-*!o8OE#w*K|P{zKs*K^(AOxzo6n`hd?OCu+Wqm z!`h0WrQ&F*J7Xlq*rkypyR}k$k70rtn0ZeMeW$Ir^nT2qE9mo)?}H&rgR8Ybf6*#z=oObM z)8vo*+`Zy?K#=((oC?Qn!nKewK~tA$y&y*K&Xfp9$sR8Ab2R5X1e!>Fqa-FuakC^Q zSR81A@H)+(o`LI)MRlufr(QRnBm=F;{#j%E@=sQkbeq>E$ZMR%+>8la0`~bJB(YoH z9~A#RG;&&i1J+l&V?x$Gx~Xq>$2bfWm~*D_{zOo^Z}E8i^8_cA`y~%`ArSH{a@bFD zVf3!PUQGsqzhE?yECb1@uMhr9>CzrQzsa8>-~Th%c|F@SUs&t>`SUrSklO?O{qaHp z6gUa~S0!${zj&iU@b{mkq>b?975 zy^KX_1}>aaU03zz+dSVmR|F1ul5g$}Ho(1x>30@Ab(H7PrM+p%Vf!aAPJx?rH@U*p zv<|58bOed+bx```5&tXi1-UwP(W1UK^1iSf-#D@ORrK1+G2htgE?z4MNi*r*hXAbLgK0AWH}QhwdFj zq|lVotu@!Bd(CyxK0Y0-s0B0dh|7K_gSpFTv{A6pqYJxb5vjK*^!v+PRiS`{2CQ4; z7BwxLi{ybo;NC%=JuF)KV{IO4zOh6L_*S+gnyn-usJB{=k=*rAhsF{gHYBaK+u%nn zu{UNy!oOzT&hS*7zH#&t;Z@#TIVwR&6tYPOw{dL!PAKj`>JW5vFp8deqp3w?!S)6l z;Gnn+2eK5tjX*+Gp)V?k6%sCjkI-amH8k&WcC&o;v^(N& zK{C^3Iz8DI;0BJPR{}6J6+jdhD)o?S8yf4?W!wz?zd;_lzq#qJQZtCh;|M3BgKmc?97SR=1E9oKWXz=%i1i|Hgwja z%ZaVFL{#tZ#9JdPVkFz2QB#es85=J1JDQ2h<${L}fuLrtZG#l6H$+IBVaEjeW`CAq znH;p({B3cuwKSIj)btGZAyqBp*9mCQG$*}T(%yFQl#w#T&m!DC4Q3-BJQw}Q+g-HL zS*gBuBMbFJn=IKmyZ^F(t^W3c&l|u>QGw}i$OpP>CbH|+zUu_cg^v+e z8-<-xlZ^vwU#=;N{~ZfZy7C$ZrrM?^)B=K>Qg+sGs+)~P5-m}p?Q0Bds)(|FMKovK z2xdeyNc}qLSIV`dBwaHa5<#hRvw*?P}Ql21J4dfkYCePu&SXHR< zAq@2mb*Vdz<^kPa>JKr(67fZ@pWGe;+%EPGw{SHWyqq*xhgc+~%eXFP$d<~)1ZBt+ zHfHV2H;wk+039o?k);+}#1?rYlb5QO$MKWmT;YVDEdmxHuP5OY#>YvOr$+MEm#IOHPUAb8#=+>MGc2)(j~E)o>-x<0n{c& zD43Fk6cU}T6FURhu&o^zEND%E98#C6E|BQd>Z!VHF!t$+r466!+Av^8R~U;0T#>{L z`cgB^7GAoPF+zQ~$qY=imR8ikz$_C46*r=)$lrvj9c~S$#Ab<%U_V|N33m^1v#X?t zi)f_~l2a!O_v%~%e^(~q=Jp(6ibFl}Cmi8jE3m9ue)kUI|CtK7Xbrr=E?WVWZXU&> zRYhK+C7{5$sl>UuO{jUGucbEBy{$Kfb!~9G(#z8ZrPI@++EpnFajL0JjPB97ku8H% zUHbzILI63SByzyaQdLGnHn9w9i4I&ValLTd(gG5H$ek9>PxQ1^D0b5|pnUI8R}t+* zrDZJ)2^Q?MFZz3Jy%J7VQ#|HQAXF`JdvL9#mPy}#?4pbtOM5_GjMG-a&$GLZ<6ci! zm*<*-ny=0P1HUm!m13E=wUsg}-kAa-g!-f3EPJ6cyW0I* zV{-7_8=YjSmY!y^!*b?e^lb5}KKQ!;`S3VqQOP@#Jq7*9Y28KfaG7mWqunB2C~RtK z${bfA^VPHB;LPcvZM3V#Eka?{c;Hu{JR3`2pyEq^s*1c@N=N)Yg7CDNkx%RiSt9q) zrmTo4HzIYQCo3*%Gfcc@rR4HjPikR|RQo`4EDB*XzzkREQ6C~b6QO7mmc708MH>@{ zbGjQ#^jIM!xR(VdXbZ|yx|k2&I1`-j_XY^ z%s{0FgC8U?emFMmmU+UFzugthXEB}_`HdBg(3J^m`U30;-3U_?xeQ-Ly^$x!VC(H{ zZPI3{t7$sW*fj@g{(c$H2tn((6ELZJ?H^SfZ0fIBVnaxuzesF?r~At`ZGwC?DmRZW z+Qr_!LD4H+X-v;n9b)w~dkj~37~rknWOMsE_!%Lc4QjAzXTR(OZQi7p=LD~J4M~{S z1$qtZXi{CRh<#%+iUY#twAp=jh+wy69i&KYPp`uDrp9!e_(n4K!uQ34<~1Qk0@X8) zO}l_$!t*>IU(Byfs&6Q+A-1e`SJ?u3byt~aE&KT9wiE;3J+Fe|(V_CPu{o_ek2cLLw+~_;my0fx2SNc$vD4XL&3RTTE*{? zlK*{td;W-byHPl0v*p{#W^Et8uk&pU2JQs3kZAc#EKEXAM;<~;;|5RasEL{(PrVkE zKW(I|1h*bbIAH^SGBJ=&(Ir^M8z0S9tQ!lpjvg-21Qq4#SE@mErri7W%^~R{Vd4AMMW56~J#uny#=$MhZ)kii|gVv+W z*Xz@WMgsDF6)1FKCFE-D9AjRu+HHMcF(?zI?R#-lluKP_uxikuQ>J;tVHZ1{wbIaN zc>GH;=jvQ#W%&|aX7lAf!Z-S)zg!1Oscd7pmZI;=?>o(tzV%1Qc5k|X4-?pf7Xv%O zZkwBG04@wq?}P&A`u5SVubG8K>^SFwb~C*40~R9oE2 zQ3w6$P)O;}9>3%``}itqjzTd22xJ~Sd}HI}kAK0Bw=>?nc<};YgEzv|G```xNdNf1 z>v$foYC210ezSLfq#Gd37`KBe~Qcm zxCBOW?<*De>E3m?ICV}FU{O&~)_bTG)dtKFqe4@W5Y!RDZ>Ipl2^iJu{oviP#9{OS za1sHe^lS6U)yT{YDczr2nb>^hW{XkycnEz7XiSXc9>+@h1>mqAIL1a+r0~=^NW8Xi zy!M^HA4$5M@li@7>G*B*k*>kPL>MVp0oW#0_ERlKO#!f;&CBZO%e8Y03#sK^xq8F* za+9!F?BZChf{a7QJtWYoYnz(}UM9Px`|R)J>N=`G%q%YU=yCXQ{N$?cy9`nD#wRy` z4^WV(s4E7$HNRQE0Q6RcMOxo9kxOVQ`G67a;9h}6M0&ONYWg?7|3Ardh2;RU3UHzS zNqUQ3`v@??TL6DG1n6_2DUeJ*r)CfD+oIMtH$~XYsHd+l4sa7>v)iPp%5Ug^qqB1x zu=%D8*oCuik5QqfoGUBH?+(i%VAho9P)hJWtc;96fh57^r~ef&WF8h%xGn(3@Lc z*55qlQf6P;qzb>4puX*c4)JlC(H}_Kt^0-8|B6Gd-%o_O;ZgYl=#FPsnxYgSTAD6M zqWN~19Ff_Gk=)%g8`=#_3(ANcq;9)RlknGyX|g|v(usCPY8&|NOqalVLl}9kL#wx~ z12?cs*C3NgOH$PzSd=IcH}X)ABTDq$ z3ErJX*8!)8O5Pr+giGpLTdOHlsrt_PF&GSBBC;D#(YV!1BnG87TN+)vf=TG5e&wlNpz`&qd`DNCZ_oDl;Lkam zX;EU(?SkvkJq$S)>en%Vr=p6A#R+(lbqfBqh0(R+Yb1JRXTaiXm(ul$ZweLmU&hkg zI}OFXp|Q^5TKLqXv2L27%PB}rj9STH6bApr%*@1?-{o z4GCz1{q&A`_!6r?%*+AkO?@~7!D9cl5r>naYdW?fo73@aE;C#QRQt8B1;=OI*PV+b zntM_v1GbkpG`PLX3C4w4qYD0RKR)%CX=N9qwB?+tL)-*#uv-r4541}x^eIw-Mnz|g zlrx1PQPq32-e@EVGNQVc_DE@Ax_~~gyh5QXvO#_|i%Ewr6jlkWZYM}sde0<7^tIJ^ z^je_L9Ib}ZmY3fmx)Sz(-Dl_8HGE6@05O*S=`W}<`|%Ymo?o5Q`{6#hBY+@vfd znj%5?8ole5_$uY5Lo094OBc(|u2vwNSVweM1QgVwghbJBuLSW6i>&4TYCC;VE#Z2p zg0>1?-z+<8&6!8IYa`bALZaY}Tn~btUC_p5b--?cztu&nZe%{SSRLNv9L8HF)uQ#z zNS#GZQ=2OHn4NVJ4M;i8#vteH_F~a;OEb-LmQ>P1Y^+0e5`ye*iz&+!K&&7vkhx<9 z*tZP2I$I5};niluPIJJIFPz*ey}9Va0rR?)*ZHM8oX?F~tjFFI7IPgE%r&@l*5Pl8~ZmFf2Qd%j{@#Zr^POY8%Wkx_H zzroYnv0+5(V99puc)XfckB_w^)@=tp=Xs4{d`P`RRVvMld#pRP(8fGA%1&%|mWAH% zx247hklGgj%w}ZOngKAG|ALxLHvf~9U9N)X7Q6hl0%gHr3E-_ol(3VM-9#@MjcaQe z&@v;bgwNG0sF75!AK2Cp+#HRo%*h&(6>AlCa#HtU5qsionBPWa6C*^^yr#%nO{==$ zn?r-Rbehe)u1kq<;wasxhB6wP#-d>gXv0lD^?p>uEC=>qU)gduX=l>aKEj(5X|D4Y78%%ou4ND_=;bg)9L9$ z5fddkhl`6lk!86xifjuKy4^M8|EXo-=?*h2J8Qjdaj3*@f>6(!6K5z{2h*%9#(h)V zMxWJ?3Dh456*;+z`qM8*_0zC`f_Ieo?lu}Us2=N*7df0oQ<7rH-%}A2H zxU7fG&Q2V-wF6lUxa+obzpp>BcZF?oDDGeT+IG&1FX)V}U z91S*eAeG7w250#v!F8N)&L(HlLH_<5&kE``kxwt?*2q{HT+#B7XMKQtWEqg(hM!sF z4#eA_Oc4HF0F&RDbcF|v3OHxUSU{UUo>NvRd{~W}5mFiSAV|>RP1z0^9R^m_HjgN=%S_06&^qHwa6c=pn^W)Fii-q=vL z9q|tCdMw=AQbsxN|!Ga2WnMxjI>jRy?d*y+mOymAZAQrPLg?6wd72Jc{aeM^58w|&&f?ctyFC9{j&hHX|5o>IvR65mOQmKobrfHiy~rOJjW^l zWCsiEUGLDSUho)K>WG@&@o%srZVao4%AYu-I{k$%FsxoM_Ak_;NGR<4&Cf;>5JLV) z>9;R@(81XHN#?_P?!s1Rae@)X!M5Y8iYP9AZE;bnx)}Kcqf{u7ed<_z9L$Z-bXviI znBXO~ixelafCLxUH*U^gETya5+}#75QQMOr{zsg{zzW;$GB6WXJ7T1*WMz|_K9Zh1 z|BOAZIg+jcBF3l#6+q6w$7?QsTsK`^%>zrD`9Kb@C8Oa@qvS*&5;8dbb>sV=@i(7C zCQo;u+V&CbOGNfy-1V>b6#*iV4tRQvH@TLUE>9%;|3oem7XkRY=exE$3V-o6Ed%$D z|FbpLc&uk8 zI=z3pDEcoX1Tc6yKY0kDK~7ed9hngo6-8H<3HWnQAp3lHPmcniwx+AXsb7S(0Oj*& z*t3ri;xosnf!KFQWW-hmyi-R6!Hn!Ff8D8=GSI}7N~Y= zS|hhxOU}Aj+14VaC~W0AeLkM}WAQ~nUEcKBWvoOVkSel;LmG`^Z7(9RHD-MOc zd$WR`IGqJW>3FDO) z-LkWmD(R(@mQ~FPyJ$4>PN6B^U?`+zPh|Chuj&3O6QB>q&2}zpBj?dOi{S{l{{Fu3 zu=dgoqTp1RU_mDmQ#5^1rI&tJSgRC5-q^a!Uh+aq@=Wu6iJKVwKRJvL_jW^g*$2B{ zAVy|>&JJ^t30?Yz{0|!XJkZ0*v>%qr-nLl$~3?Kt`b~hfFv)57#K*X1pHAI z!Aequ8fmtWP)hV&C<|_m9)&)3ic^o4ZThPgK-y*C_JrLy6=~)lGO@Xmi=^6INP7iQ z)(z~=@^{g06zca5wD$iU793(17+a3;(iyNGMrJ9DEI&#DgDwno{>_Du{D7L0v)(`- z1I7N7%7kj=$wj=DW#5M@WqI4?EYtwblR5y0ASe0FU-T%=&X$scW0w;b9FL>A= zAb7^tv?8+0{lr@%K-O~G)^eSRQ!8-ZtP0WnOR;hIY9ZJTo!+CaO*N;QI<%q^P#gIB zlL;O5ZoUGvp3CNe<}TVq)($`*r=T_$#&vWORGB^rR{aSHhBu}a0( z_Kl-PeO!_3K%w-IoEayIgWnAGX;7O1(?tzWd)Hp%aPg#cFShaQu!V(zVbbsUIr6 zykl#Dl_mAZI#e2h-x+1Zi?zy5B}eyHJN%S4FfU$d!=pJDDKzOf8wsRf~dy-choIp`Z{` zYr%PPp4{9uZuNz5-X-|Je^>bAs>?SRdkcmLx&`NZF9G_!EB{M3!{s(Rs`8gX(!KMAT2Khz~4O`V; zr>dNBpvjDIHtcqft#qVZXuw2O$Ymmfo7VI;Re*YS@-d(c0y?>n78=2%#Y)efT#c5n?@(#LE@POc))#q0%TcEvS|VCJp0~2R2SW zmvK2-s6;VpHX{vPXoRgark3tTU@R^#k(fkVwamf$0qfPH6;G%y;dgzH2UBR@?5cwq zUHtXLnkObq=N~3`=InP3!1h4cO6qmjBlUyw4s`reh2bFlXt@aHcxO*afVFZlx*kzb zT%hD*ySu39zTzA3AjB8qI?{dAulHwP7+@=p_dm=XU&H%9<`RThx`Go`aBq z$a8 z$X4*1H}>RRXRNzZ=YBYERhL|9l~Xt$e1)x(38k7pC8vz^|?w;bp8n^r`beR zyMYZ1tPw72E^5t;-ebhD@PaQjuLV!X0Y~KG@~{e!w2`3MdcP+7oo#nD z#?{}8|3$woQ_->xQhZ&s+{T|?FPX;MsSh=RI@naox~R-JbVgM{GWU{uTxrbEK=YM` z(ABCvH`B8Um!3`v*h#N#2+$I7c1}i8fU=7@F%p7W0ANT@H7>p}F-`8H>O)fWPUHOK z*0-?=mFe=FJ|9yDDcw@=R%3#=I%ME;vp20p=cQiKuKN2_RIy&lzB?*L&sg#WWa^SIhs@kts^hEbD}OQH(?E2oCz(}4;8Z4JDwU$vx1X6V6%{{ z`*;Gj^lE#wtjnIGqcA|cb@&tkI@6b_!5Joyl}! zMfKcH#xV(V&&@U;H0ZiiRxZ4^C7W=?#6upZ{Hy&bP~BC&W$Wv|wD);>M!aHdbC|0Jbq!k{K@pog@1LFoE6+1dy3YX_|j_k zVD;|p_8p?)0oCve&nF!KA^5%TV^|HZfPbY(Dp|a(p}B8zCWz&++%g)_>7-Vx)Qa|T zQ!08X(%PiDvtCWxPTOB5vFu-}QnhBnR)#qT8P`}#P;+Wm-yK!0p7sBhg1j+Uz3`dg z5dD?F3CYV6PAmu!m8y@eW0LtbhYaQplNzN0%W6~Nj&{3_ zN@B;bpM(>GGIpip>z>$AhB4Ez{gS&j(zRt3B8Bf&sLb}+5H{bihb3&B5MNgK$bDbQ zYUGdwy1(*b+A&^xrvL+MDPHJ6LC@z+qp{echJ#wW9sUW}IU09ejA{N;W!9Oe>=Csj?9sZB_+t7(^>>6r~3!8q^J? zddq2LBzib8X0xQg@!|4XiKXXzmff?ykz zWk+BF34-+ro2Jv1Bk}@X2D#(K<8dRV9-VlF7mP^R)0~LA1AuV6e<{#!BWu)5tE3M# zkYE@QrrlOs37L%GD|5XNV!Fp6#7-T(Y@q6^GgDcS?G^{hJg7?g1a==*7CdW7icEb0 zn4^L5;LR~iQD#xlcb@bX1H^Xkb9{Qp_T0*zw)#O6U}nTx80FK35};uk`GL@YxJK?& zAYEAB@!TAR5|ZdUtBJTe3bf>*?B6o-LsQ`1m67RbV=n(Iud9x+yMjaL9PXXwXBnls zUF6DPn+^n!0PZ11((RPV>cmVSt83BjeW9_pH8eDUe31zjh3pR?^aiJMyAA?*Sb-;k zfj#X1l(94}=&NG)cG8C`3;hg%{GjpkhX2?<_2$%s#&!YjJvh+m%Lz+EQ)Q416v&Z9eYJLM6 zb3W=#uKo#z^t)PmSV27&Tz-Zq>%&OVlH%r=$g23(_!JTDMl+<(o{)qKfo7Kh@Xfv2 z0*AX zuY`g3)GCl)Eqfa}whF_>73Ptb=qO+%5FCpWtozY35u!de7cv*-8tCY?!3ccPnDdlM zs*!?%YUM2t4WcL^0MQQdb4OO2S28();Kt`TpdwLFwWE6O3QyaKKM;*|?~rIx=$Xq~8NF5OX4G19tG z2CA2ap@Lufc`$@h&1_Pm+s}Zz`(KyN=IKx$!jym;$l?OT%D5eXpHKJXpP6PsC``MN zdsGzyVkT!~|5*&5LI+pZd0p|}PdvQaGYWe22iHO+#WCyc#mO^}<8z1ZC#l#5FDE!K z9~M13;T^0YXHmB{>i+AkBV8H8>XdJON?$MiHmX-4lFV_8N2H4P;m4ik(iwnC zULH!!>BeH7KGnUPM9+*cHYvGxM)-kYD}~qAC;ws4JVsC`JrKJdlun{C5SiOXTIaio z-8iG^7=&glNx*EzV0ocL=$u{64>v8G$$ciDR@z4wi=7RM;>gy`jUW^va0_jhne!bEi)Z$d`d8!Z8V2y7Yo)#G9E^yyF1e} zWH;cVY2z_R|D{Zm_6+#8S$`aJr%E%9Ns*fU1`4#w$7}Oqe&KRGT{(AZ#{0MmSA9zYKevKNpOO%R+P! z(4PzfL)3DQNm1<1bGwWrXOev^s;iHodGD^(B6i5z>9~4CD$uTqL8lp=F@ieE8B>%; zo`w|+5=#rLU@}59a#vXp49R#wk3I$!4KB1PvD70X?Su3%I?JWS~+po-Bh$uAuUcSnzD5NAo ze7`T?@&yahOsKr9Y^?lm-}a>mhItRMhTDl3P^X3Yd@o04+QbSZtedS*1j0)SSJ%p!>oyAZtlP>f z`C7sVHO$-C%JZy@_igpTqo;ty;=zwWL}q96G7D6=fK~Xo%F8lDRAVLRg2Hw}m?=#9 z-11i|h85{HD+;^G3o@L1l_kY@{v0Dk-<1)Mtr|aGpgFP&yU@Ni%D(e`Qn;<}uN#Uq z0UJB)GCVXi`;htdnAA%74tN_7DsG*QUw50Cv@i8LTXb|d=AJoN_|V)p?ree4ke^E1(M2=Q z-lq&o%~F?d!yf?%7k=Q8NQVRsQ?%t6a8IC##Wh=m<3h+U`==JDu`olrZ2#UeBT{E#RpEy8<&{R4!d=ffk&KJ6{1F zd4a?{JJr)z*ZajIP`Mp?&7|z9+*3h%&yHtE+ef&H}}0of;1J`(VBYC^WFBkigFwg6pF;$5#*`6i#Xjg)BB1%Xh< z)Z+Xb=*2*0HcApDCsdn5Z%57m2{OD`mS5f$Ms!3%XvSLo<~pW1mR&;MnFmPFii;93 z?zB@yhn^=&oc!evq{``9*m*YX23e>t7>wSleMm9+HI zU9R*(`o642x|{pmt3DxDnKiYg-LmmQq{`UDuRJ5-+;8v9(GQwV{Bg>Zs?B%8qyx{z z{Qf8APP?ghU0#oI`U$@=*7L_6a`v_RUXDlPd0^mbxsr|@Zx+KhIS&_I4PxbL=S7Qv zEnlZS#Z8|BcSM}OYeDPBr~E+@B)}I#nsM*?KGcpE4LQ3Kr8`_0#d~3#920Nq;^8(P z)8mOk!TyvS^*WdlC4tm8V<0fwXmmMHY!h2#6JRNYKCd*D^8MnkQ%dsQ*T6`9G9$?9 zgmC=P=$@qW4{jSm;jqjTZ40TT@m=@c?XL@|y<(|lCGf5x#2`nz#^Jnk!TJ$hy?CDh zW3@ps1Cn8{|wAwkHN}da{n0 zrIkBY)kIs1I(rvhin3llo^t#n$cse8T- zb{8rnBTTDlNTj&*a+zbzmaoP9h#v%F#GCYc}AjW%MS5{h;7foO4Jo-_dH$OUwFUs1dL0 zKyI5RBxGb6@9iQRSln;}eK`7|#V1qSYx(g-m_-hb!$w^Z;8tfw{)VH6UcB*o(a2=g z$B*;og!voW{%65l9}TLr^Ky0bn<=v#&FuL1P(r_s$JLeoL|OXyCn^4s*SU9tzMy~_ z*94IHg|XX6$(*7KcTK!P53gR}(VwvEO@`cAcI2|t!ug#d+QR;1hj+HQDmPxNP4CF6r=&kk{u!XMg+T zZB!vQuht9-JGP9Ndhqy!oLlQXAq{Ij)pv$ zj+Z4EWS*%h)<)%WVcqFfAok{W7y0YqqmJ@ILvM{R^BtgBQR6w{8~M@eZ5M3QkLD+= z<{*UJya8~hxb635lI>CWOa_MO_CTY;+KLD)sGZ&EVU!}LZ?!JK*bPw`DqER-s?MB> zwho>?ni&wS^h~w+8PC>_sJvPEXHNp|o`ZIuo4YYi(b9=A6+sO4K~(l-F{uTLX-5X$ zhD69<3aUadH60&m2%&Qh<2Wg`^p|6k^vj1XqWS`vacFYab3)#%BZT|GNi1&AiH9@+C~NOF~0HX?=8>^ zz}Lt>qZXWkXH{NvxnkG0b^xx7ErLtm{#496nU%C7i$tF^o(@;##Mg?psW@1@<^QGAuBj&5SpP<2M zCq&%>rzWNvbJV2@zC?UC_~^mucCK^-AT&80bypt-WLZ3;bFJHMUeP13fArpa&5ZHx z;fAZw(BMF&Zeh>KJ~#5y*jv#&>>>JYWI-00R$^6uT-@0?*cnuG zF4}4AP+G4O_IuW$y7|5wPdN#@MC$Q%Z%yo95+H^`Ow2|W-mZq!COHZXX{4(@Z^X8OQM_ zjK!(bKY#%OIHOKcJ;`lzRbyX%xrXLQNd4WxjC%aekp9}KKLNItNaxVzWy-&hF|$$_ zCjt7%XZNt>js``PH0MyRLFzm0kzZMb~SvzAGn!Jt=ot z3f~`IYTmroY9iBpW+r=-sVA}&RW)k^_&a* zca!%o#*U019SeGQ;#Y6d=DwaMuec<|uOxD$cdgmzV^Z1J69ExWWAaP!r_p69< z5-FG5B57L5$DZ0Yn+n?(DT)-jgWJs>BpJ978OU#0 zT)&YbFE6j0fCG-H`u^}IcH{{C2klyYS5k5#c#@AaYcar$*wO$(Gq)4c6uq9k-3y#z zPt|ZIDAcIa!H7&qADo^25Hf8RatFks5h^MwvN^d0J`i1BV1XmTuAe$sxOPv)!SZ!C zIqz={asduJh@e*?RJz~*Q`5MG{hNq=_GIveTm5|>2gmW~zq2g?8_oXBw2WQee|PTp z|MimD>V~4vW%Iy^Ug1`|?kV;I*Wc}O-_yeYIzVRn{5<&uDD2iHtA8Xs{lC28|67># z&(;lG{wNm8FnL%J#~***CFcscwm{az&{tlfq3UBsgc9bxP-kJwc>}+?&dM|K$&ytp zyo5=-!U?DR-=Xi%mS&)|`|EmQ)QVxEmZc_^pMo76{P)(f>GdjZ9O_sJRPT(N`Mcio7U8TNS>eSP|?lpC{$pJ9@x zHw@$Rh@Vpcr%-a5Z;o~zeg<54MPq<=~~WsfJp)WqceEan3VYmdxZ&d; z`iG(B^#Z^)yO_xJsDs=uy5#@fyT;j>clmiA$WxVUY9D2p7wsQ2b{*dzDUJ3V)P1^v4Dd>1xD*=Je5S084~F(ztM{r-EMuSO}N&#`5H| zy5o-Wi=B#-J3$;MycwEZkgS`oPnkY9R=W7co6p%*t92#*&C&@2cTC=*qsD2OtX>2F z3>7>dJnR&6lQq7f@?5*|h|%kW!YO}~Y>YbJVhyWV$Lsz37ssQl+O&|~I*IYd8u7Na z9wFWyPYY>UCY3^yiwCDi@1VqXM+?V0T$MLvp>ULr%wpr6@J;vmvDMV!o!0$q9tm)- zK>+71G#c$!%Z^F{u$}9aahnhrR9C#ZOR`dq6-djy|1)c5V_BKp{*z?I*6)!l>$WhQ z7Y^W7ow9uo7kH9VuFb7}2rM!~W3yA-t_D{i6yMd2jotxI=6u*ruopu4IJN$HiKr*{ zq9i*|L;5%U8WsGWbjZ;)Xj#1(zcsHDShbVmJ|3XCupU!x&AP|txxh*dp=nZPfu=tY zh(38BAo?}t*m)UOx1z-U`40vT+N)t!34c?jmLm;y1Bkdr^%kr1v5yCh=VApUVNSX) z&gX4DDlPzm&!VyT{#FHSP-0>NrS}!b*V|J@jh$PxDRMmUB#y{ zdSkHOM@nf_yPAnwCn=X12ffO-rdV<(EXM5vX6a3}&v2%A5jXuV;LC-am|OANx*#L) zKr=%zQ(al}Dy^gNs>gX} zwh*cBb^}m!^@;=>py6k*wK)K@nFEH=k}Uxk=WNFVdmcp2D9r#9`3~&Ynj-}Z+Y z8$QmoE6tpjNsr)JG(VnZAaeHQtAR^4s1F}9vP#ufKU4I~M*z)1U~c4Om*X(EhX(--vlz`*nD}O_mSOh>T72z9ajK{5rWqjX6B|=qG}y58kjJ*<_95 zgy(=M%)jt$>v9VZeEGt)jLci8w-?7n6dU4lDKB#UM&jRJ89cASuXj?NbU$}>JI9s8 zllAzv;aHd()lW|z^T(q*CgPUo^_DCzhCvTvmOcW+B%wVML6<_z;?eYtBVCq+s|0RFJiuv z>ia$X>JE|TX&3%N{qsueODVU?(5fr0`f`n>4*EWlp!6F!4HwV^kbnB6oa{w4D3o}V z5;--KuZKR6b?5oc8@{IWA#jToG7&O3cGOKWpBIVdh0xcyC|G4@)(`pa`Fcv%Je3g< zZS@K(puCMD!nZw`RAck*Z11d&8FQ2f5Aqv-&0`4y+IT1CSs!g9AT- zEM6Slq8K3RGzg>Q@>+P0Yy_E(f*0OH!d-K}OMyW%Ra?DBk;O(sSm^i)j=fOV+sG$H z!!_K$sU5CV#aseO&^6*mGLB9W5(?h?h+EPUz}^KpFP~Ky&3XWl-#XtCQBp3rnOY5x zV>8VB>HL873}*}c>?vjy+h0kWqDl+WV7$z$;PxK{wy!`m}j32Gzm zfO+NAXg8JhpsjMb8GebyndZdRJo?p$w$(W7`_5IK#WHI4tEc>7q_BT~vA>lGTTXDM z%t4H~3gDTx0P8wC(l(iMP}A4 zyC9T}*1CTfqP$@ik=H_e3U=XR5kxP0zwv?sS`&$b5M z;o)GRTE5@G_gjW{-9%sgf{qyswY5pNZ{0HRkhuE-cgAq94KTz8z1=5Hrr$)Xm?|Iv z|Hn{xya0jw`cyciudTyS-(U^6P|p>Uhc)VJ(a}oLcOX$Fw155wypRwVyZR^pd}IHW z<-aE4fq!uIHA2xTsfsHgD#v|j&0n30#z)?bQC8us5+TrOOX7Y+O*CUNx) z(*OG~J}?HTEzF;*B7+dD#k!rk8sWs(Q(p%gw3ujVmsp$n7oCZ$c)-o}KPQr0B8cUU zS>~OS9Q+@uB;Wo;CBb|C`s;9(8LOayh83ibpnm=IC#$0Ra8zc+i@NuiABC~9a(ZzQ zCR(XRH~uPw9Ag{*QvfBBzy{H}Tw6msqvhFD8WJtnb5S$tABuVUfJn7m{-R4lNtFq%CQg7W&ctcOcnT1W5fI}ttl>U(rN!_|a3;)MW- z_q3I>{Bg$N8D08+ELj-aI5$2j1js(NJ9gnp{UhX99(AQp*^~-^QvR&40QHsrKvJwsAQkJsmXBg%w4d{BE+PkTOU`+*sBBY$fm}>#o@Q={;l@# zg0(VYHNG|!f&ujUJ4g@|5H3J@k@MMQ0IarP7-SCb+JYcs z>X-Js_4-2C$K4Rqu;n03USn}m3hxAvtH7)tQCMakOmff@D)a3n?OwQ-;;r60gY$N~ zSF9eS{bcbC=IXta)_YA(%$c<_CFIUXQWHW{yVpmqwRh=%?-OtiS=kOd~ z0@(ywDd&|yY4xil$_3`kT4_GS)zBqfb+08NRZhd=L_#T3t}mqeXNbxo-jO<}<@tT} zw46k8!a0Au##NiM+jE82e9?1MIH=uZhAY&*x{p&!m z3K27G%@YQrgSz`uWohE$opVpXXF|GIDOUqfS1og0ufe6u>EIl4nt26@rcoaU{(;;Z zbTWvouyuPWS$y*%m{BK>=qUlI>T1pGrQ8q}`{|o(dUtxJm{PLX77p zkn5l4SDbcRe1G>3V!Q+{UJ`lQ;&Oz+6-5BQ9&XGi;D}=!Py+xBhqN6QdO-yUG(MNx z8g1u*_)L65RrYOoL`MW<`?4Y}(|FZwp$x^xSIOBN2Nd!7s*NQ6HKbNfu+T#s_CVFr zdkZQV7}_IsSw3Yk-pYzdUr`%;!|Yr#z@7LPSvI@j^Y4Laz^k-M$5cyu{r;OW@G<$W zQ#qYaQ>NmtU4@Azrsu71a^P1Gi)+cNSVe})hLXITZ_g9B3>96{@U8O03bOO>Czc7& z*B`zFPMR-WzIeG;{Hd}t^QL{LrG1&0OSvtKYM#0;8sAj}mE%ypf-BNTa(D0H#$Wo% zL!8pp6E5=a%0sWYv{cI3QoMV+Nbk)J!`&jXnKZ= zOw-Gu@srrw51X@+sqZNyQQmoN{xgOn?hOTS)$`?!C+^zX)EvEYgq_W>^eGlg&KHFE>=<-NT-u?#y zm-})2Mp1XVGrp{UL-6{$vzU?UhGx3|EP1mmzD=@IH`SQ*=R}SBu7UbP*V+N%v2uG3clqN zLmlpA?VSN_cGt97?@x<<^NtHq8(K+VNL(D=I4=^%;xPhzE_gf(b}N*lNnq9MTc=G~V;*Q^j`)Rp{-!`YE)C#bv*0mIV}Qb^ zf%}N30-mvXpFPj{FWs&ankG~>#5=D5MXshQRfF>mLZ*Ti= zVW~R1(*J1-(GJ)RvpTL@3$i7{*6;NH%(KCp{gEV}kF#s_kNoj%*wH{N?tv;1SZ@HoFcPp6-wYTl&!7ZQKA|9GQaTx^vE@5$=p=)de8fS>)}6aWab z=@h*UE&zllVew~*E>%}%_i>6Adnx-NVj1I+XbrYP*Nea6Ax+Kah}mn@Tv-G&`q378X~Kmpx~5E*h_d|r(vr2ZQ&Ns zAXjX4dwA7iw%_R|c`VPAZda>~krg^|&R1U)7MF+Bl!ybL#1xXn!R2Mmy^Jn{{b%dD z%~f8lc9DC(eT3hJAjx))L;(0nqaIph$1wCOjEknc+SG4w*q6AMLB_m6`Bm)phOjvq z@KdO|`lXx`0K8SygT}*73M3K_V3;gdNmIR0F5mE)AWaDUJIyj|EVy2Ox9wScdV9&b z3iHk#o?@aR4QS*eerr5D5yY$h4+4*XdrgP>CzjS)xCA(jhPAI)W%Pn{BN?|`$>VC= z%zu|7I5k*|I`?g?Iw$b|cz}?0T$@YbDI}54{&2J3+i=0<1XaYqtPRUA$yi=`#%9Av zZa|KLH@`rfzJCbHjtIznddY6GnVDozV7mt0ZvmX%!ePVf>C&7ZS>BJVm1r`7Clph# zvbr|1y9dRGAJVv=|59=N=2D&4GRYZ?TkU}163%=ER(*fPeFsdmX)R_Uy^5n8?WH`` zAZ&^E_$j3n%t^CV33Cf%ofAn$Qst6zsx_7^Jt&ym{Eb4%8R<2i0(U`}?RmV}#{l0+ zTI|m12Bt=l!k>0pNe(U|48MPnMIr&@hb#B6rm;LP$!-+MmPPKq9A40s8~vTgE-!25 zfU2RIr7YO?x0jkw(Su&>U03O+QLqScFTKSiPXUZvy#zslGC!0t!SNO@XrS(LGxX(O z@V>SyY_4?}PIm#1seBu3@yWn+?_Hev>Xz_&Fo-^5$MZ+O(OSctrVR zzQRPHRNV^`u2o&jC~#2$?GMxGjltCpgs^~TnS%SS8U>c(iB!q_h9+jc%i)jtdp6$q zw68`C5ducaESX|}{2X035EK|>ar(iCRw<7B#Vx)UmcC?69Ouf~1x^rd|32Gmp@SQ)l5!ZS!LXGf!N*D7;f*p z<+5l(YVW{=MZhX&Tz!`{fi2D~j<<@X;O^FN%qerqPBDWG3|eA(^m|xM@i0JhqGf7r zU}cVIk+{YNB63%aFRHa}UIZfX`0FEb{tBWn-^RpMS}-9;sBCq9c001B^fnUpW7mShWu%2|9U+2O2_fuD@7UGiiR7;jtpjSW?{Fsx@| z?dpV7=~L>L!CrX#qnZ2ouxl;7dunBFx`K0@E?vsnDL(-#rVvio9oRSldio2?V`s5yy{ig<(SrmT$9QQ;yYM*nF@W=pDu2G@FBZd&_lBxC$t_c z!c1CI=H*X*=ia{09gos+zkM^~F-c5aMXDF`DovND8_`sxdyYithVQs1iC3h|k6q|Z zY@*BU9sbgJVMf~wk#?(LeIEOcI|(RwXjI9Ax!r;5lcl%C-`9noBW?xmH5|6biBGs~ zf&N~2Z>jC^eW5nj2L4N@PuUB$-I4zgO`(a8&@YfRHyFvCQe2CN2s}@<`ik-9;cT;o~=>PWLnCbQLj*ll;>AY zBKneJ*JaHkpBEjQ5XeZw^^ws%7VFzqzkJ*4Kd(+G>>uwo<#{dF#&=8ErT4gzT3`e; zLm|v1K2Xpv^{Vk6Wv`=G*;FnkrFDT~03lv^-(gKsDl9q1%@W10F@KNz!;8V?E{K<3 z45GPt-68EkKK+kQR%~tM5IYyEb(p!C=NLjE4aJ18J7)*&L@vK{T5MU`>>E;iKkP=? z(<*RehI$2!po~y)@R1M=Q!Z+Cp>96uL4SdOvOz@`m#tft$DP!D?Bv>1pxQk~k=D~SVs-Gq=c3T-3PPFo< zR_nCD#J3te>%l9f~MjBObOD^l!pbQ3I;<$OLpGV9;>e{U?(j87G)SXLj zpKfx78BjTHHYJ*ZgHtSz%WyAn_wD{{ zOj)D58o?1H(idG?9lyrjR*yIgt?&x`XIep=|R8mbD$fP>;^WaY7-CC6l6%{H{k4oGv{piz?>23A*eRp3JtVXQjuJ`W<*ukv5oz+8nPTn|M23X5&v=G@ zglNNflDDR+DR?P8vW7h|cvkqjg-Q+WvVi;#Y8`J~RAu=~I(1VV1QIpnoqF!~ULZ2k z;mJOeP>*5cp0bHt3>z4pE_c;ycD25E zKS(Lpy>v`tc+cokK6z2QxKdgcRR0d78mTra54}ejvY?=McQPKGiQ#k!zCoS+>Xc;^5OsUUfl#K=&uwJS zMOfL_4jOiP=e1CJa0WyEQ01;*{nSfhL2)PAW2dncg{4x;+h*KAXit5zsK{hxlQMl? zblsDTU_)rn>~Yu`w^G>WE|2NkJy~-1C7vq9b6mhlY<=mrHTWXYF4#^) zbPjM2lHXafx)=yJ0`Y$MoYA!f&k%vSZ1lbGkHFQuYHR8?Rn6{D#LUcj-EwhXr%bOD zz2&CnL=}C+=anTh8P>0ejpDfzp2y}%^|@5jq?~$n`Dh`o{zJj`!A?=v@Wt>tB`Si8 z4w`oq^oYa)VTM7wE`9C`*d5-GAuiL0n@-g=Zc4s`QJAZ>V-hknK1k^D)JrISc>4ZZ z193dOojGc=YlD=`yAIs0c13k)SNp0XxMGs%`D^y5{QVDmk5v%C-V}vv*QX5KPlC%~ zDyF`X#;<`=}@cbJyMcuNCSeu0N+3 z;rb$~ghX3vWwf%laMqAClPgcW-Mg#U`@Fmy4Lx;#Bkq_;J)-DX)Q*+dpphS(!NeWE zb+z9g%3E#PS5kDW_U-!>!MJ?sDM-oAy)T?ZM8+q!y);yI*)W1^4|%O^w%zm<`tZ*y z;(fc^FLfejOB-**7_*MlP|RqJ+Bt|=_D$WBJVj^QEGF24VI3qSl>2m#i6^;TgpwW;JQp4d7QW$= z_($32NnZ1L%Kt50h^E0n@kdn2Vm?-jP07Nvly{OoGAqY~5)QnV@i7kLDKLj9ruWew z-dpVddJ{I=6K>W)w19A9q?@jra7=$379~!L7vRhP29_+WdXF^16p>pr8iTz@@2{tn zLnyp}Phc~&V}Abb-x6v@pm2CG~{K%@6u#CfaH&$q&pw=j}T+`qOyPokI$-wd1@ zF#n!#4$}2Bga{!orQz2$Px-?*q_o?gXEg1bDbhiCqf6=EPollzs1mJ2p9m$W4$cf? z%}~z2E`(sWjd2TaMYyMXdhqy%g==E z2xs6ATvhP&RU7MeG1~gk&kMy0_&7&<4|T8RtsiP?3Ey=P1pwz3e? zh>Ea&RR&C>=^tXYM?Xe;Cy%>uz;o_UMme)dX%94#@*OEWf%_Pyj18@O#%^C2pY+Rx z;^P@zJwj;O*eB%v%1N#|(p>lwL@w%n33eqy9g*7}EF_w~irIYzANy>VTnf(-nYq=4 z2-X*W=W)`BQ23=+m z_&r9LXKUY>AKy=l7XI5Bx#Wxk`M?SzMf$KM+ovAPUR+!+joFTwG4XpbgA24kM z&qv}4g+*e;h<$&SZ?f4}GykD&$JeZdC zkZFwS(p@gqqhr^R&N|^QQ~ZSAWQT6)kD>E0dZj zFW$O)x#`#J^3)@K-iVL33*a%ThaP#7k(RFw?%#HETJ)=>8kE%naOkP3s_V+g9vX|xev*?b|V*Z{8lVcWW$=Pny~t} z>C9N&Z3XzbP6WKTK{M&0L0lEmgoQ7g`Y?NDbiTLm-ItEhWdWyADgK_3vNx0r**G}Z%fs+?p*xMGa3$}>LnWls*f!xCq9RWVt|rm2@+GULEZ{X0rMmP?A+CCkvhyxq zhn2lmF4n#JiLy$JJ>(KHY74{=U?ZQoEJ4p~$zZDOicXxiQ&{z&<2Ni3G~H_Ac@6C7 z)K;WKMJGbS;d_4kO|9yqdnN}|iR3n~j8RMui(QX&< z0`OJSo6yIM2derM)m$?rtc-=l{A10C85Lo?pNhR=$t2wj+WnmXiEoK6ozSVcNZQr! z*yisFhpfM(UUMEaNl%st!qZ1IX;_#_c+c&v+acm|UwRu{bU>MFBOKkc48?u>495i> zQTP*9MHJQ76YIZPIeQNx0mVYn>oCAs*r2XWGIQ$}&YNBh3XY`2MgbMzNk=E6#&2p? z$#o9(IT7Diur%>G!G50IM%q zQwbNvL+zh*QBRK^dAY(~pJuYnuO?of!Ry)bwVMoS>t&oQoP0n6sx#k=r)6bzaWnL8 z_lb_eL$|1hP6eb${MKh?!G?GZXc4<<1NZa7RhaM^nRSZEf;BB2#fgo zf^vN7cD<#(+RUm3Dum@-;`%cud$=^@Ak7Q-W7vmX?D#~VFaub+K`ixN%=to;m$!0yQ&Dl8tAC0+V)3>)f}uEV zaos!CWa$@y(^u*90@>VI;*YOS?vWrc{jd^nEnyF=oQ$81Or&j7&G1d-{uong-oE8T z`c<`+P+X>kZy$Uv27QELHoeF6Ge<$M8YesrZzKm5?j~*DMf7`T&p7M7$y-J{t$yuN zs@)fu@tv$2`!G{FT{7obMqh{)O}1lWse1P+l$;{BxES(+#4K<6=v9X1otCSB$ZuH9 zyYGx1*5=mulv~W?tG(l09?ZYAA8})|>CtbU9bZ2$!?Hm|T~D2KqlixU=|=KkUZyl1 zwK)hF_KQqro&M}ljZbxqI`iHyYHl4;-C7pTL%wFQ$xDvoU8g~d=JAp78`T4PZHVZ1 z6nyRX(f^HeC-`m`?aA-8>I&&U~YpvV+3NEAHr7w@=n~&Dae2;#oYmPM` z&?|R^+9PSlz;yb%zYiYc z=j9UT{{fTsF%LXU&vq?s;%f&)BmiY`*w9;0cy#0#MRSxY?D~@1bWISCurGL|-n#j) z`Ec{}Vy@~u9o-N7oadf(nH7XHHAApb_0E0&D^QR6%%px-N526oZ1&ezKZ2Fqfjr=d z*ZofENWIRwvJ{#5Oa9#(U!#hgV`HLf8rZy)(~t&lsVxgKtpW};*gT(<|K*+}L0^k> z6yftvntCk1k%}+M^HkTQ({2A)sz~AA6`&U0W5f#=74ZZ-v|}vrivN0$Iv(*Y6Ei&0 zA|T`<%Zw+TZwp z>xcALG?sob-UW{&^71=Do}x}yRTTcnGXdD|;%)?ZM;+*~vVAy_-leV8PuO8xk0y8p zVBb^#E$7|e{AQ}}<kHrd$U^&(Y&IP%57Af#UWZ>Ax-R~ydeOW|YuFL?5(+i7?X*gtW~80H?5JrT`sh8} zVc4ux;oFq^0%OE7)!RDMxf)3JMIu7h?yb^8dJ*jjmoW*i zd!^2s)sVfRu6GWztD(KHq=zi6mhtT_9KG{8*_xmkyITKtd6KleQ$uZ0 z?nQ`Tc5YoM$6`E>$sOyG#KvIVZ8a_jUbDzs!?pVI4NBhG42U1#>^BSF0kuAC{V3=v zZ`@W|OZ&%_c(&F@1*-?sS&)Nb+oE;*TlvMtRb~h$e1dz6jIpjYp?#O-&G=WifUVhv ze`_7FwC?7nc1091qTZPTak$;t!(NylLd6D+)i4&!TGwk=R_-kyX+TjOzkI~(Kf?cB zD9M$e4U69x%J8cPt}Cs`{d!N3$eFxz5)DUM%FdHF+HuADKB)7v@jCn(A13c0-8%K= zDCy?~fqI3EF-plwM9BpT)|KP-5Z&-2-5UJP)#xR$Yc&oJcx$RD%Gao;$}Az=&&5BA z@mG60N~y83b&xa|s8UMdguLi!6>4u*_2=2>R!YbhMv2JVldhtZ{YK5gL_;Q>&#fJ#oB8$YH=nbaxfXhF z^z_Vm4pY7f*BLD@X$H5QpiXjkb@f8l=!jxew_1a5Plxgf|12fnmzi8A=nTRwI)`_J zO*m^^f0wfBxwTDIACJ^8)?Rg^eMw}kJK;N%Rp;MR&YN?E*4sAL%tasSPMUdth(*6_ zhW5a2C?{+^)2!69o{0Xm`8GhT6Ob#rBNu$98x2Mm)4oJ+ZXRs3U@z={sP&~!s*Go> zI(H5c!1iti72-p7l3DU>bNZ^HIAvb4TsZZG&jf;{%aGEf5M9=6A~>d8e6{A!4yB6& ztMzbu=G`G=uWnuRShdIoY2~^mEb4zCPo{|x%JARM1$$TKGb#$K*KD1`mitV<8?xV_ zM2akMM03oHOyK!685n#BrP?2D zZ!@m@-Zwx#x3qt%<9g9iGW>HN$q~)5iger6AfxXUVWu^458R8&(q4w*-%a4wH&WXb zKv$EPqOit>qO_dn&DeC}i+eK);EoonDcy%l$eR*6l?e6vkX8_)Y8s8bRfQ5xWe(lKt`KbRa3U$>HtlikO zBUrxEepJFu{*m}tN~#6$%3-Ef<4bj1#B(0<+}^{Lz#?qS}FL_ zURoGoRVv@YrzI9oC}r2-kHqcv^u@DANRzG?7xmoFe>mCsGVznyMR_bE!;c*6Lh9Hw zu2;Qy+ipfVm3(iks*efaerW+QVz&`A&(K8AcL~VnGWP9yXZJN{v&gT8C!0S8i>8Qt z{^Rni@0TpYvieB2X&QK4wNC@=a(Z`OhZ~nsaL+;o`d67y80_#>C6G)NrFLV4860Wng^PCtAbMXDLnlmE2 zW*Y5w=8}B9vXFS<_>)`o;3N}OmGWH}zS~YQeK`LS>`dE>-s#Ogc}z(0VT_-5l5ILZ zXrz@k@srynVHT#G7hjvd*q4$q;NVYcWaE3u;>*1TAEw0U9uBLHjPEhE#U81F=o5z_ z0!1c%{gK?w6UuYm_BK%`x(hCvP%2Lgw(DjZ<;9sKOYIcFL$;gaie6J>RtvUr-C%c*>E z2eEFev~VN)oSfs!Y(In5kOzG-InoCoIlfeW&-k#UIC1-1#yLSPWp+~*O^3Xthn@0{ z^6Skq=iM>h5)6LAxL%u$`p2(z#G59E0W8n_Dp=+9b&Qbi7PRd()~Za&~(B}M3N zP1rdtVmlwv9`O511 z-YA*W=)p>nw3C%)54S&UoA8;~DwK?>^+j@ViJVLE6SCg9wDx!tJ+Llr_44o?bl>9@kxh47|#Cch2zWmt%lnX1t(YQk@O5x#6Pai zpq^+Zz31J5bJL~qc6=pT>%BoOm2^2`Ia~huY+Iq8Ql4ch7jAKOJj%VaP7e(EVSZvD zlUgWc4An$pmJhltKqgIU^7PRH-`X}^@AeZd9D!o?vO32ZfD!7PMaf0?JIqKUhk|8J zvpIR@8w}i%4i15pmvFZ>2S9zc)GziuDgV ziKu?p&LPVP__|NZIWi(~!6g8@-maCB-t2z*NEX~2dV-XYRw5n0k3U?!7|P`}c8gMd ztqn^}(iS1dRAqWw&5ER|__;o+)Z-}H!K?VeoJHa{J*_-`%c~DJZ%FaR-I+d$)WM_| zwo3Qthczgmz96%9-Gqk+ zzF(32(H~OGc%sGlzj-^Z64!}I$7^tbps@5h+?4teh9+c^b`QOME!Z(Q>|VpZ`o3B8 z)#*M}3x)6HPwO#c*$=r}II;bjztE4gWRie@vf`fwY z2`+0UCNiNV?v+7@in~097~xAg2J8>1oF{8LN8q}h5J#+h&?MZZ{Tswx=S8dbd$G&> zmUC&4l2#p5A!d@Ng2WFigzrtMAaCk}?>R{%V#>%Eds#G|ZvEB%y(EWC{LjHNOt2&A z`=Jb?lkaai-u26%S`-y94tbfVVj3r}HFEp6?%j>rS5swf*0oq;e{tGRVdRfU<}yx3 z8VN6KioZaitwWwEHSE7riA|bKobtAwUH2V(dVCfHueO5&kK4t#wJ7yJd<%B-Sd}>$ zDPfQZp{CA@_}s4^FI=5Fg~t~Z-;o1-e=53oBGr_Q*dZ;r7cc7?r}1$CxCG1JgvTEt zd~<}1(yF7MrzGUFEwBl1ZocR&t4@^~N$`GG*VUEwz1xQ67uuRquxICyo1r@N=U#=+ zRKRCp51ZTR2S)&oH~Ml~MhQS>0PHRfs-)UIcQcDNww^}eS)0^x@ux(B#q4mRf&UwN(!L#!eP=H3)rCA+37ULHTf}+P`_r+UsyG*(pgz*znlLPj1mi zWu#5s;ixG0aHJQ>n#{!t@TnzuYh$h*+Yfo6_riu++~V1(4)WXuY`U!X@O1)r-3SVU z5a0DVEJly4+z&eX-oi$+D@xuoo8Qc^#(F8%`DlDUh(0pbylAK=wA)v7BfBYBznP-M z?nW#%S+7u$9gH`7qd1kWSNGY#kgCS0E{nVn`ufPBq9KJ37&63vy|6I0tWF|s1_mp6 zyXGvUoDsUN)>=0lcrn$ZlNZIi|JS1&2vm(BV1UC@6==qEy?boGRTdwtsj5bT{23xy{FQ3C^LfQ4@d-Y3@v97e}q+>{=U}07`Mmrn`&PK}RZb zM}x+q7E@$@eg-LRo(&b=(7((I40tGy8y@`U74YQ$G^*%7rcB!R;OF0s)MJ|J$No7z z@p7Xd)~l?j{4HG{&4kj~nK--i9I}6|2;To1lIuUd^8Y@cL8gf}5MtHA2@Z1-A2Bm1 z*+KSh_)Y3^^77JZ_Z`VG&R^3K3*@9bFcUELyn2v zv(s;w?hl(F6>Pp|CU`MqOdM8zLO$$vc~Zdbbex_01bWSl&82e-`oi+am9{l#@*u!( zw8Z)3RVo=Ob?@dVLW=+ss>e|4%Tu2su4Jrvq zm^&BJ{!k}`WP9#p<;kMK=rJBXHfFlntL{fnq!q{PZ)t23GCy@Ct4^5-+ymv$Si5@c z`Cw^TSAu*y8;LxBqXteGc0OQ@jj~g}3{rdh?!uN+q<1#^(CTFSR5ZM3j)W&s^jsEY zmujZNWBMPD&kJ44CAtWby=H4AdBP+&xW{KxOnTlaJ%KJ>6z zwZ$gabO+2zN~xt`iN|N6O&zLD>m^D-&x+&=*Hf{@e34yNFfz)bTAkk;(d;xb9Aw5S zJn?>F=I9&gVW6zCIYtlxv%Di>xqdZ%^-wlTu zBl_OF6a4lt_DygAUEevarsx=BephiG*00n{G|knb z{o?u~yhF-145o1Q<{X5KrMO2A8X14&xL(z9xxmoG}VN;HamQ_{=Bzt zIksyl$<_Vz+iWd;3g?6R63t<0d&XzOo`uu3WqH(Pjr*b0&ezv8vKgM8nQcfz6DU^A^lb56!Xq zertNqyMo3W^C!Z;Yv}l+oIBi4(LmzLI69<7r_r^Z|YT(`Qr^9O<7j<45oJy--jxykOzHswC`S5((<1 z;Zbv?4SVh^{Zyk6zER+Se_7L4-19=$=MMGbNy+aO@|NX~?G2~q6qRb4bYs*9CPRdO z>HkInj{nZ*oU(IW>#mzoK!;SlM4*tPx1Gk)UP-r8A3`5h5n z%2o2-zn#JI3}Vx0vHRt9d(&F)p=JAAZ1WW(Jc9%Q%d7uH>i;?8u4h_$+^&17N+x)XDjzNuRuN8vs)F-CT2!>wCM)C!nm#Dw}RNWn7UFgQ4{jtJi7B*7JWkP$FKsWWL#}S&qULlUrCx?0QWCcp~oXr7b!} z3uU?)s^nl>Oq@XI=hoAkHg^a~<}TDN6c?J5R!_Bjn)V|frMy}xy~9{)-0RUr)9JZ0 zasYQh66=~2W@o`igmV+Q^{3n(XD7ti4eKIERfg|ZYKGAF9{hmxW3k_oyT9;O!@sSD z>IEjP6Bpf{P>&;2m^cZm24_K!QsQ3Qm8O$Vt0(92S~Z*Iu9VwuHb0s*4<@+WKuh4T zLJ}33)qSCTL6MBA_UN<4KMLjA|0IjR<|fR7%Fa`riM2}R>hU@)j{k-@62xDv7}Zz? zqf2I7_Uog4%IcXRjjUwJmGi_N-AZ<}=!ls&B*~SJK7!QNd*FJy(8~2Rx~k-&!^ENy zTGSI!Thwdst&bjRYHXq84U&p9gz?l{?flQt*JkQ*Q7_lL6M0>H{$u4w3vBF#H-!!fb5l+c=q=7 zu8uGyPc<7l&5lcFk?BN#`p##|4>w2hP?%})7lR;rdBIv+WTH?{LN^&37xW((aE=ac zU>JVxZH}nJQSzwal-Jj`$3s*?$JOjeMrSgQ!ik*Tt|B}9oJRdVXkh}ZV0+M?(2Ab8TvmwTx$~*tnBkyp^R_4Z}Z`s{5?&clrt2+|=NOAD2k9f`lccxCh zy6UI?R=>q0HbTqpljlH#TssXFPzFP69MgC?zUJq$*mN}iKK2pSalE&@C+lK%?d6uj z=D_KAh}Jt9s?ACo0&9%2zC@|dw;dM0wBpp;yX-Sfd8q?~+5N6odl^5WuYqn*axxTO zhdt_cwYUQ-fUy{e(9=JvT}Y4g<)&81vaNXEB*33p{B^W{lYl0;&W6_4M-HH6b0X+HM06C`7>4q7j0OS6_&023{mB1gC?)^MSBRGb4^(iuE}b8K6a3vm`Wme)59?L-*~ARhm<_d9ZNn3mIX&Nb z#@J`bGlhQHQ2I^dYP-UX@p&3obj5m7hoV>-6Yv`#o!1uJrWb zqNGSLkIF7Phh8#g?qW&HX#E6ttF4K9c~|pzbpp4C`Rx!e)p0;mF;8ijFT2Z&N`A_g zQTCqifc~oR{Ub(2X`Qt;dMGiy$P=J0Prs7&JiYSko$M^bfww#nhpfL)Uj)U=*C+d*{?j4EvZ9${+%UL?!K$ z(h^B-sl@zJNX*=^7+%<$ZHG*4z^%0VXlj%uZ?1=*5fUTz3nnwcz3CtjC{Xou?SPGnke_nLq;1BPf z=)5>Ll;+xSP~>vVf6tKXqB_@EDZIszF}x|QsvR~7yE#{;@z)#(JR>+v;_*MnF24Wz zx5m#)|9f2z|CI%yi)|UJqjuxbdcF5Q2T?}#|9h?pFm&_qZ!7ZTzpE+azjAs0b04%2 zY>eWIB87%s_rrZP{lyWv(=}pD{+S^{)p#`{53I=*w77&_!sl?4{m3=0c|$7Wv8VZu z4C~*(4EH%76{L|ZSxgI|INA;zd#xOd1ATMVjy@wVH>5x9)p0-l=-Qo^w}`(^g5aGr z98*6pVdmv}*l)Do7SSt+D0TROm0J-^_=&2gFpz%NY@a6dZrb17MgAvt0&(kWWaGXM z>0pi5-i$NtIK(0hrJNtR)BIOG;Q=OG5W@+lw)F88aOaLq=U_Gvwl2X`W#NcTZp&l1 zNF32NEf)1@2#Syzc|}&>_OI&a`-Io#S7>Z`M0Z(j3jcNaEV@(&RVw%UuIgy2D#JSGy9uo^b*H$Z>HWNdCz==N^S`F5Uuo3rV!3cZ z?7{A3uG;-eyig-I=nzO!tkLRf%o#ZxazG2QMZ|N8Wmev}`)FpF^WbP35Y)0S!Z{7= z_pX!^kJlXU7i?AL295M<_qz)C$&M_`{oWPCX>uDZXTtr}h1F%}!)XT&H24~w1AyDk z@h@)dZ)-KBCjSBe+}2)`ROls^u6<4c_mSj!p8O=R6ja^j`fT+1Nxv#lMRRK*MDvQX zIqr020MH6Y#km|A!E~u$#M{Y`q1U9P1=iWz52>PLE3{R`*6EzYodNZkyU50EWCY79 z?DZqCb&##(*R7nU(-Kz<$jpWN({8IZ1-4WEBDN@>L7v1F<=uRwH_;*8y*I<;*5778 zp-@u&U_Pw(C=v-XBlK;`E!d`|s^J-Z~SN3@MD|*FZjwP0?3%L654#$6`y085iBO`+VIL)=2bVY-UXNb$vmqa<6~3 zNThi6cRI$PPFvOQ^P?Z2T(v7fdgo{^9MEoY_9zuLWtxsn_OCOj{<79*t2?J3S^C7` zJ?k^IWM%HHnPXs_yfl;)Xzc<|Vs+tJgStk(4VYa0dIs`al86@(4EF}H-R zO`2&ZTkwm-OKe0T`in%Jlx!u|ta+tqb!WThE}T1&f>F$_Q@_uvMQ;Vtz5;k5Vn(A`An8BKiG4XjBlsEvI!j$ZE5 z_~F#VYWQ9nu7&``MOt6;kt$H*==+_Y1OHTiC$KB5-cri0(pel6ai9}+py1bt6nv{S zxR&ioFhZpJu^Z2ko4NQf0{?|?kHW*Ec$!_`6wqwDK1%t7W5?5Zin7}!Pl!kjN7(4< zhKg|Tiu(TSTO99mn`xt=H8Z_H>gXU{9m@T?IGk6Mgmr9<+h*G-nEHr2O7m`)$9XDn z`b^8vRto{m+#b!s6S7C>c3n03%K{q)DmOw!Qb8*-Qxi286cpSN&4ol36~*txmaj8s&iVfN_=9ucz~SC|?tOXh z@A*CVslVs=34b?jMxo?(1K|8T&W5<#`=@}mUP%YlTe>r^&%`5atFKkLU}dj2a~2q;0v zsY@(L#UE1Jpa0gS%^Uz%wiG|5opS73M_2TlXXU=^dW(8IY4~p2xzjbCv-7^h1YswA z-f3~AcC8tqTYS~?x%f`8r;0oreO7O*ExL-H#uK9_FRzYg2b@wa=%*Q|8#k|VJ0sKP z{Q25X6Sl6LGCO)ogN})Di`os<*kHL$wkvu{_40f}i9U^fe=?W5?E!w7&0oWr7!xd9 zXKjHC-RRte%wJmcJ4%s0&jA*!%gZd;rnNz=Dy-t1*U8|9?|d7Xj=_xnwtlx5Dj6!+`_erKFk~m9XU7>EX%X9H?LB3hu>h7(A1BT-=NYr<++MkaP@vm5VO!LU=-S-E+-z_-ZzF;7<63ev+a7 zkn@rB3)KFqEUI{jnLvH#LRYtm0=XjXPhSy}+bqTr{qB681bsMaI{c|ZaAK$tBKbPc zk&zcncoz%q-wp7C#rv0BdTfGeBWlqPL+Z9(=o!SGd(o_*{6jY^0xDEchBit58h%_~7 z*PVwzbzfzF#KFZ5ujQ_sEGPHtvoyVEx=4+n&K_?fB9uvi8VxD!5DWfo}SnJLgw%^v|N>f88jVe4p{#07$TRms(l+V(cUladi_WQinun>U|<9ejMI^ zp3AP-JYlV_#BL5?`Ikk)vqMb;p_Ik_L{CMbqv z)hhX^6{d|ZUYQSh9E$#=V3PTC@l}9$0xm$$fZg>3G*5A zTA0mxKPCg(e5dqDNJoFEY3M@>#G40xY@EG%ndWU~LWrfu@dbo&##m-fn^yYzmSmd? z*=|mf8_=Dv*A^aG4N`Lf82sM9Il;B>fU9M=9<`w`ZxsAg!IUtG|6qBe|Dmubj^JY~ zOj1z?!kGa@jt68n5~H44%cT30J?+`c(mt03GwB~L zk>sG!u-pjOpCmyg**G$Mu#;7FVqOZRLePYpaoa;uDErMorraD|!-ll~pap%aESnu*(1t*bBR}e1Q!& z$_iOJA;S$adQx}OK^U`snPo|qepcB8I0CL*<*`rD_$Ft*fw=|T0kYvifJD2JU0pMr zF_^nVQYNY-LFS3vjYRtulc4-PZ&Q-e&({&}rl{qz>sX9?hR&D+@rAeC_ERCne_egl ziDmAmV6!Wxl+0n|Q`3Xnwq2Bem3gCoaX-lJgvarCL{ zJYTapNFAp8X)DB%wRTtZ8eGs!dWcZ{BqxrNh^wMA>KQn2O!WO5QD$7h%nm6xn#GBn zVaAgP`YFYs`1k*L~a=MfwnxBZu@l!`=6ZUr|~}kTVhnN)f_+z zqe`zODnMLLsGtLQw?i2tyYkr|E+01}bWp+F7-+CZy7z<%dTwim2&K@XRGSRx`kC3- zAh^l&2snp~9BcN7t4zI>57FEF#wB0XZ}CyJs0U0oc>dC;_8Galvh=}bB(&qx8e>2o zeyxHF?0OjYA_za=vQed`RbCzPT_^p!poL#dh)UX+lu5Kkwa| z9U5>3`ONfb@1cdq=GWS|vZRqXipB$y-sS~3vLhv%(;%|$0HlFlGAQIwQgfSggQ{mM zo^Y~yL@B@7xx|CUVRpR|?P#=|1d+HJNq;EO3 zrs`iUxW0#YN4w#~UOdQo&x*(+x%Qv!Xix0IQX+&seQc<}BCh#VGOrDHvOK*ns8 zTJ?ayw*0W{TmZW*IDF&azL-)4Gp+xc3(r~6@TPL|4u1o78T$;QBkV+b`&P!irh{>3 zD6!ngj&rapZ)O4)5It@&jXuY0%|^NyR3KPbM~nH}OHG^}eK|I?pq+?f6ztxsp!LYm z&eiI^XzI^B7b(W#mSws#0Z%FLph`dci!xYidC+Rv)sveyLGgzhC`dN7XB?B)sGA*< z15^)v8l(Z@h@L#ta3axi3frm zzh&IT&gCUS@4ULyQBK}~b=!O)4VX*^+@pmPaKW*qADNUOZ}r2UNs*?BEdwG)Kzuy` zP0kVRUJ2QM{R>EvlN0^EH?7@bX~sg_H-(jQu^nCzv+VDZW4h%lTx@mk@8q(|}ks$?$7nY=HygwiFRSuEECjrE4)Brnoygvv#*8~>; zg+^ZRzDs85b_XiLjNUJwWWceVk!w^}QY=uzir+PYCYl!_7RMa0{1DM&i$!Z9yk@FL zG|8f1B%-23p!~}rtc1l8h-;QY)AGNh)d0P#0hrwRng#wX`)u=~Gz0j@7_BhHKGECT zJ559mO9F@a4{2~TDemhy$SLw`RV|Jv1Zj_D2C>`Pa$$i2E$M((fM8c?`M2KWS?L!0 z3!pV~32Y#*9%PLAGpXR)#0cMo4-94(<00j7(VURZ?oW;YT(L) z^@*T%pn;j$z*y((ge8sw^+!#v^YdhFqG5zKvR<>RL#r5p-z*uUGT@DZTv*b_Zj!r6 z*uw1TaIS19bgW@X+dDZ+(jeJD@@V2L;0W!9@UXd~z%pnf)&M7SN`=IE7DrmnsQ1HD y|Ao%MoTV%Z($tCMz1rWzDhv1xpU}P5OIpBrE&O5Cl$)}SSs%4OQhew_#D4%cMyh52 literal 0 HcmV?d00001 diff --git a/docs/toc.yml b/docs/toc.yml index 5b81de742e..18b9e13248 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -33,9 +33,6 @@ items: - name: Persist data using volumes displayName: volumes,persist data href: fundamentals/persist-data-volumes.md - - name: Create custom resource types - displayName: custom resources,extensibility - href: extensibility/custom-resources.md - name: Dashboard items: @@ -64,14 +61,22 @@ items: - name: Telemetry href: fundamentals/telemetry.md + - name: Extensibility + items: + - name: Create custom resource types + displayName: custom resources,extensibility + href: extensibility/custom-resources.md + - name: Create custom components + displayName: custom components,extensibility + href: extensibility/custom-component.md + - name: Flow auth from resource to component + href: extensibility/flow-auth-from-resource-to-component.md + - name: Components items: - name: Overview displayName: aspire components,components,nuget packages,packages href: fundamentals/components-overview.md - - name: Create custom components - displayName: custom components,extensibility - href: extensibility/custom-component.md - name: Tutorials items: - name: Caching using Redis components From fe4f95d074a4f8ef8ba60bee1ce74b4af3124e9c Mon Sep 17 00:00:00 2001 From: David Pine Date: Wed, 17 Jul 2024 12:53:24 -0500 Subject: [PATCH 08/12] More feedback and updates --- .../flow-auth-from-resource-to-component.md | 2 +- .../{ => MailDevResource}/global.json | 0 .../MailKit.Client/MailKitClientSettings.cs | 43 ++++++++------- .../MailDevResourceAndComponent/global.json | 6 +++ .../MailKit.Client/MailKitClientSettings.cs | 53 +++++++++++-------- .../global.json | 6 +++ 6 files changed, 68 insertions(+), 42 deletions(-) rename docs/extensibility/snippets/{ => MailDevResource}/global.json (100%) create mode 100644 docs/extensibility/snippets/MailDevResourceAndComponent/global.json create mode 100644 docs/extensibility/snippets/MailDevResourceWithCredentials/global.json diff --git a/docs/extensibility/flow-auth-from-resource-to-component.md b/docs/extensibility/flow-auth-from-resource-to-component.md index f2f1c01dd5..c8662a081e 100644 --- a/docs/extensibility/flow-auth-from-resource-to-component.md +++ b/docs/extensibility/flow-auth-from-resource-to-component.md @@ -79,7 +79,7 @@ When the factory determines that credentials have been configured, it authentica ## Run the sample -Now that you've updated both the resource and corresponding component, as well as the app host, you're ready to run the sample app. To run the sample, from your IDE, select F5 or run `dotnet run` from the root directory of the solution to start the application—you should see the [.NET Aspire dashboard](../fundamentals/dashboard/overview.md). Navigate to the `maildev` container resource and view the details. You should see the username and password parameters in the resource details, under the **Environment Variables** section: +Now that you've updated both the resource and corresponding component projects, as well as the app host, you're ready to run the sample app. To run the sample from your IDE, select F5 or use `dotnet run` from the root directory of the solution to start the application—you should see the [.NET Aspire dashboard](../fundamentals/dashboard/overview.md). Navigate to the `maildev` container resource and view the details. You should see the username and password parameters in the resource details, under the **Environment Variables** section: :::image type="content" source="media/maildev-details.png" lightbox="media/maildev-details.png" alt-text=".NET Aspire Dashboard: MailDev container resource details."::: diff --git a/docs/extensibility/snippets/global.json b/docs/extensibility/snippets/MailDevResource/global.json similarity index 100% rename from docs/extensibility/snippets/global.json rename to docs/extensibility/snippets/MailDevResource/global.json diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientSettings.cs b/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientSettings.cs index 58003223ea..a68c5b0dff 100644 --- a/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientSettings.cs +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/MailKit.Client/MailKitClientSettings.cs @@ -53,27 +53,34 @@ configuration section. """); } - var builder = new DbConnectionStringBuilder + if (Uri.TryCreate(connectionString, UriKind.Absolute, out var uri)) { - ConnectionString = connectionString - }; - - if (builder.TryGetValue("Endpoint", out var endpoint) is false) - { - throw new InvalidOperationException($""" - The 'ConnectionStrings:' (or 'Endpoint' key in - '{DefaultConfigSectionName}') is missing. - """); + Endpoint = uri; } - - if (Uri.TryCreate(endpoint.ToString(), UriKind.Absolute, out var uri) is false) + else { - throw new InvalidOperationException($""" - The 'ConnectionStrings:' (or 'Endpoint' key in - '{DefaultConfigSectionName}') isn't a valid URI. - """); - } + var builder = new DbConnectionStringBuilder + { + ConnectionString = connectionString + }; + + if (builder.TryGetValue("Endpoint", out var endpoint) is false) + { + throw new InvalidOperationException($""" + The 'ConnectionStrings:' (or 'Endpoint' key in + '{DefaultConfigSectionName}') is missing. + """); + } - Endpoint = uri; + if (Uri.TryCreate(endpoint.ToString(), UriKind.Absolute, out var uri) is false) + { + throw new InvalidOperationException($""" + The 'ConnectionStrings:' (or 'Endpoint' key in + '{DefaultConfigSectionName}') isn't a valid URI. + """); + } + + Endpoint = uri; + } } } diff --git a/docs/extensibility/snippets/MailDevResourceAndComponent/global.json b/docs/extensibility/snippets/MailDevResourceAndComponent/global.json new file mode 100644 index 0000000000..a1aaefd486 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceAndComponent/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.303", + "rollForward": "latestPatch" + } +} diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientSettings.cs b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientSettings.cs index ee89028565..2e42100ad6 100644 --- a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientSettings.cs +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientSettings.cs @@ -63,34 +63,41 @@ configuration section. """); } - var builder = new DbConnectionStringBuilder + if (Uri.TryCreate(connectionString, UriKind.Absolute, out var uri)) { - ConnectionString = connectionString - }; - - if (builder.TryGetValue("Endpoint", out var endpoint) is false) - { - throw new InvalidOperationException($""" - The 'ConnectionStrings:' (or 'Endpoint' key in - '{DefaultConfigSectionName}') is missing. - """); + Endpoint = uri; } - - if (Uri.TryCreate(endpoint.ToString(), UriKind.Absolute, out var uri) is false) + else { - throw new InvalidOperationException($""" - The 'ConnectionStrings:' (or 'Endpoint' key in - '{DefaultConfigSectionName}') isn't a valid URI. - """); - } + var builder = new DbConnectionStringBuilder + { + ConnectionString = connectionString + }; + + if (builder.TryGetValue("Endpoint", out var endpoint) is false) + { + throw new InvalidOperationException($""" + The 'ConnectionStrings:' (or 'Endpoint' key in + '{DefaultConfigSectionName}') is missing. + """); + } - Endpoint = uri; + if (Uri.TryCreate(endpoint.ToString(), UriKind.Absolute, out var uri) is false) + { + throw new InvalidOperationException($""" + The 'ConnectionStrings:' (or 'Endpoint' key in + '{DefaultConfigSectionName}') isn't a valid URI. + """); + } - if (builder.TryGetValue("Username", out var username) && - builder.TryGetValue("Password", out var password)) - { - Credentials = new( - username.ToString(), password.ToString()); + Endpoint = uri; + + if (builder.TryGetValue("Username", out var username) && + builder.TryGetValue("Password", out var password)) + { + Credentials = new( + username.ToString(), password.ToString()); + } } } } diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/global.json b/docs/extensibility/snippets/MailDevResourceWithCredentials/global.json new file mode 100644 index 0000000000..a1aaefd486 --- /dev/null +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.303", + "rollForward": "latestPatch" + } +} From 33d2f8041f909c63ff080b51ad5980e311dd2f62 Mon Sep 17 00:00:00 2001 From: David Pine Date: Wed, 17 Jul 2024 12:58:21 -0500 Subject: [PATCH 09/12] Rename --- docs/extensibility/custom-component.md | 2 +- ...nt.md => implement-auth-from-resource-to-component.md} | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename docs/extensibility/{flow-auth-from-resource-to-component.md => implement-auth-from-resource-to-component.md} (95%) diff --git a/docs/extensibility/custom-component.md b/docs/extensibility/custom-component.md index dec0ab887a..d03c75ecf8 100644 --- a/docs/extensibility/custom-component.md +++ b/docs/extensibility/custom-component.md @@ -204,4 +204,4 @@ Go forth and build your own .NET Aspire components. If you believe that there's ## Next steps > [!div class="nextstepaction"] -> [Flow auth from custom resource to component](flow-auth-from-resource-to-component.md) +> [Implement auth from custom resource to component](implement-auth-from-resource-to-component.md) diff --git a/docs/extensibility/flow-auth-from-resource-to-component.md b/docs/extensibility/implement-auth-from-resource-to-component.md similarity index 95% rename from docs/extensibility/flow-auth-from-resource-to-component.md rename to docs/extensibility/implement-auth-from-resource-to-component.md index c8662a081e..0990e7e932 100644 --- a/docs/extensibility/flow-auth-from-resource-to-component.md +++ b/docs/extensibility/implement-auth-from-resource-to-component.md @@ -1,18 +1,18 @@ --- -title: Flow auth from custom resource to component -description: Learn how to flow authentication credentials from a custom resource to a custom component. +title: Implement auth from custom resource to component +description: Learn how to implement authentication credentials from a custom resource to a custom component. ms.date: 07/16/2024 ms.topic: how-to --- -# Flow auth from custom resource to component +# Implement auth from custom resource to component This article is a continuation of two previous articles: - [Create custom resource types for .NET Aspire](custom-resources.md) - [Create custom .NET Aspire component](custom-component.md) -One of the primary benefits to .NET Aspire is how it simplifies the configurability of resources and consuming clients (or components). This article demonstrates how to flow authentication credentials from a custom resource to a custom component. The custom resource is a MailDev container that allows for either incoming or outgoing credentials. The custom component is a MailKit client that sends emails. +One of the primary benefits to .NET Aspire is how it simplifies the configurability of resources and consuming clients (or components). This article demonstrates how to authentication credentials from from a custom resource to a custom component. The custom resource is a MailDev container that allows for either incoming or outgoing credentials. The custom component is a MailKit client that sends emails. ## Prerequisites From 74b6080511eddda7755934b7586b2f54bd28733b Mon Sep 17 00:00:00 2001 From: David Pine Date: Wed, 17 Jul 2024 12:58:36 -0500 Subject: [PATCH 10/12] Rename TOC too --- docs/toc.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/toc.yml b/docs/toc.yml index 18b9e13248..6b327f8576 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -69,8 +69,8 @@ items: - name: Create custom components displayName: custom components,extensibility href: extensibility/custom-component.md - - name: Flow auth from resource to component - href: extensibility/flow-auth-from-resource-to-component.md + - name: Implement auth from resource to component + href: extensibility/implement-auth-from-resource-to-component.md - name: Components items: From 1896e75569525de3517dbb8d8a29d90ecb13a259 Mon Sep 17 00:00:00 2001 From: David Pine Date: Wed, 17 Jul 2024 13:16:36 -0500 Subject: [PATCH 11/12] Better highlighting --- .../implement-auth-from-resource-to-component.md | 8 ++++---- .../MailKit.Client/MailKitClientFactory.cs | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/extensibility/implement-auth-from-resource-to-component.md b/docs/extensibility/implement-auth-from-resource-to-component.md index 0990e7e932..d237094535 100644 --- a/docs/extensibility/implement-auth-from-resource-to-component.md +++ b/docs/extensibility/implement-auth-from-resource-to-component.md @@ -35,11 +35,11 @@ To flow authentication credentials from the MailDev resource to the MailKit comp The MailDev container supports basic authentication for both incoming and outgoing SMTP. To configure the credentials for incoming, you need to set the `MAILDEV_INCOMING_USER` and `MAILDEV_INCOMING_PASS` environment variables. For more information, see [MailDev: Usage](https://maildev.github.io/maildev/#usage). Update the _MailDevResource.cs_ file in the `MailDev.Hosting` project, by replacing its contents with the following C# code: -:::code source="snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResource.cs"::: +:::code source="snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResource.cs" highlight="9-10"::: These updates add a `UsernameParameter` and `PasswordParameter` property. These properties are used to store the parameters for the MailDev username and password. The `ConnectionStringExpression` property is updated to include the username and password parameters in the connection string. Next, update the _MailDevResourceBuilderExtensions.cs_ file in the `MailDev.Hosting` project with the following C# code: -:::code source="snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResourceBuilderExtensions.cs"::: +:::code source="snippets/MailDevResourceWithCredentials/MailDev.Hosting/MailDevResourceBuilderExtensions.cs" highlight="9-10,29-30,32-34,40-41,55-59"::: 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. @@ -47,7 +47,7 @@ The preceding code updates the `AddMailDev` extension method to include the `use Now that the resource is updated to include the username and password parameters, you need to update the app host to include these parameters. Update the _:::no-loc text="Program.cs":::_ file in the `MailDevResource.AppHost` project with the following C# code: -:::code source="snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/Program.cs"::: +:::code source="snippets/MailDevResourceWithCredentials/MailDevResource.AppHost/Program.cs" highlight="3-4,6-9"::: The preceding code adds two parameters for the MailDev username and password. It assigns these parameters to the `MAILDEV_INCOMING_USER` and `MAILDEV_INCOMING_PASS` environment variables. The `AddMailDev` method has two chained calls to `WithEnvironment` which includes these environment variables. For more information on parameters, see [External parameters](../fundamentals/external-parameters.md). @@ -67,7 +67,7 @@ Next, configure the secrets for these paremeters. Right-click on the `MailDevRes It's good practice for components to expect connection strings to contain varions key/value pairs, and to parse these pairs into the appropriate properties. Update the _MailKitClientSettings.cs_ file in the `MailKit.Client` project with the following C# code: -:::code source="snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientSettings.cs" highlight="21-28"::: +:::code source="snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientSettings.cs" highlight="21-28,95-100"::: The preceding settings class, now includes a `Credentials` property of type `NetworkCredential`. The `ParseConnectionString` method is updated to parse the `Username` and `Password` keys from the connection string. If the `Username` and `Password` keys are present, a `NetworkCredential` is created and assigned to the `Credentials` property. diff --git a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientFactory.cs b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientFactory.cs index 623d3e3e72..f0400a1331 100644 --- a/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientFactory.cs +++ b/docs/extensibility/snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientFactory.cs @@ -10,9 +10,6 @@ namespace MailKit.Client; /// /// The settings for the SMTP server /// -/// -/// The optional used to authenticate to the SMTP server -/// public sealed class MailKitClientFactory(MailKitClientSettings settings) : IDisposable { private readonly SemaphoreSlim _semaphore = new(1, 1); From d8250713c467b9df06b53eb84bf0ddaaa6e5d439 Mon Sep 17 00:00:00 2001 From: David Pine Date: Wed, 17 Jul 2024 13:21:03 -0500 Subject: [PATCH 12/12] Fix highlighting --- docs/extensibility/implement-auth-from-resource-to-component.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/extensibility/implement-auth-from-resource-to-component.md b/docs/extensibility/implement-auth-from-resource-to-component.md index d237094535..0beb2f667a 100644 --- a/docs/extensibility/implement-auth-from-resource-to-component.md +++ b/docs/extensibility/implement-auth-from-resource-to-component.md @@ -73,7 +73,7 @@ The preceding settings class, now includes a `Credentials` property of type `Net With the settings class updated to understand and populate the credentials, update the factory to conditionally use the credentials if they're configured. Update the _MailKitClientFactory.cs_ file in the `MailKit.Client` project with the following C# code: -:::code source="snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientFactory.cs" highlight="47-51"::: +:::code source="snippets/MailDevResourceWithCredentials/MailKit.Client/MailKitClientFactory.cs" highlight="44-48"::: When the factory determines that credentials have been configured, it authenticates with the SMTP server after connecting before returning the `SmtpClient`.