Skip to content

Commit 3755e60

Browse files
Add Receiving and Send Attachment (#89)
1 parent 06a6866 commit 3755e60

11 files changed

Lines changed: 281 additions & 6 deletions

src/Resend/IResend.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,24 @@ public interface IResend
170170
/// <see href="https://www.resend.com/docs/api-reference/emails/cancel-email"/>
171171
Task<ResendResponse> EmailCancelAsync( Guid emailId, CancellationToken cancellationToken = default );
172172

173+
/// <summary>
174+
/// Lists email attachments from a sent email.
175+
/// </summary>
176+
/// <param name="emailId">Sent email identifier.</param>
177+
/// <param name="query">Paginated query.</param>
178+
/// <param name="cancellationToken">Cancellation token.</param>
179+
/// <returns>List of attachments.</returns>
180+
Task<ResendResponse<PaginatedResult<SentEmailAttachment>>> EmailAttachmentListAsync( Guid emailId, PaginatedQuery? query = null, CancellationToken cancellationToken = default );
181+
182+
/// <summary>
183+
/// Retrieves an email attachment of a sent email.
184+
/// </summary>
185+
/// <param name="emailId">Sent email identifier.</param>
186+
/// <param name="attachmentId">Sent email attachment identifier.</param>
187+
/// <param name="cancellationToken">Cancellation token.</param>
188+
/// <returns>Email attachment.</returns>
189+
Task<ResendResponse<SentEmailAttachment>> EmailAttachmentRetrieveAsync( Guid emailId, Guid attachmentId, CancellationToken cancellationToken = default );
190+
173191
#endregion
174192

175193
#region Domains

src/Resend/README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ Functionality
1313

1414
The `ResendClient` supports the following objects (and methods):
1515

16-
* Email (Send, Batch, Retrieve, Reschedule, Cancel)
16+
* Email (Send, Batch, Retrieve, Reschedule, Cancel, Attachment List, Attachment Retrieve)
1717
* Receiving (List, Retrieve, Attachment List, Attachment Retrieve)
1818
* Domain (List, Add, Retrieve, Update, Verify, Delete)
1919
* API key (List, Create, Delete)
20-
* Topics (List, Create, Retrieve, Update, Delete)
21-
* Templates (List, Create, Retrieve, Update, Delete, Publish, Duplicate)
22-
* Contact (List, Add, Retrieve, Update, Delete)
2320
* Broadcast (List, Add, Retrieve, Update, Send, Schedule, Delete)
2421
* Audience (List, Add, Retrieve, Delete)
22+
* Contact (List, Add, Retrieve, Update, Delete)
23+
* Segment (List, Create, Retrieve, Delete)
24+
* Topics (List, Create, Retrieve, Update, Delete)
25+
* Templates (List, Create, Retrieve, Update, Delete, Publish, Duplicate)
2526
* Webhooks (List, Create, Retrieve, Update, Delete)
2627

2728

src/Resend/ResendClient.Receiving.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public partial class ResendClient
77
/// <inheritdoc />
88
public Task<ResendResponse<PaginatedResult<ReceivedEmail>>> ReceivedEmailListAsync( PaginatedQuery? query = null, CancellationToken cancellationToken = default )
99
{
10-
var baseUrl = "/emails/receiving/";
10+
var baseUrl = "/emails/receiving";
1111
var url = baseUrl;
1212

1313
if ( query != null )

src/Resend/ResendClient.Sending.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using Microsoft.AspNetCore.WebUtilities;
2+
3+
namespace Resend;
4+
5+
public partial class ResendClient
6+
{
7+
/// <inheritdoc />
8+
public Task<ResendResponse<PaginatedResult<SentEmailAttachment>>> EmailAttachmentListAsync( Guid emailId, PaginatedQuery? query = null, CancellationToken cancellationToken = default )
9+
{
10+
var baseUrl = $"/emails/{emailId}/attachments";
11+
var url = baseUrl;
12+
13+
if ( query != null )
14+
{
15+
var qs = new Dictionary<string, string?>();
16+
17+
if ( query.Limit.HasValue == true )
18+
qs.Add( "limit", query.Limit.Value.ToString() );
19+
20+
if ( query.Before != null )
21+
qs.Add( "before", query.Before );
22+
23+
if ( query.After != null )
24+
qs.Add( "after", query.After );
25+
26+
url = QueryHelpers.AddQueryString( baseUrl, qs );
27+
}
28+
29+
var req = new HttpRequestMessage( HttpMethod.Get, url );
30+
31+
return Execute<PaginatedResult<SentEmailAttachment>, PaginatedResult<SentEmailAttachment>>( req, ( x ) => x, cancellationToken );
32+
}
33+
34+
35+
/// <inheritdoc />
36+
public Task<ResendResponse<SentEmailAttachment>> EmailAttachmentRetrieveAsync( Guid emailId, Guid attachmentId, CancellationToken cancellationToken = default )
37+
{
38+
var path = $"/emails/{emailId}/attachments/{attachmentId}";
39+
var req = new HttpRequestMessage( HttpMethod.Get, path );
40+
41+
return Execute<SentEmailAttachment, SentEmailAttachment>( req, ( x ) => x, cancellationToken );
42+
}
43+
}

src/Resend/SentEmailAttachment.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace Resend;
4+
5+
/// <summary />
6+
public class SentEmailAttachment
7+
{
8+
/// <summary>
9+
/// Sent email attachment identifier.
10+
/// </summary>
11+
[JsonPropertyName( "id" )]
12+
public Guid Id { get; set; }
13+
14+
/// <summary>
15+
/// Attachment filename.
16+
/// </summary>
17+
[JsonPropertyName( "filename" )]
18+
public string Filename { get; set; } = default!;
19+
20+
/// <summary>
21+
/// Inline content identifier.
22+
/// </summary>
23+
/// <remarks>
24+
/// Value can then be used as reference in HTML body using <code>cid</code>
25+
/// scheme, e.g. <code>&lt;img src="cid:property-value" &gt; /></code>.
26+
/// </remarks>
27+
[JsonPropertyName( "content_id" )]
28+
[JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingNull )]
29+
public string? ContentId { get; set; }
30+
31+
/// <summary>
32+
/// MIME content type.
33+
/// </summary>
34+
[JsonPropertyName( "content_type" )]
35+
public string ContentType { get; set; } = default!;
36+
37+
/// <summary>
38+
/// Content disposition.
39+
/// </summary>
40+
[JsonPropertyName( "content_disposition" )]
41+
[JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingNull )]
42+
public AttachmentContentDisposition? ContentDisposition { get; set; }
43+
44+
/// <summary>
45+
/// Attachment file size.
46+
/// </summary>
47+
[JsonPropertyName( "size" )]
48+
public long? FileSize { get; set; }
49+
50+
/// <summary>
51+
/// URL to download attachment.
52+
/// </summary>
53+
[JsonPropertyName( "download_url" )]
54+
[JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingNull )]
55+
public string? DownloadUrl { get; set; }
56+
57+
/// <summary>
58+
/// Moment in which the email attachment shall expire.
59+
/// </summary>
60+
[JsonPropertyName( "expires_at" )]
61+
[JsonConverter( typeof( JsonUtcDateTimeConverter ) )]
62+
[JsonIgnore( Condition = JsonIgnoreCondition.WhenWritingNull )]
63+
public DateTime? MomentExpiration { get; set; }
64+
}

