diff --git a/src/Intercom.Tests/Core/ClientTest.cs b/src/Intercom.Tests/Core/ClientTest.cs index 86ae70a..d998496 100755 --- a/src/Intercom.Tests/Core/ClientTest.cs +++ b/src/Intercom.Tests/Core/ClientTest.cs @@ -1,13 +1,49 @@ -using NUnit.Framework; +using System; +using System.IO; +using Intercom.Clients; +using Intercom.Core; +using Intercom.Exceptions; +using Intercom.Factories; +using Moq; +using NUnit.Framework; +using RestSharp; namespace Intercom.Test { [TestFixture ()] public class ClientTest { + private static IRestRequest CaptureRequest(Action call) + { + IRestRequest captured = null; + var restClient = new Mock(); + restClient.Setup(_ => _.Execute(It.IsAny())) + .Callback(r => captured = r) + .Returns((IRestResponse)null); + var factory = new Mock(new Authentication("DEFAULT", "DEFAULT")); + factory.Setup(_ => _.RestClient).Returns(restClient.Object); + + try { call(new ContactsClient(factory.Object)); } + catch (IntercomException) { } + + return captured; + } + [Test] - public void Test () + public void EncodesIdWhenBuildingPath () + { + var request = CaptureRequest(c => c.View("a/b?x=1#f")); + + var uri = new RestClient("https://api.intercom.io/").BuildUri(request); + Assert.AreEqual("/contacts/a%2Fb%3Fx%3D1%23f", uri.AbsolutePath); + Assert.AreEqual(String.Empty, uri.Query); + } + + [TestCase(".")] + [TestCase("..")] + public void RejectsDotSegmentId (string id) { + Assert.Throws(() => CaptureRequest(c => c.View(id))); } } } \ No newline at end of file diff --git a/src/Intercom/Clients/AdminConversationsClient.cs b/src/Intercom/Clients/AdminConversationsClient.cs index 4316288..ed36408 100644 --- a/src/Intercom/Clients/AdminConversationsClient.cs +++ b/src/Intercom/Clients/AdminConversationsClient.cs @@ -44,7 +44,7 @@ public Conversation Reply(AdminConversationReply reply) ClientResponse result = null; String body = Serialize(reply); - result = Post(body, resource: CONVERSATIONS_RESOURCE + Path.DirectorySeparatorChar + reply.conversation_id + Path.DirectorySeparatorChar + REPLY_RESOURCE); + result = Post(body, resource: CONVERSATIONS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(reply.conversation_id) + Path.DirectorySeparatorChar + REPLY_RESOURCE); return result.Result; } diff --git a/src/Intercom/Clients/AdminsClient.cs b/src/Intercom/Clients/AdminsClient.cs index cc82fb2..a0dd3c0 100644 --- a/src/Intercom/Clients/AdminsClient.cs +++ b/src/Intercom/Clients/AdminsClient.cs @@ -65,7 +65,7 @@ public Admin View (String id) } ClientResponse result = null; - result = Get (resource: ADMINS_RESOURCE + Path.DirectorySeparatorChar + id); + result = Get (resource: ADMINS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); return result.Result; } @@ -81,7 +81,7 @@ public Admin View(Admin admin) } ClientResponse result = null; - result = Get (resource: ADMINS_RESOURCE + Path.DirectorySeparatorChar + admin.id); + result = Get (resource: ADMINS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(admin.id)); return result.Result; } } diff --git a/src/Intercom/Clients/CompanyClient.cs b/src/Intercom/Clients/CompanyClient.cs index b004fdb..71ef0cb 100644 --- a/src/Intercom/Clients/CompanyClient.cs +++ b/src/Intercom/Clients/CompanyClient.cs @@ -78,7 +78,7 @@ public Company View(String id) throw new ArgumentNullException(nameof(id)); } ClientResponse result = null; - result = Get(resource: COMPANIES_RESOURCE + Path.DirectorySeparatorChar + id); + result = Get(resource: COMPANIES_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); return result.Result; } @@ -94,7 +94,7 @@ public Company View(Company company) if (!String.IsNullOrEmpty(company.id)) { - result = Get(resource: COMPANIES_RESOURCE + Path.DirectorySeparatorChar + company.id); + result = Get(resource: COMPANIES_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(company.id)); } else if (!String.IsNullOrEmpty(company.name)) { @@ -163,7 +163,7 @@ public Users ListUsers(Company company) if (!String.IsNullOrEmpty(company.id)) { - String resource = company.id + Path.DirectorySeparatorChar + "users"; + String resource = EncodePathSegment(company.id) + Path.DirectorySeparatorChar + "users"; result = Get(resource: COMPANIES_RESOURCE + Path.DirectorySeparatorChar + resource); } else if (!String.IsNullOrEmpty(company.company_id)) @@ -187,7 +187,7 @@ public Users ListUsers(String companyId) throw new ArgumentNullException(nameof(companyId)); } - String resource = companyId + Path.DirectorySeparatorChar + "users"; + String resource = EncodePathSegment(companyId) + Path.DirectorySeparatorChar + "users"; ClientResponse result = null; result = Get(resource: COMPANIES_RESOURCE + Path.DirectorySeparatorChar + resource); return result.Result; diff --git a/src/Intercom/Clients/ContactsClient.cs b/src/Intercom/Clients/ContactsClient.cs index a523066..8f04a4c 100644 --- a/src/Intercom/Clients/ContactsClient.cs +++ b/src/Intercom/Clients/ContactsClient.cs @@ -76,7 +76,7 @@ public Contact View (String id) } ClientResponse result = null; - result = Get (resource: CONTACTS_RESOURCE + Path.DirectorySeparatorChar + id); + result = Get (resource: CONTACTS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); return result.Result; } @@ -90,7 +90,7 @@ public Contact View (Contact contact) ClientResponse result = null; if (!String.IsNullOrEmpty (contact.id)) { - result = Get (resource: CONTACTS_RESOURCE + Path.DirectorySeparatorChar + contact.id); + result = Get (resource: CONTACTS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(contact.id)); } else if (!String.IsNullOrEmpty (contact.user_id)) { parameters.Add (Constants.USER_ID, contact.user_id); result = Get (parameters: parameters); @@ -162,7 +162,7 @@ public Contact Delete (Contact contact) ClientResponse result = null; if (!String.IsNullOrEmpty (contact.id)) { - result = Delete (resource: CONTACTS_RESOURCE + Path.DirectorySeparatorChar + contact.id); + result = Delete (resource: CONTACTS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(contact.id)); } else if (!String.IsNullOrEmpty (contact.user_id)) { parameters.Add (Constants.USER_ID, contact.user_id); result = Delete (parameters: parameters); @@ -180,7 +180,7 @@ public Contact Delete (String id) } ClientResponse result = null; - result = Delete (resource: CONTACTS_RESOURCE + Path.DirectorySeparatorChar + id); + result = Delete (resource: CONTACTS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); return result.Result; } diff --git a/src/Intercom/Clients/ConversationsClient.cs b/src/Intercom/Clients/ConversationsClient.cs index bcfcf97..5037084 100644 --- a/src/Intercom/Clients/ConversationsClient.cs +++ b/src/Intercom/Clients/ConversationsClient.cs @@ -57,7 +57,7 @@ public Conversation View(String id, bool? displayAsPlainText = null) } ClientResponse result = null; - result = Get(resource: CONVERSATIONS_RESOURCE + Path.DirectorySeparatorChar + id); + result = Get(resource: CONVERSATIONS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); return result.Result; } diff --git a/src/Intercom/Clients/NotesClient.cs b/src/Intercom/Clients/NotesClient.cs index 793ddf3..070e794 100644 --- a/src/Intercom/Clients/NotesClient.cs +++ b/src/Intercom/Clients/NotesClient.cs @@ -143,7 +143,7 @@ public Note View(String id) } ClientResponse result = null; - result = Get(resource: NOTES_RESOURCE + Path.DirectorySeparatorChar + id); + result = Get(resource: NOTES_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); return result.Result; } diff --git a/src/Intercom/Clients/SegmentsClient.cs b/src/Intercom/Clients/SegmentsClient.cs index fd84336..f6011ec 100644 --- a/src/Intercom/Clients/SegmentsClient.cs +++ b/src/Intercom/Clients/SegmentsClient.cs @@ -81,7 +81,7 @@ public Segment View(String id, bool? includeCount = null) }; ClientResponse result = null; - result = Get(parameters: parameters, resource: SEGMENTS_RESOURCE + Path.DirectorySeparatorChar + id); + result = Get(parameters: parameters, resource: SEGMENTS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); return result.Result; } @@ -104,7 +104,7 @@ public Segment View(Segment segment, bool? includeCount = null) }; ClientResponse result = null; - result = Get(parameters: parameters, resource: SEGMENTS_RESOURCE + Path.DirectorySeparatorChar + segment.id); + result = Get(parameters: parameters, resource: SEGMENTS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(segment.id)); return result.Result; } } diff --git a/src/Intercom/Clients/TagsClient.cs b/src/Intercom/Clients/TagsClient.cs index 68a27e8..6c9f7b4 100644 --- a/src/Intercom/Clients/TagsClient.cs +++ b/src/Intercom/Clients/TagsClient.cs @@ -83,7 +83,7 @@ public Tag View(String id) } ClientResponse result = null; - result = Get(resource: TAGS_RESOURCE + Path.DirectorySeparatorChar + id); + result = Get(resource: TAGS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); return result.Result; } @@ -98,7 +98,7 @@ public Tag View(Tag tag) if (!String.IsNullOrEmpty(tag.id)) { - result = Get(resource: TAGS_RESOURCE + Path.DirectorySeparatorChar + tag.id); + result = Get(resource: TAGS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(tag.id)); } else { @@ -144,7 +144,7 @@ public void Delete(Tag tag) throw new ArgumentException("you need to provide 'tag.id' to delete a tag."); } - Delete(resource: TAGS_RESOURCE + Path.DirectorySeparatorChar + tag.id); + Delete(resource: TAGS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(tag.id)); } public void Delete(String id) @@ -154,7 +154,7 @@ public void Delete(String id) throw new ArgumentNullException(nameof(id)); } - Delete(resource: TAGS_RESOURCE + Path.DirectorySeparatorChar + id); + Delete(resource: TAGS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); } public Tag Tag(String name, List companies) diff --git a/src/Intercom/Clients/UserConversationsClient.cs b/src/Intercom/Clients/UserConversationsClient.cs index ed31e2d..d9223f8 100644 --- a/src/Intercom/Clients/UserConversationsClient.cs +++ b/src/Intercom/Clients/UserConversationsClient.cs @@ -45,7 +45,7 @@ public Conversation Reply(UserConversationReply reply) ClientResponse result = null; String body = Serialize(reply); - result = Post(body, resource: CONVERSATIONS_RESOURCE + Path.DirectorySeparatorChar + reply.conversation_id + Path.DirectorySeparatorChar + REPLY_RESOURCE); + result = Post(body, resource: CONVERSATIONS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(reply.conversation_id) + Path.DirectorySeparatorChar + REPLY_RESOURCE); return result.Result; } diff --git a/src/Intercom/Clients/UsersClient.cs b/src/Intercom/Clients/UsersClient.cs index d2a1d23..b84c08b 100644 --- a/src/Intercom/Clients/UsersClient.cs +++ b/src/Intercom/Clients/UsersClient.cs @@ -129,7 +129,7 @@ public User View(String id) } ClientResponse result = null; - result = Get(resource: USERS_RESOURCE + Path.DirectorySeparatorChar + id); + result = Get(resource: USERS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); return result.Result; } @@ -145,7 +145,7 @@ public User View(User user) if (!String.IsNullOrEmpty(user.id)) { - result = Get(resource: USERS_RESOURCE + Path.DirectorySeparatorChar + user.id); + result = Get(resource: USERS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(user.id)); } else if (!String.IsNullOrEmpty(user.user_id)) { @@ -217,7 +217,7 @@ public User Archive(User user) if (!String.IsNullOrEmpty(user.id)) { - result = Delete(resource: USERS_RESOURCE + Path.DirectorySeparatorChar + user.id); + result = Delete(resource: USERS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(user.id)); } else if (!String.IsNullOrEmpty(user.user_id)) { @@ -251,7 +251,7 @@ public User Archive(String id) } ClientResponse result = null; - result = Delete(resource: USERS_RESOURCE + Path.DirectorySeparatorChar + id); + result = Delete(resource: USERS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); return result.Result; } diff --git a/src/Intercom/Clients/VisitorsClient.cs b/src/Intercom/Clients/VisitorsClient.cs index c9aa526..c597525 100644 --- a/src/Intercom/Clients/VisitorsClient.cs +++ b/src/Intercom/Clients/VisitorsClient.cs @@ -62,7 +62,7 @@ public Visitor View(String id) } ClientResponse result = null; - result = Get(resource: VISITORS_RESOURCE + Path.DirectorySeparatorChar + id); + result = Get(resource: VISITORS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); return result.Result; } @@ -78,7 +78,7 @@ public Visitor View(Visitor visitor) if (!String.IsNullOrEmpty(visitor.id)) { - result = Get(resource: VISITORS_RESOURCE + Path.DirectorySeparatorChar + visitor.id); + result = Get(resource: VISITORS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(visitor.id)); } else if (!String.IsNullOrEmpty(visitor.user_id)) { @@ -124,7 +124,7 @@ public Visitor Delete(Visitor visitor) Dictionary parameters = new Dictionary(); ClientResponse result = null; - result = Delete(resource: VISITORS_RESOURCE + Path.DirectorySeparatorChar + visitor.id); + result = Delete(resource: VISITORS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(visitor.id)); return result.Result; } @@ -136,7 +136,7 @@ public Visitor Delete(String id) } ClientResponse result = null; - result = Delete(resource: VISITORS_RESOURCE + Path.DirectorySeparatorChar + id); + result = Delete(resource: VISITORS_RESOURCE + Path.DirectorySeparatorChar + EncodePathSegment(id)); return result.Result; } diff --git a/src/Intercom/Core/Client.cs b/src/Intercom/Core/Client.cs index c714126..3d208e3 100644 --- a/src/Intercom/Core/Client.cs +++ b/src/Intercom/Core/Client.cs @@ -310,6 +310,14 @@ protected virtual IRestRequest BuildRequest(Method httpMethod = Method.GET, return request; } + protected static String EncodePathSegment(String id) + { + if (String.IsNullOrEmpty(id) || id == "." || id == "..") + throw new ArgumentException("The id cannot be used as a path segment.", nameof(id)); + + return Uri.EscapeDataString(id); + } + protected virtual IRestClient BuildClient() { return _restClientFactory.RestClient;