Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update add attachment interface #522

Merged
merged 9 commits into from
Nov 28, 2017
24 changes: 21 additions & 3 deletions USE_CASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ namespace Example
{
private static void Main()
{
Execute().Wait();
ExecuteManualAttachmentAdd().Wait();
ExecuteStreamAttachmentAdd().Wait();
}

static async Task Execute()
static async Task ExecuteManualAttachmentAdd()
{
var apiKey = Environment.GetEnvironmentVariable("NAME_OF_THE_ENVIRONMENT_VARIABLE_FOR_YOUR_SENDGRID_KEY");
var client = new SendGridClient(apiKey);
Expand All @@ -45,6 +46,23 @@ namespace Example
msg.AddAttachment("file.txt", file);
var response = await client.SendEmailAsync(msg);
}

static async Task ExecuteStreamAttachmentAdd()
{
var apiKey = Environment.GetEnvironmentVariable("NAME_OF_THE_ENVIRONMENT_VARIABLE_FOR_YOUR_SENDGRID_KEY");
var client = new SendGridClient(apiKey);
var from = new EmailAddress("[email protected]");
var subject = "Subject";
var to = new EmailAddress("[email protected]");
var body = "Email Body";
var msg = MailHelper.CreateSingleEmail(from, to, subject, body, "");

using (var fileStream = File.OpenRead("/Users/username/file.txt"))
{
msg.AddAttachment("file.txt", fileStream);
var response = await client.SendEmailAsync(msg);
}
}
}
}
```
Expand Down Expand Up @@ -656,4 +674,4 @@ Find more information about all of SendGrid's whitelabeling realated doucmentati

You can find documentation for how to view your email statistics via the UI [here](https://app.sendgrid.com/statistics) and via API [here](https://github.com/sendgrid/sendgrid-csharp/blob/master/USAGE.md#stats).

Alternatively, we can post events to a URL of your choice via our [Event Webhook](https://sendgrid.com/docs/API_Reference/Webhooks/event.html) about events that occur as SendGrid processes your email.
Alternatively, we can post events to a URL of your choice via our [Event Webhook](https://sendgrid.com/docs/API_Reference/Webhooks/event.html) about events that occur as SendGrid processes your email.
79 changes: 56 additions & 23 deletions src/SendGrid/Helpers/Mail/SendGridMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@

namespace SendGrid.Helpers.Mail
{
using Model;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Model;
using Newtonsoft.Json;

/// <summary>
/// Class SendGridMessage builds an object that sends an email through SendGrid.
Expand Down Expand Up @@ -984,57 +987,87 @@ public void AddContents(List<Content> contents)
return;
}

/// <summary>
/// Add an attachment from a stream to the email. No attachment will be added in the case that the stream cannot be read. Streams of length greater than int.MaxValue are truncated.
/// </summary>
/// <param name="filename">The filename the attachment will display in the email.</param>
/// <param name="contentStream">The stream to use as content of the attachment.</param>
/// <param name="type">The mime type of the content you are attaching. For example, application/pdf or image/jpeg.</param>
/// <param name="disposition">The content-disposition of the attachment specifying how you would like the attachment to be displayed. For example, "inline" results in the attached file being displayed automatically within the message while "attachment" results in the attached file requiring some action to be taken before it is displayed (e.g. opening or downloading the file). Defaults to "attachment". Can be either "attachment" or "inline".</param>
/// <param name="content_id">A unique id that you specify for the attachment. This is used when the disposition is set to "inline" and the attachment is an image, allowing the file to be displayed within the body of your email. Ex: <![CDATA[ <img src="cid:ii_139db99fdb5c3704"></img> ]]></param>
/// <param name="cancellationToken">A cancellation token which can notify if the task should be canceled.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task AddAttachmentAsync(string filename, Stream contentStream, string type = null, string disposition = null, string content_id = null, CancellationToken cancellationToken = default(CancellationToken))
{
// Stream doesn't want us to read it, can't do anything else here
if (contentStream == null || !contentStream.CanRead)
{
return;
}

var contentLength = Convert.ToInt32(contentStream.Length);
var streamBytes = new byte[contentLength];

await contentStream.ReadAsync(streamBytes, 0, contentLength, cancellationToken);

var base64Content = Convert.ToBase64String(streamBytes);

this.AddAttachment(filename, base64Content, type, disposition, content_id);
}

/// <summary>
/// Add an attachment to the email.
/// </summary>
/// <param name="filename">The filename of the attachment.</param>
/// <param name="content">The Base64 encoded content of the attachment.</param>
/// <param name="filename">The filename the attachment will display in the email.</param>
/// <param name="base64Content">The Base64 encoded content of the attachment.</param>
/// <param name="type">The mime type of the content you are attaching. For example, application/pdf or image/jpeg.</param>
/// <param name="disposition">The content-disposition of the attachment specifying how you would like the attachment to be displayed. For example, "inline" results in the attached file being displayed automatically within the message while "attachment" results in the attached file requiring some action to be taken before it is displayed (e.g. opening or downloading the file). Defaults to "attachment". Can be either "attachment" or "inline".</param>
/// <param name="content_id">A unique id that you specify for the attachment. This is used when the disposition is set to "inline" and the attachment is an image, allowing the file to be displayed within the body of your email. Ex: <![CDATA[ <img src="cid:ii_139db99fdb5c3704"></img> ]]></param>
public void AddAttachment(string filename, string content, string type = null, string disposition = null, string content_id = null)
public void AddAttachment(string filename, string base64Content, string type = null, string disposition = null, string content_id = null)
{
var attachment = new Attachment()
if (string.IsNullOrWhiteSpace(filename) || string.IsNullOrWhiteSpace(base64Content))
{
return;
}

var attachment = new Attachment
{
Filename = filename,
Content = content,
Content = base64Content,
Type = type,
Disposition = disposition,
ContentId = content_id
};

this.AddAttachment(attachment);
}

/// <summary>
/// Add an attachment to the email.
/// </summary>
/// <param name="attachment">An Attachment.</param>
public void AddAttachment(Attachment attachment)
{
if (this.Attachments == null)
{
this.Attachments = new List<Attachment>()
{
attachment
};
}
else
{
this.Attachments.Add(attachment);
this.Attachments = new List<Attachment>();
}

return;
this.Attachments.Add(attachment);
}

/// <summary>
/// Add attachments to the email.
/// </summary>
/// <param name="attachments">A list of Attachments.</param>
public void AddAttachments(List<Attachment> attachments)
public void AddAttachments(IEnumerable<Attachment> attachments)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like using the IEnumerable here, but I'm concerned about breaking changes by removing the old function signature. How about keeping the old function signature and adding this one as a function overload?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because List inherits from IEnumerable, this shouldn't be a breaking change. Anyone using lists should see things continue to work as though nothing changed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! Thanks for the super quick response :)

{
if (this.Attachments == null)
{
this.Attachments = new List<Attachment>();
this.Attachments = attachments;
}
else
{
this.Attachments.AddRange(attachments);
}

return;
this.Attachments.AddRange(attachments);
}

/// <summary>
Expand Down
43 changes: 43 additions & 0 deletions tests/SendGrid.Tests/Helpers/Mail/NonReadableStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.IO;

namespace SendGrid.Tests.Helpers.Mail
{

public class NonReadableStream : Stream
{
public override bool CanRead => false;

public override bool CanSeek => throw new System.NotImplementedException();

public override bool CanWrite => throw new System.NotImplementedException();

public override long Length => throw new System.NotImplementedException();

public override long Position { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }

public override void Flush()
{
throw new System.NotImplementedException();
}

public override int Read(byte[] buffer, int offset, int count)
{
throw new System.NotImplementedException();
}

public override long Seek(long offset, SeekOrigin origin)
{
throw new System.NotImplementedException();
}

public override void SetLength(long value)
{
throw new System.NotImplementedException();
}

public override void Write(byte[] buffer, int offset, int count)
{
throw new System.NotImplementedException();
}
}
}
Loading