src/Resend/WebhookEventType.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ public enum WebhookEventType
1919
/// <summary>
2020
/// An email was received by Resend.
2121
/// </summary>
22+
/// <remarks>
23+
/// See https://resend.com/docs/dashboard/receiving/introduction
24+
/// </remarks>
2225
[JsonStringValue( "email.received" )]
2326
EmailReceived,
2427

@@ -62,6 +65,17 @@ public enum WebhookEventType
6265
[JsonStringValue( "email.opened" )]
6366
EmailOpened,
6467

68+
/// <summary>
69+
/// Email failed to be sent due to an error.
70+
/// </summary>
71+
/// <remarks>
72+
/// This event is triggered when there are issues such as invalid
73+
/// recipients, API key problems, domain verification issues,
74+
/// quota limits, or other sending failures.
75+
/// </remarks>
76+
[JsonStringValue( "email.failed" )]
77+
EmailFailed,
78+
6579

6680
/// <summary>
6781
/// A contact was successfully created.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using McMaster.Extensions.CommandLineUtils;
2+
3+
namespace Resend.Cli.Email;
4+
5+
/// <summary />
6+
[Command( "attach", Description = "Attachment of sent emails" )]
7+
[Subcommand( typeof( EmailAttachmentListCommand ) )]
8+
[Subcommand( typeof( EmailAttachmentRetrieveCommand ) )]
9+
public class EmailAttachmentCommand
10+
{
11+
/// <summary />
12+
public int OnExecute( CommandLineApplication app )
13+
{
14+
app.ShowHelp();
15+
return 1;
16+
}
17+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using McMaster.Extensions.CommandLineUtils;
2+
using Spectre.Console;
3+
using System.ComponentModel.DataAnnotations;
4+
using System.Text.Json;
5+
6+
namespace Resend.Cli.Email;
7+
8+
/// <summary />
9+
[Command( "list", Description = "List all attachments in a sent email" )]
10+
public class EmailAttachmentListCommand
11+
{
12+
private readonly IResend _resend;
13+
14+
/// <summary />
15+
[Argument( 0, Description = "Email identifier" )]
16+
[Required]
17+
public Guid? EmailId { get; set; }
18+
19+
/// <summary />
20+
[Option( "-j|--json", CommandOptionType.NoValue, Description = "Emit output as JSON array" )]
21+
public bool InJson { get; set; }
22+
23+
24+
/// <summary />
25+
public EmailAttachmentListCommand( IResend resend )
26+
{
27+
_resend = resend;
28+
}
29+
30+
31+
/// <summary />
32+
public async Task<int> OnExecuteAsync()
33+
{
34+
var res = await _resend.EmailAttachmentListAsync( this.EmailId!.Value );
35+
var attachs = res.Content.Data;
36+
37+
if ( this.InJson == true )
38+
{
39+
var jso = new JsonSerializerOptions() { WriteIndented = true };
40+
var json = JsonSerializer.Serialize( attachs, jso );
41+
42+
Console.WriteLine( json );
43+
}
44+
else
45+
{
46+
var table = new Table();
47+
table.Border = TableBorder.SimpleHeavy;
48+
table.AddColumn( "Attach Id" );
49+
table.AddColumn( "Filename" );
50+
table.AddColumn( "File size" );
51+
table.AddColumn( "Expiration" );
52+
53+
foreach ( var c in attachs )
54+
{
55+
table.AddRow(
56+
new Markup( c.Id.ToString() ),
57+
new Markup( c.Filename ),
58+
new Markup( c.FileSize?.ToString() ?? "" ),
59+
new Markup( c.MomentExpiration?.ToString() ?? "" )
60+
);
61+
}
62+
63+
AnsiConsole.Write( table );
64+
}
65+
66+
return 0;
67+
}
68+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using McMaster.Extensions.CommandLineUtils;
2+
using System.ComponentModel.DataAnnotations;
3+
using System.Text.Json;
4+
5+
namespace Resend.Cli.Email;
6+
7+
/// <summary />
8+
[Command( "get", Description = "Retrieves an attachment from a sent email" )]
9+
public class EmailAttachmentRetrieveCommand
10+
{
11+
private readonly IResend _resend;
12+
13+
14+
/// <summary />
15+
[Argument( 0, Description = "Email identifier" )]
16+
[Required]
17+
public Guid? EmailId { get; set; }
18+
19+
/// <summary />
20+
[Argument( 1, Description = "Attachment identifier" )]
21+
[Required]
22+
public Guid? AttachmentId { get; set; }
23+
24+
25+
/// <summary />
26+
public EmailAttachmentRetrieveCommand( IResend resend )
27+
{
28+
_resend = resend;
29+
}
30+
31+
32+
/// <summary />
33+
public async Task<int> OnExecuteAsync()
34+
{
35+
var res = await _resend.EmailAttachmentRetrieveAsync( this.EmailId!.Value, this.AttachmentId!.Value );
36+
var attach = res.Content;
37+
38+
39+
/*
40+
*
41+
*/
42+
var jso = new JsonSerializerOptions() { WriteIndented = true };
43+
var json = JsonSerializer.Serialize( attach, jso );
44+
45+
Console.WriteLine( json );
46+
47+
return 0;
48+
}
49+
}

tools/Resend.Cli/EmailCommand.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ namespace Resend.Cli;
44

55
/// <summary />
66
[Command( "email", Description = "Send emails" )]
7+
[Subcommand( typeof( Email.EmailAttachmentCommand ) )]
78
[Subcommand( typeof( Email.EmailBatchCommand ) )]
89
[Subcommand( typeof( Email.EmailCancelCommand ) )]
910
[Subcommand( typeof( Email.EmailListCommand ) )]

0 commit comments

Comments
 (0)