Skip to content

Commit

Permalink
Merge pull request #124 from lwdeveloper/empty-headers-dictionary
Browse files Browse the repository at this point in the history
Handle empty headers on inbound webhooks
  • Loading branch information
feinoujc authored Aug 11, 2020
2 parents fb7a537 + e031c9c commit 38dfede
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/Mandrill.net/Model/WebHook/MandrillInboundMessageInfo.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using Mandrill.net.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Mandrill.Model
{
Expand All @@ -13,6 +16,8 @@ public class MandrillInboundMessageInfo

public string FromName { get; set; }

/// Inbound webhooks contain either a dictionary or an empty array for the headers property
[JsonConverter(typeof(EmptyArrayOrDictionaryConverter))]
public Dictionary<string, object> Headers { get; set; } = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

public string Html { get; set; }
Expand Down
47 changes: 47 additions & 0 deletions src/Mandrill.net/Serialization/EmptyArrayOrDictionaryConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Mandrill.net.Serialization
{
/// <summary>
/// This converter will allow an empty array to be converted to a dictionary
/// </summary>
internal class EmptyArrayOrDictionaryConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsAssignableFrom(typeof(Dictionary<string, object>));
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);

if (token.Type == JTokenType.Object)
{
return token.ToObject(objectType);
}
else if (token.Type == JTokenType.Array)
{
if (!token.HasValues)
{
// Create empty dictionary
return Activator.CreateInstance(objectType);
}
}

throw new JsonSerializationException("Object or empty array expected");
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ
var dictionary = prop.GetValue(instance) as IDictionary<string, string>;
return dictionary?.Count > 0;
};

}

if (typeof(IDictionary<string, object>).GetTypeInfo().IsAssignableFrom(propertyType.GetTypeInfo()))
Expand Down
15 changes: 14 additions & 1 deletion tests/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,21 @@ public void Can_serialize_inbound_web_hook()
events[1].Msg.FromName.Should().BeNullOrEmpty();

Output.WriteLine(JArray.FromObject(events, MandrillSerializer.Instance).ToString());
}

[Fact]
public void Can_serialize_inbound_web_hook_with_empty_headers()
{
string json = TestData.mandrill_inbound_empty_headers;

var events = MandrillInboundEvent.ParseMandrillEvents(json);

events.Should().NotBeNullOrEmpty();
events.Should().HaveCount(1);

events[0].Msg.Headers.Should().BeEmpty();

Output.WriteLine(JArray.FromObject(events, MandrillSerializer.Instance).ToString());
}

[Fact]
Expand All @@ -474,7 +488,6 @@ public void Can_serialize_sync_web_hook()
events[0].Type.Should().Be(MandrillSyncType.Blacklist);
}


