aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Emails.Transactional.Extensions/src/EmailSystemConfig.cs54
-rw-r--r--lib/Emails.Transactional.Extensions/src/TEmailConfig.cs204
-rw-r--r--lib/Emails.Transactional.Extensions/src/TransactionalEmailExtensions.cs150
-rw-r--r--lib/Emails.Transactional.Plugin/src/Api Endpoints/SendEndpoint.cs22
-rw-r--r--lib/Emails.Transactional.Plugin/src/TEmailEntryPoint.cs15
-rw-r--r--lib/Emails.Transactional.Plugin/src/Transactional Emails.csproj13
6 files changed, 234 insertions, 224 deletions
diff --git a/lib/Emails.Transactional.Extensions/src/EmailSystemConfig.cs b/lib/Emails.Transactional.Extensions/src/EmailSystemConfig.cs
deleted file mode 100644
index fdce935..0000000
--- a/lib/Emails.Transactional.Extensions/src/EmailSystemConfig.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: VNLib.Plugins.Extentions.TransactionalEmail
-* File: EmailSystemConfig.cs
-*
-* EmailSystemConfig.cs is part of VNLib.Plugins.Extentions.TransactionalEmail which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* VNLib.Plugins.Extentions.TransactionalEmail is free software: you can redistribute it and/or modify
-* it under the terms of the GNU Affero General Public License as
-* published by the Free Software Foundation, either version 3 of the
-* License, or (at your option) any later version.
-*
-* VNLib.Plugins.Extentions.TransactionalEmail is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU Affero General Public License for more details.
-*
-* You should have received a copy of the GNU Affero General Public License
-* along with this program. If not, see https://www.gnu.org/licenses/.
-*/
-
-using RestSharp;
-
-using Emails.Transactional.Client;
-
-using VNLib.Net.Rest.Client;
-
-namespace VNLib.Plugins.Extentions.TransactionalEmail
-{
- /// <summary>
- /// An extended <see cref="TransactionalEmailConfig"/> configuration
- /// object that contains a <see cref="Net.Rest.Client.RestClientPool"/> pool for making
- /// transactions
- /// </summary>
- internal sealed class EmailSystemConfig : TransactionalEmailConfig
- {
- /// <summary>
- /// A shared <see cref="Net.Rest.Client.RestClientPool"/> for renting configuraed
- /// <see cref="RestClient"/>
- /// </summary>
- public RestClientPool RestClientPool { get; init; }
- /// <summary>
- /// A global from email address name
- /// </summary>
- public string EmailFromName { get; init; }
- /// <summary>
- /// A global from email address
- /// </summary>
- public string EmailFromAddress { get; init; }
- }
-} \ No newline at end of file
diff --git a/lib/Emails.Transactional.Extensions/src/TEmailConfig.cs b/lib/Emails.Transactional.Extensions/src/TEmailConfig.cs
new file mode 100644
index 0000000..7905982
--- /dev/null
+++ b/lib/Emails.Transactional.Extensions/src/TEmailConfig.cs
@@ -0,0 +1,204 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: Emails.Transactional.Client.Extensions
+* File: TEmailConfig.cs
+*
+* TEmailConfig.cs is part of Emails.Transactional.Client.Extensions which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* Emails.Transactional.Client.Extensions is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+*
+* Emails.Transactional.Client.Extensions is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see https://www.gnu.org/licenses/.
+*/
+
+using System.Text;
+using System.Text.Json.Serialization;
+
+using RestSharp;
+
+using VNLib.Utils.Logging;
+using VNLib.Plugins;
+using VNLib.Net.Rest.Client;
+using VNLib.Net.Rest.Client.OAuth2;
+using VNLib.Plugins.Extensions.Loading;
+
+namespace Emails.Transactional.Client.Extensions
+{
+ /// <summary>
+ /// Provides transactional emails based on templates and variable complilation. Should be loaded as a singleton for a plugin
+ /// </summary>
+ [ConfigurationName(EMAIL_CONFIG_KEY)]
+ public class TEmailConfig : TransactionalEmailConfig, IAsyncConfigurable, IDisposable
+ {
+ const string DEFAULT_USER_AGENT = "VN Transactional Email System";
+
+ public const string EMAIL_CONFIG_KEY = "emails";
+ public const string REQUIRED_EMAIL_TEMPALTE_CONFIG_KEY = "required_email_templates";
+
+ public const int DEFAULT_MAX_CLIENTS = 5;
+ public const int DEFAULT_CLIENT_TIMEOUT_MS = 10000;
+
+ private bool disposedValue;
+
+ private readonly OAuth2Authenticator _authenticator;
+
+ /// <summary>
+ /// A shared <see cref="VNLib.Net.Rest.Client.RestClientPool"/> for renting configuraed
+ /// <see cref="RestClient"/>
+ /// </summary>
+ public RestClientPool RestClientPool { get; }
+ /// <summary>
+ /// A global from email address name
+ /// </summary>
+ public string EmailFromName { get; }
+ /// <summary>
+ /// A global from email address
+ /// </summary>
+ public string EmailFromAddress { get; }
+
+ private SecretResult? _clientId;
+ private SecretResult? _clientSecret;
+
+
+ /// <summary>
+ /// Intialzies the singleton using the <see cref="LoadingExtensions"/>
+ /// loading system
+ /// </summary>
+ /// <param name="plugin">The plugin loading the instance</param>
+ /// <param name="config">The configuration scope for the instance</param>
+ public TEmailConfig(PluginBase plugin, IConfigScope config)
+ {
+ //The current config scope should be the required data, so we can deserialze it
+ JsonConfig mailConfig = config.DeserialzeAndValidate<JsonConfig>();
+
+ //Get required templates
+ Dictionary<string, string> templates = plugin.GetConfig(REQUIRED_EMAIL_TEMPALTE_CONFIG_KEY).Deserialze<Dictionary<string, string>>();
+
+ EmailFromName = mailConfig.EmailFromName!;
+ EmailFromAddress = mailConfig.EmailFromAddress!;
+
+ if (plugin.IsDebug())
+ {
+ plugin.Log.Debug("Required email templates {t}", templates);
+ }
+
+ //Options for auth token endpoint
+ RestClientOptions clientOptions = new()
+ {
+ AllowMultipleDefaultParametersWithSameName = true,
+ //Server supports compression
+ AutomaticDecompression = System.Net.DecompressionMethods.All,
+ PreAuthenticate = false,
+ Encoding = Encoding.UTF8,
+ MaxTimeout = mailConfig.TimeoutMs,
+ UserAgent = mailConfig.UserAgent,
+ //Server should not redirect
+ FollowRedirects = false
+ };
+
+ //Init Oauth authenticator
+ _authenticator = new(clientOptions, OAuthCredentialGet, mailConfig.TokenServerUri.ToString());
+
+ //Create client pool
+ RestClientPool = new(mailConfig.MaxClients, clientOptions, authenticator: _authenticator);
+
+ //Init templates and service url
+ WithTemplates(templates)
+ .WithUrl(mailConfig.ServiceUri);
+ }
+
+ private Credential OAuthCredentialGet()
+ {
+ _ = _clientId ?? throw new InvalidOperationException("Oauth2 client id was not loaded");
+ _ = _clientSecret ?? throw new InvalidOperationException("Oauth2 client secret was not loaded");
+
+ //Create new credential
+ return Credential.Create(_clientId.Result, _clientSecret.Result);
+ }
+
+ async Task IAsyncConfigurable.ConfigureServiceAsync(PluginBase plugin)
+ {
+ //Load oauth secrets
+ _clientId = await plugin.TryGetSecretAsync("email_client_id");
+ _clientSecret = await plugin.TryGetSecretAsync("email_client_secret");
+ }
+
+ ///<inheritdoc/>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposedValue)
+ {
+ if (disposing)
+ {
+ _clientId?.Dispose();
+ _clientSecret?.Dispose();
+
+ _authenticator.Dispose();
+ RestClientPool.Dispose();
+ }
+ disposedValue = true;
+ }
+ }
+
+ void IDisposable.Dispose()
+ {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ private sealed class JsonConfig : IOnConfigValidation
+ {
+ [JsonPropertyName("from_name")]
+ public string? EmailFromName { get; set; }
+ [JsonPropertyName("from_address")]
+ public string? EmailFromAddress { get; set; }
+ [JsonPropertyName("email_url")]
+ public string? ServiceUrl { get; set; }
+ [JsonPropertyName("auth_url")]
+ public string? AuthUrl { get; set; }
+ [JsonPropertyName("user_agent")]
+ public string UserAgent { get; set; } = DEFAULT_USER_AGENT;
+ [JsonPropertyName("timeout_ms")]
+ public int TimeoutMs { get; set; } = DEFAULT_CLIENT_TIMEOUT_MS;
+ [JsonPropertyName("max_clients")]
+ public int MaxClients { get; set; } = DEFAULT_MAX_CLIENTS;
+
+
+ [JsonIgnore]
+ public Uri ServiceUri => new(ServiceUrl!);
+
+ [JsonIgnore]
+ public Uri TokenServerUri => new(AuthUrl!);
+
+ void IOnConfigValidation.Validate()
+ {
+ _ = EmailFromName ?? throw new KeyNotFoundException("Missing required mail configuration key 'from_name'");
+ _ = EmailFromAddress ?? throw new KeyNotFoundException("Missing required mail configuration key 'from_address'");
+ _ = ServiceUri ?? throw new KeyNotFoundException("Missing required mail configuration key 'email_url'");
+ _ = AuthUrl ?? throw new KeyNotFoundException("Missing required mail configuration key 'auth_url'");
+
+ if (MaxClients < 1)
+ {
+ throw new ArgumentOutOfRangeException("max_client", "Max clients config variable must be a non-zero positive integer");
+ }
+
+ if (TimeoutMs < 100)
+ {
+ throw new ArgumentOutOfRangeException("timeout_ms", "Client timeout should be a value greater than 100ms");
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/lib/Emails.Transactional.Extensions/src/TransactionalEmailExtensions.cs b/lib/Emails.Transactional.Extensions/src/TransactionalEmailExtensions.cs
index 2345f7e..e6cf3ed 100644
--- a/lib/Emails.Transactional.Extensions/src/TransactionalEmailExtensions.cs
+++ b/lib/Emails.Transactional.Extensions/src/TransactionalEmailExtensions.cs
@@ -1,19 +1,19 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
-* Package: VNLib.Plugins.Extentions.TransactionalEmail
+* Package: Emails.Transactional.Client.Extensions
* File: TransactionalEmailExtensions.cs
*
-* TransactionalEmailExtensions.cs is part of VNLib.Plugins.Extentions.TransactionalEmail which is part of the larger
-* VNLib collection of libraries and utilities.
+* TransactionalEmailExtensions.cs is part of Emails.Transactional.Client.Extensions which
+* is part of the larger VNLib collection of libraries and utilities.
*
-* VNLib.Plugins.Extentions.TransactionalEmail is free software: you can redistribute it and/or modify
+* Emails.Transactional.Client.Extensions is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
-* VNLib.Plugins.Extentions.TransactionalEmail is distributed in the hope that it will be useful,
+* Emails.Transactional.Client.Extensions is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
@@ -22,41 +22,19 @@
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
-using System.Text;
-using System.Text.Json;
-using RestSharp;
-
-using Emails.Transactional.Client;
-
-using VNLib.Utils.Logging;
-using VNLib.Utils.Extensions;
using VNLib.Net.Rest.Client;
-using VNLib.Net.Rest.Client.OAuth2;
-using VNLib.Plugins.Extensions.Loading;
-namespace VNLib.Plugins.Extentions.TransactionalEmail
+namespace Emails.Transactional.Client.Extensions
{
+
/// <summary>
/// Contains extension methods for implementing templated
/// transactional emails
/// </summary>
public static class TransactionalEmailExtensions
{
- public const string EMAIL_CONFIG_KEY = "emails";
- public const string REQUIRED_EMAIL_TEMPALTE_CONFIG_KEY = "required_email_templates";
-
- public const uint DEFAULT_MAX_CLIENTS = 5;
- public const uint DEFAULT_CLIENT_TIMEOUT_MS = 10000;
-
- /// <summary>
- /// Gets (or loads) the ambient <see cref="TransactionalEmailConfig"/> configuration object
- /// to send transactional emails against
- /// </summary>
- /// <param name="pbase"></param>
- /// <returns>The <see cref="TransactionalEmailConfig"/> from the current plugins config</returns>
- public static TransactionalEmailConfig GetEmailConfig(this PluginBase pbase) => LoadingExtensions.GetOrCreateSingleton(pbase, LoadConfig);
/// <summary>
/// Sends an <see cref="EmailTransactionRequest"/> on the current configuration resource pool
@@ -67,119 +45,9 @@ namespace VNLib.Plugins.Extentions.TransactionalEmail
public static async Task<TransactionResult> SendEmailAsync(this TransactionalEmailConfig config, EmailTransactionRequest request)
{
//Get a new client contract from the configuration's pool assuming its a EmailSystemConfig class
- using ClientContract client = ((EmailSystemConfig)config).RestClientPool.Lease();
+ using ClientContract client = ((TEmailConfig)config).RestClientPool.Lease();
//Send the email and await the result before releasing the client
return await client.Resource.SendEmailAsync(request);
}
-
- private static TransactionalEmailConfig LoadConfig(PluginBase pbase)
- {
- //Get the required email config
- IReadOnlyDictionary<string, JsonElement> conf = pbase.GetConfig(EMAIL_CONFIG_KEY);
-
- string emailFromName = conf["from_name"].GetString() ?? throw new KeyNotFoundException("Missing required configuration key 'from_name'");
- string emailFromAddress = conf["from_address"].GetString() ?? throw new KeyNotFoundException("Missing required configuration key 'from_address'");
- Uri baseServerPath = new(conf["base_url"].GetString()!, UriKind.RelativeOrAbsolute);
-
- //Get the token server url or use the base path if no set
- Uri tokenServerBase = conf.TryGetValue("token_server_url", out JsonElement tksEl) && tksEl.GetString() != null ?
- new(tksEl.GetString()!, UriKind.RelativeOrAbsolute)
- : baseServerPath;
-
- //Get the transaction endpoint path, should be a realative path
- Uri transactionEndpoint = new(conf["transaction_path"].GetString()!, UriKind.Relative);
-
- //Load credentials
- string authEndpoint = conf["token_path"].GetString() ?? throw new KeyNotFoundException("Missing required configuration key 'token_path'");
-
- //Optional user-agent
- string? userAgent = conf.GetPropString("user_agent");
-
- //Get optional timeout ms
- int timeoutMs = (int)(conf.TryGetValue("request_timeout_ms", out JsonElement timeoutEl) ? timeoutEl.GetUInt32() : DEFAULT_CLIENT_TIMEOUT_MS);
-
- //Get maximum client limit
- int maxClients = (int)(conf.TryGetValue("max_clients", out JsonElement mxcEl) ? mxcEl.GetUInt32() : DEFAULT_MAX_CLIENTS);
-
- //Load all templates from the plugin config
- Dictionary<string, string> templates = pbase.PluginConfig.GetProperty(REQUIRED_EMAIL_TEMPALTE_CONFIG_KEY)
- .EnumerateObject()
- .ToDictionary(static jp => jp.Name, static jp => jp.Value.GetString()!);
-
- pbase.Log.Verbose("Required email templates {t}", templates);
-
- //Load oauth secrets from vault
- Task<SecretResult?> oauth2ClientID = pbase.TryGetSecretAsync("email_client_id");
- Task<SecretResult?> oauth2Password = pbase.TryGetSecretAsync("email_client_secret");
-
- //Lazy cred loaded, tasks should be loaded before this method will ever get called
- Credential lazyCredentialGet()
- {
- //Load the results
- SecretResult cliendId = oauth2ClientID.GetAwaiter().GetResult() ?? throw new KeyNotFoundException("Missing required oauth2 client id");
- SecretResult password = oauth2Password.GetAwaiter().GetResult() ?? throw new KeyNotFoundException("Missing required oauth2 client secret");
-
- //Creat credential
- return Credential.Create(cliendId.Result, password.Result);
- }
-
- //Init client creation options
- RestClientOptions poolOptions = new(baseServerPath)
- {
- AllowMultipleDefaultParametersWithSameName = true,
- AutomaticDecompression = System.Net.DecompressionMethods.All,
- PreAuthenticate = true,
- Encoding = Encoding.UTF8,
- MaxTimeout = timeoutMs,
- UserAgent = userAgent,
- //Server should not redirect
- FollowRedirects = false,
- };
-
- //Options for auth token endpoint
- RestClientOptions oAuth2ClientOptions = new(tokenServerBase)
- {
- AllowMultipleDefaultParametersWithSameName = true,
- //Server supports compression
- AutomaticDecompression = System.Net.DecompressionMethods.All,
- PreAuthenticate = false,
- Encoding = Encoding.UTF8,
- MaxTimeout = timeoutMs,
- UserAgent = userAgent,
- //Server should not redirect
- FollowRedirects = false
- };
-
- //Init Oauth authenticator
- OAuth2Authenticator authenticator = new(oAuth2ClientOptions, lazyCredentialGet, authEndpoint);
-
- //Create client pool
- RestClientPool pool = new(maxClients, poolOptions, authenticator: authenticator);
-
- void Cleanup()
- {
- authenticator.Dispose();
- pool.Dispose();
- oauth2ClientID.Dispose();
- oauth2Password.Dispose();
- }
-
- //register password cleanup
- _ = pbase.RegisterForUnload(Cleanup);
-
- //Create config
- EmailSystemConfig config = new ()
- {
- EmailFromName = emailFromName,
- EmailFromAddress = emailFromAddress,
- RestClientPool = pool,
- };
-
- //Store templates and set service url
- config.WithTemplates(templates)
- .WithUrl(transactionEndpoint);
-
- return config;
- }
}
} \ No newline at end of file
diff --git a/lib/Emails.Transactional.Plugin/src/Api Endpoints/SendEndpoint.cs b/lib/Emails.Transactional.Plugin/src/Api Endpoints/SendEndpoint.cs
index 9bcc966..e5ca88b 100644
--- a/lib/Emails.Transactional.Plugin/src/Api Endpoints/SendEndpoint.cs
+++ b/lib/Emails.Transactional.Plugin/src/Api Endpoints/SendEndpoint.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: Transactional Emails
@@ -26,7 +26,6 @@ using System;
using System.IO;
using System.Net;
using System.Text;
-using System.Text.Json;
using System.Threading.Tasks;
using System.Collections.Generic;
@@ -94,7 +93,7 @@ namespace Emails.Transactional.Endpoints
private SmtpProvider? EmailService;
- public SendEndpoint(PluginBase plugin, IReadOnlyDictionary<string, JsonElement> config)
+ public SendEndpoint(PluginBase plugin, IConfigScope config)
{
string? path = config["path"].GetString();
FromAddress = config["from_address"].GetString() ?? throw new KeyNotFoundException("Missing required key 'from_address' in 'transaction_endpoint'");
@@ -105,13 +104,13 @@ namespace Emails.Transactional.Endpoints
//Smtp setup
{
- IReadOnlyDictionary<string, JsonElement> smtp = plugin.GetConfig("smtp");
+ IConfigScope smtp = plugin.GetConfig("smtp");
Uri serverUri = new(smtp["server_address"].GetString()!);
string username = smtp["username"].GetString() ?? throw new KeyNotFoundException("Missing required key 'usename' in 'smtp' config");
TimeSpan timeout = smtp["timeout_sec"].GetTimeSpan(TimeParseType.Seconds);
//Load SMTP
- _ = plugin.ObserveTask(async () =>
+ _ = plugin.ObserveWork(async () =>
{
using SecretResult? password = await plugin.TryGetSecretAsync("smtp_password") ?? throw new KeyNotFoundException("Missing required 'smtp_password' in secrets");
//Copy the secre to the network credential
@@ -129,24 +128,25 @@ namespace Emails.Transactional.Endpoints
Client = new();
//Load the client when the secret finishes loading
- _ = plugin.ObserveTask(async () =>
+ _ = plugin.ObserveWork(async () =>
{
using SecretResult? secret = await plugin.TryGetSecretAsync("s3_secret") ?? throw new KeyNotFoundException("Missing required s3 client secret in config");
+
Client.WithEndpoint(S3Config.ServerAddress)
.WithCredentials(S3Config.ClientId, secret.Result.ToString());
- if (S3Config.UseSsl == true)
- {
- Client.WithSSL();
- }
+ Client.WithSSL(S3Config.UseSsl.HasValue && S3Config.UseSsl.Value);
+
//Accept optional region
if (!string.IsNullOrWhiteSpace(S3Config.Region))
{
Client.WithRegion(S3Config.Region);
}
+
//Build client
Client.Build();
+
//If the plugin is in debug mode, log requests to logger
if (plugin.IsDebug())
{
@@ -161,6 +161,7 @@ namespace Emails.Transactional.Endpoints
TemplateCache = new(20, StringComparer.OrdinalIgnoreCase);
}
+
protected override async ValueTask<VfReturnType> PostAsync(HttpEntity entity)
{
//Make sure the user has the required scope to send an email
@@ -182,6 +183,7 @@ namespace Emails.Transactional.Endpoints
{
Success = false
};
+
//Set a default from name/address if not set by user
transaction.From ??= FromAddress;
transaction.FromName ??= FromAddress;
diff --git a/lib/Emails.Transactional.Plugin/src/TEmailEntryPoint.cs b/lib/Emails.Transactional.Plugin/src/TEmailEntryPoint.cs
index c95ca08..8e7319a 100644
--- a/lib/Emails.Transactional.Plugin/src/TEmailEntryPoint.cs
+++ b/lib/Emails.Transactional.Plugin/src/TEmailEntryPoint.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: Transactional Emails
@@ -61,17 +61,10 @@ namespace Emails.Transactional
protected override void OnLoad()
{
- try
- {
- //Route send oauth endpoint
- this.Route<SendEndpoint>();
+ //Route send oauth endpoint
+ this.Route<SendEndpoint>();
- Log.Information("Plugin loaded");
- }
- catch (KeyNotFoundException kne)
- {
- Log.Warn("Missing required configuration keys {err}", kne.Message);
- }
+ Log.Information("Plugin loaded");
}
diff --git a/lib/Emails.Transactional.Plugin/src/Transactional Emails.csproj b/lib/Emails.Transactional.Plugin/src/Transactional Emails.csproj
index 267987d..0d628f8 100644
--- a/lib/Emails.Transactional.Plugin/src/Transactional Emails.csproj
+++ b/lib/Emails.Transactional.Plugin/src/Transactional Emails.csproj
@@ -5,8 +5,9 @@
<RootNamespace>Emails.Transactional</RootNamespace>
<AssemblyName>Emails.Transactional</AssemblyName>
<Nullable>enable</Nullable>
-
- <Version>1.0.0.1</Version>
+
+ <!-- Resolve nuget dll files and store them in the output dir -->
+ <EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
@@ -21,10 +22,6 @@
</Description>
</PropertyGroup>
- <!-- Resolve nuget dll files and store them in the output dir -->
- <PropertyGroup>
- <EnableDynamicLoading>true</EnableDynamicLoading>
- </PropertyGroup>
<ItemGroup>
<PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2">
@@ -35,8 +32,8 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="Fluid.Core" Version="2.3.1" />
- <PackageReference Include="MailKit" Version="3.5.0" />
+ <PackageReference Include="Fluid.Core" Version="2.4.0" />
+ <PackageReference Include="MailKit" Version="3.6.0" />
<PackageReference Include="Minio" Version="4.0.7" />
</ItemGroup>