Skip to content

Commit

Permalink
Add .Create static method / Add tool to validate API examples (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
filipetoscano authored Feb 4, 2025
1 parent c3d4efd commit 7a97ba4
Show file tree
Hide file tree
Showing 8 changed files with 282 additions and 5 deletions.
7 changes: 7 additions & 0 deletions resend-dotnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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}
Expand Down
5 changes: 2 additions & 3 deletions src/Resend.Webhooks/WebApplicationExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -12,6 +10,7 @@ public static class WebApplicationExtensions
///
/// </summary>
/// <param name="services"></param>
/// <param name="configureOptions"></param>
/// <returns></returns>
public static IServiceCollection AddResendWebhooks( this IServiceCollection services, Action<WebhookValidatorOptions>? configureOptions = null )
{
Expand Down
2 changes: 1 addition & 1 deletion src/Resend/IResend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public interface IResend
/// Domain.
/// </returns>
/// <see href="https://resend.com/docs/api-reference/domains/create-domain"/>
Task<ResendResponse<Domain>> DomainAddAsync( string domainName, DeliveryRegion? region, CancellationToken cancellationToken = default );
Task<ResendResponse<Domain>> DomainAddAsync( string domainName, DeliveryRegion? region = null, CancellationToken cancellationToken = default );


/// <summary>
Expand Down
40 changes: 39 additions & 1 deletion src/Resend/ResendClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public ResendClient( IOptions<ResendClientOptions> options, HttpClient httpClien


/// <inheritdoc />
public async Task<ResendResponse<Domain>> DomainAddAsync( string domainName, DeliveryRegion? region, CancellationToken cancellationToken = default )
public async Task<ResendResponse<Domain>> DomainAddAsync( string domainName, DeliveryRegion? region = null, CancellationToken cancellationToken = default )
{
var path = $"/domains";
var req = new HttpRequestMessage( HttpMethod.Post, path );
Expand Down Expand Up @@ -653,4 +653,42 @@ private ResendRateLimit FromHeaders( HttpResponseHeaders headers )

return (T) Convert.ChangeType( v, typeof( T ) );
}


/// <summary>
/// Creates an instance of Resend client with the given client
/// options.
/// </summary>
/// <param name="options">Resend client options.</param>
/// <returns>Instance of Resend client.</returns>
/// <remarks>
/// Utility method for examples/one-off apps. For most use-cases it is
/// preferable to use dependency injection to configure/inject `IResend`
/// instances.
/// </remarks>
public static IResend Create( ResendClientOptions options )
{
var opt = Options.Create( options );

return new ResendClient( opt, new HttpClient() );
}


/// <summary>
/// Creates an instance of Resend with the given API token.
/// </summary>
/// <param name="apiToken">API token</param>
/// <returns>Instance of Resend client.</returns>
/// <remarks>
/// Utility method for examples/one-off apps. For most use-cases it is
/// preferable to use dependency injection to configure/inject `IResend`
/// instances.
/// </remarks>
public static IResend Create( string apiToken )
{
var opt = new ResendClientOptions();
opt.ApiToken = apiToken;

return Create( opt );
}
}
211 changes: 211 additions & 0 deletions tools/Resend.DocsCheck/Program.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary />
public class Program
{
/// <summary />
public static int Main( string[] args )
{
/*
*
*/
var app = new CommandLineApplication<Program>();

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;
}
}


/// <summary />
[Option( "-r|--root", Description = "" )]
[DirectoryExists]
[Required]
public string? RootFolder { get; set; }


/// <summary />
public async Task<int> OnExecute()
{
var dir = new DirectoryInfo( this.RootFolder! );

foreach ( var f in dir.GetFiles( "*.mdx", SearchOption.AllDirectories ) )
{
await FileCheck( dir, f );
}

return 0;
}


/// <summary />
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<int> 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<Diagnostic> 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 );
}
}
}


/// <summary />
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 );
}
}
20 changes: 20 additions & 0 deletions tools/Resend.DocsCheck/Resend.DocsCheck.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Resend\Resend.csproj" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions tools/Resend.DocsCheck/api-check.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dotnet run -- --root=..\..\..\resend-docs\api-reference
1 change: 1 addition & 0 deletions tools/Resend.DocsCheck/api-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dotnet run -- --root=../../../resend-docs/api-reference

0 comments on commit 7a97ba4

Please sign in to comment.