[Fact]
public void Can_serialize_case_insensitive_header_dictionary()
{
Expand Down
44 changes: 44 additions & 0 deletions tests/TestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,50 @@ public class TestData

#endregion

#region mandrill_inbound_empty_headers

public const string mandrill_inbound_empty_headers = @"[
{
""event"": ""inbound"",
""msg"": {
""dkim"": {
""signed"": true,
""valid"": true
},
""email"": ""[email protected]"",
""from_email"": ""[email protected]"",
""from_name"": ""Example Sender"",
""headers"": [],
""html"": ""<p>This is an example inbound message.</p><img src=\""http://mandrillapp.com/track/open.php?u=999&id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&tags=_all,[email protected]\"" height=\""1\"" width=\""1\"">\n"",
""raw_msg"": ""Received: from mail115.us4.mandrillapp.com (mail115.us4.mandrillapp.com [205.201.136.115])\n\tby mail.example.com (Postfix) with ESMTP id AAAAAAAAAAA\n\tfor <[email protected]>; Fri, 10 May 2013 19:28:20 +0000 (UTC)\nDKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; s=mandrill; d=mail115.us4.mandrillapp.com;\n h=From:Sender:Subject:List-Unsubscribe:To:Message-Id:Date:MIME-Version:Content-Type; [email protected];\n bh=d60x72jf42gLILD7IiqBL0OBb40=;\n b=iJd7eBugdIjzqW84UZ2xynlg1SojANJR6xfz0JDD44h78EpbqJiYVcMIfRG7mkrn741Bd5YaMR6p\n 9j41OA9A5am+8BE1Ng2kLRGwou5hRInn+xXBAQm2NUt5FkoXSpvm4gC4gQSOxPbQcuzlLha9JqxJ\n 8ZZ89/20txUrRq9cYxk=\nDomainKey-Signature: a=rsa-sha1; c=nofws; q=dns; s=mandrill; d=mail115.us4.mandrillapp.com;\n b=X6qudHd4oOJvVQZcoAEUCJgB875SwzEO5UKf6NvpfqyCVjdaO79WdDulLlfNVELeuoK2m6alt2yw\n 5Qhp4TW5NegyFf6Ogr/Hy0Lt411r/0lRf0nyaVkqMM/9g13B6D9CS092v70wshX8+qdyxK8fADw8\n kEelbCK2cEl0AGIeAeo=;\nReceived: from localhost (127.0.0.1) by mail115.us4.mandrillapp.com id hhl55a14i282 for <[email protected]>; Fri, 10 May 2013 19:28:20 +0000 (envelope-from <bounce-md_999.aaaaaaaa.v1-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@mail115.us4.mandrillapp.com>)\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=c.mandrillapp.com; \n [email protected]; q=dns/txt; s=mandrill; t=1368214100; h=From : \n Sender : Subject : List-Unsubscribe : To : Message-Id : Date : \n MIME-Version : Content-Type : From : Subject : Date : X-Mandrill-User : \n List-Unsubscribe; bh=y5Vz+RDcKZmWzRc9s0xUJR6k4APvBNktBqy1EhSWM8o=; \n b=PLAUIuw8zk8kG5tPkmcnSanElxt6I5lp5t32nSvzVQE7R8u0AmIEjeIDZEt31+Q9PWt+nY\n sHHRsXUQ9SZpndT9Bk++/SmyA2ntU/2AKuqDpPkMZiTqxmGF80Wz4JJgx2aCEB1LeLVmFFwB\n 5Nr/LBSlsBlRcjT9QiWw0/iRvCn74=\nFrom: <[email protected]>\nSender: <[email protected]>\nSubject: This is an example webhook message\nList-Unsubscribe: <mailto:unsubscribe-md_999.aaaaaaaa.v1-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@mailin1.us2.mcsv.net?subject=unsub>\nTo: <[email protected]>\nX-Report-Abuse: Please forward a copy of this message, including all headers, to [email protected]\nX-Mandrill-User: md_999\nMessage-Id: <999.20130510192820.aaaaaaaaaaaaaa.aaaaaaaa@mail115.us4.mandrillapp.com>\nDate: Fri, 10 May 2013 19:28:20 +0000\nMIME-Version: 1.0\nContent-Type: multipart/alternative; boundary=\""_av-7r7zDhHxVEAo2yMWasfuFw\""\n\n--_av-7r7zDhHxVEAo2yMWasfuFw\nContent-Type: text/plain; charset=utf-8\nContent-Transfer-Encoding: 7bit\n\nThis is an example inbound message.\n--_av-7r7zDhHxVEAo2yMWasfuFw\nContent-Type: text/html; charset=utf-8\nContent-Transfer-Encoding: 7bit\n\n<p>This is an example inbound message.</p><img src=\""http://mandrillapp.com/track/open.php?u=999&id=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&tags=_all,[email protected]\"" height=\""1\"" width=\""1\"">\n--_av-7r7zDhHxVEAo2yMWasfuFw--"",
""sender"": null,
""spam_report"": {
""matched_rules"": [],
""score"": 0
},
""spf"": {
""detail"": ""sender SPF authorized"",
""result"": ""pass""
},
""subject"": ""This is an example webhook message"",
""tags"": [],
""template"": null,
""text"": ""This is an example inbound message.\n"",
""text_flowed"": false,
""to"": [
[
""[email protected]"",
null
]
],
""cc"": []
},
""ts"": 1368214102
}
]";

#endregion

#region mandrill_webhook_example

public const string mandrill_webhook_example = @"[
Expand Down

0 comments on commit 38dfede

Please sign in to comment.