diff --git a/resend-dotnet.sln b/resend-dotnet.sln index 5e0551a..bc51def 100644 --- a/resend-dotnet.sln +++ b/resend-dotnet.sln @@ -60,6 +60,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderLiquid", "examples\Re EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebControllerApi", "examples\WebControllerApi\WebControllerApi.csproj", "{64049053-1C1E-43D6-A23B-5A609354934B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resend.DocsCheck", "tools\Resend.DocsCheck\Resend.DocsCheck.csproj", "{ED268270-FD7F-4111-8B54-020C81F8D683}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -130,6 +132,10 @@ Global {64049053-1C1E-43D6-A23B-5A609354934B}.Debug|Any CPU.Build.0 = Debug|Any CPU {64049053-1C1E-43D6-A23B-5A609354934B}.Release|Any CPU.ActiveCfg = Release|Any CPU {64049053-1C1E-43D6-A23B-5A609354934B}.Release|Any CPU.Build.0 = Release|Any CPU + {ED268270-FD7F-4111-8B54-020C81F8D683}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED268270-FD7F-4111-8B54-020C81F8D683}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED268270-FD7F-4111-8B54-020C81F8D683}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED268270-FD7F-4111-8B54-020C81F8D683}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -151,6 +157,7 @@ Global {C33A6269-8657-4487-8CDD-588210E20B65} = {121B647E-18F4-41CB-AC00-49D5F06D5320} {FD4407E3-551D-48F8-9FFB-63E409F4C4BB} = {121B647E-18F4-41CB-AC00-49D5F06D5320} {64049053-1C1E-43D6-A23B-5A609354934B} = {121B647E-18F4-41CB-AC00-49D5F06D5320} + {ED268270-FD7F-4111-8B54-020C81F8D683} = {CAC9B80A-B362-4135-AC30-8013D0DB6124} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {15EEB6F3-A067-45F5-987C-824BD8FDEAF9} diff --git a/src/Resend.Webhooks/WebApplicationExtensions.cs b/src/Resend.Webhooks/WebApplicationExtensions.cs index 0c2072b..7efbec2 100644 --- a/src/Resend.Webhooks/WebApplicationExtensions.cs +++ b/src/Resend.Webhooks/WebApplicationExtensions.cs @@ -1,6 +1,4 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Resend.Webhooks; namespace Microsoft.AspNetCore.Builder; @@ -12,6 +10,7 @@ public static class WebApplicationExtensions /// /// /// + /// /// public static IServiceCollection AddResendWebhooks( this IServiceCollection services, Action? configureOptions = null ) { diff --git a/src/Resend/IResend.cs b/src/Resend/IResend.cs index 970f200..32d28e1 100644 --- a/src/Resend/IResend.cs +++ b/src/Resend/IResend.cs @@ -110,7 +110,7 @@ public interface IResend /// Domain. /// /// - Task> DomainAddAsync( string domainName, DeliveryRegion? region, CancellationToken cancellationToken = default ); + Task> DomainAddAsync( string domainName, DeliveryRegion? region = null, CancellationToken cancellationToken = default ); /// diff --git a/src/Resend/ResendClient.cs b/src/Resend/ResendClient.cs index 4ff375d..f0ecd70 100644 --- a/src/Resend/ResendClient.cs +++ b/src/Resend/ResendClient.cs @@ -113,7 +113,7 @@ public ResendClient( IOptions options, HttpClient httpClien /// - public async Task> DomainAddAsync( string domainName, DeliveryRegion? region, CancellationToken cancellationToken = default ) + public async Task> DomainAddAsync( string domainName, DeliveryRegion? region = null, CancellationToken cancellationToken = default ) { var path = $"/domains"; var req = new HttpRequestMessage( HttpMethod.Post, path ); @@ -653,4 +653,42 @@ private ResendRateLimit FromHeaders( HttpResponseHeaders headers ) return (T) Convert.ChangeType( v, typeof( T ) ); } + + + /// + /// Creates an instance of Resend client with the given client + /// options. + /// + /// Resend client options. + /// Instance of Resend client. + /// + /// Utility method for examples/one-off apps. For most use-cases it is + /// preferable to use dependency injection to configure/inject `IResend` + /// instances. + /// + public static IResend Create( ResendClientOptions options ) + { + var opt = Options.Create( options ); + + return new ResendClient( opt, new HttpClient() ); + } + + + /// + /// Creates an instance of Resend with the given API token. + /// + /// API token + /// Instance of Resend client. + /// + /// Utility method for examples/one-off apps. For most use-cases it is + /// preferable to use dependency injection to configure/inject `IResend` + /// instances. + /// + public static IResend Create( string apiToken ) + { + var opt = new ResendClientOptions(); + opt.ApiToken = apiToken; + + return Create( opt ); + } } diff --git a/tools/Resend.DocsCheck/Program.cs b/tools/Resend.DocsCheck/Program.cs new file mode 100644 index 0000000..f9039e7 --- /dev/null +++ b/tools/Resend.DocsCheck/Program.cs @@ -0,0 +1,211 @@ +using McMaster.Extensions.CommandLineUtils; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Emit; +using Microsoft.Extensions.DependencyInjection; +using System.ComponentModel.DataAnnotations; +using System.Reflection; +using System.Text; + +namespace Resend.DocsCheck; + +/// +public class Program +{ + /// + public static int Main( string[] args ) + { + /* + * + */ + var app = new CommandLineApplication(); + + var svc = new ServiceCollection(); + + var sp = svc.BuildServiceProvider(); + + + /* + * + */ + try + { + app.Conventions + .UseDefaultConventions() + .UseConstructorInjection( sp ); + } + catch ( Exception ex ) + { + Console.WriteLine( "ftl: unhandled exception during setup" ); + Console.WriteLine( ex.ToString() ); + + return 2; + } + + + /* + * + */ + try + { + return app.Execute( args ); + } + catch ( UnrecognizedCommandParsingException ex ) + { + Console.WriteLine( "err: " + ex.Message ); + + return 2; + } + catch ( Exception ex ) + { + Console.WriteLine( "ftl: unhandled exception during execution" ); + Console.WriteLine( ex.ToString() ); + + return 2; + } + } + + + /// + [Option( "-r|--root", Description = "" )] + [DirectoryExists] + [Required] + public string? RootFolder { get; set; } + + + /// + public async Task OnExecute() + { + var dir = new DirectoryInfo( this.RootFolder! ); + + foreach ( var f in dir.GetFiles( "*.mdx", SearchOption.AllDirectories ) ) + { + await FileCheck( dir, f ); + } + + return 0; + } + + + /// + private async Task FileCheck( DirectoryInfo root, FileInfo f ) + { + var rel = GetRelativePath( root, f ); + var mdx = await File.ReadAllTextAsync( f.FullName ); + + + /* + * + */ + var startIx = mdx.IndexOf( "```csharp" ); + + if ( startIx < 0 ) + return; + + var endIx = mdx.IndexOf( "```", startIx + 4 ); + + + var fragment = mdx.Substring( startIx, endIx - startIx + 3 ); + + + /* + * + */ + var lines = fragment.Split( "\n" ); + + var sb = new StringBuilder(); + sb.AppendLine( "using System;" ); + sb.AppendLine( "using System.Threading.Tasks;" ); + + foreach ( var l in lines ) + if ( l.StartsWith( "using " ) == true ) + sb.AppendLine( l ); + + sb.AppendLine(); + sb.AppendLine( "public class Program {" ); + sb.AppendLine( "public static async Task Main( string[] args ) {" ); + + foreach ( var l in lines.Skip( 1 ).Take( lines.Count() - 2 ) ) + { + if ( l.StartsWith( "using " ) == true ) + continue; + + sb.AppendLine( l ); + } + + sb.AppendLine( "return 0; } }" ); + + var csharp = sb.ToString(); + + + /* + * + */ + SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText( csharp ); + + var assemblyName = Path.GetRandomFileName(); + var references = new MetadataReference[] + { + MetadataReference.CreateFromFile( typeof( Object ).Assembly.Location ), + MetadataReference.CreateFromFile( Assembly.Load( "System.Console" ).Location ), + MetadataReference.CreateFromFile( Assembly.Load( "System.Runtime" ).Location ), + MetadataReference.CreateFromFile( Assembly.Load( "System.Collections" ).Location ), + MetadataReference.CreateFromFile( typeof( List<> ).Assembly.Location ), + MetadataReference.CreateFromFile( typeof( IResend ).Assembly.Location ), + }; + + CSharpCompilation compilation = CSharpCompilation.Create( + assemblyName, + syntaxTrees: new[] { syntaxTree }, + references: references, + options: new CSharpCompilationOptions( OutputKind.DynamicallyLinkedLibrary ) ); + + using ( var ms = new MemoryStream() ) + { + EmitResult result = compilation.Emit( ms ); + + if ( result.Success == false ) + { + // handle exceptions + IEnumerable failures = result.Diagnostics.Where( diagnostic => + diagnostic.IsWarningAsError || + diagnostic.Severity == DiagnosticSeverity.Error ); + + var fg = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Red; + Console.Write( "err" ); + Console.ForegroundColor = fg; + + Console.Write( " " ); + Console.WriteLine( rel ); + + foreach ( Diagnostic diagnostic in failures ) + { + Console.Error.WriteLine( "{0}: {1}", diagnostic.Id, diagnostic.GetMessage() ); + } + } + else + { + var fg = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Green; + Console.Write( "aok" ); + Console.ForegroundColor = fg; + + Console.Write( " " ); + Console.WriteLine( rel ); + } + } + } + + + /// + private static string GetRelativePath( DirectoryInfo directoryInfo, FileInfo fileInfo ) + { + Uri directoryUri = new Uri( directoryInfo.FullName + Path.DirectorySeparatorChar ); + Uri fileUri = new Uri( fileInfo.FullName ); + Uri relativeUri = directoryUri.MakeRelativeUri( fileUri ); + string relativePath = Uri.UnescapeDataString( relativeUri.ToString() ); + + return relativePath.Replace( '/', Path.DirectorySeparatorChar ); + } +} \ No newline at end of file diff --git a/tools/Resend.DocsCheck/Resend.DocsCheck.csproj b/tools/Resend.DocsCheck/Resend.DocsCheck.csproj new file mode 100644 index 0000000..af4c46d --- /dev/null +++ b/tools/Resend.DocsCheck/Resend.DocsCheck.csproj @@ -0,0 +1,20 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/tools/Resend.DocsCheck/api-check.cmd b/tools/Resend.DocsCheck/api-check.cmd new file mode 100644 index 0000000..271d148 --- /dev/null +++ b/tools/Resend.DocsCheck/api-check.cmd @@ -0,0 +1 @@ +dotnet run -- --root=..\..\..\resend-docs\api-reference \ No newline at end of file diff --git a/tools/Resend.DocsCheck/api-check.sh b/tools/Resend.DocsCheck/api-check.sh new file mode 100644 index 0000000..8aae991 --- /dev/null +++ b/tools/Resend.DocsCheck/api-check.sh @@ -0,0 +1 @@ +dotnet run -- --root=../../../resend-docs/api-reference