From 45f7670386fd0c16c4a8d72c9064949b3a13b27f Mon Sep 17 00:00:00 2001 From: David Edgar Date: Wed, 3 Oct 2018 16:20:21 +1000 Subject: [PATCH 1/4] Adds property for the Subject field used in MMS messages. --- MessageMedia.SDK.Messages/Models/Message.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MessageMedia.SDK.Messages/Models/Message.cs b/MessageMedia.SDK.Messages/Models/Message.cs index 60db7a1..10bb5e9 100644 --- a/MessageMedia.SDK.Messages/Models/Message.cs +++ b/MessageMedia.SDK.Messages/Models/Message.cs @@ -15,6 +15,13 @@ public struct Message /// [JsonProperty("media")] public string[] Media { get; set; } + /// + /// Subject of the Message + /// + /// Only valid if the Format is MMS + /// + [JsonProperty("subject")] public string Subject { get; set; } + [JsonProperty("status")] public MessageStatus Status { get; set; } /// From c8338023d96a9411b012bc7a2336dc83873d7cb0 Mon Sep 17 00:00:00 2001 From: David Edgar Date: Wed, 3 Oct 2018 16:34:31 +1000 Subject: [PATCH 2/4] Fix syntax errors in README sample code --- README.md | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index f9b1036..6d05cba 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,12 @@ It's easy to get started. Simply enter the API Key and secret you obtained from Destination numbers (`destination_number`) should be in the [E.164](http://en.wikipedia.org/wiki/E.164) format. For example, `+61491570156`. ```csharp using System; +using System.Linq; using MessageMedia.Messages; using MessageMedia.Messages.Controllers; using MessageMedia.Messages.Models; + namespace TestCSharpSDK { class Program @@ -89,25 +91,24 @@ namespace TestCSharpSDK String basicAuthUserName = "YOUR_API_KEY"; String basicAuthPassword = "YOUR_API_SECRET"; bool useHmacAuthentication = false; //Change this to true if you are using HMAC keys - + // Instantiate the client MessageMediaMessagesClient client = new MessageMediaMessagesClient(basicAuthUserName, basicAuthPassword, useHmacAuthentication); IMessagesController messages = client.Messages; - // Perform API call - string bodyValue = @"{ - ""messages"":[ - { - ""content"":""Greetings from MessageMedia!"", - ""destination_number"":""YOUR_MOBILE_NUMBER"" - } - ] - }"; - - var body = Newtonsoft.Json.JsonConvert.DeserializeObject(bodyValue); - - MessageMedia.Messages.Models.SendMessagesResponse result = messages.CreateSendMessages(body); - Console.WriteLine(result.Messages); + var request = new SendMessagesRequest() { + Messages = new []{ + new Message() { + Content = "Greetings from MessageMedia!", + DestinationNumber = "YOUR_MOBILE_NUMBER" + } + } + }; + + SendMessagesResponse result = messages.CreateSendMessages(request); + Message message = result.Messages.First(); + + Console.WriteLine("Status: {0}, Message Id: {1}", message.Status, message.MessageId); Console.ReadKey(); } } @@ -118,6 +119,7 @@ namespace TestCSharpSDK Destination numbers (`destination_number`) should be in the [E.164](http://en.wikipedia.org/wiki/E.164) format. For example, `+61491570156`. ```csharp using System; +using System.Linq; using MessageMedia.Messages; using MessageMedia.Messages.Controllers; using MessageMedia.Messages.Models; @@ -153,8 +155,8 @@ namespace TestCSharpSDK } } }; - - Message message = result.Messages.First(); + SendMessagesResponse result = messages.CreateSendMessages(request); + Message message = result.Messages.First(); Console.WriteLine("Status: {0}, Message Id: {1}", message.Status, message.MessageId); Console.ReadKey(); @@ -181,7 +183,7 @@ namespace TestCSharpSDK String basicAuthUserName = "YOUR_API_KEY"; String basicAuthPassword = "YOUR_API_SECRET"; bool useHmacAuthentication = false; //Change this to true if you are using HMAC keys - + // Instantiate the client MessageMediaMessagesClient client = new MessageMediaMessagesClient(basicAuthUserName, basicAuthPassword, useHmacAuthentication); IMessagesController messages = client.Messages; @@ -214,7 +216,7 @@ namespace TestCSharpSDK String basicAuthUserName = "YOUR_API_KEY"; String basicAuthPassword = "YOUR_API_SECRET"; bool useHmacAuthentication = false; //Change this to true if you are using HMAC keys - + // Instantiate the client MessageMediaMessagesClient client = new MessageMediaMessagesClient(basicAuthUserName, basicAuthPassword, useHmacAuthentication); IRepliesController replies = client.Replies; @@ -246,7 +248,7 @@ namespace TestCSharpSDK String basicAuthUserName = "YOUR_API_KEY"; String basicAuthPassword = "YOUR_API_SECRET"; bool useHmacAuthentication = false; //Change this to true if you are using HMAC keys - + // Instantiate the client MessageMediaMessagesClient client = new MessageMediaMessagesClient(basicAuthUserName, basicAuthPassword, useHmacAuthentication); IDeliveryReportsController deliveryReports = client.DeliveryReports; @@ -267,4 +269,4 @@ Check out the [full API documentation](https://developers.messagemedia.com/code/ Please contact developer support at developers@messagemedia.com or check out the developer portal at [developers.messagemedia.com](https://developers.messagemedia.com/) ## :page_with_curl: License -Apache License. See the [LICENSE](LICENSE) file. +Apache License. See the [LICENSE](LICENSE) file. \ No newline at end of file From e8d1ee45283ee25cc1d766d42625480b8ccbefb4 Mon Sep 17 00:00:00 2001 From: David Edgar Date: Thu, 4 Oct 2018 10:27:38 +1000 Subject: [PATCH 3/4] ANnotations for JSON serialisation to ensure the json is serialised in the manner the API expects NULL values should be ignored. Enums must be passed as their string name, not that int value. DateTimes should be nullable so that DateTime.MinValue is not sent, which would be bad for the expiry time. --- MessageMedia.SDK.Messages/Models/Message.cs | 37 ++++++++++++--------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/MessageMedia.SDK.Messages/Models/Message.cs b/MessageMedia.SDK.Messages/Models/Message.cs index 10bb5e9..ea5fcd7 100644 --- a/MessageMedia.SDK.Messages/Models/Message.cs +++ b/MessageMedia.SDK.Messages/Models/Message.cs @@ -1,73 +1,78 @@ using System; using System.Collections.Generic; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace MessageMedia.Messages.Models { public struct Message { - [JsonProperty("message_id")] public string MessageId { get; set; } + [JsonProperty("message_id", NullValueHandling = NullValueHandling.Ignore)] public string MessageId { get; set; } /// /// Urls of the media files to send in the Message /// /// Only valid if the Format is MMS /// - [JsonProperty("media")] public string[] Media { get; set; } + [JsonProperty("media", NullValueHandling = NullValueHandling.Ignore)] public string[] Media { get; set; } /// /// Subject of the Message /// /// Only valid if the Format is MMS /// - [JsonProperty("subject")] public string Subject { get; set; } + [JsonProperty("subject", NullValueHandling = NullValueHandling.Ignore)] public string Subject { get; set; } - [JsonProperty("status")] public MessageStatus Status { get; set; } + [JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] + public MessageStatus? Status { get; set; } /// /// Replies and delivery reports for this message will be pushed to the URL" /// - [JsonProperty("callback_url")] public string CallbackUrl { get; set; } + [JsonProperty("callback_url", NullValueHandling = NullValueHandling.Ignore)] public string CallbackUrl { get; set; } /// /// Content of the message /// Hello world! /// - [JsonProperty("content")] public string Content { get; set; } + [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)] public string Content { get; set; } /// /// Destination number of the message /// +61491570156 /// - [JsonProperty("destination_number")] public string DestinationNumber { get; set; } + [JsonProperty("destination_number", NullValueHandling = NullValueHandling.Ignore)] public string DestinationNumber { get; set; } /// /// Request a delivery report for this message /// - [JsonProperty("delivery_report")] public bool DeliveryReport { get; set; } + [JsonProperty("delivery_report", NullValueHandling = NullValueHandling.Ignore)] public bool DeliveryReport { get; set; } /// /// Format of message, SMS or TTS (Text To Speech). /// - [JsonProperty("format")] public MessageFormat Format { get; set; } + [JsonProperty("format", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] + public MessageFormat Format { get; set; } /// /// Date time after which the message expires and will not be sent /// - [JsonProperty("message_expiry_timestamp")] - public DateTime MessageExpiryTimestamp { get; set; } + [JsonProperty("message_expiry_timestamp", NullValueHandling = NullValueHandling.Ignore)] + public DateTime? MessageExpiryTimestamp { get; set; } /// /// Metadata for the message specified as a set of key value pairs. /// /// Each key can be up to 100 characters long and each value can be up to 256 characters long. /// - [JsonProperty("metadata")] public Dictionary Metadata { get; set; } + [JsonProperty("metadata", NullValueHandling = NullValueHandling.Ignore)] public Dictionary Metadata { get; set; } /// /// Scheduled delivery date time of the message /// - [JsonProperty("scheduled")] public DateTime Scheduled { get; set; } + [JsonProperty("scheduled", NullValueHandling = NullValueHandling.Ignore)] public DateTime? Scheduled { get; set; } /// /// Source of the message @@ -77,11 +82,13 @@ public struct Message /// /// By default this feature is not available and will be ignored in the request. Please contact support@messagemedia.com for more information. Specifying a source number is optional and a by default a source number will be assigned to the message. /// - [JsonProperty("source_number")] public string SourceNumber { get; set; } + [JsonProperty("source_number", NullValueHandling = NullValueHandling.Ignore)] public string SourceNumber { get; set; } /// /// Type of source address specified /// - [JsonProperty("source_number_type")] public NumberType SourceNumberType { get; set; } + [JsonProperty("source_number_type", NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] + public NumberType SourceNumberType { get; set; } } } \ No newline at end of file From 5c43e646093a20b5019cbec0ac8b5bf419f2c47f Mon Sep 17 00:00:00 2001 From: David Edgar Date: Thu, 4 Oct 2018 10:29:01 +1000 Subject: [PATCH 4/4] Update unit tests to cope with the strong typing of the Messsage object --- .../MessagesControllerTest.cs | 84 ++++++++++++------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/MessageMediaMessages.Tests/MessagesControllerTest.cs b/MessageMediaMessages.Tests/MessagesControllerTest.cs index ab6a965..f0b1e96 100644 --- a/MessageMediaMessages.Tests/MessagesControllerTest.cs +++ b/MessageMediaMessages.Tests/MessagesControllerTest.cs @@ -99,7 +99,38 @@ public static void SetUpClass() public async Task TestSendMessages1() { // Parameters for the API call - SendMessagesRequest body = APIHelper.JsonDeserialize("{ \"messages\": [ { \"callback_url\": \"https://my.callback.url.com\", \"content\": \"My first message\", \"destination_number\": \"+61491570156\", \"delivery_report\": true, \"format\": \"SMS\", \"message_expiry_timestamp\": \"2016-11-03T11:49:02.807Z\", \"metadata\": { \"key1\": \"value1\", \"key2\": \"value2\" }, \"scheduled\": \"2016-11-03T11:49:02.807Z\", \"source_number\": \"+61491570157\", \"source_number_type\": \"INTERNATIONAL\" }, { \"callback_url\": \"https://my.callback.url.com\", \"content\": \"My second message\", \"destination_number\": \"+61491570158\", \"delivery_report\": true, \"format\": \"SMS\", \"message_expiry_timestamp\": \"2016-11-03T11:49:02.807Z\", \"metadata\": { \"key1\": \"value1\", \"key2\": \"value2\" }, \"scheduled\": \"2016-11-03T11:49:02.807Z\", \"source_number\": \"+61491570159\", \"source_number_type\": \"INTERNATIONAL\" } ]}"); + SendMessagesRequest body = new SendMessagesRequest() + { + Messages = new List + { + new Message() + { + CallbackUrl = "https://my.callback.url.com", + Content = "My first message", + DestinationNumber = "+61491570156", + DeliveryReport = true, + Format = MessageFormat.SMS, + MessageExpiryTimestamp = new DateTime(2016, 11, 03, 11, 49, 02, DateTimeKind.Utc), + Metadata = new Dictionary() {{"key1", "value1"}, {"key2", "value2"}}, + Scheduled = new DateTime(2016, 11, 03, 11, 49, 02, DateTimeKind.Utc), + SourceNumber = "+61491570157", + SourceNumberType = NumberType.INTERNATIONAL + }, + new Message() + { + CallbackUrl = "https://my.callback.url.com", + Content = "My second message", + DestinationNumber = "+61491570158", + DeliveryReport = true, + Format = MessageFormat.SMS, + MessageExpiryTimestamp = new DateTime(2016, 11, 03, 11, 49, 02, DateTimeKind.Utc), + Metadata = new Dictionary() {{"key1", "value1"}, {"key2", "value2"}}, + Scheduled = new DateTime(2016, 11, 03, 11, 49, 02, DateTimeKind.Utc), + SourceNumber = "+61491570159", + SourceNumberType = NumberType.INTERNATIONAL + } + } + }; // Perform API call SendMessagesResponse result = null; @@ -122,7 +153,7 @@ public async Task TestSendMessages1() // Test whether the captured response is as we expected Assert.IsNotNull(result, "Result should exist"); - dynamic messages = result.Messages;//JObject.Parse(TestHelper.ConvertStreamToString(httpCallBackHandler.Response.RawBody)); + dynamic messages = result.Messages; int count = (int)messages.Count; Assert.AreEqual(count, 2); @@ -134,19 +165,19 @@ public async Task TestSendMessages1() AssertSendMessageResponseValid(secondMessage, "SMS", "My second message", "https://my.callback.url.com", true, "+61491570158", "+61491570159", "queued"); } - private void AssertSendMessageResponseValid(dynamic message, string expectedFormat, string expectedContent, string expectedCallbackUrl, + private void AssertSendMessageResponseValid(Message message, string expectedFormat, string expectedContent, string expectedCallbackUrl, bool expectedDeliveryReport, string expectedDestinationNumber, string expectedSourceNumber, string expectedStatus) { - var format = (string)message.format; - var content = (string)message.content; - var callbackUrl = (string)message.callback_url; - var deliveryReport = (bool)message.delivery_report; - var destinationNumber = (string)message.destination_number; - var sourceNumber = (string)message.source_number; - var status = (string)message.status; - var messageId = (string)message.message_id; - var messageExpiry = (string)message.message_expiry_timestamp; - var scheduled = (string)message.scheduled; + var format = message.Format.ToString(); + var content = (string)message.Content; + var callbackUrl = (string)message.CallbackUrl; + var deliveryReport = (bool)message.DeliveryReport; + var destinationNumber = (string)message.DestinationNumber; + var sourceNumber = (string) message.SourceNumber; + var status = (string)message.Status.ToString(); + var messageId = (string)message.MessageId; + var messageExpiry = message.MessageExpiryTimestamp; + var scheduled = message.Scheduled; Assert.AreEqual(format, expectedFormat, "Format should match exactly (string literal match)"); Assert.AreEqual(content, expectedContent, "Content should match exactly (string literal match)"); @@ -158,18 +189,10 @@ private void AssertSendMessageResponseValid(dynamic message, string expectedForm // note, these are non-deterministic, so we only check for their existence. Assert.IsNotEmpty(messageId, "Message ID should not be empty."); - Assert.IsNotEmpty(messageExpiry, "Message Expiry should not be empty."); - Assert.IsNotEmpty(scheduled, "Scheduled time should not be empty."); - - DateTime date; - bool canParse = DateTime.TryParse(messageExpiry, out date); - - Assert.IsTrue(canParse, "Message Expiry must be a valid DateTime"); + Assert.IsNotNull(messageExpiry, "Message Expiry should not be empty."); + Assert.IsNotNull(scheduled, "Scheduled time should not be empty."); - canParse = DateTime.TryParse(scheduled, out date); - Assert.IsTrue(canParse, "Scheduled time must be a valid DateTime"); - - JObject metadata = message.metadata as JObject; + var metadata = message.Metadata; Assert.IsNotNull(metadata, "Metadata must not be null."); @@ -177,16 +200,13 @@ private void AssertSendMessageResponseValid(dynamic message, string expectedForm Assert.AreEqual(metadataCount, 2, "Metadata must have two children."); - var firstKey = ((dynamic)metadata).key1; - var secondKey = ((dynamic)metadata).key2; - - Assert.IsNotNull(firstKey, "Metadata must contain key1."); - Assert.IsNotNull(secondKey, "Metadata must contain key2."); + Assert.IsTrue(metadata.ContainsKey("key1"), "Metadata must contain key1."); + Assert.IsTrue(metadata.ContainsKey("key2"), "Metadata must contain key2."); - var firstKeyValue = (string)firstKey; - var secondKeyValue = (string)secondKey; + var firstKeyValue = metadata["key1"]; + var secondKeyValue = metadata["key2"]; - Assert.AreEqual(firstKeyValue, "value1", "key1 must equal value1."); + Assert.AreEqual(firstKeyValue, "value1", "key1 must equal value1."); Assert.AreEqual(secondKeyValue, "value2", "key2 must equal value1."); } }