From 1e14c05dc8ca386a4eae7cefd5b0956241ac18ce Mon Sep 17 00:00:00 2001 From: Nick George Date: Mon, 10 Apr 2017 14:30:11 -0500 Subject: [PATCH 01/11] Added new solution for targeting against .NET Standard 1.6. Copied over class files. --- createsend-netstandard.sln | 22 + createsend-netstandard/Account.cs | 29 ++ createsend-netstandard/Administrator.cs | 35 ++ .../AuthenticationDetails.cs | 82 ++++ createsend-netstandard/Campaign.cs | 375 ++++++++++++++++++ createsend-netstandard/Client.cs | 196 +++++++++ createsend-netstandard/CreateSendBase.cs | 87 ++++ createsend-netstandard/CreateSendOptions.cs | 43 ++ .../CreateSendOptionsWrapper.cs | 24 ++ .../CustomCreateSendOptions.cs | 24 ++ createsend-netstandard/General.cs | 91 +++++ createsend-netstandard/HttpHelper.cs | 306 ++++++++++++++ createsend-netstandard/ICreateSendOptions.cs | 11 + createsend-netstandard/List.cs | 282 +++++++++++++ .../Models/Administrator.cs | 17 + .../Models/BillingDetails.cs | 11 + createsend-netstandard/Models/Campaign.cs | 110 +++++ createsend-netstandard/Models/Client.cs | 82 ++++ .../Models/CreatesendException.cs | 18 + createsend-netstandard/Models/EmailClient.cs | 14 + .../Models/ExternalSession.cs | 16 + createsend-netstandard/Models/List.cs | 90 +++++ .../Models/OAuthTokenDetails.cs | 13 + .../Models/PagedCollection.cs | 19 + createsend-netstandard/Models/Person.cs | 22 + createsend-netstandard/Models/Result.cs | 59 +++ createsend-netstandard/Models/Segment.cs | 55 +++ createsend-netstandard/Models/Subscriber.cs | 52 +++ .../Models/SubscriberHistory.cs | 22 + createsend-netstandard/Models/Template.cs | 14 + .../Models/TemplateContent.cs | 34 ++ createsend-netstandard/Models/Webhook.cs | 23 ++ createsend-netstandard/MonthlyScheme.cs | 9 + createsend-netstandard/Person.cs | 42 ++ .../Properties/AssemblyInfo.cs | 36 ++ createsend-netstandard/Segment.cs | 113 ++++++ createsend-netstandard/Subscriber.cs | 148 +++++++ createsend-netstandard/Template.cs | 52 +++ .../Transactional/Attachment.cs | 10 + .../Transactional/AttachmentMetadata.cs | 13 + .../Transactional/Authenticate.cs | 118 ++++++ .../Transactional/ClassicEmail.cs | 100 +++++ .../Transactional/ClassicEmailDetail.cs | 10 + .../Transactional/DisplayedTimeZone.cs | 9 + .../Transactional/EmailAddress.cs | 43 ++ .../Transactional/EmailAddressConverter.cs | 35 ++ .../Transactional/Geolocation.cs | 13 + createsend-netstandard/Transactional/Image.cs | 10 + .../Transactional/MailClient.cs | 9 + .../Transactional/MessageBody.cs | 9 + .../Transactional/MessageBuilder.cs | 274 +++++++++++++ .../Transactional/MessageContent.cs | 18 + .../Transactional/MessageDetail.cs | 24 ++ .../Transactional/MessageListDetail.cs | 18 + .../Transactional/MessageListStatus.cs | 11 + .../Transactional/Messages.cs | 87 ++++ .../Transactional/PropertyContent.cs | 11 + .../Transactional/RateLimitStatus.cs | 10 + .../Transactional/RateLimited`1.cs | 15 + .../Transactional/RecipientStatus.cs | 11 + .../RequiredClientIdentifierException.cs | 40 ++ .../Transactional/SmartEmail.cs | 95 +++++ .../Transactional/SmartEmailDetail.cs | 13 + .../Transactional/SmartEmailListDetail.cs | 12 + .../Transactional/SmartEmailListStatus.cs | 10 + .../Transactional/SmartEmailProperties.cs | 13 + .../Transactional/Statistics.cs | 65 +++ .../Transactional/StatisticsQuery.cs | 13 + .../Transactional/SubscriberAction.cs | 13 + .../Transactional/Transactional.cs | 86 ++++ .../createsend-dotnet.nuspec | 19 + .../createsend-netstandard.csproj | 11 + 72 files changed, 3926 insertions(+) create mode 100644 createsend-netstandard.sln create mode 100644 createsend-netstandard/Account.cs create mode 100644 createsend-netstandard/Administrator.cs create mode 100644 createsend-netstandard/AuthenticationDetails.cs create mode 100644 createsend-netstandard/Campaign.cs create mode 100644 createsend-netstandard/Client.cs create mode 100644 createsend-netstandard/CreateSendBase.cs create mode 100644 createsend-netstandard/CreateSendOptions.cs create mode 100644 createsend-netstandard/CreateSendOptionsWrapper.cs create mode 100644 createsend-netstandard/CustomCreateSendOptions.cs create mode 100644 createsend-netstandard/General.cs create mode 100644 createsend-netstandard/HttpHelper.cs create mode 100644 createsend-netstandard/ICreateSendOptions.cs create mode 100644 createsend-netstandard/List.cs create mode 100644 createsend-netstandard/Models/Administrator.cs create mode 100644 createsend-netstandard/Models/BillingDetails.cs create mode 100644 createsend-netstandard/Models/Campaign.cs create mode 100644 createsend-netstandard/Models/Client.cs create mode 100644 createsend-netstandard/Models/CreatesendException.cs create mode 100644 createsend-netstandard/Models/EmailClient.cs create mode 100644 createsend-netstandard/Models/ExternalSession.cs create mode 100644 createsend-netstandard/Models/List.cs create mode 100644 createsend-netstandard/Models/OAuthTokenDetails.cs create mode 100644 createsend-netstandard/Models/PagedCollection.cs create mode 100644 createsend-netstandard/Models/Person.cs create mode 100644 createsend-netstandard/Models/Result.cs create mode 100644 createsend-netstandard/Models/Segment.cs create mode 100644 createsend-netstandard/Models/Subscriber.cs create mode 100644 createsend-netstandard/Models/SubscriberHistory.cs create mode 100644 createsend-netstandard/Models/Template.cs create mode 100644 createsend-netstandard/Models/TemplateContent.cs create mode 100644 createsend-netstandard/Models/Webhook.cs create mode 100644 createsend-netstandard/MonthlyScheme.cs create mode 100644 createsend-netstandard/Person.cs create mode 100644 createsend-netstandard/Properties/AssemblyInfo.cs create mode 100644 createsend-netstandard/Segment.cs create mode 100644 createsend-netstandard/Subscriber.cs create mode 100644 createsend-netstandard/Template.cs create mode 100644 createsend-netstandard/Transactional/Attachment.cs create mode 100644 createsend-netstandard/Transactional/AttachmentMetadata.cs create mode 100644 createsend-netstandard/Transactional/Authenticate.cs create mode 100644 createsend-netstandard/Transactional/ClassicEmail.cs create mode 100644 createsend-netstandard/Transactional/ClassicEmailDetail.cs create mode 100644 createsend-netstandard/Transactional/DisplayedTimeZone.cs create mode 100644 createsend-netstandard/Transactional/EmailAddress.cs create mode 100644 createsend-netstandard/Transactional/EmailAddressConverter.cs create mode 100644 createsend-netstandard/Transactional/Geolocation.cs create mode 100644 createsend-netstandard/Transactional/Image.cs create mode 100644 createsend-netstandard/Transactional/MailClient.cs create mode 100644 createsend-netstandard/Transactional/MessageBody.cs create mode 100644 createsend-netstandard/Transactional/MessageBuilder.cs create mode 100644 createsend-netstandard/Transactional/MessageContent.cs create mode 100644 createsend-netstandard/Transactional/MessageDetail.cs create mode 100644 createsend-netstandard/Transactional/MessageListDetail.cs create mode 100644 createsend-netstandard/Transactional/MessageListStatus.cs create mode 100644 createsend-netstandard/Transactional/Messages.cs create mode 100644 createsend-netstandard/Transactional/PropertyContent.cs create mode 100644 createsend-netstandard/Transactional/RateLimitStatus.cs create mode 100644 createsend-netstandard/Transactional/RateLimited`1.cs create mode 100644 createsend-netstandard/Transactional/RecipientStatus.cs create mode 100644 createsend-netstandard/Transactional/RequiredClientIdentifierException.cs create mode 100644 createsend-netstandard/Transactional/SmartEmail.cs create mode 100644 createsend-netstandard/Transactional/SmartEmailDetail.cs create mode 100644 createsend-netstandard/Transactional/SmartEmailListDetail.cs create mode 100644 createsend-netstandard/Transactional/SmartEmailListStatus.cs create mode 100644 createsend-netstandard/Transactional/SmartEmailProperties.cs create mode 100644 createsend-netstandard/Transactional/Statistics.cs create mode 100644 createsend-netstandard/Transactional/StatisticsQuery.cs create mode 100644 createsend-netstandard/Transactional/SubscriberAction.cs create mode 100644 createsend-netstandard/Transactional/Transactional.cs create mode 100644 createsend-netstandard/createsend-dotnet.nuspec create mode 100644 createsend-netstandard/createsend-netstandard.csproj diff --git a/createsend-netstandard.sln b/createsend-netstandard.sln new file mode 100644 index 0000000..8587382 --- /dev/null +++ b/createsend-netstandard.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26403.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "createsend-netstandard", "createsend-netstandard\createsend-netstandard.csproj", "{74932B63-D31D-4682-BE98-5F96D0B91E04}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {74932B63-D31D-4682-BE98-5F96D0B91E04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {74932B63-D31D-4682-BE98-5F96D0B91E04}.Debug|Any CPU.Build.0 = Debug|Any CPU + {74932B63-D31D-4682-BE98-5F96D0B91E04}.Release|Any CPU.ActiveCfg = Release|Any CPU + {74932B63-D31D-4682-BE98-5F96D0B91E04}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/createsend-netstandard/Account.cs b/createsend-netstandard/Account.cs new file mode 100644 index 0000000..4b8fae2 --- /dev/null +++ b/createsend-netstandard/Account.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace createsend_dotnet +{ + public class Account : CreateSendBase + { + public Account(AuthenticationDetails auth) : base(auth) { } + + public string GetPrimaryContact() + { + return HttpGet("/primarycontact.json", null) + .EmailAddress; + } + + public string SetPrimaryContact(string emailAddress) + { + return HttpPut("/primarycontact.json", + new NameValueCollection { { "email", emailAddress } }, null) + .EmailAddress; + } + + public IEnumerable Administrators() + { + return HttpGet>( + "/admins.json", null); + } + } +} diff --git a/createsend-netstandard/Administrator.cs b/createsend-netstandard/Administrator.cs new file mode 100644 index 0000000..cd41622 --- /dev/null +++ b/createsend-netstandard/Administrator.cs @@ -0,0 +1,35 @@ +using System.Collections.Specialized; + +namespace createsend_dotnet +{ + public class Administrator : CreateSendBase + { + private string AdminsUrl { get { return string.Format("/admins.json"); } } + + public Administrator(AuthenticationDetails auth) : base(auth) { } + + public AdministratorDetails Details(string emailAddress) + { + return HttpGet( + AdminsUrl, new NameValueCollection {{"email", emailAddress}}); + } + + public string Add(AdministratorDetails admin) + { + return HttpPost( + AdminsUrl, null, admin).EmailAddress; + } + + public string Update(string emailAddress, AdministratorDetails admin) + { + return HttpPut( + AdminsUrl, new NameValueCollection {{ "email", emailAddress }}, + admin).EmailAddress; + } + + public void Delete(string emailAddress) + { + HttpDelete(AdminsUrl, new NameValueCollection {{"email", emailAddress}}); + } + } +} diff --git a/createsend-netstandard/AuthenticationDetails.cs b/createsend-netstandard/AuthenticationDetails.cs new file mode 100644 index 0000000..7639c01 --- /dev/null +++ b/createsend-netstandard/AuthenticationDetails.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public abstract class AuthenticationDetails { } + + public class OAuthAuthenticationDetails : AuthenticationDetails + { + public string AccessToken { get; set; } + public string RefreshToken { get; set; } + + public OAuthAuthenticationDetails( + string accessToken, + string refreshToken) + { + AccessToken = accessToken; + RefreshToken = refreshToken; + } + } + + public class ApiKeyAuthenticationDetails : AuthenticationDetails + { + public string ApiKey { get; set; } + + public ApiKeyAuthenticationDetails(string apiKey) + { + ApiKey = apiKey; + } + } + + internal sealed class ClientApiKey : ApiKeyAuthenticationDetails + { + public ClientApiKey(string apiKey) : base(apiKey) + { + + } + } + + internal sealed class AccountApiKey : ApiKeyAuthenticationDetails, IProvideClientId + { + public string ClientId { get; private set; } + public AccountApiKey(string apiKey, string clientId = null) : base(apiKey) + { + ClientId = clientId; + } + } + + internal sealed class OAuthWithClientId : OAuthAuthenticationDetails, IProvideClientId + { + public string ClientId { get; private set; } + public OAuthWithClientId( + string accessToken, + string refreshToken, + string clientId) : base(accessToken, refreshToken) + { + if(clientId == null) throw new ArgumentNullException("clientId"); + + ClientId = clientId; + } + } + + public interface IProvideClientId + { + string ClientId { get; } + } + + public class BasicAuthAuthenticationDetails : AuthenticationDetails + { + public string Username { get; set; } + public string Password { get; set; } + + public BasicAuthAuthenticationDetails( + string username, + string password) + { + Username = username; + Password = password; + } + } +} \ No newline at end of file diff --git a/createsend-netstandard/Campaign.cs b/createsend-netstandard/Campaign.cs new file mode 100644 index 0000000..88d2380 --- /dev/null +++ b/createsend-netstandard/Campaign.cs @@ -0,0 +1,375 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; + +namespace createsend_dotnet +{ + public class Campaign : CreateSendBase + { + public string CampaignID { get; set; } + + public Campaign(AuthenticationDetails auth, string campaignID) + : base(auth) + { + CampaignID = campaignID; + } + + /// + /// Creates a campaign using the API key and campaign details provided. + /// + /// API key to use + /// Client ID of the client for whom the + /// campaign should be created + /// A subject for the campaign + /// A name for the campaign + /// From name for the campaign + /// From email address for the campaign + /// Reply-to address for the campaign + /// URL for the HTML content for the + /// campaign + /// URL for the text content for the campaign. + /// Note that you may provide textUrl as null or an empty string and + /// the text content for the campaign will be generated from the HTML + /// content. + /// IDs of the lists to which the campaign + /// will be sent + /// IDs of the segments to which the + /// campaign will be sent + /// The ID of the newly created campaign + public static string Create(AuthenticationDetails auth, string clientID, + string subject, string name, string fromName, string fromEmail, + string replyTo, string htmlUrl, string textUrl, List + listIDs, List segmentIDs) + { + return HttpHelper.Post, string>( + auth, string.Format("/campaigns/{0}.json", clientID), null, + new Dictionary() + { + { "Subject", subject }, + { "Name", name }, + { "FromName", fromName}, + { "FromEmail", fromEmail }, + { "ReplyTo", replyTo }, + { "HtmlUrl", htmlUrl }, + { "TextUrl", textUrl }, + { "ListIDs", listIDs }, + { "SegmentIDs", segmentIDs } + }); + } + + public static string CreateFromTemplate(AuthenticationDetails auth, string clientID, + string subject, string name, string fromName, string fromEmail, + string replyTo, List listIDs, List segmentIDs, + string templateID, TemplateContent templateContent) + { + return HttpHelper.Post, string>( + auth, string.Format("/campaigns/{0}/fromtemplate.json", clientID), null, + new Dictionary() + { + { "Subject", subject }, + { "Name", name }, + { "FromName", fromName}, + { "FromEmail", fromEmail }, + { "ReplyTo", replyTo }, + { "ListIDs", listIDs }, + { "SegmentIDs", segmentIDs }, + { "TemplateID", templateID }, + { "TemplateContent", templateContent } + }); + } + + public void SendPreview(List recipients, string personalize) + { + HttpPost, string>( + string.Format("/campaigns/{0}/sendpreview.json", CampaignID), + null, + new Dictionary() + { + { "PreviewRecipients", recipients}, + { "Personalize", personalize} + }); + } + + public void Send(string confirmationEmail) + { + Send(confirmationEmail, "immediately"); + } + + public void Send(string confirmationEmail, DateTime sendDate) + { + Send(confirmationEmail, sendDate.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + } + + private void Send(string confirmationEmail, string sendDate) + { + HttpPost, string>( + string.Format("/campaigns/{0}/send.json", CampaignID), + null, + new Dictionary() + { + { "ConfirmationEmail", confirmationEmail }, + { "SendDate", sendDate } + }); + } + + public void Unschedule() + { + HttpPost, string>( + string.Format("/campaigns/{0}/unschedule.json", CampaignID), + null, null); + } + + public void Delete() + { + HttpDelete(string.Format("/campaigns/{0}.json", CampaignID), null); + } + + public CampaignSummary Summary() + { + return HttpGet( + string.Format("/campaigns/{0}/summary.json", CampaignID), null); + } + + public IEnumerable EmailClientUsage() + { + return HttpGet>( + string.Format("/campaigns/{0}/emailclientusage.json", + CampaignID), null); + } + + public CampaignListsAndSegments ListsAndSegments() + { + return HttpGet( + string.Format("/campaigns/{0}/listsandsegments.json", CampaignID), null); + } + + public PagedCollection Recipients( + int page, int pageSize, string orderField, string orderDirection) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("page", page.ToString()); + queryArguments.Add("pagesize", pageSize.ToString()); + queryArguments.Add("orderfield", orderField); + queryArguments.Add("orderdirection", orderDirection); + + return HttpGet>( + string.Format("/campaigns/{0}/recipients.json", CampaignID), queryArguments); + } + + public PagedCollection Opens() + { + return Opens(1, 1000, "date", "asc"); + } + + public PagedCollection Opens( + int page, + int pageSize, + string orderField, + string orderDirection) + { + return Opens("", page, pageSize, orderField, orderDirection); + } + + public PagedCollection Opens( + DateTime fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + return Opens(fromDate.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), page, + pageSize, orderField, orderDirection); + } + + private PagedCollection Opens( + string fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("date", fromDate); + queryArguments.Add("page", page.ToString()); + queryArguments.Add("pagesize", pageSize.ToString()); + queryArguments.Add("orderfield", orderField); + queryArguments.Add("orderdirection", orderDirection); + + return HttpGet>( + string.Format("/campaigns/{0}/opens.json", CampaignID), queryArguments); + } + + public PagedCollection Unsubscribes() + { + return Unsubscribes(1, 1000, "date", "asc"); + } + + public PagedCollection Unsubscribes( + int page, + int pageSize, + string orderField, + string orderDirection) + { + return Unsubscribes("", page, pageSize, orderField, orderDirection); + } + + public PagedCollection Unsubscribes( + DateTime fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + return Unsubscribes(fromDate.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), + page, pageSize, orderField, orderDirection); + } + + private PagedCollection Unsubscribes( + string fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("date", fromDate); + queryArguments.Add("page", page.ToString()); + queryArguments.Add("pagesize", pageSize.ToString()); + queryArguments.Add("orderfield", orderField); + queryArguments.Add("orderdirection", orderDirection); + + return HttpGet>( + string.Format("/campaigns/{0}/unsubscribes.json", CampaignID), queryArguments); + } + + public PagedCollection SpamComplaints() + { + return SpamComplaints(1, 1000, "date", "asc"); + } + + public PagedCollection SpamComplaints( + int page, + int pageSize, + string orderField, + string orderDirection) + { + return SpamComplaints("", page, pageSize, orderField, orderDirection); + } + + public PagedCollection SpamComplaints( + DateTime fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + return SpamComplaints(fromDate.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), + page, pageSize, orderField, orderDirection); + } + + private PagedCollection SpamComplaints( + string fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("date", fromDate); + queryArguments.Add("page", page.ToString()); + queryArguments.Add("pagesize", pageSize.ToString()); + queryArguments.Add("orderfield", orderField); + queryArguments.Add("orderdirection", orderDirection); + + return HttpGet>( + string.Format("/campaigns/{0}/spam.json", CampaignID), queryArguments); + } + + public PagedCollection Clicks() + { + return Clicks(1, 1000, "date", "asc"); + } + + public PagedCollection Clicks( + int page, + int pageSize, + string orderField, + string orderDirection) + { + return Clicks("", page, pageSize, orderField, orderDirection); + } + + public PagedCollection Clicks( + DateTime fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + return Clicks(fromDate.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), + page, pageSize, orderField, orderDirection); + } + + private PagedCollection Clicks( + string fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("date", fromDate); + queryArguments.Add("page", page.ToString()); + queryArguments.Add("pagesize", pageSize.ToString()); + queryArguments.Add("orderfield", orderField); + queryArguments.Add("orderdirection", orderDirection); + + return HttpGet>( + string.Format("/campaigns/{0}/clicks.json", CampaignID), queryArguments); + } + + public PagedCollection Bounces() + { + return Bounces(1, 1000, "date", "asc"); + } + + public PagedCollection Bounces( + int page, + int pageSize, + string orderField, + string orderDirection) + { + return Bounces("", page, pageSize, orderField, orderDirection); + } + + public PagedCollection Bounces( + DateTime fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + return Bounces(fromDate.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), + page, pageSize, orderField, orderDirection); + } + + private PagedCollection Bounces( + string fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("date", fromDate); + queryArguments.Add("page", page.ToString()); + queryArguments.Add("pagesize", pageSize.ToString()); + queryArguments.Add("orderfield", orderField); + queryArguments.Add("orderdirection", orderDirection); + + return HttpGet>( + string.Format("/campaigns/{0}/bounces.json", CampaignID), queryArguments); + } + } +} diff --git a/createsend-netstandard/Client.cs b/createsend-netstandard/Client.cs new file mode 100644 index 0000000..dbeeb3b --- /dev/null +++ b/createsend-netstandard/Client.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace createsend_dotnet +{ + public class Client : CreateSendBase + { + public string ClientID { get; set; } + + public Client(AuthenticationDetails auth, string clientID) + : base(auth) + { + ClientID = clientID; + } + + public static string Create(AuthenticationDetails auth, string companyName, string country, string timezone) + { + return HttpHelper.Post( + auth, "/clients.json", null, + new ClientDetail() + { + CompanyName = companyName, + Country = country, + TimeZone = timezone + }); + } + + public ClientWithSettings Details() + { + return HttpGet(string.Format("/clients/{0}.json", ClientID), null); + } + + public IEnumerable Campaigns() + { + return HttpGet(string.Format("/clients/{0}/campaigns.json", ClientID), null); + } + + public IEnumerable Scheduled() + { + return HttpGet(string.Format("/clients/{0}/scheduled.json", ClientID), null); + } + + public IEnumerable Drafts() + { + return HttpGet(string.Format("/clients/{0}/drafts.json", ClientID), null); + } + + public IEnumerable Lists() + { + return HttpGet(string.Format("/clients/{0}/lists.json", ClientID), null); + } + + public IEnumerable ListsForEmail(string email) + { + NameValueCollection args = new NameValueCollection(); + args.Add("email", email); + return HttpGet(string.Format("/clients/{0}/listsforemail.json", ClientID), args); + } + + public IEnumerable Segments() + { + return HttpGet(string.Format("/clients/{0}/segments.json", ClientID), null); + } + + public PagedCollection SuppressionList(int page, int pageSize, string orderField, string orderDirection) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("page", page.ToString()); + queryArguments.Add("pagesize", pageSize.ToString()); + queryArguments.Add("orderfield", orderField); + queryArguments.Add("orderdirection", orderDirection); + + return HttpGet>(string.Format("/clients/{0}/suppressionlist.json", ClientID), queryArguments); + } + + public void Suppress(string[] emails) + { + HttpPost( + string.Format("/clients/{0}/suppress.json", ClientID), null, + new SuppressionDetails { EmailAddresses = emails }); + } + + public void Unsuppress(string email) + { + HttpPut( + string.Format("/clients/{0}/unsuppress.json", ClientID), + new NameValueCollection { { "email", email } }, null); + } + + public IEnumerable Templates() + { + return HttpGet(string.Format("/clients/{0}/templates.json", ClientID), null); + } + + public void SetBasics(string companyName, string country, string timezone) + { + HttpPut( + string.Format("/clients/{0}/setbasics.json", ClientID), null, + new ClientDetail() + { + CompanyName = companyName, + Country = country, + TimeZone = timezone + }); + } + + public void SetPAYGBilling(string currency, bool clientPays, bool canPurchaseCredits, int markupPercentage, decimal markupOnDelivery, decimal markupPerRecipient, decimal markupOnDesignSpamTest) + { + HttpPut( + string.Format("/clients/{0}/setpaygbilling.json", ClientID), null, + new BillingOptions() + { + Currency = currency, + ClientPays = clientPays, + CanPurchaseCredits = canPurchaseCredits, + MarkupPercentage = markupPercentage, + MarkupOnDelivery = markupOnDelivery, + MarkupPerRecipient = markupPerRecipient, + MarkupOnDesignSpamTest = markupOnDesignSpamTest + }); + } + + public void SetMonthlyBilling(string currency, bool clientPays, bool canPurchaseCredits, int markupPercentage) + { + SetMonthlyBilling(currency, clientPays, canPurchaseCredits, markupPercentage, null); + } + + public void SetMonthlyBilling(string currency, bool clientPays, bool canPurchaseCredits, int markupPercentage, MonthlyScheme scheme) + { + SetMonthlyBilling(currency, clientPays, canPurchaseCredits, markupPercentage, (MonthlyScheme?)scheme); + } + + private void SetMonthlyBilling(string currency, bool clientPays, bool canPurchaseCredits, int markupPercentage, MonthlyScheme? scheme) + { + HttpPut( + string.Format("/clients/{0}/setmonthlybilling.json", ClientID), null, + new BillingOptions() + { + Currency = currency, + ClientPays = clientPays, + CanPurchaseCredits = canPurchaseCredits, + MarkupPercentage = markupPercentage, + MonthlyScheme = scheme + }); + } + + /// + /// Transfer credits to or from this client. + /// + /// The number of credits to transfer. This + /// value may be either positive if you want to allocate credits + /// from your account to the client, or negative if you want to + /// deduct credits from the client back into your account. + /// If set to true, will + /// allow the client to continue sending using your credits or payment + /// details once they run out of credits, and if set to false, will + /// prevent the client from using your credits to continue sending + /// until you allocate more credits to them. + /// The details of the credits transfer, including the credits + /// in your account now, as well as the credits belonging to the client + /// now. + public CreditsTransferResult TransferCredits( + int credits, bool canUseMyCreditsWhenTheyRunOut) + { + return HttpPost( + string.Format("/clients/{0}/credits.json", ClientID), null, + new CreditsTransferDetails + { + Credits = credits, + CanUseMyCreditsWhenTheyRunOut = + canUseMyCreditsWhenTheyRunOut + }); + } + + public void Delete() + { + HttpDelete(string.Format("/clients/{0}.json", ClientID), null); + } + + public string GetPrimaryContact() + { + return HttpGet(string.Format("/clients/{0}/primarycontact.json", ClientID), null).EmailAddress; + } + + public string SetPrimaryContact(string emailAddress) + { + return HttpPut(string.Format("/clients/{0}/primarycontact.json", ClientID), new NameValueCollection { { "email", emailAddress } }, null).EmailAddress; + } + + public IEnumerable People() + { + return HttpGet>(string.Format("/clients/{0}/people.json", ClientID), null); + } + } +} diff --git a/createsend-netstandard/CreateSendBase.cs b/createsend-netstandard/CreateSendBase.cs new file mode 100644 index 0000000..f0d33d5 --- /dev/null +++ b/createsend-netstandard/CreateSendBase.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Text; +using System.Web; + +namespace createsend_dotnet +{ + public abstract class CreateSendBase + { + public AuthenticationDetails AuthDetails { get; set; } + private readonly ICreateSendOptions options; + + public CreateSendBase(AuthenticationDetails auth, ICreateSendOptions options = null) + { + this.options = options ?? new CreateSendOptionsWrapper(); + + Authenticate(auth); + } + + public void Authenticate(AuthenticationDetails auth) + { + AuthDetails = auth; + } + + public OAuthTokenDetails RefreshToken() + { + if (AuthDetails == null || + !(AuthDetails is OAuthAuthenticationDetails) || + string.IsNullOrEmpty( + (AuthDetails as OAuthAuthenticationDetails) + .RefreshToken)) + throw new InvalidOperationException( + "You cannot refresh an OAuth token when you don't have a refresh token."); + + string refreshToken = (this.AuthDetails as OAuthAuthenticationDetails) + .RefreshToken; + string body = string.Format( + "grant_type=refresh_token&refresh_token={0}", + HttpUtility.UrlEncode(refreshToken)); + + OAuthTokenDetails newTokenDetails = + HttpHelper.Post( + null, "/token", new NameValueCollection(), body, + options.BaseOAuthUri, + HttpHelper.APPLICATION_FORM_URLENCODED_CONTENT_TYPE); + Authenticate( + new OAuthAuthenticationDetails( + newTokenDetails.access_token, newTokenDetails.refresh_token)); + return newTokenDetails; + } + + public U HttpGet(string path, NameValueCollection queryArguments) + { + return HttpGet(path, queryArguments); + } + + public U HttpGet(string path, NameValueCollection queryArguments) + where EX : ErrorResult + { + return HttpHelper.Get(AuthDetails, options.BaseUri, path, queryArguments); + } + + public U HttpPost(string path, NameValueCollection queryArguments, T payload) + where T : class + { + return HttpPost(path, queryArguments, payload); + } + + public U HttpPost(string path, NameValueCollection queryArguments, T payload) + where T : class + where EX : ErrorResult + { + return HttpHelper.Post(AuthDetails, path, queryArguments, payload, options.BaseUri, HttpHelper.APPLICATION_JSON_CONTENT_TYPE); + } + + public U HttpPut(string path, NameValueCollection queryArguments, T payload) where T : class + { + return HttpHelper.Put(AuthDetails, path, queryArguments, payload, options.BaseUri, HttpHelper.APPLICATION_JSON_CONTENT_TYPE); + } + + public string HttpDelete(string path, NameValueCollection queryArguments) + { + return HttpHelper.Delete(AuthDetails, path, queryArguments, options.BaseUri, HttpHelper.APPLICATION_JSON_CONTENT_TYPE); + } + } +} diff --git a/createsend-netstandard/CreateSendOptions.cs b/createsend-netstandard/CreateSendOptions.cs new file mode 100644 index 0000000..0606a60 --- /dev/null +++ b/createsend-netstandard/CreateSendOptions.cs @@ -0,0 +1,43 @@ +using System; +using System.Configuration; + +namespace createsend_dotnet +{ + public static class CreateSendOptions + { + static string base_uri; + static string base_oauth_uri; + + static CreateSendOptions() + { + base_uri = string.IsNullOrEmpty( + ConfigurationManager.AppSettings["base_uri"]) ? + "https://api.createsend.com/api/v3.1" : + ConfigurationManager.AppSettings["base_uri"]; + base_oauth_uri = string.IsNullOrEmpty( + ConfigurationManager.AppSettings["base_oauth_uri"]) ? + "https://api.createsend.com/oauth" : + ConfigurationManager.AppSettings["base_oauth_uri"]; + } + + public static string BaseUri + { + get { return base_uri; } + set { base_uri = value; } + } + + public static string BaseOAuthUri + { + get { return base_oauth_uri; } + set { base_oauth_uri = value; } + } + + public static string VersionNumber + { + get + { + return "4.2.2"; + } + } + } +} diff --git a/createsend-netstandard/CreateSendOptionsWrapper.cs b/createsend-netstandard/CreateSendOptionsWrapper.cs new file mode 100644 index 0000000..7aad761 --- /dev/null +++ b/createsend-netstandard/CreateSendOptionsWrapper.cs @@ -0,0 +1,24 @@ +using System; + +namespace createsend_dotnet +{ + public class CreateSendOptionsWrapper : ICreateSendOptions + { + public string BaseUri + { + get { return CreateSendOptions.BaseUri; } + set { CreateSendOptions.BaseUri = value; } + } + + public string BaseOAuthUri + { + get { return CreateSendOptions.BaseOAuthUri; } + set { CreateSendOptions.BaseOAuthUri = value; } + } + + public string VersionNumber + { + get { return CreateSendOptions.VersionNumber; } + } + } +} diff --git a/createsend-netstandard/CustomCreateSendOptions.cs b/createsend-netstandard/CustomCreateSendOptions.cs new file mode 100644 index 0000000..e5af113 --- /dev/null +++ b/createsend-netstandard/CustomCreateSendOptions.cs @@ -0,0 +1,24 @@ +using System; + +namespace createsend_dotnet +{ + public class CustomCreateSendOptions : ICreateSendOptions + { + public string BaseUri + { + get; + set; + } + + public string BaseOAuthUri + { + get; + set; + } + + public string VersionNumber + { + get { return CreateSendOptions.VersionNumber; } + } + } +} \ No newline at end of file diff --git a/createsend-netstandard/General.cs b/createsend-netstandard/General.cs new file mode 100644 index 0000000..5ba16ab --- /dev/null +++ b/createsend-netstandard/General.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Web; + +namespace createsend_dotnet +{ + public class General : CreateSendBase + { + public General() : base(null) { } + public General(AuthenticationDetails auth) : base(auth) { } + + public static string AuthorizeUrl( + int clientID, + string redirectUri, + string scope) + { + return AuthorizeUrl( + clientID, + redirectUri, + scope, + null); + } + + public static string AuthorizeUrl( + int clientID, + string redirectUri, + string scope, + string state) + { + string result = CreateSendOptions.BaseOAuthUri; + result += string.Format( + "?client_id={0}&redirect_uri={1}&scope={2}", + clientID.ToString(), HttpUtility.UrlEncode(redirectUri), + HttpUtility.UrlEncode(scope)); + if (!string.IsNullOrEmpty(state)) + result += "&state=" + HttpUtility.UrlEncode(state); + return result; + } + + public static OAuthTokenDetails ExchangeToken( + int clientID, + string clientSecret, + string redirectUri, + string code) + { + string body = "grant_type=authorization_code"; + body += string.Format("&client_id={0}", clientID.ToString()); + body += string.Format("&client_secret={0}", HttpUtility.UrlEncode(clientSecret)); + body += string.Format("&redirect_uri={0}", HttpUtility.UrlEncode(redirectUri)); + body += string.Format("&code={0}", HttpUtility.UrlEncode(code)); + + return HttpHelper.Post( + null, "/token", new NameValueCollection(), body, + CreateSendOptions.BaseOAuthUri, + HttpHelper.APPLICATION_FORM_URLENCODED_CONTENT_TYPE); + } + + public DateTime SystemDate() + { + return HttpGet("/systemdate.json", null).SystemDate; + } + + public IEnumerable Countries(string apiKey) + { + return HttpGet("/countries.json", null); + } + + public IEnumerable Timezones() + { + return HttpGet("/timezones.json", null); + } + + public IEnumerable Clients() + { + return HttpGet("/clients.json", null); + } + + public BillingDetails BillingDetails() + { + return HttpGet("/billingdetails.json", null); + } + + public string ExternalSessionUrl( + ExternalSessionOptions options) + { + return HttpPut( + "/externalsession.json", null, options).SessionUrl; + } + } +} diff --git a/createsend-netstandard/HttpHelper.cs b/createsend-netstandard/HttpHelper.cs new file mode 100644 index 0000000..a642ab3 --- /dev/null +++ b/createsend-netstandard/HttpHelper.cs @@ -0,0 +1,306 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Net; +using System.Collections.Specialized; +using System.Web; +using System.Reflection; +#if SUPPORTED_FRAMEWORK_VERSION +using createsend_dotnet.Transactional; +#endif +using Newtonsoft.Json; + +namespace createsend_dotnet +{ + public static class HttpHelper + { + public const string APPLICATION_JSON_CONTENT_TYPE = "application/json"; + public const string APPLICATION_FORM_URLENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded"; + + public static U Get( + AuthenticationDetails auth, + string path, + NameValueCollection queryArguments) + { + return Get(auth, path, queryArguments); + } + + public static U Get( + AuthenticationDetails auth, + string path, + NameValueCollection queryArguments) + where EX : ErrorResult + { + return MakeRequest("GET", auth, path, queryArguments, null); + } + + public static U Get( + AuthenticationDetails auth, + string baseUri, + string path, + NameValueCollection queryArguments) + where EX : ErrorResult + { + return MakeRequest("GET", auth, path, queryArguments, null, baseUri, APPLICATION_JSON_CONTENT_TYPE); + } + + public static U Post( + AuthenticationDetails auth, + string path, + NameValueCollection queryArguments, + T payload) + where T : class + { + return Post(auth, path, queryArguments, payload); + } + + public static U Post( + AuthenticationDetails auth, + string path, + NameValueCollection queryArguments, + T payload) + where T : class + where EX : ErrorResult + { + return MakeRequest("POST", auth, path, queryArguments, payload); + } + + public static U Post( + AuthenticationDetails auth, + string path, + NameValueCollection queryArguments, + T payload, + string baseUri, + string contentType) + where T : class + where EX : ErrorResult + { + return MakeRequest("POST", auth, path, queryArguments, payload, baseUri, contentType); + } + + public static U Put(AuthenticationDetails auth, string path, NameValueCollection queryArguments, T payload) where T : class + { + return MakeRequest("PUT", auth, path, queryArguments, payload); + } + + // NEW + public static U Put(AuthenticationDetails auth, string path, NameValueCollection queryArguments, T payload, string baseUri, string contentType) where T : class + { + return MakeRequest("PUT", auth, path, queryArguments, payload, baseUri, contentType); + } + + public static string Delete(AuthenticationDetails auth, string path, NameValueCollection queryArguments) + { + return MakeRequest("DELETE", auth, path, queryArguments, null); + } + + // NEW + public static string Delete(AuthenticationDetails auth, string path, NameValueCollection queryArguments, string baseUri, string contentType) + { + return MakeRequest("DELETE", auth, path, queryArguments, null, baseUri, contentType); + } + + static U MakeRequest( + string method, + AuthenticationDetails auth, + string path, + NameValueCollection queryArguments, + T payload) + where T : class + where EX : ErrorResult + { + return MakeRequest(method, auth, path, queryArguments, + payload, CreateSendOptions.BaseUri, APPLICATION_JSON_CONTENT_TYPE); + } + + static U MakeRequest( + string method, + AuthenticationDetails auth, + string path, + NameValueCollection queryArguments, + T payload, + string baseUri, + string contentType) + where T : class + where EX : ErrorResult + { + JsonSerializerSettings serialiserSettings = new JsonSerializerSettings(); + serialiserSettings.NullValueHandling = NullValueHandling.Ignore; + serialiserSettings.MissingMemberHandling = MissingMemberHandling.Ignore; + #if SUPPORTED_FRAMEWORK_VERSION + serialiserSettings.Converters.Add(new EmailAddressConverter()); + #endif + string uri = baseUri + path + NameValueCollectionExtension.ToQueryString(queryArguments); + + HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri); + req.Method = method; + req.ContentType = contentType; + req.AutomaticDecompression = DecompressionMethods.GZip; + + if (auth != null) + { + if (auth is OAuthAuthenticationDetails) + { + OAuthAuthenticationDetails oauthDetails = auth as OAuthAuthenticationDetails; + req.Headers["Authorization"] = "Bearer " + oauthDetails.AccessToken; + } + else if (auth is ApiKeyAuthenticationDetails) + { + ApiKeyAuthenticationDetails apiKeyDetails = auth as ApiKeyAuthenticationDetails; + req.Headers["Authorization"] = "Basic " + Convert.ToBase64String( + Encoding.Default.GetBytes(apiKeyDetails.ApiKey + ":x")); + } + else if (auth is BasicAuthAuthenticationDetails) + { + BasicAuthAuthenticationDetails basicDetails = auth as BasicAuthAuthenticationDetails; + req.Headers["Authorization"] = "Basic " + Convert.ToBase64String( + Encoding.Default.GetBytes(basicDetails.Username + ":" + basicDetails.Password)); + } + } + + req.UserAgent = string.Format("createsend-dotnet-#{0} .Net: {1} OS: {2} DLL: {3}", + CreateSendOptions.VersionNumber, Environment.Version, Environment.OSVersion, Assembly.GetExecutingAssembly().FullName); + + if (method != "GET") + { + if (payload != null) + { + using (System.IO.StreamWriter os = new System.IO.StreamWriter(req.GetRequestStream())) + { + if (contentType == APPLICATION_FORM_URLENCODED_CONTENT_TYPE) + os.Write(payload); + else + os.Write(JsonConvert.SerializeObject(payload, Formatting.None, serialiserSettings)); + os.Close(); + } + } + else + req.ContentLength = 0; + } + + try + { + using (var resp = (HttpWebResponse)req.GetResponse()) + { + if (resp == null) + return default(U); + else + { + using (var sr = new System.IO.StreamReader(resp.GetResponseStream())) + { + #if SUPPORTED_FRAMEWORK_VERSION + var type = typeof(U); + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(RateLimited<>)) + { + var responseType = type.GetGenericArguments()[0]; + var response = JsonConvert.DeserializeObject(sr.ReadToEnd().Trim(), responseType, serialiserSettings); + var status = new RateLimitStatus + { + Credit = resp.Headers["X-RateLimit-Limit"].UInt(0), + Remaining = resp.Headers["X-RateLimit-Remaining"].UInt(0), + Reset = resp.Headers["X-RateLimit-Reset"].UInt(0) + }; + return (U)Activator.CreateInstance(type, response, status); + } + #endif + return JsonConvert.DeserializeObject(sr.ReadToEnd().Trim(), serialiserSettings); + } + } + } + } + catch (WebException we) + { + if (we.Status == WebExceptionStatus.ProtocolError) + { + switch ((int)((HttpWebResponse)we.Response).StatusCode) + { + case 400: + case 401: + throw ThrowReworkedCustomException(we); + case 404: + default: + throw we; + } + } + else + { + throw we; + } + } + } + + #if SUPPORTED_FRAMEWORK_VERSION + private static uint UInt(this string value, uint defaultValue) + { + uint v; + if (uint.TryParse(value, out v)) + { + return v; + } + return defaultValue; + } + #endif + + private static Exception ThrowReworkedCustomException(WebException we) where EX : ErrorResult + { + using (System.IO.StreamReader sr = new System.IO.StreamReader(((HttpWebResponse)we.Response).GetResponseStream())) + { + string response = sr.ReadToEnd().Trim(); + ErrorResult result = JsonConvert.DeserializeObject(response); + string message; + + if (result is OAuthErrorResult) + message = string.Format( + "The CreateSend OAuth receiver responded with the following error - {0}: {1}", + (result as OAuthErrorResult).error, + (result as OAuthErrorResult).error_description); + else // Regular ErrorResult format. + message = string.Format( + "The CreateSend API responded with the following error - {0}: {1}", + result.Code, result.Message); + + CreatesendException exception; + if (result.Code == "121") + exception = new ExpiredOAuthTokenException(message); + else + exception = new CreatesendException(message); + + exception.Data.Add("ErrorResponse", response); + exception.Data.Add("ErrorResult", result); + return exception; + } + } + } + + public static class NameValueCollectionExtension + { + public static string ToQueryString(NameValueCollection nvc) + { + if (nvc != null && nvc.Count > 0) + return "?" + string.Join("&", GetPairs(nvc)); + else + return ""; + } + + private static string[] GetPairs(NameValueCollection nvc) + { + List keyValuePair = new List(); + + foreach (string key in nvc.AllKeys) + { + string encodedKey = HttpUtility.UrlEncode(key) + "="; + var values = nvc.GetValues(key); + + if(values != null) + foreach (string value in values) + { + + keyValuePair.Add(encodedKey + HttpUtility.UrlEncode(value)); + + } + } + + return keyValuePair.ToArray(); + } + } +} diff --git a/createsend-netstandard/ICreateSendOptions.cs b/createsend-netstandard/ICreateSendOptions.cs new file mode 100644 index 0000000..88f30db --- /dev/null +++ b/createsend-netstandard/ICreateSendOptions.cs @@ -0,0 +1,11 @@ +using System; + +namespace createsend_dotnet +{ + public interface ICreateSendOptions + { + string BaseUri { get; set; } + string BaseOAuthUri { get; set; } + string VersionNumber { get; } + } +} diff --git a/createsend-netstandard/List.cs b/createsend-netstandard/List.cs new file mode 100644 index 0000000..4d82d1a --- /dev/null +++ b/createsend-netstandard/List.cs @@ -0,0 +1,282 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; + +namespace createsend_dotnet +{ + public class List : CreateSendBase + { + public string ListID { get; set; } + + public List(AuthenticationDetails auth, string listID) + : base(auth) + { + ListID = listID; + } + + public static string Create( + AuthenticationDetails auth, + string clientID, + string title, + string unsubscribePage, + bool confirmedOptIn, + string confirmationSuccessPage, + UnsubscribeSetting unsubscribeSetting) + { + return HttpHelper.Post( + auth, string.Format("/lists/{0}.json", clientID), null, + new ListDetail() + { + Title = title, + UnsubscribePage = unsubscribePage, + ConfirmedOptIn = confirmedOptIn, + ConfirmationSuccessPage = confirmationSuccessPage, + UnsubscribeSetting = unsubscribeSetting.ToString() + }); + } + + public void Update( + string title, + string unsubscribePage, + bool confirmedOptIn, + string confirmationSuccessPage, + UnsubscribeSetting unsubscribeSetting, + bool addUnsubscribesToSuppList, + bool scrubActiveWithSuppList) + { + HttpPut( + string.Format("/lists/{0}.json", ListID), null, + new ListDetailForUpdate() + { + Title = title, + UnsubscribePage = unsubscribePage, + ConfirmedOptIn = confirmedOptIn, + ConfirmationSuccessPage = confirmationSuccessPage, + UnsubscribeSetting = unsubscribeSetting.ToString(), + AddUnsubscribesToSuppList = addUnsubscribesToSuppList, + ScrubActiveWithSuppList = scrubActiveWithSuppList + }); + } + + public ListDetail Details() + { + return HttpGet( + string.Format("/lists/{0}.json", ListID), null); + } + + public void Delete() + { + HttpDelete(string.Format("/lists/{0}.json", ListID), null); + } + + public string CreateCustomField( + string fieldName, + CustomFieldDataType dataType, + List options) + { + return CreateCustomField(fieldName, dataType, options, true); + } + + public string CreateCustomField( + string fieldName, + CustomFieldDataType dataType, + List options, + bool visibleInPreferenceCenter) + { + return HttpPost, string>( + string.Format("/lists/{0}/customfields.json", ListID), null, + new Dictionary() + { + { "FieldName", fieldName }, + { "DataType", dataType.ToString() }, + { "Options", options }, + { "VisibleInPreferenceCenter", visibleInPreferenceCenter } + }); + } + + public string UpdateCustomField( + string customFieldKey, + string fieldName, + bool visibleInPreferenceCenter) + { + return HttpPut, string>( + string.Format("/lists/{0}/customfields/{1}.json", + ListID, System.Web.HttpUtility.UrlEncode(customFieldKey)), null, + new Dictionary() + { + { "FieldName", fieldName }, + { "VisibleInPreferenceCenter", visibleInPreferenceCenter } + }); + } + + public void DeleteCustomField(string customFieldKey) + { + HttpDelete( + string.Format("/lists/{0}/customfields/{1}.json", + ListID, System.Web.HttpUtility.UrlEncode(customFieldKey)), null); + } + + public IEnumerable CustomFields() + { + return HttpGet( + string.Format("/lists/{0}/customfields.json", ListID), null); + } + + public void UpdateCustomFieldOptions( + string customFieldKey, + List options, + bool keepExistingOptions) + { + HttpPut( + string.Format("/lists/{0}/customfields/{1}/options.json", + ListID, System.Web.HttpUtility.UrlEncode(customFieldKey)), null, + new { + KeepExistingOptions = keepExistingOptions, + Options = options + }); + } + + [Obsolete("Use UpdateCustomFieldOptions instead. UpdateCustomFields will eventually be removed.", false)] + public void UpdateCustomFields(string customFieldKey, List options, bool keepExistingOptions) + { + UpdateCustomFieldOptions(customFieldKey, options, keepExistingOptions); + } + + public IEnumerable Segments() + { + return HttpGet( + string.Format("/lists/{0}/segments.json", ListID), null); + } + + public ListStats Stats() + { + return HttpGet( + string.Format("/lists/{0}/stats.json", ListID), null); + } + + public PagedCollection Active() + { + return GenericPagedSubscriberGet("active", "", 1, 1000, "email", "asc"); + } + + public PagedCollection Active(DateTime fromDate, int page, int pageSize, string orderField, string orderDirection) + { + return GenericPagedSubscriberGet("active", fromDate, page, pageSize, orderField, orderDirection); + } + + public PagedCollection Unconfirmed() + { + return GenericPagedSubscriberGet("unconfirmed", "", 1, 1000, "email", "asc"); + } + + public PagedCollection Unconfirmed(DateTime fromDate, int page, int pageSize, string orderField, string orderDirection) + { + return GenericPagedSubscriberGet("unconfirmed", fromDate, page, pageSize, orderField, orderDirection); + } + + public PagedCollection Unsubscribed() + { + return GenericPagedSubscriberGet("unsubscribed", "", 1, 1000, "email", "asc"); + } + + public PagedCollection Unsubscribed(DateTime fromDate, int page, int pageSize, string orderField, string orderDirection) + { + return GenericPagedSubscriberGet("unsubscribed", fromDate, page, pageSize, orderField, orderDirection); + } + + public PagedCollection Bounced() + { + return GenericPagedSubscriberGet("bounced", "", 1, 1000, "email", "asc"); + } + + public PagedCollection Bounced(DateTime fromDate, int page, int pageSize, string orderField, string orderDirection) + { + return GenericPagedSubscriberGet("bounced", fromDate, page, pageSize, orderField, orderDirection); + } + + public PagedCollection Deleted() + { + return GenericPagedSubscriberGet("deleted", "", 1, 1000, "email", "asc"); + } + + public PagedCollection Deleted(DateTime fromDate, int page, int pageSize, string orderField, string orderDirection) + { + return GenericPagedSubscriberGet("deleted", fromDate, page, pageSize, orderField, orderDirection); + } + + private PagedCollection GenericPagedSubscriberGet( + string type, + DateTime fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + return GenericPagedSubscriberGet(type, + fromDate.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), page, pageSize, + orderField, orderDirection); + } + + private PagedCollection GenericPagedSubscriberGet(string type, string fromDate, int page, int pageSize, string orderField, string orderDirection) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("date", fromDate); + queryArguments.Add("page", page.ToString()); + queryArguments.Add("pagesize", pageSize.ToString()); + queryArguments.Add("orderfield", orderField); + queryArguments.Add("orderdirection", orderDirection); + + return HttpGet>( + string.Format("/lists/{0}/{1}.json", ListID, type), queryArguments); + } + + public IEnumerable Webhooks() + { + return HttpGet( + string.Format("/lists/{0}/webhooks.json", ListID), null); + } + + public string CreateWebhook(List events, string url, string payloadFormat) + { + return HttpPost, string>( + string.Format("/lists/{0}/webhooks.json", ListID), null, + new Dictionary() + { + { "Events", events }, + { "Url", url }, + { "PayloadFormat", payloadFormat } + }); + } + + public bool TestWebhook(string webhookID) + { + HttpGet>( + string.Format("/lists/{0}/webhooks/{1}/test.json", + ListID, System.Web.HttpUtility.UrlEncode(webhookID)), null); + + return true; //an exception will be thrown if there is a problem + } + + public void DeleteWebhook(string webhookID) + { + HttpDelete( + string.Format("/lists/{0}/webhooks/{1}.json", + ListID, System.Web.HttpUtility.UrlEncode(webhookID)), null); + } + + public void ActivateWebhook(string webhookID) + { + HttpPut( + string.Format("/lists/{0}/webhooks/{1}/activate.json", + ListID, System.Web.HttpUtility.UrlEncode(webhookID)), null, null); + } + + public void DeactivateWebhook(string webhookID) + { + HttpPut( + string.Format("/lists/{0}/webhooks/{1}/deactivate.json", + ListID, System.Web.HttpUtility.UrlEncode(webhookID)), null, null); + } + } +} diff --git a/createsend-netstandard/Models/Administrator.cs b/createsend-netstandard/Models/Administrator.cs new file mode 100644 index 0000000..227cf42 --- /dev/null +++ b/createsend-netstandard/Models/Administrator.cs @@ -0,0 +1,17 @@ +namespace createsend_dotnet +{ + public class AdministratorDetails + { + public string EmailAddress { get; set; } + public string Name { get; set; } + /// + /// status is only used when retrieving an administrator (or administrators) + /// + public string Status { get; set; } + } + + public class AdministratorResult + { + public string EmailAddress { get; set; } + } +} diff --git a/createsend-netstandard/Models/BillingDetails.cs b/createsend-netstandard/Models/BillingDetails.cs new file mode 100644 index 0000000..7290ecf --- /dev/null +++ b/createsend-netstandard/Models/BillingDetails.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class BillingDetails + { + public int Credits { get; set; } + } +} diff --git a/createsend-netstandard/Models/Campaign.cs b/createsend-netstandard/Models/Campaign.cs new file mode 100644 index 0000000..9b8bc84 --- /dev/null +++ b/createsend-netstandard/Models/Campaign.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class CampaignDetail + { + public string CampaignID { get; set; } + public string Subject { get; set; } + public string Name { get; set; } + public string FromName { get; set; } + public string FromEmail { get; set; } + public string ReplyTo { get; set; } + public string SentDate { get; set; } + public int TotalRecipients { get; set; } + public string WebVersionURL { get; set; } + public string WebVersionTextURL { get; set; } + } + + public class ScheduledCampaignDetail + { + public string CampaignID { get; set; } + public string Subject { get; set; } + public string Name { get; set; } + public string FromName { get; set; } + public string FromEmail { get; set; } + public string ReplyTo { get; set; } + public string DateCreated { get; set; } + public string PreviewURL { get; set; } + public string PreviewTextURL { get; set; } + public string DateScheduled { get; set; } + public string ScheduledTimeZone { get; set; } + } + + public class DraftDetail + { + public string CampaignID { get; set; } + public string Subject { get; set; } + public string Name { get; set; } + public string FromName { get; set; } + public string FromEmail { get; set; } + public string ReplyTo { get; set; } + public string DateCreated { get; set; } + public string PreviewURL { get; set; } + public string PreviewTextURL { get; set; } + } + + public class CampaignSummary + { + public int Recipients { get; set; } + public int TotalOpened { get; set; } + public int Clicks { get; set; } + public int Unsubscribed { get; set; } + public int SpamComplaints { get; set; } + public int Bounced { get; set; } + public int UniqueOpened { get; set; } + public int Mentions { get; set; } + public int Forwards { get; set; } + public int Likes { get; set; } + public string WebVersionURL { get; set; } + public string WebVersionTextURL { get; set; } + public string WorldviewURL { get; set; } + } + + public class CampaignDetailBase + { + public string EmailAddress { get; set; } + public string ListID { get; set; } + public DateTime Date { get; set; } + } + + public class CampaignDetailBaseWithIPAddress : CampaignDetailBase + { + public string IPAddress { get; set; } + } + + public class CampaignDetailWithGeoBase : CampaignDetailBaseWithIPAddress + { + public double Latitude { get; set; } + public double Longitude { get; set; } + public string City { get; set; } + public string Region { get; set; } + public string CountryCode { get; set; } + public string CountryName { get; set; } + } + + public class CampaignOpenDetail : CampaignDetailWithGeoBase { } + + public class CampaignClickDetail : CampaignDetailWithGeoBase + { + public string URL { get; set; } + } + + public class CampaignUnsubscribeDetail : CampaignDetailBaseWithIPAddress { } + + public class CampaignSpamComplaint : CampaignDetailBase { } + + public class CampaignBounceDetail : CampaignDetailBaseWithIPAddress + { + public string BounceType { get; set; } + public string Reason { get; set; } + } + + public class CampaignListsAndSegments + { + public Segments Segments { get; set; } + public Lists Lists { get; set; } + } +} diff --git a/createsend-netstandard/Models/Client.cs b/createsend-netstandard/Models/Client.cs new file mode 100644 index 0000000..17bfbce --- /dev/null +++ b/createsend-netstandard/Models/Client.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace createsend_dotnet +{ + public class BasicClient + { + public string ClientID { get; set; } + public string Name { get; set; } + } + + public class Clients : List { } + + public class ClientWithSettings + { + public string ApiKey { get; set; } + public ClientDetail BasicDetails { get; set; } + public ClientAccessSettings AccessDetails { get; set; } + public BillingDetail BillingDetails { get; set; } + } + + public class ClientDetail + { + public string ClientID { get; set; } + public string CompanyName { get; set; } + public string ContactName { get; set; } + public string EmailAddress { get; set; } + public string Country { get; set; } + public string TimeZone { get; set; } + } + + public class BillingOptions + { + public string Currency { get; set; } + public bool ClientPays { get; set; } + public int MarkupPercentage { get; set; } + public bool CanPurchaseCredits { get; set; } + public int Credits { get; set; } + public decimal? MarkupOnDelivery { get; set; } + public decimal? MarkupPerRecipient { get; set; } + public decimal? MarkupOnDesignSpamTest { get; set; } + [JsonConverter(typeof(StringEnumConverter))] + public MonthlyScheme? MonthlyScheme { get; set; } + } + + public class BillingDetail : BillingOptions + { + public string CurrentTier { get; set; } + public decimal CurrentMonthlyRate { get; set; } + public decimal BaseDeliveryRate { get; set; } + public decimal BaseRatePerRecipient { get; set; } + public decimal BaseDesignSpamTestRate { get; set; } + } + + public class ClientAccessSettings + { + public int AccessLevel { get; set; } + public string Username { get; set; } + public string Password { get; set; } + } + + public class SuppressionDetails + { + public string[] EmailAddresses { get; set; } + } + + public class CreditsTransferDetails + { + public int Credits { get; set; } + public bool CanUseMyCreditsWhenTheyRunOut { get; set; } + } + + public class CreditsTransferResult + { + public int AccountCredits { get; set; } + public int ClientCredits { get; set; } + } +} diff --git a/createsend-netstandard/Models/CreatesendException.cs b/createsend-netstandard/Models/CreatesendException.cs new file mode 100644 index 0000000..bb6f701 --- /dev/null +++ b/createsend-netstandard/Models/CreatesendException.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class ExpiredOAuthTokenException : CreatesendException + { + public ExpiredOAuthTokenException(string message) : base(message) { } + } + + public class CreatesendException : Exception + { + public ErrorResult Error { get { return Data["ErrorResult"] as ErrorResult; } } + + public CreatesendException(string message) : base (message) { } + } +} diff --git a/createsend-netstandard/Models/EmailClient.cs b/createsend-netstandard/Models/EmailClient.cs new file mode 100644 index 0000000..add0b0e --- /dev/null +++ b/createsend-netstandard/Models/EmailClient.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class EmailClient + { + public string Client { get; set; } + public string Version { get; set; } + public double Percentage { get; set; } + public int Subscribers { get; set; } + } +} diff --git a/createsend-netstandard/Models/ExternalSession.cs b/createsend-netstandard/Models/ExternalSession.cs new file mode 100644 index 0000000..3a50f7b --- /dev/null +++ b/createsend-netstandard/Models/ExternalSession.cs @@ -0,0 +1,16 @@ +namespace createsend_dotnet +{ + public class ExternalSessionOptions + { + public string Email { get; set; } + public string Chrome { get; set; } + public string Url { get; set; } + public string IntegratorID { get; set; } + public string ClientID { get; set; } + } + + public class ExternalSessionResult + { + public string SessionUrl { get; set; } + } +} diff --git a/createsend-netstandard/Models/List.cs b/createsend-netstandard/Models/List.cs new file mode 100644 index 0000000..0d7caca --- /dev/null +++ b/createsend-netstandard/Models/List.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class BasicList + { + public string ListID { get; set; } + public string Name { get; set; } + } + + public class Lists : List { } + + public class ListDetail + { + public string ListID { get; set; } + public string Title { get; set; } + public string UnsubscribePage { get; set; } + public bool ConfirmedOptIn { get; set; } + public string ConfirmationSuccessPage { get; set; } + public string UnsubscribeSetting { get; set; } + } + + public class ListDetailForUpdate : ListDetail + { + public bool AddUnsubscribesToSuppList { get; set; } + public bool ScrubActiveWithSuppList { get; set; } + } + + public class ListCustomField + { + public string FieldName { get; set; } + public string Key { get; set; } + public string DataType { get; set; } + public List FieldOptions { get; set; } + public bool VisibleInPreferenceCenter { get; set; } + } + + public enum UnsubscribeSetting + { + AllClientLists, + OnlyThisList + } + + public enum CustomFieldDataType + { + Text = 1, + Number = 2, + MultiSelectOne = 3, + MultiSelectMany = 4, + Date = 5, + } + + public class ListStats + { + public int TotalActiveSubscribers { get; set; } + public int NewActiveSubscribersToday { get; set; } + public int NewActiveSubscribersYesterday { get; set; } + public int NewActiveSubscribersThisWeek { get; set; } + public int NewActiveSubscribersThisMonth { get; set; } + public int NewActiveSubscribersThisYear { get; set; } + public int TotalUnsubscribes { get; set; } + public int UnsubscribesToday { get; set; } + public int UnsubscribesYesterday { get; set; } + public int UnsubscribesThisWeek { get; set; } + public int UnsubscribesThisMonth { get; set; } + public int UnsubscribesThisYear { get; set; } + public int TotalDeleted { get; set; } + public int DeletedToday { get; set; } + public int DeletedYesterday { get; set; } + public int DeletedThisWeek { get; set; } + public int DeletedThisMonth { get; set; } + public int DeletedThisYear { get; set; } + public int TotalBounces { get; set; } + public int BouncesToday { get; set; } + public int BouncesYesterday { get; set; } + public int BouncesThisWeek { get; set; } + public int BouncesThisMonth { get; set; } + public int BouncesThisYear { get; set; } + } + + public class ListForEmail + { + public string ListID { get; set; } + public string ListName { get; set; } + public string SubscriberState { get; set; } + public DateTime DateSubscriberAdded { get; set; } + } +} diff --git a/createsend-netstandard/Models/OAuthTokenDetails.cs b/createsend-netstandard/Models/OAuthTokenDetails.cs new file mode 100644 index 0000000..a123d63 --- /dev/null +++ b/createsend-netstandard/Models/OAuthTokenDetails.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class OAuthTokenDetails + { + public string access_token; + public int expires_in; + public string refresh_token; + } +} diff --git a/createsend-netstandard/Models/PagedCollection.cs b/createsend-netstandard/Models/PagedCollection.cs new file mode 100644 index 0000000..1ebebe8 --- /dev/null +++ b/createsend-netstandard/Models/PagedCollection.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class PagedCollection + { + //deserializer does not allow deserialization from JSON list straight to IEnumerable + public List Results { get; set; } + public string ResultsOrderedBy { get; set; } + public string OrderDirection { get; set; } + public int PageNumber { get; set; } + public int PageSize { get; set; } + public int RecordsOnThisPage { get; set; } + public int TotalNumberOfRecords { get; set; } + public int NumberOfPages { get; set; } + } +} diff --git a/createsend-netstandard/Models/Person.cs b/createsend-netstandard/Models/Person.cs new file mode 100644 index 0000000..8bb158a --- /dev/null +++ b/createsend-netstandard/Models/Person.cs @@ -0,0 +1,22 @@ +namespace createsend_dotnet +{ + public class PersonDetails + { + public string EmailAddress { get; set; } + public string Name { get; set; } + public int AccessLevel { get; set; } + /// + /// status is only used when retrieving a person or people + /// + public string Status { get; set; } + /// + /// password is only used when adding a person + /// + public string Password { get; set; } + } + + public class PersonResult + { + public string EmailAddress { get; set; } + } +} diff --git a/createsend-netstandard/Models/Result.cs b/createsend-netstandard/Models/Result.cs new file mode 100644 index 0000000..110f6d7 --- /dev/null +++ b/createsend-netstandard/Models/Result.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml.Serialization; +using System.Collections; + +namespace createsend_dotnet +{ + [Serializable] + public class ErrorResult : ErrorResult + { + public ErrorResult() : base() { } + + public ErrorResult(ErrorResult errorResult) + { + Message = errorResult.Message; + Code = errorResult.Code; + } + public T ResultData { get; set; } + } + + [Serializable] + public class OAuthErrorResult : ErrorResult + { + public string error { get; set; } + public string error_description { get; set; } + } + + [Serializable] + public class ErrorResult + { + public string Code { get; set; } + public string Message { get; set; } + } + + public class ApiKeyResult + { + public string ApiKey { get; set; } + } + + public class SystemDateResult + { + public DateTime SystemDate { get; set; } + } + + public class BulkImportResults + { + public int TotalUniqueEmailsSubmitted { get; set; } + public int TotalExistingSubscribers { get; set; } + public int TotalNewSubscribers { get; set; } + public List DuplicateEmailsInSubmission { get; set; } + public List FailureDetails { get; set; } + } + + public class ImportResult : ErrorResult + { + public string EmailAddress { get; set; } + } +} diff --git a/createsend-netstandard/Models/Segment.cs b/createsend-netstandard/Models/Segment.cs new file mode 100644 index 0000000..4100ce2 --- /dev/null +++ b/createsend-netstandard/Models/Segment.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class BasicSegment + { + public string ListID { get; set; } + public string SegmentID { get; set; } + public string Title { get; set; } + } + + public class Segments : List { } + + public class SegmentDetail : BasicSegment + { + public int ActiveSubscribers { get; set; } + public SegmentRuleGroups RuleGroups { get; set; } + } + + public class SegmentRules : List { } + + public class Rule + { + public string RuleType { get; set; } + public string Clause { get; set; } + } + + public class SegmentRuleGroups : List { } + + public class SegmentRuleGroup + { + public SegmentRules Rules { get; set; } + } + + public class RuleErrorResults : List { } + + public class RuleErrorResult + { + public string Subject { get; set; } + public string Code { get; set; } + public string Message { get; set; } + public ClauseErrorResults ClauseResults { get; set; } + } + + public class ClauseErrorResults : List { } + + public class ClauseErrorResult + { + public string Clause { get; set; } + public string Code { get; set; } + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/createsend-netstandard/Models/Subscriber.cs b/createsend-netstandard/Models/Subscriber.cs new file mode 100644 index 0000000..586abc2 --- /dev/null +++ b/createsend-netstandard/Models/Subscriber.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class BasicSubscriber + { + public string EmailAddress { get; set; } + public DateTime Date { get; set; } + public string State { get; set; } + } + + public class SuppressedSubscriber : BasicSubscriber + { + public string SuppressionReason { get; set; } + } + + public class CampaignRecipient + { + public string EmailAddress { get; set; } + public string ListID { get; set; } + } + + public class CampaignRecipients : List { } + + public class SubscriberCustomField + { + public string Key { get; set; } + public string Value{ get ;set; } + public bool Clear { get; set; } + } + + public class SubscriberDetail : BasicSubscriber + { + public SubscriberDetail() : base() { } + + public SubscriberDetail( + string emailAddress, + string name, + List customFields) + { + EmailAddress = emailAddress; + Name = name; + CustomFields = customFields; + } + + public string Name { get; set; } + public List CustomFields { get; set; } + public string ReadsEmailWith { get; set; } + } +} diff --git a/createsend-netstandard/Models/SubscriberHistory.cs b/createsend-netstandard/Models/SubscriberHistory.cs new file mode 100644 index 0000000..70afa59 --- /dev/null +++ b/createsend-netstandard/Models/SubscriberHistory.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class HistoryItem + { + public string ID { get; set; } + public string Type { get; set; } + public string Name { get; set; } + public IEnumerable Actions { get; set; } + } + + public class SubscriberAction + { + public string Event { get; set; } + public string IPAddress { get; set; } + public string Detail { get; set; } + public DateTime Date { get; set; } + } +} diff --git a/createsend-netstandard/Models/Template.cs b/createsend-netstandard/Models/Template.cs new file mode 100644 index 0000000..4921aea --- /dev/null +++ b/createsend-netstandard/Models/Template.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class BasicTemplate + { + public string TemplateID { get; set; } + public string Name { get; set; } + public string PreviewURL { get; set; } + public string ScreenshotURL { get; set; } + } +} \ No newline at end of file diff --git a/createsend-netstandard/Models/TemplateContent.cs b/createsend-netstandard/Models/TemplateContent.cs new file mode 100644 index 0000000..cdd468e --- /dev/null +++ b/createsend-netstandard/Models/TemplateContent.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class EditableField + { + public string Content; + public string Alt; + public string Href; + } + + public class Repeater + { + public List Items; + } + + public class RepeaterItem + { + public string Layout; + public List Singlelines; + public List Multilines; + public List Images; + } + + public class TemplateContent + { + public List Singlelines; + public List Multilines; + public List Images; + public List Repeaters; + } +} diff --git a/createsend-netstandard/Models/Webhook.cs b/createsend-netstandard/Models/Webhook.cs new file mode 100644 index 0000000..d837563 --- /dev/null +++ b/createsend-netstandard/Models/Webhook.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_dotnet +{ + public class BasicWebhook + { + public string WebhookID { get; set; } + public List Events { get; set; } + public string Url { get; set; } + public string Status { get; set; } + public string PayloadFormat { get; set; } + } + + public class WebhookTestErrorResult + { + public string FailureStatus { get; set; } + public string FailureResponseMessage { get; set; } + public string FailureResponseCode { get; set; } + public string FailureResponse { get; set; } + } +} diff --git a/createsend-netstandard/MonthlyScheme.cs b/createsend-netstandard/MonthlyScheme.cs new file mode 100644 index 0000000..7fbde30 --- /dev/null +++ b/createsend-netstandard/MonthlyScheme.cs @@ -0,0 +1,9 @@ +namespace createsend_dotnet +{ + public enum MonthlyScheme + { + Basic, + Unlimited, + Premier + } +} diff --git a/createsend-netstandard/Person.cs b/createsend-netstandard/Person.cs new file mode 100644 index 0000000..3f85b24 --- /dev/null +++ b/createsend-netstandard/Person.cs @@ -0,0 +1,42 @@ +using System.Collections.Specialized; + +namespace createsend_dotnet +{ + public class Person : CreateSendBase + { + private readonly string clientID; + + public Person(AuthenticationDetails auth, string clientID) + : base(auth) + { + this.clientID = clientID; + } + + private string PeopleUrl { get { return string.Format("/clients/{0}/people.json", clientID); }} + + public PersonDetails Details(string emailAddress) + { + return HttpGet( + PeopleUrl, new NameValueCollection {{"email", emailAddress}}); + } + + public string Add(PersonDetails person) + { + return HttpPost( + PeopleUrl, null, person).EmailAddress; + } + + public string Update(string emailAddress, PersonDetails person) + { + return HttpPut( + PeopleUrl, new NameValueCollection {{"email", emailAddress}}, + person).EmailAddress; + } + + public void Delete(string emailAddress) + { + HttpDelete(PeopleUrl, + new NameValueCollection {{"email", emailAddress}}); + } + } +} diff --git a/createsend-netstandard/Properties/AssemblyInfo.cs b/createsend-netstandard/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1b7cb51 --- /dev/null +++ b/createsend-netstandard/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("createsend-dotnet")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("CreateSend")] +[assembly: AssemblyProduct("createsend-dotnet")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4af76d47-92e5-4636-a9bb-b48046a17f25")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("4.2.2.0")] +[assembly: AssemblyFileVersion("4.2.2.0")] diff --git a/createsend-netstandard/Segment.cs b/createsend-netstandard/Segment.cs new file mode 100644 index 0000000..4452b44 --- /dev/null +++ b/createsend-netstandard/Segment.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Globalization; + +namespace createsend_dotnet +{ + public class Segment : CreateSendBase + { + public string SegmentID { get; set; } + + public Segment(AuthenticationDetails auth, string segmentID) + : base(auth) + { + SegmentID = segmentID; + } + + public static string Create( + AuthenticationDetails auth, string listID, string title, SegmentRuleGroups ruleGroups) + { + return HttpHelper.Post, string, ErrorResult>( + auth, string.Format("/segments/{0}.json", listID), null, + new Dictionary() + { + { "ListID", listID }, + { "Title", title }, + { "RuleGroups", ruleGroups } + }); + } + + public void Update(string title, SegmentRuleGroups ruleGroups) + { + HttpPut, string>( + string.Format("/segments/{0}.json", SegmentID), null, + new Dictionary() + { + { "Title", title }, + { "RuleGroups", ruleGroups } + }); + } + + public void AddRuleGroup(SegmentRuleGroup ruleGroup) + { + HttpPost, string>( + string.Format("/segments/{0}/rules.json", SegmentID), null, + new Dictionary() + { + { "Rules", ruleGroup.Rules } + }); + } + + public PagedCollection Subscribers() + { + return Subscribers(1, 1000, "email", "asc"); + } + + public PagedCollection Subscribers( + int page, + int pageSize, + string orderField, + string orderDirection) + { + return Subscribers("", page, pageSize, orderField, + orderDirection); + } + + public PagedCollection Subscribers( + DateTime fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + return Subscribers(fromDate.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), + page, pageSize, orderField, orderDirection); + } + + private PagedCollection Subscribers( + string fromDate, + int page, + int pageSize, + string orderField, + string orderDirection) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("date", fromDate); + queryArguments.Add("page", page.ToString()); + queryArguments.Add("pagesize", pageSize.ToString()); + queryArguments.Add("orderfield", orderField); + queryArguments.Add("orderdirection", orderDirection); + + return HttpGet>( + string.Format("/segments/{0}/active.json", SegmentID), queryArguments); + } + + public SegmentDetail Details() + { + return HttpGet( + string.Format("/segments/{0}.json", SegmentID), null); + } + + public void ClearRules() + { + HttpDelete( + string.Format("/segments/{0}/rules.json", SegmentID), null); + } + + public void Delete() + { + HttpDelete(string.Format("/segments/{0}.json", SegmentID), null); + } + } +} diff --git a/createsend-netstandard/Subscriber.cs b/createsend-netstandard/Subscriber.cs new file mode 100644 index 0000000..d15134b --- /dev/null +++ b/createsend-netstandard/Subscriber.cs @@ -0,0 +1,148 @@ +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace createsend_dotnet +{ + public class Subscriber : CreateSendBase + { + public string ListID { get; set; } + + public Subscriber(AuthenticationDetails auth, string listID) + : base(auth) + { + ListID = listID; + } + + public SubscriberDetail Get(string emailAddress) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("email", emailAddress); + + return HttpGet( + string.Format("/subscribers/{0}.json", ListID), queryArguments); + } + + public IEnumerable GetHistory(string emailAddress) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("email", emailAddress); + + return HttpGet>( + string.Format("/subscribers/{0}/history.json", ListID), + queryArguments); + } + + public string Add(string emailAddress, string name, + List customFields, bool resubscribe) + { + return Add(emailAddress, name, customFields, resubscribe, false); + } + + public string Add(string emailAddress, string name, + List customFields, bool resubscribe, + bool restartSubscriptionBasedAutoresponders) + { + return HttpPost, string>( + string.Format("/subscribers/{0}.json", ListID), null, + new Dictionary() + { + { "EmailAddress", emailAddress }, + { "Name", name }, + { "CustomFields", customFields }, + { "Resubscribe", resubscribe }, + { "RestartSubscriptionBasedAutoresponders", + restartSubscriptionBasedAutoresponders } + }); + } + + public void Update(string emailAddress, string newEmailAddress, + string name, List customFields, + bool resubscribe) + { + Update(emailAddress, newEmailAddress, name, customFields, + resubscribe, false); + } + + public void Update(string emailAddress, string newEmailAddress, + string name, List customFields, + bool resubscribe, bool restartSubscriptionBasedAutoresponders) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("email", emailAddress); + + HttpPut, string>( + string.Format("/subscribers/{0}.json", ListID), queryArguments, + new Dictionary() + { + { "EmailAddress", newEmailAddress }, + { "Name", name }, + { "CustomFields", customFields }, + { "Resubscribe", resubscribe }, + { "RestartSubscriptionBasedAutoresponders", + restartSubscriptionBasedAutoresponders } + }); + } + + public void Delete(string emailAddress) + { + NameValueCollection queryArguments = new NameValueCollection(); + queryArguments.Add("email", emailAddress); + HttpDelete(string.Format("/subscribers/{0}.json", ListID), queryArguments); + } + + public BulkImportResults Import(List subscribers, bool resubscribe) + { + return Import(subscribers, resubscribe, false); + } + + public BulkImportResults Import(List subscribers, + bool resubscribe, bool queueSubscriptionBasedAutoResponders) + { + return Import(subscribers, resubscribe, + queueSubscriptionBasedAutoResponders, false); + } + + public BulkImportResults Import(List subscribers, + bool resubscribe, bool queueSubscriptionBasedAutoResponders, + bool restartSubscriptionBasedAutoresponders) + { + List reworkedSubscribers = new List(); + foreach (SubscriberDetail subscriber in subscribers) + { + Dictionary subscriberWithoutDate = + new Dictionary() + { + { "EmailAddress", subscriber.EmailAddress }, + { "Name", subscriber.Name }, + { "CustomFields", subscriber.CustomFields } + }; + reworkedSubscribers.Add(subscriberWithoutDate); + } + + return HttpPost, BulkImportResults, + ErrorResult>( + string.Format("/subscribers/{0}/import.json", ListID), null, + new Dictionary() + { + { "Subscribers", reworkedSubscribers }, + { "Resubscribe", resubscribe }, + { "QueueSubscriptionBasedAutoResponders", + queueSubscriptionBasedAutoResponders }, + { "RestartSubscriptionBasedAutoresponders", + restartSubscriptionBasedAutoresponders } + }); + } + + public bool Unsubscribe(string emailAddress) + { + string result = HttpPost, string>( + string.Format("/subscribers/{0}/unsubscribe.json", ListID), null, + new Dictionary() + { + {"EmailAddress", emailAddress } + }); + + return result != null; + } + } +} diff --git a/createsend-netstandard/Template.cs b/createsend-netstandard/Template.cs new file mode 100644 index 0000000..8b99384 --- /dev/null +++ b/createsend-netstandard/Template.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; + +namespace createsend_dotnet +{ + public class Template : CreateSendBase + { + public string TemplateID { get; set; } + + public Template(AuthenticationDetails auth, string templateID) + : base(auth) + { + TemplateID = templateID; + } + + public static string Create(AuthenticationDetails auth, + string clientID, string name, string htmlPageUrl, string zipUrl) + { + return HttpHelper.Post, string>( + auth, string.Format("/templates/{0}.json", clientID), null, + new Dictionary() + { + { "Name", name }, + { "HtmlPageURL", htmlPageUrl }, + { "ZipFileUrl", zipUrl } + }); + } + + public void Update(string name, string htmlPageUrl, string zipUrl) + { + HttpPut, string>( + string.Format("/templates/{0}.json", TemplateID), null, + new Dictionary() + { + { "Name", name }, + { "HtmlPageURL", htmlPageUrl }, + { "ZipFileUrl", zipUrl } + }); + } + + public BasicTemplate Details() + { + return HttpGet( + string.Format("/templates/{0}.json", TemplateID), null); + } + + public void Delete() + { + HttpDelete( + string.Format("/templates/{0}.json", TemplateID), null); + } + } +} diff --git a/createsend-netstandard/Transactional/Attachment.cs b/createsend-netstandard/Transactional/Attachment.cs new file mode 100644 index 0000000..6def5f5 --- /dev/null +++ b/createsend-netstandard/Transactional/Attachment.cs @@ -0,0 +1,10 @@ + +namespace createsend_dotnet.Transactional +{ + public class Attachment + { + public string Name { get; set; } + public string Type { get; set; } + public byte[] Content { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/AttachmentMetadata.cs b/createsend-netstandard/Transactional/AttachmentMetadata.cs new file mode 100644 index 0000000..ca5aec5 --- /dev/null +++ b/createsend-netstandard/Transactional/AttachmentMetadata.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace createsend_dotnet.Transactional +{ + public class AttachmentMetadata + { + public string Name { get; set; } + public string Type { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/Authenticate.cs b/createsend-netstandard/Transactional/Authenticate.cs new file mode 100644 index 0000000..51e3c63 --- /dev/null +++ b/createsend-netstandard/Transactional/Authenticate.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Specialized; +using System.Globalization; + +namespace createsend_dotnet.Transactional +{ + public static class Authenticate + { + public static IAgencyTransactional ByAccountApiKey(string apiKey, ICreateSendOptions options = null) + { + if (apiKey == null) throw new ArgumentNullException("apiKey"); + + return new TransactionalContext(new AccountApiKey(apiKey), options); + } + + public static IAgencyTransactional ByAccountApiKey(string apiKey, string clientId, ICreateSendOptions options = null) + { + if (apiKey == null) throw new ArgumentNullException("apiKey"); + if (clientId == null) throw new ArgumentNullException("clientId"); + + return new TransactionalContext(new AccountApiKey(apiKey, clientId), options); + } + + public static ITransactional ByClientApiKey(string apiKey, ICreateSendOptions options = null) + { + if (apiKey == null) throw new ArgumentNullException("apiKey"); + + return new TransactionalContext(new ClientApiKey(apiKey), options); + } + + public static ITransactional ByOAuth(string accessToken, string refreshToken, string clientId, ICreateSendOptions options = null) + { + if (accessToken == null) throw new ArgumentNullException("accessToken"); + if (refreshToken == null) throw new ArgumentNullException("refreshToken"); + if (clientId == null) throw new ArgumentNullException("clientId"); + + return new TransactionalContext(new OAuthWithClientId(accessToken, refreshToken, clientId), options); + } + + internal static string PossiblyOptionalClientId(this CreateSendBase source, string clientId) + { + if (source == null) throw new ArgumentNullException("source"); + + var auth = source.AuthDetails as IProvideClientId; + + if (auth != null) + { + clientId = clientId ?? auth.ClientId; + + if (clientId == null) + { + throw new RequiredClientIdentierException(); + } + } + + return clientId; + } + + internal static NameValueCollection CreateQueryString(this CreateSendBase source, string clientId = null, NameValueCollection query = null) + { + if (source == null) throw new ArgumentNullException("source"); + + query = query ?? new NameValueCollection(); + clientId = source.PossiblyOptionalClientId(query.Get("clientid") ?? clientId); + if (clientId != null) + { + query["clientid"] = clientId; + } + + return query; + } + + internal static string Encode(this string value) + { + return value; + } + + internal static string Encode(this bool value) + { + return Encode(value.ToString().ToLowerInvariant()); + } + + internal static string Encode(this Enum value) + { + return value == null ? null : Encode(value.ToString().ToLowerInvariant()); + } + + internal static string Encode(this Guid value) + { + return Encode(value.ToString()); + } + + internal static string Encode(this Guid? value) + { + return value.HasValue ? Encode(value.Value) : null; + } + + internal static string Encode(this int value) + { + return Encode(value.ToString(CultureInfo.InvariantCulture)); + } + + internal static string Encode(this int? value) + { + return value.HasValue ? Encode(value.Value) : null; + } + + internal static string EncodeIso8601DateOnly(this DateTime date) + { + return Encode(date.ToString("yyyy-MM-dd")); + } + + internal static string EncodeIso8601DateOnly(this DateTime? date) + { + return date.HasValue ? EncodeIso8601DateOnly(date.Value) : null; + } + } +} diff --git a/createsend-netstandard/Transactional/ClassicEmail.cs b/createsend-netstandard/Transactional/ClassicEmail.cs new file mode 100644 index 0000000..4573e44 --- /dev/null +++ b/createsend-netstandard/Transactional/ClassicEmail.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Specialized; + +namespace createsend_dotnet.Transactional +{ + public interface IClassicEmail + { + RateLimited Send(EmailAddress from, string subject, string html, string text, EmailAddress replyTo = null, EmailAddress[] cc = null, EmailAddress[] bcc = null, Image[] images = null, Attachment[] attachments = null, bool trackOpens = true, bool trackClicks = true, bool inlineCss = true, string @group = null, string addRecipientsToListId = null, params EmailAddress[] to); + RateLimited Groups(); + } + + public interface IAgencyClassicEmail : IClassicEmail + { + RateLimited Send(string clientId, EmailAddress from, string subject, string html, string text, EmailAddress replyTo = null, EmailAddress[] cc = null, EmailAddress[] bcc = null, Image[] images = null, Attachment[] attachments = null, bool trackOpens = true, bool trackClicks = true, bool inlineCss = true, string @group = null, string addRecipientsToListId = null, params EmailAddress[] to); + RateLimited Groups(string clientId); + } + + internal class ClassicEmailContext : CreateSendBase, IClassicEmail, IAgencyClassicEmail + { + public ClassicEmailContext(AuthenticationDetails authenticationDetails, ICreateSendOptions options) + : base(authenticationDetails, options) + { + + } + + public RateLimited Send(EmailAddress from, string subject, string html, string text, EmailAddress replyTo = null, EmailAddress[] cc = null, EmailAddress[] bcc = null, Image[] images = null, Attachment[] attachments = null, bool trackOpens = true, bool trackClicks = true, bool inlineCss = true, string @group = null, string addRecipientsToListId = null, params EmailAddress[] to) + { + return Send(new ClassicEmail(from, replyTo, to, cc, bcc, subject, html, text, images, attachments, trackOpens, trackClicks, inlineCss, @group, addRecipientsToListId), this.CreateQueryString()); + } + + public RateLimited Send(string clientId, EmailAddress from, string subject, string html, string text, EmailAddress replyTo = null, EmailAddress[] cc = null, EmailAddress[] bcc = null, Image[] images = null, Attachment[] attachments = null, bool trackOpens = true, bool trackClicks = true, bool inlineCss = true, string @group = null, string addRecipientsToListId = null, params EmailAddress[] to) + { + if (clientId == null) throw new ArgumentNullException("clientId"); + + return Send(new ClassicEmail(from, replyTo, to, cc, bcc, subject, html, text, images, attachments, trackOpens, trackClicks, inlineCss, @group, addRecipientsToListId), this.CreateQueryString(clientId)); + } + + private RateLimited Send(ClassicEmail payload, NameValueCollection query) + { + return HttpPost>("/transactional/classicemail/send", query, payload); + } + + public RateLimited Groups() + { + return Groups(this.CreateQueryString()); + } + + public RateLimited Groups(string clientId) + { + if (clientId == null) throw new ArgumentNullException("clientId"); + + return Groups(this.CreateQueryString(clientId)); + } + + private RateLimited Groups(NameValueCollection query) + { + return HttpGet>("/transactional/classicemail/groups", query); + } + } + + internal class ClassicEmail + { + public EmailAddress From { get; set; } + public EmailAddress ReplyTo { get; set; } + public EmailAddress[] To { get; set; } + public EmailAddress[] CC { get; set; } + public EmailAddress[] BCC { get; set; } + public string Subject { get; set; } + public string Html { get; set; } + public string Text { get; set; } + public Image[] Images { get; set; } + public Attachment[] Attachments { get; set; } + public bool TrackOpens { get; set; } + public bool TrackClicks { get; set; } + public bool InlineCss { get; set; } + public string Group { get; set; } + public string AddRecipientsToListId { get; set; } + + public ClassicEmail(EmailAddress from, EmailAddress replyTo, EmailAddress[] to, EmailAddress[] cc, + EmailAddress[] bcc, string subject, string html, string text, Image[] images, Attachment[] attachments, + bool trackOpens, bool trackClicks, bool inlineCss, string @group, string addRecipientsToListId) + { + From = from; + ReplyTo = replyTo; + To = to; + CC = cc; + BCC = bcc; + Subject = subject; + Html = html; + Text = text; + Images = images; + Attachments = attachments; + TrackOpens = trackOpens; + TrackClicks = trackClicks; + InlineCss = inlineCss; + Group = @group; + AddRecipientsToListId = addRecipientsToListId; + } + } +} diff --git a/createsend-netstandard/Transactional/ClassicEmailDetail.cs b/createsend-netstandard/Transactional/ClassicEmailDetail.cs new file mode 100644 index 0000000..f295135 --- /dev/null +++ b/createsend-netstandard/Transactional/ClassicEmailDetail.cs @@ -0,0 +1,10 @@ +using System; + +namespace createsend_dotnet.Transactional +{ + public class ClassicEmailDetail + { + public string Group { get; set; } + public DateTimeOffset CreatedAt { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/DisplayedTimeZone.cs b/createsend-netstandard/Transactional/DisplayedTimeZone.cs new file mode 100644 index 0000000..cae6fef --- /dev/null +++ b/createsend-netstandard/Transactional/DisplayedTimeZone.cs @@ -0,0 +1,9 @@ + +namespace createsend_dotnet.Transactional +{ + public enum DisplayedTimeZone + { + Utc, + Client, + } +} diff --git a/createsend-netstandard/Transactional/EmailAddress.cs b/createsend-netstandard/Transactional/EmailAddress.cs new file mode 100644 index 0000000..b278150 --- /dev/null +++ b/createsend-netstandard/Transactional/EmailAddress.cs @@ -0,0 +1,43 @@ +using System.Net.Mail; + +namespace createsend_dotnet.Transactional +{ + public class EmailAddress + { + public string Name { get { return mail.DisplayName; } } + public string Email { get { return mail.Address; } } + + private readonly MailAddress mail; + + public EmailAddress(string email, string name) + { + mail = new MailAddress(email, name); + } + public EmailAddress(string email) : this(email, null) { } + + public static implicit operator EmailAddress(string email) + { + return FromString(email); + } + + public static implicit operator string(EmailAddress email) + { + return ToString(email); + } + + public static EmailAddress FromString(string email) + { + return email == null ? null : new EmailAddress(email); + } + + public static string ToString(EmailAddress email) + { + return email == null ? null : email.ToString(); + } + + public override string ToString() + { + return mail.ToString(); + } + } +} diff --git a/createsend-netstandard/Transactional/EmailAddressConverter.cs b/createsend-netstandard/Transactional/EmailAddressConverter.cs new file mode 100644 index 0000000..3173e2a --- /dev/null +++ b/createsend-netstandard/Transactional/EmailAddressConverter.cs @@ -0,0 +1,35 @@ +using System; +using Newtonsoft.Json; + +namespace createsend_dotnet.Transactional +{ + internal class EmailAddressConverter : JsonConverter + { + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(EmailAddress); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.String) + { + return new EmailAddress((string)reader.Value); + } + return null; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + } + else + { + writer.WriteValue(value.ToString()); + } + } + } +} diff --git a/createsend-netstandard/Transactional/Geolocation.cs b/createsend-netstandard/Transactional/Geolocation.cs new file mode 100644 index 0000000..07a7087 --- /dev/null +++ b/createsend-netstandard/Transactional/Geolocation.cs @@ -0,0 +1,13 @@ + +namespace createsend_dotnet.Transactional +{ + public class Geolocation + { + public double Longitude { get; set; } + public double Latitude { get; set; } + public string City { get; set; } + public string Region { get; set; } + public string CountryCode { get; set; } + public string CountryName { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/Image.cs b/createsend-netstandard/Transactional/Image.cs new file mode 100644 index 0000000..7f018e7 --- /dev/null +++ b/createsend-netstandard/Transactional/Image.cs @@ -0,0 +1,10 @@ + +namespace createsend_dotnet.Transactional +{ + public class Image + { + public string Name { get; set; } + public string Type { get; set; } + public byte[] Content { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/MailClient.cs b/createsend-netstandard/Transactional/MailClient.cs new file mode 100644 index 0000000..2499546 --- /dev/null +++ b/createsend-netstandard/Transactional/MailClient.cs @@ -0,0 +1,9 @@ + +namespace createsend_dotnet.Transactional +{ + public class MailClient + { + public string Name { get; set; } + public string Version { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/MessageBody.cs b/createsend-netstandard/Transactional/MessageBody.cs new file mode 100644 index 0000000..e26e943 --- /dev/null +++ b/createsend-netstandard/Transactional/MessageBody.cs @@ -0,0 +1,9 @@ + +namespace createsend_dotnet.Transactional +{ + public class MessageBody + { + public string Html { get; set; } + public string Text { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/MessageBuilder.cs b/createsend-netstandard/Transactional/MessageBuilder.cs new file mode 100644 index 0000000..227c96b --- /dev/null +++ b/createsend-netstandard/Transactional/MessageBuilder.cs @@ -0,0 +1,274 @@ +using System; +using System.Collections.Generic; + +namespace createsend_dotnet.Transactional +{ + public interface IMessageBuilder + { + IMessageBuilder From(EmailAddress from); + IMessageBuilder ReplyTo(EmailAddress replyTo); + IMessageBuilder Subject(string subject); + IMessageBuilder To(EmailAddress to); + IMessageBuilder To(EmailAddress[] to); + IMessageBuilder CC(EmailAddress cc); + IMessageBuilder CC(EmailAddress[] cc); + IMessageBuilder BCC(EmailAddress bcc); + IMessageBuilder BCC(EmailAddress[] bcc); + IMessageBuilder Text(string text); + IMessageBuilder Html(string html); + IMessageBuilder Attachment(Attachment attachment); + IMessageBuilder Attachment(Attachment[] attachments); + IMessageBuilder Image(Image image); + IMessageBuilder Image(Image[] images); + IMessageBuilder Data(IDictionary data); + IMessageBuilder TrackOpens(bool trackOpens = true); + IMessageBuilder TrackClicks(bool trackClicks = true); + IMessageBuilder InlineCss(bool inlineCss = true); + IMessageBuilder Group(string @group); + IMessageBuilder AddRecipientsToList(bool addRecipientsToList); + IMessageBuilder AddRecipientsToListId(string addRecipientsToListId); + + RateLimited Send(); + RateLimited Send(Guid smartEmailId); + } + + public interface IAgencyMessageBuilder : IMessageBuilder + { + RateLimited Send(string clientId); + } + + internal class MessageBuilder : IMessageBuilder, IAgencyMessageBuilder + { + private readonly SmartEmailContext smart; + private readonly ClassicEmailContext classic; + private readonly List to; + private readonly List cc; + private readonly List bcc; + private readonly List attachments; + private readonly List images; + private IDictionary data; + private string subject; + private EmailAddress from; + private EmailAddress replyTo; + private string html; + private string text; + private bool trackOpens = true; + private bool trackClicks = true; + private bool inlineCss = true; + private string @group; + private bool addRecipientsToList = true; + private string listId; + + public MessageBuilder(SmartEmailContext smart, ClassicEmailContext classic) + { + this.smart = smart; + this.classic = classic; + + to = new List(); + cc = new List(); + bcc = new List(); + attachments = new List(); + images = new List(); + } + + public IMessageBuilder From(EmailAddress from) + { + if(from == null) throw new ArgumentNullException("from"); + + this.from = from; + return this; + } + + public IMessageBuilder ReplyTo(EmailAddress replyTo) + { + if(replyTo == null) throw new ArgumentNullException("replyTo"); + + this.replyTo = replyTo; + return this; + } + + public IMessageBuilder Subject(string subject) + { + if(subject == null) throw new ArgumentNullException("subject"); + + this.subject = subject; + return this; + } + + public IMessageBuilder To(EmailAddress to) + { + if(to == null) throw new ArgumentNullException("to"); + + this.to.Add(to); + return this; + } + + public IMessageBuilder To(EmailAddress[] to) + { + if(to == null) throw new ArgumentNullException("to"); + if(to.Length == 0) throw new ArgumentException("Cannot be empty", "to"); + + this.to.AddRange(to); + return this; + } + + public IMessageBuilder CC(EmailAddress cc) + { + if(cc == null) throw new ArgumentNullException("cc"); + + this.cc.Add(cc); + return this; + } + + public IMessageBuilder CC(EmailAddress[] cc) + { + if(cc == null) throw new ArgumentNullException("cc"); + if (cc.Length == 0) throw new ArgumentException("Cannot be empty", "cc"); + + this.cc.AddRange(cc); + return this; + } + + public IMessageBuilder BCC(EmailAddress bcc) + { + if(bcc == null) throw new ArgumentNullException("bcc"); + + this.bcc.Add(bcc); + return this; + } + + public IMessageBuilder BCC(EmailAddress[] bcc) + { + if (bcc == null) throw new ArgumentNullException("bcc"); + if (bcc.Length == 0) throw new ArgumentException("Cannot be empty", "bcc"); + this.bcc.AddRange(bcc); + return this; + } + + public IMessageBuilder Text(string text) + { + if(text == null) throw new ArgumentNullException("text"); + + this.text = text; + return this; + } + + public IMessageBuilder Html(string html) + { + if(html == null) throw new ArgumentNullException("html"); + + this.html = html; + return this; + } + + public IMessageBuilder Attachment(Attachment attachment) + { + if(attachment == null) throw new ArgumentNullException("attachment"); + + this.attachments.Add(attachment); + return this; + } + + public IMessageBuilder Attachment(Attachment[] attachments) + { + if(attachments == null) throw new ArgumentNullException("attachments"); + if (attachments.Length == 0) throw new ArgumentException("Cannot be empty", "attachments"); + + this.attachments.AddRange(attachments); + return this; + } + + public IMessageBuilder Image(Image image) + { + if(image == null) throw new ArgumentNullException("image"); + + this.images.Add(image); + return this; + } + + public IMessageBuilder Image(Image[] images) + { + if(images == null) throw new ArgumentNullException("images"); + if(images.Length == 0) throw new ArgumentException("Cannot be empty", "images"); + + this.images.AddRange(images); + return this; + } + + public IMessageBuilder Data(IDictionary data) + { + if(data == null) throw new ArgumentNullException("data"); + + this.data = data; + return this; + } + + public IMessageBuilder TrackOpens(bool trackOpens = true) + { + this.trackOpens = trackOpens; + + return this; + } + + public IMessageBuilder TrackClicks(bool trackClicks = true) + { + this.trackClicks = trackClicks; + + return this; + } + + public IMessageBuilder InlineCss(bool inlineCss = true) + { + this.inlineCss = inlineCss; + + return this; + } + + public IMessageBuilder Group(string @group) + { + if(@group == null) throw new ArgumentNullException("group"); + + this.@group = @group; + return this; + } + + public IMessageBuilder AddRecipientsToList(bool addRecipientsToList) + { + this.addRecipientsToList = addRecipientsToList; + + return this; + } + + public IMessageBuilder AddRecipientsToListId(string listId) + { + this.listId = listId; + + return this; + } + + public RateLimited Send() + { + return classic.Send(from, subject, html, text, replyTo, cc.ToArray(), bcc.ToArray(), images.ToArray(), + attachments.ToArray(), trackOpens, trackClicks, inlineCss, @group, listId, to.ToArray()); + } + + public RateLimited Send(string clientId) + { + return classic.Send(clientId, from, subject, html, text, replyTo, cc.ToArray(), bcc.ToArray(), images.ToArray(), + attachments.ToArray(), trackOpens, trackClicks, inlineCss, @group, listId, to.ToArray()); + } + + + public RateLimited Send(Guid smartEmailId) + { + return smart.Send( + smartEmailId, + cc.ToArray(), + bcc.ToArray(), + attachments.ToArray(), + data, + addRecipientsToList, + to.ToArray()); + } + } +} diff --git a/createsend-netstandard/Transactional/MessageContent.cs b/createsend-netstandard/Transactional/MessageContent.cs new file mode 100644 index 0000000..c970487 --- /dev/null +++ b/createsend-netstandard/Transactional/MessageContent.cs @@ -0,0 +1,18 @@ + +using System.Collections.Generic; + +namespace createsend_dotnet.Transactional +{ + public class MessageContent + { + public EmailAddress From { get; set; } + public EmailAddress ReplyTo { get; set; } + public string Subject { get; set; } + public EmailAddress[] To { get; set; } + public EmailAddress[] CC { get; set; } + public EmailAddress BCC { get; set; } + public MessageBody Body { get; set; } + public Dictionary Data { get; set; } + public AttachmentMetadata[] Attachments { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/MessageDetail.cs b/createsend-netstandard/Transactional/MessageDetail.cs new file mode 100644 index 0000000..39ff511 --- /dev/null +++ b/createsend-netstandard/Transactional/MessageDetail.cs @@ -0,0 +1,24 @@ +using System; + +namespace createsend_dotnet.Transactional +{ + public class MessageDetail + { + public Guid MessageId { get; set; } + public string Status { get; set; } + public DateTimeOffset SentAt { get; set; } + + public bool CanBeResent { get; set; } + public EmailAddress Recipient { get; set; } + + public MessageContent Message { get; set; } + public string Group { get; set; } + public Guid? SmartEmailId { get; set; } + + public long TotalOpens { get; set; } + public long TotalClicks { get; set; } + + public SubscriberAction[] Opens { get; set; } + public SubscriberAction[] Clicks { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/MessageListDetail.cs b/createsend-netstandard/Transactional/MessageListDetail.cs new file mode 100644 index 0000000..bff5a68 --- /dev/null +++ b/createsend-netstandard/Transactional/MessageListDetail.cs @@ -0,0 +1,18 @@ +using System; + +namespace createsend_dotnet.Transactional +{ + public class MessageListDetail + { + public Guid MessageId { get; set; } + public EmailAddress Recipient { get; set; } + public EmailAddress From { get; set; } + public string Subject { get; set; } + public string Status { get; set; } + public string Group { get; set; } + public Guid? SmartEmailId { get; set; } + public long TotalOpens { get; set; } + public long TotalClicks { get; set; } + public bool CanBeResent { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/MessageListStatus.cs b/createsend-netstandard/Transactional/MessageListStatus.cs new file mode 100644 index 0000000..a102fb5 --- /dev/null +++ b/createsend-netstandard/Transactional/MessageListStatus.cs @@ -0,0 +1,11 @@ + +namespace createsend_dotnet.Transactional +{ + public enum MessageListStatus + { + All, + Delivered, + Bounced, + Spam, + } +} diff --git a/createsend-netstandard/Transactional/Messages.cs b/createsend-netstandard/Transactional/Messages.cs new file mode 100644 index 0000000..9d2e2fd --- /dev/null +++ b/createsend-netstandard/Transactional/Messages.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Specialized; + +namespace createsend_dotnet.Transactional +{ + public interface IMessages + { + RateLimited Resend(Guid messageId); + RateLimited Details(Guid messageId, bool statistics = false); + RateLimited List(Guid? sentAfterId = null, Guid? sentBeforeId = null, int count = 50, string @group = null, MessageListStatus status = MessageListStatus.All); + } + + public interface IAgencyMessages : IMessages + { + RateLimited List(string clientId, Guid? sentAfterId = null, Guid? sentBeforeId = null, int count = 50, string @group = null, MessageListStatus status = MessageListStatus.All); + } + + internal class MessagesContext : CreateSendBase, IMessages, IAgencyMessages + { + public MessagesContext(AuthenticationDetails authenticationDetails, ICreateSendOptions options) + : base(authenticationDetails, options) + { + + } + + public RateLimited Resend(Guid messageId) + { + if (messageId == Guid.Empty) throw new ArgumentException("Must not be empty", "messageId"); + + return Resend(messageId, new NameValueCollection()); + } + + private RateLimited Resend(Guid messageId, NameValueCollection query) + { + return HttpPost>(String.Format("/transactional/messages/{0}/resend", messageId), query, null); + } + + public RateLimited Details(Guid messageId, bool statistics = false) + { + return Details(messageId, CreateQueryString(statistics)); + } + + private RateLimited Details(Guid messageId, NameValueCollection query) + { + return HttpGet>(String.Format("/transactional/messages/{0}", messageId), query); + } + + public RateLimited List(Guid? sentAfterId = null, Guid? sentBeforeId = null, int count = 50, string @group = null, MessageListStatus status = MessageListStatus.All) + { + return ListMessages(CreateQueryString(@group, sentAfterId, sentBeforeId, count, status)); + } + + public RateLimited List(string clientId, Guid? sentAfterId = null, Guid? sentBeforeId = null, int count = 50, string @group = null, MessageListStatus status = MessageListStatus.All) + { + if (clientId == null) throw new ArgumentNullException("clientId"); + + return ListMessages(CreateQueryString(@group, sentAfterId, sentBeforeId, count, status, clientId)); + } + + private RateLimited ListMessages(NameValueCollection query) + { + return HttpGet>("/transactional/messages", query); + } + + private NameValueCollection CreateQueryString(bool statistics) + { + return new NameValueCollection + { + { "statistics", statistics.Encode() }, + }; + } + + private NameValueCollection CreateQueryString(string @group, Guid? sentAfterId, Guid? sentBeforeId, int count, MessageListStatus status, string clientId = null) + { + return this.CreateQueryString( + clientId, + query: new NameValueCollection + { + { "group", @group.Encode() }, + { "sentAfterId", sentAfterId.Encode() }, + { "sentBeforeId", sentBeforeId.Encode() }, + { "count", count.Encode() }, + { "status", status.Encode() }, + }); + } + } +} diff --git a/createsend-netstandard/Transactional/PropertyContent.cs b/createsend-netstandard/Transactional/PropertyContent.cs new file mode 100644 index 0000000..8b90d7a --- /dev/null +++ b/createsend-netstandard/Transactional/PropertyContent.cs @@ -0,0 +1,11 @@ + +namespace createsend_dotnet.Transactional +{ + public class PropertyContent + { + public string Html { get; set; } + public string Text { get; set; } + public string[] EmailVariables { get; set; } + public bool InlineCss { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/RateLimitStatus.cs b/createsend-netstandard/Transactional/RateLimitStatus.cs new file mode 100644 index 0000000..9e9109a --- /dev/null +++ b/createsend-netstandard/Transactional/RateLimitStatus.cs @@ -0,0 +1,10 @@ + +namespace createsend_dotnet.Transactional +{ + public class RateLimitStatus + { + public uint Remaining { get; set; } + public uint Credit { get; set; } + public uint Reset { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/RateLimited`1.cs b/createsend-netstandard/Transactional/RateLimited`1.cs new file mode 100644 index 0000000..61d6403 --- /dev/null +++ b/createsend-netstandard/Transactional/RateLimited`1.cs @@ -0,0 +1,15 @@ + +namespace createsend_dotnet.Transactional +{ + public sealed class RateLimited + { + public T Response { get; private set; } + public RateLimitStatus RateLimit { get; private set; } + + public RateLimited(T response, RateLimitStatus rateLimit) + { + Response = response; + RateLimit = rateLimit; + } + } +} diff --git a/createsend-netstandard/Transactional/RecipientStatus.cs b/createsend-netstandard/Transactional/RecipientStatus.cs new file mode 100644 index 0000000..5a074a2 --- /dev/null +++ b/createsend-netstandard/Transactional/RecipientStatus.cs @@ -0,0 +1,11 @@ +using System; + +namespace createsend_dotnet.Transactional +{ + public class RecipientStatus + { + public Guid MessageId { get; set; } + public EmailAddress Recipient { get; set; } + public string Status { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/RequiredClientIdentifierException.cs b/createsend-netstandard/Transactional/RequiredClientIdentifierException.cs new file mode 100644 index 0000000..9a54b06 --- /dev/null +++ b/createsend-netstandard/Transactional/RequiredClientIdentifierException.cs @@ -0,0 +1,40 @@ +using System; +using System.Runtime.Serialization; + +namespace createsend_dotnet.Transactional +{ + public class RequiredClientIdentierException : Exception + { + private const string defaultMessage = + "ClientId has not been provided. Note to agencies: If you are using an account API key, this is required as you need to specify the client"; + public RequiredClientIdentierException() + : base(defaultMessage) + { + + } + + public RequiredClientIdentierException(string message) + : base(message) + { + + } + + public RequiredClientIdentierException(string message, Exception exception) + : base(message, exception) + { + + } + + public RequiredClientIdentierException(Exception exception) + : base(defaultMessage, exception) + { + + } + + public RequiredClientIdentierException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + + } + } +} diff --git a/createsend-netstandard/Transactional/SmartEmail.cs b/createsend-netstandard/Transactional/SmartEmail.cs new file mode 100644 index 0000000..5fc886b --- /dev/null +++ b/createsend-netstandard/Transactional/SmartEmail.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace createsend_dotnet.Transactional +{ + public interface ISmartEmail + { + RateLimited Send(Guid smartEmailId, EmailAddress[] cc = null, EmailAddress[] bcc = null, Attachment[] attachments = null, IDictionary data = null, bool addRecipientsToList = true, params EmailAddress[] to); + RateLimited Details(Guid smartEmailId); + RateLimited List(SmartEmailListStatus status = SmartEmailListStatus.All); + } + + public interface IAgencySmartEmail : ISmartEmail + { + RateLimited List(string clientId, SmartEmailListStatus status = SmartEmailListStatus.All); + } + + internal class SmartEmailContext : CreateSendBase, ISmartEmail, IAgencySmartEmail + { + public SmartEmailContext(AuthenticationDetails authenticationDetails, ICreateSendOptions options) + : base(authenticationDetails, options) + { + + } + + public RateLimited Send(Guid smartEmailId, EmailAddress[] cc = null, EmailAddress[] bcc = null, Attachment[] attachments = null, IDictionary data = null, bool addRecipientsToList = true, params EmailAddress[] to) + { + return Send(smartEmailId, new SmartEmail(to, cc, bcc, attachments, data, addRecipientsToList), new NameValueCollection()); + } + + private RateLimited Send(Guid smartEmailId, SmartEmail payload, NameValueCollection query) + { + return HttpPost>(String.Format("/transactional/smartemail/{0}/send", smartEmailId), query, payload); + } + + public RateLimited List(SmartEmailListStatus status = SmartEmailListStatus.All) + { + return List(CreateQueryString(status)); + } + + public RateLimited List(string clientId, SmartEmailListStatus status = SmartEmailListStatus.All) + { + if (clientId == null) throw new ArgumentNullException("clientId"); + + return List(CreateQueryString(status, clientId)); + } + + private RateLimited List(NameValueCollection query) + { + return HttpGet>("/transactional/smartemail", query); + } + + public RateLimited Details(Guid smartEmailId) + { + return Details(smartEmailId, new NameValueCollection()); + } + + private RateLimited Details(Guid smartEmailId, NameValueCollection query) + { + return HttpGet>(String.Format("/transactional/smartemail/{0}", smartEmailId), query); + } + + private NameValueCollection CreateQueryString(SmartEmailListStatus status, string clientId = null) + { + return this.CreateQueryString( + clientId, + query: new NameValueCollection + { + { "status", status.Encode() } + }); + } + } + + internal class SmartEmail + { + public EmailAddress[] To { get; private set; } + public EmailAddress[] CC { get; private set; } + public EmailAddress[] BCC { get; private set; } + public Attachment[] Attachments { get; private set; } + public IDictionary Data { get; private set; } + public bool AddRecipientsToList { get; private set; } + + public SmartEmail(EmailAddress[] to, EmailAddress[] cc, EmailAddress[] bcc, Attachment[] attachments, + IDictionary data, bool addRecipientsToList) + { + To = to; + CC = cc; + BCC = bcc; + Attachments = attachments; + Data = data; + AddRecipientsToList = addRecipientsToList; + } + } +} diff --git a/createsend-netstandard/Transactional/SmartEmailDetail.cs b/createsend-netstandard/Transactional/SmartEmailDetail.cs new file mode 100644 index 0000000..e98afee --- /dev/null +++ b/createsend-netstandard/Transactional/SmartEmailDetail.cs @@ -0,0 +1,13 @@ +using System; + +namespace createsend_dotnet.Transactional +{ + public class SmartEmailDetail + { + public Guid SmartEmailId { get; set; } + public string Name { get; set; } + public DateTimeOffset CreatedAt { get; set; } + public string Status { get; set; } + public SmartEmailProperties Properties { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/SmartEmailListDetail.cs b/createsend-netstandard/Transactional/SmartEmailListDetail.cs new file mode 100644 index 0000000..1fa05e0 --- /dev/null +++ b/createsend-netstandard/Transactional/SmartEmailListDetail.cs @@ -0,0 +1,12 @@ +using System; + +namespace createsend_dotnet.Transactional +{ + public class SmartEmailListDetail + { + public Guid Id { get; set; } + public string Name { get; set; } + public DateTimeOffset CreatedAt { get; set; } + public string Status { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/SmartEmailListStatus.cs b/createsend-netstandard/Transactional/SmartEmailListStatus.cs new file mode 100644 index 0000000..effd736 --- /dev/null +++ b/createsend-netstandard/Transactional/SmartEmailListStatus.cs @@ -0,0 +1,10 @@ + +namespace createsend_dotnet.Transactional +{ + public enum SmartEmailListStatus + { + All, + Active, + Draft, + } +} diff --git a/createsend-netstandard/Transactional/SmartEmailProperties.cs b/createsend-netstandard/Transactional/SmartEmailProperties.cs new file mode 100644 index 0000000..3ea048e --- /dev/null +++ b/createsend-netstandard/Transactional/SmartEmailProperties.cs @@ -0,0 +1,13 @@ + +namespace createsend_dotnet.Transactional +{ + public class SmartEmailProperties + { + public EmailAddress From { get; set; } + public EmailAddress ReplyTo { get; set; } + public string Subject { get; set; } + public PropertyContent Content { get; set; } + public string TextPreviewUrl { get; set; } + public string HtmlPreviewUrl { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/Statistics.cs b/createsend-netstandard/Transactional/Statistics.cs new file mode 100644 index 0000000..5e0bd8f --- /dev/null +++ b/createsend-netstandard/Transactional/Statistics.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Specialized; + +namespace createsend_dotnet.Transactional +{ + public interface IStatistics + { + RateLimited Statistics(Guid? smartEmailId = null, string @group = null, DateTime? from = null, DateTime? to = null, DisplayedTimeZone timezone = DisplayedTimeZone.Client); + } + + public interface IAgencyStatistics : IStatistics + { + RateLimited Statistics(string clientId, Guid? smartEmailId = null, string @group = null, DateTime? from = null, DateTime? to = null, DisplayedTimeZone timezone = DisplayedTimeZone.Client); + } + + internal class StatisticsContext : CreateSendBase, IStatistics, IAgencyStatistics + { + public StatisticsContext(AuthenticationDetails authenticationDetails, ICreateSendOptions options) + : base(authenticationDetails, options) + { + + } + + public RateLimited Statistics(Guid? smartEmailId = null, string @group = null, DateTime? from = null, DateTime? to = null, DisplayedTimeZone timezone = DisplayedTimeZone.Client) + { + return Statistics(CreateQueryString(smartEmailId, @group, from, to, timezone)); + } + + public RateLimited Statistics(string clientId, Guid? smartEmailId = null, string @group = null, DateTime? from = null, DateTime? to = null, DisplayedTimeZone timezone = DisplayedTimeZone.Client) + { + if (clientId == null) throw new ArgumentNullException("clientId"); + + return Statistics(CreateQueryString(smartEmailId, @group, from, to, timezone, clientId)); + } + + private RateLimited Statistics(NameValueCollection query) + { + return HttpGet>("/transactional/statistics", query); + } + + private NameValueCollection CreateQueryString(Guid? smartEmailId, string @group, DateTime? from, DateTime? to, DisplayedTimeZone timezone, string clientId = null) + { + return this.CreateQueryString( + clientId, + query: new NameValueCollection + { + { "smartemailid", smartEmailId.Encode() }, + { "group", @group.Encode() }, + { "from", from.EncodeIso8601DateOnly() }, + { "to", to.EncodeIso8601DateOnly() }, + { "timezone", timezone.Encode() } + }); + } + } + + public class Statistics + { + public StatisticsQuery Query { get; set; } + public long Sent { get; set; } + public long Bounces { get; set; } + public long Delivered { get; set; } + public long Opened { get; set; } + public long Clicked { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/StatisticsQuery.cs b/createsend-netstandard/Transactional/StatisticsQuery.cs new file mode 100644 index 0000000..ff1ff96 --- /dev/null +++ b/createsend-netstandard/Transactional/StatisticsQuery.cs @@ -0,0 +1,13 @@ +using System; + +namespace createsend_dotnet.Transactional +{ + public class StatisticsQuery + { + public Guid? SmartEmailId { get; set; } + public string Group { get; set; } + public DateTime From { get; set; } + public DateTime To { get; set; } + public string TimeZone { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/SubscriberAction.cs b/createsend-netstandard/Transactional/SubscriberAction.cs new file mode 100644 index 0000000..2271c92 --- /dev/null +++ b/createsend-netstandard/Transactional/SubscriberAction.cs @@ -0,0 +1,13 @@ +using System; + +namespace createsend_dotnet.Transactional +{ + public class SubscriberAction + { + public EmailAddress EmailAddress { get; set; } + public DateTimeOffset Date { get; set; } + public string IpAddress { get; set; } + public Geolocation Geolocation { get; set; } + public MailClient MailClient { get; set; } + } +} diff --git a/createsend-netstandard/Transactional/Transactional.cs b/createsend-netstandard/Transactional/Transactional.cs new file mode 100644 index 0000000..35b8f93 --- /dev/null +++ b/createsend-netstandard/Transactional/Transactional.cs @@ -0,0 +1,86 @@ +using System; + +namespace createsend_dotnet.Transactional +{ + public interface ITransactional : IStatistics + { + IClassicEmail ClassicEmail { get; } + ISmartEmail SmartEmail { get; } + IMessageBuilder MessageBuilder(); + IMessages Messages { get; } + } + + public interface IAgencyTransactional : IAgencyStatistics + { + IAgencyClassicEmail ClassicEmail { get; } + IAgencySmartEmail SmartEmail { get; } + IAgencyMessageBuilder MessageBuilder(); + IAgencyMessages Messages { get; } + } + + internal class TransactionalContext : ITransactional, IAgencyTransactional + { + private readonly ClassicEmailContext classicEmail; + private readonly SmartEmailContext smartEmail; + private readonly MessagesContext messages; + private readonly StatisticsContext statistics; + + public TransactionalContext(AuthenticationDetails auth, ICreateSendOptions options) + { + classicEmail = new ClassicEmailContext(auth, options); + smartEmail = new SmartEmailContext(auth, options); + messages = new MessagesContext(auth, options); + statistics = new StatisticsContext(auth, options); + } + + IClassicEmail ITransactional.ClassicEmail + { + get { return classicEmail; } + } + + IAgencyClassicEmail IAgencyTransactional.ClassicEmail + { + get { return classicEmail; } + } + + ISmartEmail ITransactional.SmartEmail + { + get { return smartEmail; } + } + + IAgencySmartEmail IAgencyTransactional.SmartEmail + { + get { return smartEmail; } + } + + IMessages ITransactional.Messages + { + get { return messages; } + } + + IAgencyMessages IAgencyTransactional.Messages + { + get { return messages; } + } + + IMessageBuilder ITransactional.MessageBuilder() + { + return new MessageBuilder(smartEmail, classicEmail); + } + + IAgencyMessageBuilder IAgencyTransactional.MessageBuilder() + { + return new MessageBuilder(smartEmail, classicEmail); + } + + public RateLimited Statistics(Guid? smartEmailId = null, string @group = null, DateTime? from = null, DateTime? to = null, DisplayedTimeZone timezone = DisplayedTimeZone.Client) + { + return statistics.Statistics(smartEmailId, @group, from, to, timezone); + } + + public RateLimited Statistics(string clientId, Guid? smartEmailId = null, string @group = null, DateTime? from = null, DateTime? to = null, DisplayedTimeZone timezone = DisplayedTimeZone.Client) + { + return statistics.Statistics(clientId, smartEmailId, @group, from, to, timezone); + } + } +} diff --git a/createsend-netstandard/createsend-dotnet.nuspec b/createsend-netstandard/createsend-dotnet.nuspec new file mode 100644 index 0000000..1520002 --- /dev/null +++ b/createsend-netstandard/createsend-dotnet.nuspec @@ -0,0 +1,19 @@ + + + + $id$ + createsend-dotnet + $version$ + Campaign Monitor + A .NET library for the Campaign Monitor API with enhancements for Transactional + http://opensource.org/licenses/mit-license + http://campaignmonitor.github.io/createsend-dotnet/ + api campaign monitor .net transactional + + + + + + + + diff --git a/createsend-netstandard/createsend-netstandard.csproj b/createsend-netstandard/createsend-netstandard.csproj new file mode 100644 index 0000000..509776b --- /dev/null +++ b/createsend-netstandard/createsend-netstandard.csproj @@ -0,0 +1,11 @@ + + + + netstandard1.6 + + + + + + + \ No newline at end of file From e670a6fc548c3117617b08346baef0d3672da171 Mon Sep 17 00:00:00 2001 From: davidkirkham Date: Thu, 20 Apr 2017 12:06:12 +0100 Subject: [PATCH 02/11] Reworked for .net core [untested] --- createsend-netstandard/CreateSendBase.cs | 58 ++-- createsend-netstandard/CreateSendOptions.cs | 29 +- createsend-netstandard/General.cs | 59 +++-- createsend-netstandard/HttpHelper.cs | 164 ++++++------ createsend-netstandard/List.cs | 249 +++++++++--------- createsend-netstandard/Models/MailAddress.cs | 33 +++ createsend-netstandard/Models/Result.cs | 52 ++-- .../Properties/AssemblyInfo.cs | 36 --- .../Transactional/EmailAddress.cs | 25 +- .../RequiredClientIdentifierException.cs | 13 +- .../createsend-netstandard.csproj | 6 + 11 files changed, 357 insertions(+), 367 deletions(-) create mode 100644 createsend-netstandard/Models/MailAddress.cs delete mode 100644 createsend-netstandard/Properties/AssemblyInfo.cs diff --git a/createsend-netstandard/CreateSendBase.cs b/createsend-netstandard/CreateSendBase.cs index f0d33d5..837c156 100644 --- a/createsend-netstandard/CreateSendBase.cs +++ b/createsend-netstandard/CreateSendBase.cs @@ -2,13 +2,12 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Text; -using System.Web; +using Microsoft.AspNetCore.WebUtilities; namespace createsend_dotnet { public abstract class CreateSendBase { - public AuthenticationDetails AuthDetails { get; set; } private readonly ICreateSendOptions options; public CreateSendBase(AuthenticationDetails auth, ICreateSendOptions options = null) @@ -18,36 +17,16 @@ public CreateSendBase(AuthenticationDetails auth, ICreateSendOptions options = n Authenticate(auth); } + public AuthenticationDetails AuthDetails { get; set; } + public void Authenticate(AuthenticationDetails auth) { AuthDetails = auth; } - public OAuthTokenDetails RefreshToken() + public string HttpDelete(string path, NameValueCollection queryArguments) { - if (AuthDetails == null || - !(AuthDetails is OAuthAuthenticationDetails) || - string.IsNullOrEmpty( - (AuthDetails as OAuthAuthenticationDetails) - .RefreshToken)) - throw new InvalidOperationException( - "You cannot refresh an OAuth token when you don't have a refresh token."); - - string refreshToken = (this.AuthDetails as OAuthAuthenticationDetails) - .RefreshToken; - string body = string.Format( - "grant_type=refresh_token&refresh_token={0}", - HttpUtility.UrlEncode(refreshToken)); - - OAuthTokenDetails newTokenDetails = - HttpHelper.Post( - null, "/token", new NameValueCollection(), body, - options.BaseOAuthUri, - HttpHelper.APPLICATION_FORM_URLENCODED_CONTENT_TYPE); - Authenticate( - new OAuthAuthenticationDetails( - newTokenDetails.access_token, newTokenDetails.refresh_token)); - return newTokenDetails; + return HttpHelper.Delete(AuthDetails, path, queryArguments, options.BaseUri, HttpHelper.APPLICATION_JSON_CONTENT_TYPE); } public U HttpGet(string path, NameValueCollection queryArguments) @@ -79,9 +58,30 @@ public U HttpPut(string path, NameValueCollection queryArguments, T payloa return HttpHelper.Put(AuthDetails, path, queryArguments, payload, options.BaseUri, HttpHelper.APPLICATION_JSON_CONTENT_TYPE); } - public string HttpDelete(string path, NameValueCollection queryArguments) + public OAuthTokenDetails RefreshToken() { - return HttpHelper.Delete(AuthDetails, path, queryArguments, options.BaseUri, HttpHelper.APPLICATION_JSON_CONTENT_TYPE); + if (AuthDetails == null || + !(AuthDetails is OAuthAuthenticationDetails) || + string.IsNullOrEmpty( + (AuthDetails as OAuthAuthenticationDetails) + .RefreshToken)) + throw new InvalidOperationException( + "You cannot refresh an OAuth token when you don't have a refresh token."); + + string refreshToken = (this.AuthDetails as OAuthAuthenticationDetails) + .RefreshToken; + string body = QueryHelpers.AddQueryString("", "grant_type", "refresh_token"); + body = QueryHelpers.AddQueryString(body, "refresh_token", refreshToken); + + OAuthTokenDetails newTokenDetails = + HttpHelper.Post( + null, "/token", new NameValueCollection(), body, + options.BaseOAuthUri, + HttpHelper.APPLICATION_FORM_URLENCODED_CONTENT_TYPE); + Authenticate( + new OAuthAuthenticationDetails( + newTokenDetails.access_token, newTokenDetails.refresh_token)); + return newTokenDetails; } } -} +} \ No newline at end of file diff --git a/createsend-netstandard/CreateSendOptions.cs b/createsend-netstandard/CreateSendOptions.cs index 0606a60..a066f70 100644 --- a/createsend-netstandard/CreateSendOptions.cs +++ b/createsend-netstandard/CreateSendOptions.cs @@ -1,29 +1,16 @@ using System; -using System.Configuration; namespace createsend_dotnet { public static class CreateSendOptions { - static string base_uri; - static string base_oauth_uri; + private static string base_oauth_uri; + private static string base_uri; static CreateSendOptions() { - base_uri = string.IsNullOrEmpty( - ConfigurationManager.AppSettings["base_uri"]) ? - "https://api.createsend.com/api/v3.1" : - ConfigurationManager.AppSettings["base_uri"]; - base_oauth_uri = string.IsNullOrEmpty( - ConfigurationManager.AppSettings["base_oauth_uri"]) ? - "https://api.createsend.com/oauth" : - ConfigurationManager.AppSettings["base_oauth_uri"]; - } - - public static string BaseUri - { - get { return base_uri; } - set { base_uri = value; } + base_uri = "https://api.createsend.com/api/v3.1"; + base_oauth_uri = "https://api.createsend.com/oauth"; } public static string BaseOAuthUri @@ -32,6 +19,12 @@ public static string BaseOAuthUri set { base_oauth_uri = value; } } + public static string BaseUri + { + get { return base_uri; } + set { base_uri = value; } + } + public static string VersionNumber { get @@ -40,4 +33,4 @@ public static string VersionNumber } } } -} +} \ No newline at end of file diff --git a/createsend-netstandard/General.cs b/createsend-netstandard/General.cs index 5ba16ab..292b7ae 100644 --- a/createsend-netstandard/General.cs +++ b/createsend-netstandard/General.cs @@ -1,14 +1,19 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; -using System.Web; +using Microsoft.AspNetCore.WebUtilities; namespace createsend_dotnet { public class General : CreateSendBase { - public General() : base(null) { } - public General(AuthenticationDetails auth) : base(auth) { } + public General() : base(null) + { + } + + public General(AuthenticationDetails auth) : base(auth) + { + } public static string AuthorizeUrl( int clientID, @@ -29,12 +34,12 @@ public static string AuthorizeUrl( string state) { string result = CreateSendOptions.BaseOAuthUri; - result += string.Format( - "?client_id={0}&redirect_uri={1}&scope={2}", - clientID.ToString(), HttpUtility.UrlEncode(redirectUri), - HttpUtility.UrlEncode(scope)); + result += QueryHelpers.AddQueryString(result, "client_id", clientID.ToString()); + result += QueryHelpers.AddQueryString(result, "redirect_uri", redirectUri); if (!string.IsNullOrEmpty(state)) - result += "&state=" + HttpUtility.UrlEncode(state); + result += QueryHelpers.AddQueryString(result, "state", state); + result += QueryHelpers.AddQueryString(result, "scope", scope); + return result; } @@ -45,10 +50,10 @@ public static OAuthTokenDetails ExchangeToken( string code) { string body = "grant_type=authorization_code"; - body += string.Format("&client_id={0}", clientID.ToString()); - body += string.Format("&client_secret={0}", HttpUtility.UrlEncode(clientSecret)); - body += string.Format("&redirect_uri={0}", HttpUtility.UrlEncode(redirectUri)); - body += string.Format("&code={0}", HttpUtility.UrlEncode(code)); + body += QueryHelpers.AddQueryString(body, "client_id", clientID.ToString()); + body += QueryHelpers.AddQueryString(body, "client_secret", clientSecret); + body += QueryHelpers.AddQueryString(body, "redirect_uri", redirectUri); + body += QueryHelpers.AddQueryString(body, "code", code); return HttpHelper.Post( null, "/token", new NameValueCollection(), body, @@ -56,19 +61,9 @@ public static OAuthTokenDetails ExchangeToken( HttpHelper.APPLICATION_FORM_URLENCODED_CONTENT_TYPE); } - public DateTime SystemDate() - { - return HttpGet("/systemdate.json", null).SystemDate; - } - - public IEnumerable Countries(string apiKey) - { - return HttpGet("/countries.json", null); - } - - public IEnumerable Timezones() + public BillingDetails BillingDetails() { - return HttpGet("/timezones.json", null); + return HttpGet("/billingdetails.json", null); } public IEnumerable Clients() @@ -76,9 +71,9 @@ public IEnumerable Clients() return HttpGet("/clients.json", null); } - public BillingDetails BillingDetails() + public IEnumerable Countries(string apiKey) { - return HttpGet("/billingdetails.json", null); + return HttpGet("/countries.json", null); } public string ExternalSessionUrl( @@ -87,5 +82,15 @@ public string ExternalSessionUrl( return HttpPut( "/externalsession.json", null, options).SessionUrl; } + + public DateTime SystemDate() + { + return HttpGet("/systemdate.json", null).SystemDate; + } + + public IEnumerable Timezones() + { + return HttpGet("/timezones.json", null); + } } -} +} \ No newline at end of file diff --git a/createsend-netstandard/HttpHelper.cs b/createsend-netstandard/HttpHelper.cs index a642ab3..42a385e 100644 --- a/createsend-netstandard/HttpHelper.cs +++ b/createsend-netstandard/HttpHelper.cs @@ -1,24 +1,39 @@ using System; using System.Collections.Generic; using System.Text; -using System.Net; +using System.Net.Http; using System.Collections.Specialized; -using System.Web; using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using System.IO; + #if SUPPORTED_FRAMEWORK_VERSION using createsend_dotnet.Transactional; #endif + using Newtonsoft.Json; namespace createsend_dotnet { public static class HttpHelper { - public const string APPLICATION_JSON_CONTENT_TYPE = "application/json"; public const string APPLICATION_FORM_URLENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded"; + public const string APPLICATION_JSON_CONTENT_TYPE = "application/json"; + + public static string Delete(AuthenticationDetails auth, string path, NameValueCollection queryArguments) + { + return MakeRequest("DELETE", auth, path, queryArguments, null); + } + + // NEW + public static string Delete(AuthenticationDetails auth, string path, NameValueCollection queryArguments, string baseUri, string contentType) + { + return MakeRequestAsync("DELETE", auth, path, queryArguments, null, baseUri, contentType).Result; + } public static U Get( - AuthenticationDetails auth, + AuthenticationDetails auth, string path, NameValueCollection queryArguments) { @@ -41,7 +56,7 @@ public static U Get( NameValueCollection queryArguments) where EX : ErrorResult { - return MakeRequest("GET", auth, path, queryArguments, null, baseUri, APPLICATION_JSON_CONTENT_TYPE); + return MakeRequestAsync("GET", auth, path, queryArguments, null, baseUri, APPLICATION_JSON_CONTENT_TYPE).Result; } public static U Post( @@ -75,7 +90,7 @@ public static U Post( where T : class where EX : ErrorResult { - return MakeRequest("POST", auth, path, queryArguments, payload, baseUri, contentType); + return MakeRequestAsync("POST", auth, path, queryArguments, payload, baseUri, contentType).Result; } public static U Put(AuthenticationDetails auth, string path, NameValueCollection queryArguments, T payload) where T : class @@ -86,21 +101,10 @@ public static U Put(AuthenticationDetails auth, string path, NameValueColl // NEW public static U Put(AuthenticationDetails auth, string path, NameValueCollection queryArguments, T payload, string baseUri, string contentType) where T : class { - return MakeRequest("PUT", auth, path, queryArguments, payload, baseUri, contentType); - } - - public static string Delete(AuthenticationDetails auth, string path, NameValueCollection queryArguments) - { - return MakeRequest("DELETE", auth, path, queryArguments, null); + return MakeRequestAsync("PUT", auth, path, queryArguments, payload, baseUri, contentType).Result; } - // NEW - public static string Delete(AuthenticationDetails auth, string path, NameValueCollection queryArguments, string baseUri, string contentType) - { - return MakeRequest("DELETE", auth, path, queryArguments, null, baseUri, contentType); - } - - static U MakeRequest( + private static U MakeRequest( string method, AuthenticationDetails auth, string path, @@ -109,11 +113,11 @@ static U MakeRequest( where T : class where EX : ErrorResult { - return MakeRequest(method, auth, path, queryArguments, - payload, CreateSendOptions.BaseUri, APPLICATION_JSON_CONTENT_TYPE); + return MakeRequestAsync(method, auth, path, queryArguments, + payload, CreateSendOptions.BaseUri, APPLICATION_JSON_CONTENT_TYPE).Result; } - static U MakeRequest( + private static async Task MakeRequestAsync( string method, AuthenticationDetails auth, string path, @@ -127,68 +131,78 @@ static U MakeRequest( JsonSerializerSettings serialiserSettings = new JsonSerializerSettings(); serialiserSettings.NullValueHandling = NullValueHandling.Ignore; serialiserSettings.MissingMemberHandling = MissingMemberHandling.Ignore; - #if SUPPORTED_FRAMEWORK_VERSION +#if SUPPORTED_FRAMEWORK_VERSION serialiserSettings.Converters.Add(new EmailAddressConverter()); - #endif +#endif string uri = baseUri + path + NameValueCollectionExtension.ToQueryString(queryArguments); - HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri); - req.Method = method; - req.ContentType = contentType; - req.AutomaticDecompression = DecompressionMethods.GZip; + HttpClient client = new HttpClient(); + + //HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri); + //req.Method = method; + //req.ContentType = contentType; + //req.AutomaticDecompression = DecompressionMethods.GZip; if (auth != null) { if (auth is OAuthAuthenticationDetails) { OAuthAuthenticationDetails oauthDetails = auth as OAuthAuthenticationDetails; - req.Headers["Authorization"] = "Bearer " + oauthDetails.AccessToken; + client.DefaultRequestHeaders.Add("Authorization", "Bearer " + oauthDetails.AccessToken); } else if (auth is ApiKeyAuthenticationDetails) { ApiKeyAuthenticationDetails apiKeyDetails = auth as ApiKeyAuthenticationDetails; - req.Headers["Authorization"] = "Basic " + Convert.ToBase64String( - Encoding.Default.GetBytes(apiKeyDetails.ApiKey + ":x")); + client.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String( + Encoding.GetEncoding(0).GetBytes(apiKeyDetails.ApiKey + ":x"))); } else if (auth is BasicAuthAuthenticationDetails) { BasicAuthAuthenticationDetails basicDetails = auth as BasicAuthAuthenticationDetails; - req.Headers["Authorization"] = "Basic " + Convert.ToBase64String( - Encoding.Default.GetBytes(basicDetails.Username + ":" + basicDetails.Password)); + client.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String( + Encoding.GetEncoding(0).GetBytes(basicDetails.Username + ":" + basicDetails.Password))); } } - req.UserAgent = string.Format("createsend-dotnet-#{0} .Net: {1} OS: {2} DLL: {3}", - CreateSendOptions.VersionNumber, Environment.Version, Environment.OSVersion, Assembly.GetExecutingAssembly().FullName); + client.DefaultRequestHeaders.Add("User-Agent", string.Format("createsend-dotnet-#{0} .Net: {1} OS: {2} DLL: {3}", + CreateSendOptions.VersionNumber, RuntimeInformation.FrameworkDescription, RuntimeInformation.OSDescription, typeof(HttpHelper).GetTypeInfo().Assembly)); + + HttpContent content; if (method != "GET") { + Stream s = new MemoryStream(); + if (payload != null) { - using (System.IO.StreamWriter os = new System.IO.StreamWriter(req.GetRequestStream())) + using (System.IO.StreamWriter os = new System.IO.StreamWriter(s)) { if (contentType == APPLICATION_FORM_URLENCODED_CONTENT_TYPE) os.Write(payload); else os.Write(JsonConvert.SerializeObject(payload, Formatting.None, serialiserSettings)); - os.Close(); + os.Dispose(); } } - else - req.ContentLength = 0; + + content = new StreamContent(s); } try { - using (var resp = (HttpWebResponse)req.GetResponse()) + var response = await client.PostAsync(uri, null); + + if (response.IsSuccessStatusCode) { + var resp = await response.Content.ReadAsStreamAsync(); + if (resp == null) return default(U); - else + { - using (var sr = new System.IO.StreamReader(resp.GetResponseStream())) + using (var sr = new System.IO.StreamReader(resp)) { - #if SUPPORTED_FRAMEWORK_VERSION +#if SUPPORTED_FRAMEWORK_VERSION var type = typeof(U); if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(RateLimited<>)) { @@ -202,34 +216,31 @@ static U MakeRequest( }; return (U)Activator.CreateInstance(type, response, status); } - #endif +#endif return JsonConvert.DeserializeObject(sr.ReadToEnd().Trim(), serialiserSettings); } } } - } - catch (WebException we) - { - if (we.Status == WebExceptionStatus.ProtocolError) + else { - switch ((int)((HttpWebResponse)we.Response).StatusCode) + switch (response.StatusCode) { - case 400: - case 401: - throw ThrowReworkedCustomException(we); - case 404: + case System.Net.HttpStatusCode.BadRequest: + case System.Net.HttpStatusCode.Unauthorized: + throw ThrowReworkedCustomException(response); + case System.Net.HttpStatusCode.NotFound: default: - throw we; + throw new HttpRequestException(response.Content.ReadAsStringAsync().Result); } } - else - { - throw we; - } + } + catch (Exception ex) + { + throw; } } - #if SUPPORTED_FRAMEWORK_VERSION +#if SUPPORTED_FRAMEWORK_VERSION private static uint UInt(this string value, uint defaultValue) { uint v; @@ -239,11 +250,11 @@ private static uint UInt(this string value, uint defaultValue) } return defaultValue; } - #endif +#endif - private static Exception ThrowReworkedCustomException(WebException we) where EX : ErrorResult + private static Exception ThrowReworkedCustomException(HttpResponseMessage messageResponse) where EX : ErrorResult { - using (System.IO.StreamReader sr = new System.IO.StreamReader(((HttpWebResponse)we.Response).GetResponseStream())) + using (System.IO.StreamReader sr = new System.IO.StreamReader(messageResponse.Content.ReadAsStreamAsync().Result)) { string response = sr.ReadToEnd().Trim(); ErrorResult result = JsonConvert.DeserializeObject(response); @@ -256,7 +267,7 @@ private static Exception ThrowReworkedCustomException(WebException we) where (result as OAuthErrorResult).error_description); else // Regular ErrorResult format. message = string.Format( - "The CreateSend API responded with the following error - {0}: {1}", + "The CreateSend API responded with the following error - {0}: {1}", result.Code, result.Message); CreatesendException exception; @@ -276,31 +287,14 @@ public static class NameValueCollectionExtension { public static string ToQueryString(NameValueCollection nvc) { - if (nvc != null && nvc.Count > 0) - return "?" + string.Join("&", GetPairs(nvc)); - else - return ""; - } - - private static string[] GetPairs(NameValueCollection nvc) - { - List keyValuePair = new List(); + string url = string.Empty; - foreach (string key in nvc.AllKeys) + foreach (KeyValuePair nv in nvc) { - string encodedKey = HttpUtility.UrlEncode(key) + "="; - var values = nvc.GetValues(key); - - if(values != null) - foreach (string value in values) - { - - keyValuePair.Add(encodedKey + HttpUtility.UrlEncode(value)); - - } + url += Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(url, nv.Key, nv.Value); } - return keyValuePair.ToArray(); + return url; } } -} +} \ No newline at end of file diff --git a/createsend-netstandard/List.cs b/createsend-netstandard/List.cs index 4d82d1a..ea3efdb 100644 --- a/createsend-netstandard/List.cs +++ b/createsend-netstandard/List.cs @@ -7,14 +7,14 @@ namespace createsend_dotnet { public class List : CreateSendBase { - public string ListID { get; set; } - public List(AuthenticationDetails auth, string listID) : base(auth) { ListID = listID; } + public string ListID { get; set; } + public static string Create( AuthenticationDetails auth, string clientID, @@ -36,38 +36,31 @@ public static string Create( }); } - public void Update( - string title, - string unsubscribePage, - bool confirmedOptIn, - string confirmationSuccessPage, - UnsubscribeSetting unsubscribeSetting, - bool addUnsubscribesToSuppList, - bool scrubActiveWithSuppList) + public void ActivateWebhook(string webhookID) { - HttpPut( - string.Format("/lists/{0}.json", ListID), null, - new ListDetailForUpdate() - { - Title = title, - UnsubscribePage = unsubscribePage, - ConfirmedOptIn = confirmedOptIn, - ConfirmationSuccessPage = confirmationSuccessPage, - UnsubscribeSetting = unsubscribeSetting.ToString(), - AddUnsubscribesToSuppList = addUnsubscribesToSuppList, - ScrubActiveWithSuppList = scrubActiveWithSuppList - }); + HttpPut( + string.Format("/lists/{0}/webhooks/{1}/activate.json", + ListID, System.Net.WebUtility.HtmlEncode(webhookID)), null, null); } - public ListDetail Details() + public PagedCollection Active() { - return HttpGet( - string.Format("/lists/{0}.json", ListID), null); + return GenericPagedSubscriberGet("active", "", 1, 1000, "email", "asc"); } - public void Delete() + public PagedCollection Active(DateTime fromDate, int page, int pageSize, string orderField, string orderDirection) { - HttpDelete(string.Format("/lists/{0}.json", ListID), null); + return GenericPagedSubscriberGet("active", fromDate, page, pageSize, orderField, orderDirection); + } + + public PagedCollection Bounced() + { + return GenericPagedSubscriberGet("bounced", "", 1, 1000, "email", "asc"); + } + + public PagedCollection Bounced(DateTime fromDate, int page, int pageSize, string orderField, string orderDirection) + { + return GenericPagedSubscriberGet("bounced", fromDate, page, pageSize, orderField, orderDirection); } public string CreateCustomField( @@ -86,61 +79,73 @@ public string CreateCustomField( { return HttpPost, string>( string.Format("/lists/{0}/customfields.json", ListID), null, - new Dictionary() - { - { "FieldName", fieldName }, - { "DataType", dataType.ToString() }, + new Dictionary() + { + { "FieldName", fieldName }, + { "DataType", dataType.ToString() }, { "Options", options }, { "VisibleInPreferenceCenter", visibleInPreferenceCenter } }); } - public string UpdateCustomField( - string customFieldKey, - string fieldName, - bool visibleInPreferenceCenter) + public string CreateWebhook(List events, string url, string payloadFormat) { - return HttpPut, string>( - string.Format("/lists/{0}/customfields/{1}.json", - ListID, System.Web.HttpUtility.UrlEncode(customFieldKey)), null, - new Dictionary() + return HttpPost, string>( + string.Format("/lists/{0}/webhooks.json", ListID), null, + new Dictionary() { - { "FieldName", fieldName }, - { "VisibleInPreferenceCenter", visibleInPreferenceCenter } + { "Events", events }, + { "Url", url }, + { "PayloadFormat", payloadFormat } }); } + public IEnumerable CustomFields() + { + return HttpGet( + string.Format("/lists/{0}/customfields.json", ListID), null); + } + + public void DeactivateWebhook(string webhookID) + { + HttpPut( + string.Format("/lists/{0}/webhooks/{1}/deactivate.json", + ListID, System.Net.WebUtility.HtmlEncode(webhookID)), null, null); + } + + public void Delete() + { + HttpDelete(string.Format("/lists/{0}.json", ListID), null); + } + public void DeleteCustomField(string customFieldKey) { HttpDelete( - string.Format("/lists/{0}/customfields/{1}.json", - ListID, System.Web.HttpUtility.UrlEncode(customFieldKey)), null); + string.Format("/lists/{0}/customfields/{1}.json", + ListID, System.Net.WebUtility.HtmlEncode(customFieldKey)), null); } - public IEnumerable CustomFields() + public PagedCollection Deleted() { - return HttpGet( - string.Format("/lists/{0}/customfields.json", ListID), null); + return GenericPagedSubscriberGet("deleted", "", 1, 1000, "email", "asc"); } - public void UpdateCustomFieldOptions( - string customFieldKey, - List options, - bool keepExistingOptions) + public PagedCollection Deleted(DateTime fromDate, int page, int pageSize, string orderField, string orderDirection) { - HttpPut( - string.Format("/lists/{0}/customfields/{1}/options.json", - ListID, System.Web.HttpUtility.UrlEncode(customFieldKey)), null, - new { - KeepExistingOptions = keepExistingOptions, - Options = options - }); + return GenericPagedSubscriberGet("deleted", fromDate, page, pageSize, orderField, orderDirection); } - [Obsolete("Use UpdateCustomFieldOptions instead. UpdateCustomFields will eventually be removed.", false)] - public void UpdateCustomFields(string customFieldKey, List options, bool keepExistingOptions) + public void DeleteWebhook(string webhookID) { - UpdateCustomFieldOptions(customFieldKey, options, keepExistingOptions); + HttpDelete( + string.Format("/lists/{0}/webhooks/{1}.json", + ListID, System.Net.WebUtility.HtmlEncode(webhookID)), null); + } + + public ListDetail Details() + { + return HttpGet( + string.Format("/lists/{0}.json", ListID), null); } public IEnumerable Segments() @@ -155,14 +160,13 @@ public ListStats Stats() string.Format("/lists/{0}/stats.json", ListID), null); } - public PagedCollection Active() + public bool TestWebhook(string webhookID) { - return GenericPagedSubscriberGet("active", "", 1, 1000, "email", "asc"); - } + HttpGet>( + string.Format("/lists/{0}/webhooks/{1}/test.json", + ListID, System.Net.WebUtility.HtmlEncode(webhookID)), null); - public PagedCollection Active(DateTime fromDate, int page, int pageSize, string orderField, string orderDirection) - { - return GenericPagedSubscriberGet("active", fromDate, page, pageSize, orderField, orderDirection); + return true; //an exception will be thrown if there is a problem } public PagedCollection Unconfirmed() @@ -185,28 +189,73 @@ public PagedCollection Unsubscribed(DateTime fromDate, int pag return GenericPagedSubscriberGet("unsubscribed", fromDate, page, pageSize, orderField, orderDirection); } - public PagedCollection Bounced() + public void Update( + string title, + string unsubscribePage, + bool confirmedOptIn, + string confirmationSuccessPage, + UnsubscribeSetting unsubscribeSetting, + bool addUnsubscribesToSuppList, + bool scrubActiveWithSuppList) { - return GenericPagedSubscriberGet("bounced", "", 1, 1000, "email", "asc"); + HttpPut( + string.Format("/lists/{0}.json", ListID), null, + new ListDetailForUpdate() + { + Title = title, + UnsubscribePage = unsubscribePage, + ConfirmedOptIn = confirmedOptIn, + ConfirmationSuccessPage = confirmationSuccessPage, + UnsubscribeSetting = unsubscribeSetting.ToString(), + AddUnsubscribesToSuppList = addUnsubscribesToSuppList, + ScrubActiveWithSuppList = scrubActiveWithSuppList + }); } - public PagedCollection Bounced(DateTime fromDate, int page, int pageSize, string orderField, string orderDirection) + public string UpdateCustomField( + string customFieldKey, + string fieldName, + bool visibleInPreferenceCenter) { - return GenericPagedSubscriberGet("bounced", fromDate, page, pageSize, orderField, orderDirection); + return HttpPut, string>( + string.Format("/lists/{0}/customfields/{1}.json", + ListID, System.Net.WebUtility.HtmlEncode(customFieldKey)), null, + new Dictionary() + { + { "FieldName", fieldName }, + { "VisibleInPreferenceCenter", visibleInPreferenceCenter } + }); } - public PagedCollection Deleted() + public void UpdateCustomFieldOptions( + string customFieldKey, + List options, + bool keepExistingOptions) { - return GenericPagedSubscriberGet("deleted", "", 1, 1000, "email", "asc"); + HttpPut( + string.Format("/lists/{0}/customfields/{1}/options.json", + ListID, System.Net.WebUtility.HtmlEncode(customFieldKey)), null, + new + { + KeepExistingOptions = keepExistingOptions, + Options = options + }); } - public PagedCollection Deleted(DateTime fromDate, int page, int pageSize, string orderField, string orderDirection) + [Obsolete("Use UpdateCustomFieldOptions instead. UpdateCustomFields will eventually be removed.", false)] + public void UpdateCustomFields(string customFieldKey, List options, bool keepExistingOptions) { - return GenericPagedSubscriberGet("deleted", fromDate, page, pageSize, orderField, orderDirection); + UpdateCustomFieldOptions(customFieldKey, options, keepExistingOptions); + } + + public IEnumerable Webhooks() + { + return HttpGet( + string.Format("/lists/{0}/webhooks.json", ListID), null); } private PagedCollection GenericPagedSubscriberGet( - string type, + string type, DateTime fromDate, int page, int pageSize, @@ -230,53 +279,5 @@ private PagedCollection GenericPagedSubscriberGet(string type, return HttpGet>( string.Format("/lists/{0}/{1}.json", ListID, type), queryArguments); } - - public IEnumerable Webhooks() - { - return HttpGet( - string.Format("/lists/{0}/webhooks.json", ListID), null); - } - - public string CreateWebhook(List events, string url, string payloadFormat) - { - return HttpPost, string>( - string.Format("/lists/{0}/webhooks.json", ListID), null, - new Dictionary() - { - { "Events", events }, - { "Url", url }, - { "PayloadFormat", payloadFormat } - }); - } - - public bool TestWebhook(string webhookID) - { - HttpGet>( - string.Format("/lists/{0}/webhooks/{1}/test.json", - ListID, System.Web.HttpUtility.UrlEncode(webhookID)), null); - - return true; //an exception will be thrown if there is a problem - } - - public void DeleteWebhook(string webhookID) - { - HttpDelete( - string.Format("/lists/{0}/webhooks/{1}.json", - ListID, System.Web.HttpUtility.UrlEncode(webhookID)), null); - } - - public void ActivateWebhook(string webhookID) - { - HttpPut( - string.Format("/lists/{0}/webhooks/{1}/activate.json", - ListID, System.Web.HttpUtility.UrlEncode(webhookID)), null, null); - } - - public void DeactivateWebhook(string webhookID) - { - HttpPut( - string.Format("/lists/{0}/webhooks/{1}/deactivate.json", - ListID, System.Web.HttpUtility.UrlEncode(webhookID)), null, null); - } } -} +} \ No newline at end of file diff --git a/createsend-netstandard/Models/MailAddress.cs b/createsend-netstandard/Models/MailAddress.cs new file mode 100644 index 0000000..e194539 --- /dev/null +++ b/createsend-netstandard/Models/MailAddress.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace createsend_netstandard.Models +{ + public class MailAddress + { + private string displayName; + + private string emailAddress; + + public MailAddress(string emailAddress, string displayName) + { + this.emailAddress = emailAddress; + this.displayName = displayName; + } + + public string Address { get { return this.emailAddress; } } + + public string DisplayName + { + get + { + if (string.IsNullOrWhiteSpace(this.displayName)) + { + return this.emailAddress; + } + return this.displayName; + } + } + } +} \ No newline at end of file diff --git a/createsend-netstandard/Models/Result.cs b/createsend-netstandard/Models/Result.cs index 110f6d7..f72e81d 100644 --- a/createsend-netstandard/Models/Result.cs +++ b/createsend-netstandard/Models/Result.cs @@ -6,54 +6,54 @@ namespace createsend_dotnet { - [Serializable] + public class ApiKeyResult + { + public string ApiKey { get; set; } + } + + public class BulkImportResults + { + public List DuplicateEmailsInSubmission { get; set; } + public List FailureDetails { get; set; } + public int TotalExistingSubscribers { get; set; } + public int TotalNewSubscribers { get; set; } + public int TotalUniqueEmailsSubmitted { get; set; } + } + public class ErrorResult : ErrorResult { - public ErrorResult() : base() { } + public ErrorResult() : base() + { + } public ErrorResult(ErrorResult errorResult) { Message = errorResult.Message; Code = errorResult.Code; } - public T ResultData { get; set; } - } - [Serializable] - public class OAuthErrorResult : ErrorResult - { - public string error { get; set; } - public string error_description { get; set; } + public T ResultData { get; set; } } - [Serializable] public class ErrorResult { public string Code { get; set; } public string Message { get; set; } } - public class ApiKeyResult - { - public string ApiKey { get; set; } - } - - public class SystemDateResult + public class ImportResult : ErrorResult { - public DateTime SystemDate { get; set; } + public string EmailAddress { get; set; } } - public class BulkImportResults + public class OAuthErrorResult : ErrorResult { - public int TotalUniqueEmailsSubmitted { get; set; } - public int TotalExistingSubscribers { get; set; } - public int TotalNewSubscribers { get; set; } - public List DuplicateEmailsInSubmission { get; set; } - public List FailureDetails { get; set; } + public string error { get; set; } + public string error_description { get; set; } } - public class ImportResult : ErrorResult + public class SystemDateResult { - public string EmailAddress { get; set; } + public DateTime SystemDate { get; set; } } -} +} \ No newline at end of file diff --git a/createsend-netstandard/Properties/AssemblyInfo.cs b/createsend-netstandard/Properties/AssemblyInfo.cs deleted file mode 100644 index 1b7cb51..0000000 --- a/createsend-netstandard/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("createsend-dotnet")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("CreateSend")] -[assembly: AssemblyProduct("createsend-dotnet")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4af76d47-92e5-4636-a9bb-b48046a17f25")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("4.2.2.0")] -[assembly: AssemblyFileVersion("4.2.2.0")] diff --git a/createsend-netstandard/Transactional/EmailAddress.cs b/createsend-netstandard/Transactional/EmailAddress.cs index b278150..f4b49c1 100644 --- a/createsend-netstandard/Transactional/EmailAddress.cs +++ b/createsend-netstandard/Transactional/EmailAddress.cs @@ -1,19 +1,27 @@ -using System.Net.Mail; +using createsend_netstandard.Models; namespace createsend_dotnet.Transactional { public class EmailAddress { - public string Name { get { return mail.DisplayName; } } - public string Email { get { return mail.Address; } } - private readonly MailAddress mail; public EmailAddress(string email, string name) { mail = new MailAddress(email, name); } - public EmailAddress(string email) : this(email, null) { } + + public EmailAddress(string email) : this(email, null) + { + } + + public string Email { get { return mail.Address; } } + public string Name { get { return mail.DisplayName; } } + + public static EmailAddress FromString(string email) + { + return email == null ? null : new EmailAddress(email); + } public static implicit operator EmailAddress(string email) { @@ -25,11 +33,6 @@ public static implicit operator string(EmailAddress email) return ToString(email); } - public static EmailAddress FromString(string email) - { - return email == null ? null : new EmailAddress(email); - } - public static string ToString(EmailAddress email) { return email == null ? null : email.ToString(); @@ -40,4 +43,4 @@ public override string ToString() return mail.ToString(); } } -} +} \ No newline at end of file diff --git a/createsend-netstandard/Transactional/RequiredClientIdentifierException.cs b/createsend-netstandard/Transactional/RequiredClientIdentifierException.cs index 9a54b06..a2c61e6 100644 --- a/createsend-netstandard/Transactional/RequiredClientIdentifierException.cs +++ b/createsend-netstandard/Transactional/RequiredClientIdentifierException.cs @@ -7,34 +7,25 @@ public class RequiredClientIdentierException : Exception { private const string defaultMessage = "ClientId has not been provided. Note to agencies: If you are using an account API key, this is required as you need to specify the client"; + public RequiredClientIdentierException() : base(defaultMessage) { - } public RequiredClientIdentierException(string message) : base(message) { - } public RequiredClientIdentierException(string message, Exception exception) : base(message, exception) { - } public RequiredClientIdentierException(Exception exception) : base(defaultMessage, exception) { - - } - - public RequiredClientIdentierException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } -} +} \ No newline at end of file diff --git a/createsend-netstandard/createsend-netstandard.csproj b/createsend-netstandard/createsend-netstandard.csproj index 509776b..33bf22d 100644 --- a/createsend-netstandard/createsend-netstandard.csproj +++ b/createsend-netstandard/createsend-netstandard.csproj @@ -5,7 +5,13 @@ + + + + + + \ No newline at end of file From 52769e480345f9a3455cb079c10f8bfe6528deb4 Mon Sep 17 00:00:00 2001 From: davidkirkham Date: Thu, 20 Apr 2017 14:17:18 +0100 Subject: [PATCH 03/11] Removed request header --- createsend-netstandard/HttpHelper.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/createsend-netstandard/HttpHelper.cs b/createsend-netstandard/HttpHelper.cs index 42a385e..83cbaf5 100644 --- a/createsend-netstandard/HttpHelper.cs +++ b/createsend-netstandard/HttpHelper.cs @@ -164,8 +164,12 @@ private static async Task MakeRequestAsync( } } - client.DefaultRequestHeaders.Add("User-Agent", string.Format("createsend-dotnet-#{0} .Net: {1} OS: {2} DLL: {3}", - CreateSendOptions.VersionNumber, RuntimeInformation.FrameworkDescription, RuntimeInformation.OSDescription, typeof(HttpHelper).GetTypeInfo().Assembly)); + /*client.DefaultRequestHeaders.Add("User-Agent", + string.Format(@"createsend -dotnet-#{0} .Net: {1} OS: {2} DLL: {3}", + CreateSendOptions.VersionNumber "1.0.0.0", + RuntimeInformation.FrameworkDescription, + RuntimeInformation.OSDescription, + typeof(HttpHelper).GetTypeInfo().Assembly));*/ HttpContent content; @@ -288,6 +292,10 @@ public static class NameValueCollectionExtension public static string ToQueryString(NameValueCollection nvc) { string url = string.Empty; + if (nvc == null) + { + return url; + } foreach (KeyValuePair nv in nvc) { From 49c376aa8cf8e7b51f9bd377d58aa8d5495d8dfa Mon Sep 17 00:00:00 2001 From: davidkirkham Date: Thu, 20 Apr 2017 15:52:03 +0100 Subject: [PATCH 04/11] Json Fromatter updated/included --- createsend-netstandard/HttpHelper.cs | 125 ++++++++++-------- .../Transactional/EmailAddressConverter.cs | 8 +- 2 files changed, 72 insertions(+), 61 deletions(-) diff --git a/createsend-netstandard/HttpHelper.cs b/createsend-netstandard/HttpHelper.cs index 83cbaf5..9dee891 100644 --- a/createsend-netstandard/HttpHelper.cs +++ b/createsend-netstandard/HttpHelper.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; using System.IO; +using createsend_dotnet.Transactional; #if SUPPORTED_FRAMEWORK_VERSION using createsend_dotnet.Transactional; @@ -128,73 +129,83 @@ private static async Task MakeRequestAsync( where T : class where EX : ErrorResult { - JsonSerializerSettings serialiserSettings = new JsonSerializerSettings(); - serialiserSettings.NullValueHandling = NullValueHandling.Ignore; - serialiserSettings.MissingMemberHandling = MissingMemberHandling.Ignore; -#if SUPPORTED_FRAMEWORK_VERSION - serialiserSettings.Converters.Add(new EmailAddressConverter()); -#endif - string uri = baseUri + path + NameValueCollectionExtension.ToQueryString(queryArguments); + try + { + JsonSerializerSettings serialiserSettings = new JsonSerializerSettings(); + serialiserSettings.NullValueHandling = NullValueHandling.Ignore; + serialiserSettings.MissingMemberHandling = MissingMemberHandling.Ignore; + serialiserSettings.Converters.Add(new EmailAddressConverter()); - HttpClient client = new HttpClient(); + string uri = baseUri + path + NameValueCollectionExtension.ToQueryString(queryArguments); - //HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri); - //req.Method = method; - //req.ContentType = contentType; - //req.AutomaticDecompression = DecompressionMethods.GZip; + HttpClientHandler handler = new HttpClientHandler(); + handler.AutomaticDecompression = System.Net.DecompressionMethods.GZip; - if (auth != null) - { - if (auth is OAuthAuthenticationDetails) - { - OAuthAuthenticationDetails oauthDetails = auth as OAuthAuthenticationDetails; - client.DefaultRequestHeaders.Add("Authorization", "Bearer " + oauthDetails.AccessToken); - } - else if (auth is ApiKeyAuthenticationDetails) - { - ApiKeyAuthenticationDetails apiKeyDetails = auth as ApiKeyAuthenticationDetails; - client.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String( - Encoding.GetEncoding(0).GetBytes(apiKeyDetails.ApiKey + ":x"))); - } - else if (auth is BasicAuthAuthenticationDetails) + HttpClient client = new HttpClient(handler); + + if (auth != null) { - BasicAuthAuthenticationDetails basicDetails = auth as BasicAuthAuthenticationDetails; - client.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String( - Encoding.GetEncoding(0).GetBytes(basicDetails.Username + ":" + basicDetails.Password))); + if (auth is OAuthAuthenticationDetails) + { + OAuthAuthenticationDetails oauthDetails = auth as OAuthAuthenticationDetails; + client.DefaultRequestHeaders.Add("Authorization", "Bearer " + oauthDetails.AccessToken); + } + else if (auth is ApiKeyAuthenticationDetails) + { + ApiKeyAuthenticationDetails apiKeyDetails = auth as ApiKeyAuthenticationDetails; + client.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String( + Encoding.GetEncoding(0).GetBytes(apiKeyDetails.ApiKey + ":x"))); + } + else if (auth is BasicAuthAuthenticationDetails) + { + BasicAuthAuthenticationDetails basicDetails = auth as BasicAuthAuthenticationDetails; + client.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String( + Encoding.GetEncoding(0).GetBytes(basicDetails.Username + ":" + basicDetails.Password))); + } } - } - - /*client.DefaultRequestHeaders.Add("User-Agent", - string.Format(@"createsend -dotnet-#{0} .Net: {1} OS: {2} DLL: {3}", - CreateSendOptions.VersionNumber "1.0.0.0", - RuntimeInformation.FrameworkDescription, - RuntimeInformation.OSDescription, - typeof(HttpHelper).GetTypeInfo().Assembly));*/ - HttpContent content; + /*client.DefaultRequestHeaders.Add("User-Agent", + string.Format(@"createsend -dotnet-#{0} .Net: {1} OS: {2} DLL: {3}", + CreateSendOptions.VersionNumber "1.0.0.0", + RuntimeInformation.FrameworkDescription, + RuntimeInformation.OSDescription, + typeof(HttpHelper).GetTypeInfo().Assembly));*/ - if (method != "GET") - { - Stream s = new MemoryStream(); + HttpContent content = null; + HttpResponseMessage response = null; - if (payload != null) + if (method != "GET") { - using (System.IO.StreamWriter os = new System.IO.StreamWriter(s)) + Stream s = new MemoryStream(); + Stream requestStream = new MemoryStream(); + + if (payload != null) { - if (contentType == APPLICATION_FORM_URLENCODED_CONTENT_TYPE) - os.Write(payload); - else - os.Write(JsonConvert.SerializeObject(payload, Formatting.None, serialiserSettings)); - os.Dispose(); - } - } + using (System.IO.StreamWriter os = new System.IO.StreamWriter(s)) + { + if (contentType == APPLICATION_FORM_URLENCODED_CONTENT_TYPE) + os.Write(payload); + else + { + string json = JsonConvert.SerializeObject(payload, Formatting.None, serialiserSettings); + os.Write(json); + } - content = new StreamContent(s); - } + await os.FlushAsync(); + s.Seek(0, SeekOrigin.Begin); + await s.CopyToAsync(requestStream); + os.Dispose(); + } - try - { - var response = await client.PostAsync(uri, null); + requestStream.Seek(0, SeekOrigin.Begin); + content = new StreamContent(requestStream); + response = await client.PostAsync(uri, content); + } + else + { + response = await client.PostAsync(uri, null); + } + } if (response.IsSuccessStatusCode) { @@ -297,9 +308,9 @@ public static string ToQueryString(NameValueCollection nvc) return url; } - foreach (KeyValuePair nv in nvc) + foreach (string key in nvc) { - url += Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(url, nv.Key, nv.Value); + url += Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(url, key, nvc[key]); } return url; diff --git a/createsend-netstandard/Transactional/EmailAddressConverter.cs b/createsend-netstandard/Transactional/EmailAddressConverter.cs index 3173e2a..23b9347 100644 --- a/createsend-netstandard/Transactional/EmailAddressConverter.cs +++ b/createsend-netstandard/Transactional/EmailAddressConverter.cs @@ -5,7 +5,6 @@ namespace createsend_dotnet.Transactional { internal class EmailAddressConverter : JsonConverter { - public override bool CanConvert(Type objectType) { return objectType == typeof(EmailAddress); @@ -22,14 +21,15 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - if (value == null) + if (value == null || value.GetType() != typeof(EmailAddress)) { writer.WriteNull(); } else { - writer.WriteValue(value.ToString()); + EmailAddress e = (EmailAddress)value; + writer.WriteValue(string.Format("{0} <{1}>", e.Name, e.Email)); } } } -} +} \ No newline at end of file From 355c649baf8681cd04d5a9edc20b112b8283f319 Mon Sep 17 00:00:00 2001 From: davidkirkham Date: Thu, 20 Apr 2017 15:53:15 +0100 Subject: [PATCH 05/11] Removed Use-Agent header. --- createsend-netstandard/HttpHelper.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/createsend-netstandard/HttpHelper.cs b/createsend-netstandard/HttpHelper.cs index 9dee891..ba4c62c 100644 --- a/createsend-netstandard/HttpHelper.cs +++ b/createsend-netstandard/HttpHelper.cs @@ -164,13 +164,6 @@ private static async Task MakeRequestAsync( } } - /*client.DefaultRequestHeaders.Add("User-Agent", - string.Format(@"createsend -dotnet-#{0} .Net: {1} OS: {2} DLL: {3}", - CreateSendOptions.VersionNumber "1.0.0.0", - RuntimeInformation.FrameworkDescription, - RuntimeInformation.OSDescription, - typeof(HttpHelper).GetTypeInfo().Assembly));*/ - HttpContent content = null; HttpResponseMessage response = null; From 2b575db1ed361f5337b5df4e7c50f5817098a700 Mon Sep 17 00:00:00 2001 From: davidkirkham Date: Fri, 21 Apr 2017 09:04:57 +0100 Subject: [PATCH 06/11] Fix when not get --- createsend-netstandard/HttpHelper.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/createsend-netstandard/HttpHelper.cs b/createsend-netstandard/HttpHelper.cs index ba4c62c..4925ab8 100644 --- a/createsend-netstandard/HttpHelper.cs +++ b/createsend-netstandard/HttpHelper.cs @@ -199,6 +199,10 @@ private static async Task MakeRequestAsync( response = await client.PostAsync(uri, null); } } + else + { + response = await client.PostAsync(uri, null); + } if (response.IsSuccessStatusCode) { From e36fcef66c5eee6655e8065bd4d53abd306f4f20 Mon Sep 17 00:00:00 2001 From: davidkirkham Date: Fri, 21 Apr 2017 09:20:11 +0100 Subject: [PATCH 07/11] Added Get --- createsend-netstandard/HttpHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/createsend-netstandard/HttpHelper.cs b/createsend-netstandard/HttpHelper.cs index 4925ab8..128a63e 100644 --- a/createsend-netstandard/HttpHelper.cs +++ b/createsend-netstandard/HttpHelper.cs @@ -201,7 +201,7 @@ private static async Task MakeRequestAsync( } else { - response = await client.PostAsync(uri, null); + response = await client.GetAsync(uri); } if (response.IsSuccessStatusCode) From 34c6bd4e84120f08137f50b77748af054d9040fd Mon Sep 17 00:00:00 2001 From: davidkirkham Date: Fri, 21 Apr 2017 14:21:52 +0100 Subject: [PATCH 08/11] Updated to return response based on generic funcitonality --- createsend-netstandard/HttpHelper.cs | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/createsend-netstandard/HttpHelper.cs b/createsend-netstandard/HttpHelper.cs index 128a63e..7bb9bbb 100644 --- a/createsend-netstandard/HttpHelper.cs +++ b/createsend-netstandard/HttpHelper.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using System.IO; using createsend_dotnet.Transactional; +using System.Linq; #if SUPPORTED_FRAMEWORK_VERSION using createsend_dotnet.Transactional; @@ -199,10 +200,6 @@ private static async Task MakeRequestAsync( response = await client.PostAsync(uri, null); } } - else - { - response = await client.GetAsync(uri); - } if (response.IsSuccessStatusCode) { @@ -214,21 +211,19 @@ private static async Task MakeRequestAsync( { using (var sr = new System.IO.StreamReader(resp)) { -#if SUPPORTED_FRAMEWORK_VERSION var type = typeof(U); - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(RateLimited<>)) + if (type.GetGenericTypeDefinition() == typeof(RateLimited<>)) { - var responseType = type.GetGenericArguments()[0]; - var response = JsonConvert.DeserializeObject(sr.ReadToEnd().Trim(), responseType, serialiserSettings); + var responseType = type.GenericTypeArguments[0]; + var result = JsonConvert.DeserializeObject(sr.ReadToEnd().Trim(), responseType, serialiserSettings); var status = new RateLimitStatus - { - Credit = resp.Headers["X-RateLimit-Limit"].UInt(0), - Remaining = resp.Headers["X-RateLimit-Remaining"].UInt(0), - Reset = resp.Headers["X-RateLimit-Reset"].UInt(0) - }; - return (U)Activator.CreateInstance(type, response, status); + { + Credit = uint.Parse(response.Headers.GetValues("X-RateLimit-Limit").First()), + Remaining = uint.Parse(response.Headers.GetValues("X-RateLimit-Remaining").First()), + Reset = uint.Parse(response.Headers.GetValues("X-RateLimit-Reset").First()) + }; + return (U)Activator.CreateInstance(type, result, status); } -#endif return JsonConvert.DeserializeObject(sr.ReadToEnd().Trim(), serialiserSettings); } } From 5ecef087756dffc44965a47e510223bdf057da3f Mon Sep 17 00:00:00 2001 From: davidkirkham Date: Fri, 21 Apr 2017 15:45:23 +0100 Subject: [PATCH 09/11] Re-added Get --- createsend-netstandard/HttpHelper.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/createsend-netstandard/HttpHelper.cs b/createsend-netstandard/HttpHelper.cs index 7bb9bbb..327b5bb 100644 --- a/createsend-netstandard/HttpHelper.cs +++ b/createsend-netstandard/HttpHelper.cs @@ -200,6 +200,10 @@ private static async Task MakeRequestAsync( response = await client.PostAsync(uri, null); } } + else + { + response = await client.GetAsync(uri); + } if (response.IsSuccessStatusCode) { @@ -302,7 +306,10 @@ public static string ToQueryString(NameValueCollection nvc) foreach (string key in nvc) { - url += Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(url, key, nvc[key]); + if (!string.IsNullOrWhiteSpace(nvc[key])) + { + url += Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(url, key, nvc[key]); + } } return url; From 0291b57f786df85e7f0823f0abab148f4f3b7257 Mon Sep 17 00:00:00 2001 From: davidkirkham Date: Fri, 21 Apr 2017 16:05:16 +0100 Subject: [PATCH 10/11] Fix for stats where headers not containing limit information --- createsend-netstandard/HttpHelper.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/createsend-netstandard/HttpHelper.cs b/createsend-netstandard/HttpHelper.cs index 327b5bb..5ebb014 100644 --- a/createsend-netstandard/HttpHelper.cs +++ b/createsend-netstandard/HttpHelper.cs @@ -222,9 +222,9 @@ private static async Task MakeRequestAsync( var result = JsonConvert.DeserializeObject(sr.ReadToEnd().Trim(), responseType, serialiserSettings); var status = new RateLimitStatus { - Credit = uint.Parse(response.Headers.GetValues("X-RateLimit-Limit").First()), - Remaining = uint.Parse(response.Headers.GetValues("X-RateLimit-Remaining").First()), - Reset = uint.Parse(response.Headers.GetValues("X-RateLimit-Reset").First()) + Credit = response.Headers.Contains("X-RateLimit-Limit") ? uint.Parse(response.Headers.GetValues("X-RateLimit-Limit").First()) : 0, + Remaining = response.Headers.Contains("X-RateLimit-Remaining") ? uint.Parse(response.Headers.GetValues("X-RateLimit-Remaining").First()) : 0, + Reset = response.Headers.Contains("X-RateLimit-Reset") ? uint.Parse(response.Headers.GetValues("X-RateLimit-Reset").First()) : 0 }; return (U)Activator.CreateInstance(type, result, status); } @@ -304,15 +304,17 @@ public static string ToQueryString(NameValueCollection nvc) return url; } + Dictionary queryValues = new Dictionary(); + foreach (string key in nvc) { if (!string.IsNullOrWhiteSpace(nvc[key])) { - url += Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(url, key, nvc[key]); + queryValues.Add(key, nvc[key]); } } - return url; + return Microsoft.AspNetCore.WebUtilities.QueryHelpers.AddQueryString(url, queryValues); } } } \ No newline at end of file From 052b94f0ba3e6198ade1a487cdc89fdfbf10a879 Mon Sep 17 00:00:00 2001 From: davidkirkham Date: Mon, 24 Apr 2017 08:31:30 +0100 Subject: [PATCH 11/11] Improved querybuilding --- createsend-netstandard/CreateSendBase.cs | 7 +++++-- createsend-netstandard/General.cs | 25 +++++++++++++++--------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/createsend-netstandard/CreateSendBase.cs b/createsend-netstandard/CreateSendBase.cs index 837c156..c3d120b 100644 --- a/createsend-netstandard/CreateSendBase.cs +++ b/createsend-netstandard/CreateSendBase.cs @@ -70,8 +70,11 @@ public OAuthTokenDetails RefreshToken() string refreshToken = (this.AuthDetails as OAuthAuthenticationDetails) .RefreshToken; - string body = QueryHelpers.AddQueryString("", "grant_type", "refresh_token"); - body = QueryHelpers.AddQueryString(body, "refresh_token", refreshToken); + + var values = new Dictionary(); + values.Add("grant_type", "refresh_token"); + values.Add("refresh_token", refreshToken); + string body = QueryHelpers.AddQueryString("", values); OAuthTokenDetails newTokenDetails = HttpHelper.Post( diff --git a/createsend-netstandard/General.cs b/createsend-netstandard/General.cs index 292b7ae..d47cf20 100644 --- a/createsend-netstandard/General.cs +++ b/createsend-netstandard/General.cs @@ -33,12 +33,15 @@ public static string AuthorizeUrl( string scope, string state) { - string result = CreateSendOptions.BaseOAuthUri; - result += QueryHelpers.AddQueryString(result, "client_id", clientID.ToString()); - result += QueryHelpers.AddQueryString(result, "redirect_uri", redirectUri); + var values = new Dictionary(); + values.Add("client_id", clientID.ToString()); + values.Add("redirect_uri", redirectUri); if (!string.IsNullOrEmpty(state)) - result += QueryHelpers.AddQueryString(result, "state", state); - result += QueryHelpers.AddQueryString(result, "scope", scope); + values.Add("state", state); + values.Add("scope", scope); + + string result = CreateSendOptions.BaseOAuthUri; + result += QueryHelpers.AddQueryString(result, values); return result; } @@ -50,10 +53,14 @@ public static OAuthTokenDetails ExchangeToken( string code) { string body = "grant_type=authorization_code"; - body += QueryHelpers.AddQueryString(body, "client_id", clientID.ToString()); - body += QueryHelpers.AddQueryString(body, "client_secret", clientSecret); - body += QueryHelpers.AddQueryString(body, "redirect_uri", redirectUri); - body += QueryHelpers.AddQueryString(body, "code", code); + + var values = new Dictionary(); + values.Add("client_id", clientID.ToString()); + values.Add("client_secret", clientSecret); + values.Add("redirect_uri", redirectUri); + values.Add("code", code); + + body = QueryHelpers.AddQueryString(body, values); return HttpHelper.Post( null, "/token", new NameValueCollection(), body,