diff --git a/README.md b/README.md index a9259fd..6f9a699 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,15 @@ -# intercom-dotnet - -[![Circle CI](https://circleci.com/gh/intercom/intercom-dotnet.png?style=shield)](https://circleci.com/gh/intercom/intercom-dotnet) -[![nuget](https://img.shields.io/nuget/v/Intercom.Dotnet.Client)](https://www.nuget.org/packages/Intercom.Dotnet.Client) -![Intercom API Version](https://img.shields.io/badge/Intercom%20API%20Version-1.3-blue) - -> .NET bindings for the [Intercom API](https://developers.intercom.io/reference) - -## Project Updates - -### Maintenance - -We're currently building a new team to provide in-depth and dedicated SDK support. +![Build Status](https://www.myget.org/BuildSource/Badge/intercom-dotnet?identifier=9009384b-3f0e-4d23-aa53-3bac173f29bb) -In the meantime, we'll be operating on limited capacity, meaning all pull requests will be evaluated on a best effort basis and will be limited to critical issues. +# intercom-dotnet -We'll communicate all relevant updates as we build this new team and support strategy in the coming months. +.NET bindings for the [Intercom API](https://developers.intercom.io/reference) -- [Installation](#add-a-dependency) -- [Resources](#resources) -- [Authorization](#authorization) -- [Usage](#usage) -- [Todo](#todo) -- [Pull Requests](#pull-requests) + - [Installation](#add-a-dependency) + - [Resources](#resources) + - [Authorization](#authorization) + - [Usage](#usage) + - [Todo](#todo) + - [Pull Requests](#pull-requests) ## Add a dependency @@ -43,11 +31,13 @@ Resources this API supports: - [Notes](#notes) - [Conversations](#conversations) - [Counts](#counts) +- [Articles](#Articles) *Intercom API 2.0+* Each of these resources is represented through the dotnet client by a Class as `ResourceClient`. **E.g.**: for **users**, you can use the `UsersClient`. For **segments**, you can use `SegmentsClient`. + ## Authorization > If you already have an access token you can find it [here](https://app.intercom.com/developers/_). If you want to create or learn more about access tokens then you can find more info [here](https://developers.intercom.io/docs/personal-access-tokens). @@ -74,20 +64,17 @@ UsersClient usersClient = new UsersClient(factory); ### Users -#### Create UsersClient instance - +**Create UsersClient instance** ```cs UsersClient usersClient = new UsersClient(new Authentication("MyPersonalAccessToken")); ``` -#### Create user - +**Create user** ```cs User user = usersClient.Create(new User() { user_id = "my_id", name = "first last" }); ``` -#### View a user (by id, user_id or email) - +**View a user (by id, user_id or email)** ```cs User user = usersClient.View("100300231"); User user = usersClient.View(new User() { email = "example@example.com" }); @@ -95,17 +82,15 @@ User user = usersClient.View(new User() { id = "100300231" }); User user = usersClient.View(new User() { user_id = "my_id" }); ``` -#### Update a user with a company - +**Update a user with a company** ```cs User user = usersClient.Update(new User() { email = "example@example.com", companies = new List() { new Company() { company_id = "new_company" } } }); -``` - -#### Update user's custom attributes +``` +**Update user's custom attributes** ```cs Dictionary customAttributes = new Dictionary(); customAttributes.Add("total", "100.00"); @@ -117,9 +102,8 @@ user.custom_attributes = customAttributes; user = usersClient.Update(user); ``` -#### List users and iterating through them - -Limited to up to 10k records, if you want to list more records please use the Scroll API +**List users and iterating through them** +
Limited to up to 10k records, if you want to list more records please use the Scroll API ```cs Users users = usersClient.List(); @@ -128,38 +112,33 @@ foreach(User u in users.users) Console.WriteLine(u.email); ``` -#### List users by Tag, Segment, Company - +**List users by Tag, Segment, Company** ```cs Dictionary parameters = new Dictionary(); parameters.Add("segment_id", "57553e93a32843ca09000277"); Users users = usersClient.List(parameters); ``` -#### List users via the Scroll API - +**List users via the Scroll API** ```cs Users users = usersClient.Scroll(); String scroll_param_value = users.scroll_param; Users users = usersClient.Scroll(scroll_param_value); ``` -#### Delete a user - +**Delete a user** ```cs usersClient.Archive("100300231"); // with intercom generated user's id usersClient.Archive(new User() { email = "example@example.com" }); usersClient.Archive(new User() { user_id = "my_id" }); ``` -#### Permanently delete a user - +**Permanently delete a user** ```cs usersClient.PermanentlyDeleteUser("100300231"); // with intercom generated user's id ``` -#### Update User's LastSeenAt (multiple ways) - +**Update User's LastSeenAt (multiple ways)** ```cs User user = usersClient.UpdateLastSeenAt("100300231"); User user = usersClient.UpdateLastSeenAt(new User() { id = "100300231" }); @@ -167,8 +146,7 @@ User user = usersClient.UpdateLastSeenAt("100300231", 1462110718); User user = usersClient.UpdateLastSeenAt(new User() { id = "100300231" }, 1462110718); ``` -#### Increment User's Session - +**Increment User's Session** ```cs usersClient.IncrementUserSession(new User() { id = "100300231" }); usersClient.IncrementUserSession("100300231", new List() { "company_is_blue" }}); @@ -176,39 +154,34 @@ usersClient.IncrementUserSession("100300231", new List() { "company_is_b // You can also update a User's session by updating a User record with a "new_session = true" attribute ``` -#### Removing User's companies - +**Removing User's companies** ```cs User user = usersClient.RemoveCompanyFromUser("100300231", new List() { "true_company" }); ``` -*** +*** ### Contacts -#### Create ContactsClient instance - +**Create ContactsClient instance** ```cs ContactsClient contactsClient = new ContactsClient(new Authentication("MyPersonalAccessToken")); ``` -#### Create a contact - +**Create a contact** ```cs Contact contact = contactsClient.Create(new Contact() { }); Contact contact = contactsClient.Create(new Contact() { name = "lead_name" }); ``` -#### View a contact (by id, or user_id) - +**View a contact (by id, or user_id)** ```cs Contact contact = contactsClient.View("100300231"); Contact contact = contactsClient.View(new Contact() { id = "100300231" }); Contact contact = contactsClient.View(new Contact() { user_id = "my_lead_id" }); ``` -#### Update a contact (by id, or user_id) - +**Update a contact (by id, or user_id)** ```cs Contact contact = contactsClient.Update( new Contact() @@ -218,10 +191,8 @@ Contact contact = contactsClient.Update( }); ``` -#### List contacts and iterate through them - +**List contacts and iterate through them** Limited to up to 10k records, if you want to list more records please use the Scroll API - ```cs Contacts contacts = contactsClient.List(); @@ -229,30 +200,25 @@ foreach (Contact c in contacts.contacts) Console.WriteLine(c.email); ``` -#### List contacts by email - +**List contacts by email** ```cs Contacts contacts = contactsClient.List("email@example.com"); ``` -#### List contacts via Scroll API - +**List contacts via Scroll API** ```cs Contacts contacts = contactsClient.Scroll(); String scroll_param_value = contacts.scroll_param; Contacts contacts = contactsClient.Scroll(scroll_param_value); ``` -#### Convert a contact to a User - +**Convert a contact to a User** Note that if the user does not exist they will be created, otherwise they will be merged. - ```cs User user = contactsClient.Convert(contact, new User() { user_id = "120" }); ``` -#### Delete a contact - +**Delete a contact** ```cs contactsClient.Delete("100300231"); contactsClient.Delete(new Contact() { id = "100300231" }); @@ -263,52 +229,44 @@ contactsClient.Delete(new Contact() { user_id = "my_id" }); ### Visitors -#### Create VisitorsClient instance - +**Create VisitorsClient instance** ```cs VisitorsClient visitorsClient = new VisitorsClient(new Authentication("MyPersonalAccessToken")); ``` -#### View a visitor - +**View a visitor** ```cs Visitor visitor = VisitorsClient.View("573479f784c5acde6a000575"); ``` -#### View a visitor by user_id - +**View a visitor by user_id** ```cs Dictionary parameters = new Dictionary(); parameters.Add("user_id", "16e690c0-485a-4e87-ae98-a326e788a4f7"); Visitor visitor = VisitorsClient.View(parameters); ``` -#### Update a visitor - +**Update a visitor** ```cs Visitor visitor = VisitorsClient.Update(visitor); ``` -#### Delete a visitor - +**Delete a visitor** ```cs Visitor visitor = VisitorsClient.Delete(visitor); ``` -#### Convert to existing user - +**Convert to existing user** ```cs Visitor visitor = VisitorsClient.ConvertToUser(visitor, user); ``` -#### Convert to new user - +**Convert to new user** ```cs Visitor visitor = VisitorsClient.ConvertToUser(visitor, new User(){ user_id = "25" }); ``` -#### Convert to contact - +**Convert to contact** ```cs Visitor visitor = VisitorsClient.ConvertToContact(visitor); ``` @@ -317,21 +275,18 @@ Visitor visitor = VisitorsClient.ConvertToContact(visitor); ### Companies -#### Create CompanyClient instance - +**Create CompanyClient instance** ```cs CompanyClient companyClient = new CompanyClient(new Authentication("MyPersonalAccessToken")); ``` -#### Create a company - +**Create a company** ```cs Company company = companyClient.Create(new Company()); Company company = companyClient.Create(new Company() { name = "company_name" }); ``` -#### View a company - +**View a company** ```cs Company company = companyClient.View("100300231"); Company company = companyClient.View(new Company() { id = "100300231" }); @@ -339,8 +294,7 @@ Company company = companyClient.View(new Company() { company_id = "my_company_id Company company = companyClient.View(new Company() { name = "my_company_name" }); ``` -#### Update a company - +**Update a company** ```cs Company company = companyClient.Update( new Company() @@ -350,16 +304,13 @@ Company company = companyClient.Update( }); ``` -#### List companies - -Limited to up to 10k records, if you want to list more records please use the Scroll API - +**List companies** +
Limited to up to 10k records, if you want to list more records please use the Scroll API ```cs Companies companies = companyClient.List(); ``` -#### List companies via Scroll API - +**List companies via Scroll API** ```cs Companies companies = companyClient.Scroll(); String scrollParam = companies.scroll_param; @@ -369,8 +320,7 @@ foreach (Company c in companies.companies) Console.WriteLine(c.name); ``` -#### List a Company's registered users - +**List a Company's registered users** ```cs Users users = companyClient.ListUsers(new Company() { id = "100300231" }); Users users = companyClient.ListUsers(new Company() { company_id = "my_company_id" }); @@ -380,21 +330,18 @@ Users users = companyClient.ListUsers(new Company() { company_id = "my_company_i ### Admins -#### Create AdminsClient instance - +**Create AdminsClient instance** ```cs AdminsClient adminsClient = new AdminsClient(new Authentication("MyPersonalAccessToken")); ``` -#### View an admin - +**View an admin** ```cs Admin admin = adminsClient.View("100300231"); Admin admin = adminsClient.View(new Admin() { id = "100300231" }); ``` -#### List admins - +**List admins** ```cs Admins admins = adminsClient.List(); ``` @@ -403,32 +350,27 @@ Admins admins = adminsClient.List(); ### Tags -#### Create TagsClient instance - +**Create TagsClient instance** ```cs TagsClient tagsClient = new TagsClient(new Authentication("MyPersonalAccessToken")); ``` -#### Create a tag - +**Create a tag** ```cs Tag tag = tagsClient.Create(new Tag() { name = "new_tag" }); ``` -#### List tags - +**List tags** ```cs Tags tags = tagsClient.List(); ``` -#### Delete a tag - +**Delete a tag** ```cs tagsClient.Delete(new Tag() { id = "100300231" }); ``` -#### Tag User, Company or Contact - +**Tag User, Company or Contact** ```cs tagsClient.Tag("new_tag", new List() { new Company(){ company_id = "blue" } }); tagsClient.Tag("new_tag", new List() { new Company(){ id = "5911bd8bf0c7223d2d1d045d" } }); @@ -436,8 +378,7 @@ tagsClient.Tag("new_tag", new List() { new Contact(){ id = "5911bd8bf0c tagsClient.Tag("new_tag", new List() { new User(){ id = "5911bd8bf0c7446d2d1d045d", email = "example@example.com", user_id = "25" } }); ``` -#### Untag User, Company or Contact - +**Untag User, Company or Contact** ```cs tagsClient.Untag("new_tag", new List() { new Company(){ company_id = "1000_company_id" } }); tagsClient.Untag("new_tag", new List() { new Contact(){ id = "5911bd8bf0c7223d2d1d045d" } }); @@ -448,21 +389,18 @@ tagsClient.Untag("new_tag", new List() { new User(){ user_id = "1000_user_ ### Segments -#### Create SegmentsClient instance - +**Create SegmentsClient instance** ```cs SegmentsClient segmentsClient = new SegmentsClient(new Authentication("MyPersonalAccessToken")); ``` -#### View a segment (by id) - +**View a segment (by id)** ```cs Segment segment = segmentsClient.View("100300231"); Segment segment = segmentsClient.View(new Segment() { id = "100300231" }); ``` -#### List segments - +**List segments** ```cs Segments segments = segmentsClient.List(); ``` @@ -471,14 +409,12 @@ Segments segments = segmentsClient.List(); ### Notes -#### Create NotesClient instance - +**Create NotesClient instance** ```cs NotesClient notesClient = new NotesClient(new Authentication("MyPersonalAccessToken")); ``` -#### Create a note (by User, body and admin_id) - +**Create a note (by User, body and admin_id)** ```cs Note note = notesClient.Create( new Note() { @@ -490,14 +426,12 @@ Note note = notesClient.Create( Note note = notesClient.Create(new User() { email = "example@example.com" }, "this is a new note", "100300231_admin_id"); ``` -#### View a note - +**View a note** ```cs Note note = notesClient.View("2001"); ``` -#### List User's notes - +**List User's notes** ```cs Notes notes = notesClient.List(new User() { id = "100300231"}); @@ -509,20 +443,17 @@ foreach (Note n in notes.notes) ### Events -#### Create EventsClient instance - +**Create EventsClient instance** ```cs EventsClient eventsClient = new EventsClient(new Authentication("MyPersonalAccessToken")); ``` -#### Create an event - +**Create an event** ```cs Event ev = eventsClient.Create(new Event() { user_id = "1000_user_id", email = "user_email@example.com", event_name = "new_event", created_at = 1462110718 }); ``` -#### Create an event with Metadata (Simple, MonetaryAmounts and RichLinks) - +**Create an event with Metadata (Simple, MonetaryAmounts and RichLinks)** ```cs Metadata metadata = new Metadata(); metadata.Add("simple", 100); @@ -533,8 +464,7 @@ metadata.Add("richlink", new Metadata.RichLink("www.example.com", "value1")); Event ev = eventsClient.Create(new Event() { user_id = "1000_user_id", email = "user_email@example.com", event_name = "new_event", created_at = 1462110718, metadata = metadata }); ``` -#### List events by user - +**List events by user** ```cs Events events = eventsClient.List(new User() { user_id = "my_id" }); ``` @@ -543,20 +473,17 @@ Events events = eventsClient.List(new User() { user_id = "my_id" }); ### Counts -#### Create CountsClient instance - +**Create CountsClient instance** ```cs CountsClient countsClient = new CountsClient(new Authentication("MyPersonalAccessToken")); ``` -#### Get AppCount - +**Get AppCount** ```cs AppCount appCount = countsClient.GetAppCount(); ``` -#### Get Specific Counts - +**Get Specific Counts** ```cs ConversationAppCount conversationAppCount = countsClient.GetConversationAppCount(); ConversationAdminCount conversationAdminCount = countsClient.GetConversationAdminCount(); @@ -571,21 +498,18 @@ UserTagCount userTagCount = countsClient.GetUserTagCount(); ### Conversations -#### Create ConversationsClient instance - +**Create ConversationsClient instance** ```cs ConversationsClient conversationsClient = new ConversationsClient(new Authentication("MyPersonalAccessToken")); ``` -#### View any type of conversation - +**View any type of conversation** ```cs conversationsClient.View("100300231"); conversationsClient.View("100300231", displayAsPlainText: true); ``` -#### List all conversations - +**List all conversations** ```cs conversationsClient.ListAll(); @@ -594,14 +518,12 @@ parameters.Add("order", "asc"); conversationsClient.ListAll(parameters); ``` -#### Create AdminConversationsClient instance - +**Create AdminConversationsClient instance** ```cs AdminConversationsClient adminConversationsClient = new AdminConversationsClient(new Authentication("MyPersonalAccessToken")); ``` -#### Create Admin initiated Conversation - +**Create Admin initiated Conversation** ```cs AdminConversationMessage admin_message = adminConversationsClient.Create(new AdminConversationMessage( @@ -613,8 +535,7 @@ AdminConversationMessage admin_message = body: "this is an email body")); ``` -#### Create Admin initiated Conversation's reply - +**Create Admin initiated Conversation's reply** ```cs Conversation conversation = adminConversationsClient.Reply( @@ -625,8 +546,7 @@ Conversation conversation = body: "this is a reply body")); ``` -#### Reply to user's last conversation - +**Reply to user's last conversation** ```cs Conversation reply = adminConversationsClient.ReplyLastConversation( @@ -639,14 +559,13 @@ Conversation reply = }); ``` -#### Create UserConversationsClient instance +**Create UserConversationsClient instance** ```cs UserConversationsClient userConversationsClient = new UserConversationsClient(new Authentication("MyPersonalAccessToken")); ``` -#### Create User initiated Conversation - +**Create User initiated Conversation** ```cs UserConversationMessage user_message = userConversationsClient.Create( @@ -655,8 +574,7 @@ UserConversationMessage user_message = body: "this is a user's message body")); ``` -#### Create User initiated Conversation's reply - +**Create User initiated Conversation's reply** ```cs Conversation conversation = userConversationsClient.Reply( @@ -682,8 +600,8 @@ Not yet supported by these bindings. ## Pull Requests -- **Add tests!** Your patch won't be accepted if it doesn't have tests. -- **Document any change in behaviour.** Make sure the README and any other relevant documentation are kept up-to-date. -- **Create topic branches.** Don't ask us to pull from your master branch. -- **One pull request per feature.** If you want to do more than one thing, send multiple pull requests. -- **Send coherent history.** Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before sending them to us. +* **Add tests!** Your patch won't be accepted if it doesn't have tests. +* **Document any change in behaviour.** Make sure the README and any other relevant documentation are kept up-to-date. +* **Create topic branches.** Don't ask us to pull from your master branch. +* **One pull request per feature.** If you want to do more than one thing, send multiple pull requests. +* **Send coherent history.** Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before sending them to us.** diff --git a/src/Intercom.Tests/Clients/ArticlesClientTest.cs b/src/Intercom.Tests/Clients/ArticlesClientTest.cs new file mode 100644 index 0000000..7adb679 --- /dev/null +++ b/src/Intercom.Tests/Clients/ArticlesClientTest.cs @@ -0,0 +1,59 @@ +using NUnit.Framework; +using System; +using Intercom.Core; +using Intercom.Data; +using Intercom.Clients; +using Intercom.Exceptions; +using Intercom.Factories; +using RestSharp; +using RestSharp.Authenticators; +using System.Collections.Generic; +using Newtonsoft.Json; +using Moq; + +namespace Intercom.Test +{ + [TestFixture()] + internal class ArticlesClientTest : TestBase + { + private ArticlesClient articlesClient; + + public ArticlesClientTest() + : base() + { + var auth = new Authentication(AppId, AppKey); + var restClientFactory = new RestClientFactory(auth); + articlesClient = new ArticlesClient(restClientFactory); + } + + [Test()] + public void View_WithEmptyString_ThrowException() + { + Assert.Throws(() => articlesClient.View(String.Empty)); + } + + [Test()] + public void View_NoId_ThrowException() + { + Assert.Throws(() => articlesClient.View(new Article())); + } + + [Test()] + public void Create_WithNull_ThrowException() + { + Assert.Throws(() => articlesClient.Create(null)); + } + + [Test()] + public void GetArticles_WithNull_ThrowException() + { + Assert.Throws(() => articlesClient.GetArticles(null)); + } + + [Test()] + public void GetArticles_WithNoId_ThrowException() + { + Assert.Throws(() => articlesClient.GetArticles(new Article())); + } + } +} diff --git a/src/Intercom.Tests/Converters/ClassConverters/DateTimeOffsetJsonConverterTest.cs b/src/Intercom.Tests/Converters/ClassConverters/DateTimeOffsetJsonConverterTest.cs new file mode 100644 index 0000000..04293a9 --- /dev/null +++ b/src/Intercom.Tests/Converters/ClassConverters/DateTimeOffsetJsonConverterTest.cs @@ -0,0 +1,117 @@ +using System; +using System.IO; + +using Intercom.Test; +using Newtonsoft.Json; +using NUnit.Framework; + +using Intercom.Converters.ClassConverters; + +namespace Intercom.Tests.Converters.ClassConverters +{ + [TestFixture] + public class DateTimeOffsetJsonConverterTest : TestBase + { + private const string _DateCreatedISO = "1989-04-16T00:15:00Z"; + private const string _DateCreatedUnix = "608688900"; + private const string _Null = "null"; + + private readonly DateTimeOffsetJsonConverter _converter; + + public DateTimeOffsetJsonConverterTest() + { + _converter = new DateTimeOffsetJsonConverter(); + } + + [TestCase(_DateCreatedUnix)] + public void ReadJson_ForDateTimeOffset_ReturnsValidDateTimeOffset(string json) + { + using (StringReader stringReader = new StringReader(json)) + using (JsonReader jsonReader = new JsonTextReader(stringReader)) + { + DateTimeOffset dateCreated = (DateTimeOffset)_converter.ReadJson(jsonReader, typeof(DateTimeOffset), null, null); + + Assert.AreEqual(_ParseUtcDateString(_DateCreatedISO), dateCreated); + } + } + + [TestCase(_DateCreatedISO)] + public void WriteJsonForDateTimeOffset_ForcesUtc(string input) + { + DateTimeOffset inputDate; + + try + { + inputDate = _ParseUtcDateString(input); + } + + catch (Exception ex) + { + throw new Exception("Failed to properly parse the test input.", ex); + } + + inputDate.ToOffset(TimeSpan.FromHours(-5)); + + using (StringWriter stringWriter = new StringWriter()) + using (JsonWriter jsonWriter = new JsonTextWriter(stringWriter)) + { + _converter.WriteJson(jsonWriter, inputDate, null); + + Assert.AreEqual(_DateCreatedUnix, stringWriter.GetStringBuilder().ToString()); + } + } + + [TestCase(_Null)] + public void ReadJson_ForNullableDateTimeOffset_ReturnsNull(string json) + { + using (StringReader stringReader = new StringReader(json)) + using (JsonReader jsonReader = new JsonTextReader(stringReader)) + { + DateTimeOffset? dateCreated = (DateTimeOffset?)_converter.ReadJson(jsonReader, typeof(DateTimeOffset?), null, null); + + Assert.AreEqual(null, dateCreated); + } + } + + [TestCase(null)] + public void WriteJsonForNullableDateTimeOffset_ReturnsNull(DateTimeOffset? input) + { + using (StringWriter stringWriter = new StringWriter()) + using (JsonWriter jsonWriter = new JsonTextWriter(stringWriter)) + { + _converter.WriteJson(jsonWriter, input, null); + + Assert.AreEqual(_Null, stringWriter.GetStringBuilder().ToString()); + } + } + + [TestCase(_DateCreatedISO)] + public void WriteJson_ForDateTimeOffset_ReturnsValidUnixTimestamp(string input) + { + DateTimeOffset inputDate; + + try + { + inputDate = _ParseUtcDateString(input); + } + + catch (Exception ex) + { + throw new Exception("Failed to properly parse the test input.", ex); + } + + using (StringWriter stringWriter = new StringWriter()) + using (JsonWriter jsonWriter = new JsonTextWriter(stringWriter)) + { + _converter.WriteJson(jsonWriter, inputDate, null); + + Assert.AreEqual(_DateCreatedUnix, stringWriter.GetStringBuilder().ToString()); + } + } + + private DateTimeOffset _ParseUtcDateString(string input) + { + return DateTimeOffset.Parse(input); + } + } +} \ No newline at end of file diff --git a/src/Intercom/Clients/ArticlesClient.cs b/src/Intercom/Clients/ArticlesClient.cs new file mode 100644 index 0000000..50346f6 --- /dev/null +++ b/src/Intercom/Clients/ArticlesClient.cs @@ -0,0 +1,272 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Intercom.Core; +using Intercom.Data; +using Intercom.Factories; +using Intercom.Converters; +using Newtonsoft.Json; + +namespace Intercom.Clients +{ + [Obsolete("Articles Object Model can only be used with Intercom API v2.0 or greater.")] + public class ArticlesClient : Client + { + private const String ARTICLES_RESOURCE = "articles"; + + public ArticlesClient(RestClientFactory restClientFactory) + : base(ARTICLES_RESOURCE, restClientFactory) + { + } + + [Obsolete("This constructor is deprecated as of 3.0.0 and will soon be removed, please use CompanyClient(RestClientFactory restClientFactory)")] + public ArticlesClient(Authentication authentication) + : base(INTERCOM_API_BASE_URL, ARTICLES_RESOURCE, authentication) + { + } + + [Obsolete("This constructor is deprecated as of 3.0.0 and will soon be removed, please use CompanyClient(RestClientFactory restClientFactory)")] + public ArticlesClient(String intercomApiUrl, Authentication authentication) + : base(String.IsNullOrEmpty(intercomApiUrl) ? INTERCOM_API_BASE_URL : intercomApiUrl, ARTICLES_RESOURCE, authentication) + { + } + + public Article Create(Article article) + { + return CreateOrUpdate(article); + } + + public Article Update(Article article) + { + return CreateOrUpdate(article); + } + + private Article CreateOrUpdate(Article article) + { + if (article == null) + { + throw new ArgumentNullException(nameof(article)); + } + + if (article.custom_attributes != null && article.custom_attributes.Any()) + { + if (article.custom_attributes.Count > 100) + throw new ArgumentException("Maximum of 100 fields."); + + foreach (var attr in article.custom_attributes) + { + if (attr.Key.Contains(".") || attr.Key.Contains("$")) + throw new ArgumentException(String.Format("Field names must not contain Periods (.) or Dollar ($) characters. key: {0}", attr.Key)); + + if (attr.Key.Length > 190) + throw new ArgumentException(String.Format("Field names must be no longer than 190 characters. key: {0}", attr.Key)); + + if (attr.Value == null) + throw new ArgumentException(String.Format("'value' is null. key: {0}", attr.Key)); + } + } + + ClientResponse
result = null; + result = Post
(Transform(article)); + return result.Result; + } + + public Article View(String id) + { + if (String.IsNullOrEmpty(id)) + { + throw new ArgumentNullException(nameof(id)); + } + ClientResponse
result = null; + result = Get
(resource: ARTICLES_RESOURCE + Path.DirectorySeparatorChar + id); + return result.Result; + } + + public Articles GetArticles(Article article) + { + if (article == null) + { + throw new ArgumentNullException(nameof(article)); + } + + Dictionary parameters = new Dictionary(); + ClientResponse result = null; + parameters.Add("per_page", "150"); + + if (!String.IsNullOrEmpty(article.id)) + { + result = Get(resource: ARTICLES_RESOURCE + Path.DirectorySeparatorChar + article.id); + } + else if (!String.IsNullOrEmpty(article.title)) + { + parameters.Add(Constants.TITLE, article.title); + result = Get(parameters: parameters); + } + else if (!String.IsNullOrEmpty(article.article_id)) + { + parameters.Add(Constants.ARTICLE_ID, article.article_id); + result = Get(parameters: parameters); + } + else if (!String.IsNullOrEmpty(article.state)) + { + result = Get(parameters: parameters); + } + else + { + throw new ArgumentException("you need to provide either 'article.id', 'article.article_id' to view an article."); + } + + return result.Result; + } + + public ArticlesMultiLanguage GetArticlesML(ArticleMultiLanguage article) + { + if (article == null) + { + throw new ArgumentNullException(nameof(article)); + } + + Dictionary parameters = new Dictionary(); + ClientResponse result = null; + parameters.Add("per_page", "150"); + + if (!String.IsNullOrEmpty(article.id)) + { + result = Get(resource: ARTICLES_RESOURCE + Path.DirectorySeparatorChar + article.id); + } + else if (!String.IsNullOrEmpty(article.title)) + { + parameters.Add(Constants.TITLE, article.title); + result = Get(parameters: parameters); + } + else if (!String.IsNullOrEmpty(article.state)) + { + result = Get(parameters: parameters); + } + else + { + throw new ArgumentException("you need to provide either 'article.id', to view an article."); + } + + return result.Result; + } + + public Article View(Article article) + { + if (article == null) + { + throw new ArgumentNullException(nameof(article)); + } + + Dictionary parameters = new Dictionary(); + ClientResponse
result = null; + + if (!String.IsNullOrEmpty(article.id)) + { + result = Get
(resource: ARTICLES_RESOURCE + Path.DirectorySeparatorChar + article.id); + } + else if (!String.IsNullOrEmpty(article.title)) + { + parameters.Add(Constants.TITLE, article.title); + result = Get
(parameters: parameters); + } + else if (!String.IsNullOrEmpty(article.article_id)) + { + parameters.Add(Constants.ARTICLE_ID, article.article_id); + result = Get
(parameters: parameters); + } + else + { + throw new ArgumentException("you need to provide either 'article.id', 'article.article_id' to view an article."); + } + return result.Result; + } + public ArticleMultiLanguage ViewML(ArticleMultiLanguage article) + { + if (article == null) + { + throw new ArgumentNullException(nameof(article)); + } + + Dictionary parameters = new Dictionary(); + ClientResponse result = null; + + if (!String.IsNullOrEmpty(article.id)) + { + result = Get(resource: ARTICLES_RESOURCE + Path.DirectorySeparatorChar + article.id); + } + else if (!String.IsNullOrEmpty(article.title)) + { + parameters.Add(Constants.TITLE, article.title); + result = Get(parameters: parameters); + } + else + { + throw new ArgumentException("you need to provide either 'article.id', to view an article."); + } + return result.Result; + } + public Articles List() + { + ClientResponse result = null; + result = Get(); + return result.Result; + } + + public Articles List(Dictionary parameters) + { + if (parameters == null) + { + throw new ArgumentNullException(nameof(parameters)); + } + + if (!parameters.Any()) + { + throw new ArgumentException("'parameters' argument is empty."); + } + + ClientResponse result = null; + result = Get(parameters: parameters); + return result.Result; + } + + public Articles Scroll(String scrollParam = null) + { + Dictionary parameters = new Dictionary(); + ClientResponse result = null; + + if (!String.IsNullOrWhiteSpace(scrollParam)) + { + parameters.Add("scroll_param", scrollParam); + } + + result = Get(parameters: parameters, resource: ARTICLES_RESOURCE + Path.DirectorySeparatorChar + "scroll"); + return result.Result; + } + + private String Transform(Article article) + { + var body = new + { + created_at = article.created_at, + article_id = article.article_id, + title = article.title, + description = article.description, + custom_attributes = article.custom_attributes, + article_body = article.body, + author_id = article.author_id, + state = article.state, + updated_at= article.updated_at, + url = article.url + }; + + return JsonConvert.SerializeObject(body, + Formatting.None, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }); + } + } +} \ No newline at end of file diff --git a/src/Intercom/Clients/ContactsClient.cs b/src/Intercom/Clients/ContactsClient.cs index a523066..43780c6 100644 --- a/src/Intercom/Clients/ContactsClient.cs +++ b/src/Intercom/Clients/ContactsClient.cs @@ -94,8 +94,12 @@ public Contact View (Contact contact) } else if (!String.IsNullOrEmpty (contact.user_id)) { parameters.Add (Constants.USER_ID, contact.user_id); result = Get (parameters: parameters); + } + else if (!String.IsNullOrEmpty(contact.email)) { + parameters.Add(Constants.EMAIL, contact.email); + result = Get(parameters: parameters); } else { - throw new ArgumentException ("you need to provide either 'contact.id', 'contact.user_id' to view a contact."); + throw new ArgumentException ("you need to provide either 'contact.id', 'contact.user_id', or 'contact.email' to view a contact."); } return result.Result; diff --git a/src/Intercom/Clients/UsersClient.cs b/src/Intercom/Clients/UsersClient.cs index d2a1d23..cc3bc41 100644 --- a/src/Intercom/Clients/UsersClient.cs +++ b/src/Intercom/Clients/UsersClient.cs @@ -16,6 +16,7 @@ namespace Intercom.Clients { public class UsersClient : Client { + [Obsolete("User Object Model removed as of Intercom API v2.0, please use ContactsClient instead.")] // TODO: Implement paging private static class UserSortBy { diff --git a/src/Intercom/Converters/AttributeConverters/ListJsonConverter.cs b/src/Intercom/Converters/AttributeConverters/ListJsonConverter.cs index caeaf1a..ef87747 100644 --- a/src/Intercom/Converters/AttributeConverters/ListJsonConverter.cs +++ b/src/Intercom/Converters/AttributeConverters/ListJsonConverter.cs @@ -41,6 +41,14 @@ public override object ReadJson(JsonReader reader, result = GetList(jobject, "segments"); else if (objectType == typeof(List)) result = GetList(jobject, "conversation_parts"); + else if (objectType == typeof(List
)) + result = GetList
(jobject, "articles"); + else if (objectType == typeof(List)) + result = GetList
(jobject, "articles"); + else if (objectType == typeof(List)) + result = GetListArticlesML(jobject, "articles_content"); + else if (objectType == typeof(List)) + result = GetList(jobject, "contacts"); return result; } @@ -80,5 +88,28 @@ private List GetList(JObject jobject, String key) var result = (JsonConvert.DeserializeObject(value.ToString(), typeof(T[])) as T[]).ToList(); return result; } + + private List GetListArticlesML(JObject jobject, String key) + { + List result = new List(); + + foreach (JObject articleml in jobject["children"]) + { + ArticleContent resultArticle = new ArticleContent(); + resultArticle.locale = articleml["root"].ToString(); + resultArticle.title = articleml["title"].ToString(); + resultArticle.description = articleml["description"].ToString(); + resultArticle.body = articleml["body"].ToString(); + resultArticle.author_id = articleml["author_id"].ToString(); + resultArticle.state = articleml["state"].ToString(); + resultArticle.created_at = articleml["created_at"].ToObject(); + resultArticle.updated_at = articleml["updated_at"].ToObject(); + resultArticle.url = articleml["url"].ToString(); + resultArticle.type = articleml["article_content"].ToString(); + + result.Add(resultArticle); + } + return result; + } } } \ No newline at end of file diff --git a/src/Intercom/Converters/ClassConverters/ArticleTranslatedContentConverter.cs b/src/Intercom/Converters/ClassConverters/ArticleTranslatedContentConverter.cs new file mode 100644 index 0000000..4c2f13d --- /dev/null +++ b/src/Intercom/Converters/ClassConverters/ArticleTranslatedContentConverter.cs @@ -0,0 +1,95 @@ +using System; +using Intercom.Core; +using Intercom.Data; +using Intercom.Clients; +using Intercom.Exceptions; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using RestSharp; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Intercom.Converters.ClassConverters +{ + public class ArticleTranslatedContentConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(ArticleContent); + } + + public override object ReadJson(JsonReader reader, + Type objectType, + object existingValue, + JsonSerializer serializer) + { + JObject j = null; + + try + { + j = JObject.Load(reader); + ArticleContent content = new ArticleContent(); + JObject result = new JObject(); + ArticleContents articleTranslations = new ArticleContents(); + + + foreach (JToken child in AllChildren(j.ToString())) + { + string test = child.ToString(); + } + + content.locale = result.Root.ToString(); + content.title = result["title"].Value(); + content.description = result["description"].Value(); + content.body = result["body"].Value(); + content.author_id = result["author_id"].Value(); + content.state = result["state"].Value(); + content.created_at = result["created_at"].Value(); + content.updated_at = result["updated_at"].Value(); + content.url = result["url"].Value(); + content.type = result["type"].Value(); + + return content; + } + catch (Exception ex) + { + throw new JsonConverterException("Error while serializing ArticleTranslatedContentConverter endpoint json result.", ex) + { + Json = j == null ? String.Empty : j.ToString(), + SerializationType = objectType.FullName + }; + } + } + + public override void WriteJson(JsonWriter writer, + object value, + JsonSerializer serializer) + { + String s = JsonConvert.SerializeObject(value, + Formatting.None, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }); + + writer.WriteRawValue(s); + } + + public override bool CanRead + { + get { return true; } + } + private static IEnumerable AllChildren(JToken json) + { + foreach (var c in json.Children()) + { + yield return c; + foreach (var cc in AllChildren(c)) + { + yield return cc; + } + } + } + } +} \ No newline at end of file diff --git a/src/Intercom/Converters/ClassConverters/DateTimeOffsetJsonConverter.cs b/src/Intercom/Converters/ClassConverters/DateTimeOffsetJsonConverter.cs new file mode 100644 index 0000000..b109250 --- /dev/null +++ b/src/Intercom/Converters/ClassConverters/DateTimeOffsetJsonConverter.cs @@ -0,0 +1,64 @@ +using System; +using Newtonsoft.Json; + +namespace Intercom.Converters.ClassConverters +{ + public class DateTimeOffsetJsonConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return (objectType.Equals(typeof(DateTimeOffset)) || objectType.Equals(typeof(DateTimeOffset?))); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + object value = reader.Value; + + if (value == null) + { + return null; + } + + long unixTimestamp; + + try + { + unixTimestamp = Convert.ToInt64(value); + } + + catch (InvalidCastException ex) + { + throw new FormatException("Dates must be represented as UNIX timestamps in JSON.", ex); + } + + DateTimeOffset unixEpoch = _GetUnixEpoch(); + DateTimeOffset result = unixEpoch.AddSeconds(unixTimestamp); + + return result; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteRawValue("null"); + + return; + } + + DateTimeOffset unixEpoch = _GetUnixEpoch(); + DateTimeOffset dateTimeOffset = (DateTimeOffset)value; + + dateTimeOffset = dateTimeOffset.ToOffset(TimeSpan.Zero); + + long unixTimestamp = Convert.ToInt64((dateTimeOffset - unixEpoch).TotalSeconds); + + writer.WriteRawValue(unixTimestamp.ToString()); + } + + private DateTimeOffset _GetUnixEpoch() + { + return new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); + } + } +} diff --git a/src/Intercom/Core/Client.cs b/src/Intercom/Core/Client.cs index c714126..adfff6e 100644 --- a/src/Intercom/Core/Client.cs +++ b/src/Intercom/Core/Client.cs @@ -401,7 +401,9 @@ protected ClientResponse HandleErrorResponse(IRestResponse response) protected ClientResponse HandleNormalResponse(IRestResponse response) where T : class { - return new ClientResponse(response: response, result: Deserialize(response.Content)); + ClientResponse result = new ClientResponse(response: response, result: Deserialize(response.Content)); + + return result; } protected void AssertIfAnyErrors(ClientResponse response) diff --git a/src/Intercom/Core/Constants.cs b/src/Intercom/Core/Constants.cs index 98e40d7..0a79dae 100644 --- a/src/Intercom/Core/Constants.cs +++ b/src/Intercom/Core/Constants.cs @@ -11,7 +11,10 @@ internal static class Constants public const String USER_ID = "user_id"; public const String ADMIN_ID = "admin_id"; public const String COMPANY_ID = "company_id"; - public const String SEGMENT_ID = "segment_id"; + public const String ARTICLE_ID = "article_id"; + public const String TITLE = "title"; + public const String STATE = "state"; + public const String SEGMENT_ID = "segment_id"; public const String REMOTE_COMPANY_ID = "remote_company_id"; public const String INTERCOM_USER_ID = "intercom_user_id"; public const String UPDATE_LAST_REQUEST_AT = "update_last_request_at"; diff --git a/src/Intercom/Data/Article.cs b/src/Intercom/Data/Article.cs new file mode 100644 index 0000000..e6ad48d --- /dev/null +++ b/src/Intercom/Data/Article.cs @@ -0,0 +1,30 @@ +using System; +using Intercom.Core; +using System.Collections.Generic; +using Newtonsoft.Json; +using Intercom.Converters.AttributeConverters; +using Intercom.Converters.ClassConverters; + +namespace Intercom.Data +{ + public class Article : Model + { + public string article_id { get; set; } = null; + public string workspace_id { get; set; } + public string parent_id { get; set; } + public string parent_type { get; set; } + public string title { get; set; } + public string description { get; set; } + public string body { get; set; } + public string author_id { get; set; } + public string state { get; set; } + public DateTimeOffset? created_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? updated_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public string url { get; set; } + public Dictionary custom_attributes { get; set; } + } + + +} diff --git a/src/Intercom/Data/ArticleContent.cs b/src/Intercom/Data/ArticleContent.cs new file mode 100644 index 0000000..e56a3a4 --- /dev/null +++ b/src/Intercom/Data/ArticleContent.cs @@ -0,0 +1,25 @@ +using System; +using Intercom.Core; +using System.Collections.Generic; +using Newtonsoft.Json; +using Intercom.Converters.AttributeConverters; +using Intercom.Converters.ClassConverters; + +namespace Intercom.Data +{ + public class ArticleContent : Model + { + public string locale { get; set; } + public string title { get; set; } + public string description { get; set; } + public string body { get; set; } + public string author_id { get; set; } + public string state { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? created_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? updated_at { get; set; } + public string url { get; set; } + + } +} diff --git a/src/Intercom/Data/ArticleContents.cs b/src/Intercom/Data/ArticleContents.cs new file mode 100644 index 0000000..ddf53f9 --- /dev/null +++ b/src/Intercom/Data/ArticleContents.cs @@ -0,0 +1,119 @@ +using System; +using Intercom.Clients; +using Intercom.Exceptions; +using System.Collections.Generic; +using Intercom.Core; +using System.Collections; +using Newtonsoft.Json; +using Intercom.Converters.AttributeConverters; +using Intercom.Converters.ClassConverters; +using Newtonsoft.Json.Linq; + +namespace Intercom.Data +{ + public class CustomTranslatedContentConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, ArticleContents value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override ArticleContents ReadJson(JsonReader reader, Type objectType, ArticleContents existingValue, bool hasExistingValue, JsonSerializer serializer) + { + ArticleContents result = new ArticleContents(); + + JObject obj = JObject.Load(reader); + foreach (JToken childToken in obj.Children()) + { + if (childToken.Path == "type") + { + result.type = childToken.First.ToString(); + } + else if (childToken.Path == "id") + { + result.id = childToken.First.ToString(); + } + else + { + ArticleContent content = JsonConvert.DeserializeObject(childToken.First.ToString()); + content.locale = childToken.Path; + result.data.Add(childToken.Path, content); + } + } + + return result; + } + } + + public class ArticleContents : Model + { + public Dictionary data { get; set; } + + public ArticleContents() + { + data = new Dictionary(); + } + public IDictionaryEnumerator GetEnumerator() + { + return data.GetEnumerator(); + } + public ArticleContent FindExact(string searchString) + { + ArticleContent result = new ArticleContent(); + + //foreach (ArticleContent article in data) + //{ + // if (article.title == searchString) + // { + // result = article; + // break; + // } + // else if (!string.IsNullOrEmpty(article.description) && article.description.Contains(searchString)) + // { + // result = article; + // break; + // } + // else + // result = null; + //} + + return result; + } + + public void RemoveDraftArticles() + { + //List articles = new List(); + + //foreach (ArticleContent article in data) + //{ + // if (article.state == "draft") + // { + // articles.Add(article); + // } + //} + + //foreach (ArticleContent remove in articles) + //{ + // data.Remove(remove); + //} + } + + public void RemovePublishedArticles() + { + //List articles = new List(); + + //foreach (ArticleContent article in data) + //{ + // if (article.state == "published") + // { + // articles.Add(article); + // } + //} + + //foreach (ArticleContent remove in articles) + //{ + // data.Remove(remove); + //} + } + } +} diff --git a/src/Intercom/Data/ArticleMultiLanguage .cs b/src/Intercom/Data/ArticleMultiLanguage .cs new file mode 100644 index 0000000..0e5cc51 --- /dev/null +++ b/src/Intercom/Data/ArticleMultiLanguage .cs @@ -0,0 +1,36 @@ +using System; +using Intercom.Core; +using System.Collections.Generic; +using Newtonsoft.Json; +using Intercom.Converters.AttributeConverters; +using Intercom.Converters.ClassConverters; + +namespace Intercom.Data +{ + public class ArticleMultiLanguage : Model + { + public string workspace_id { get; set; } + public string parent_id { get; set; } + public string parent_type { get; set; } + public string default_locale { get; set; } = "en"; + + [JsonConverter(typeof(CustomTranslatedContentConverter))] + public ArticleContents translated_content { get; set; } + + public string title { get; set; } + public string description { get; set; } + public string body { get; set; } + public string author_id { get; set; } + public string state { get; set; } + + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? created_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? updated_at { get; set; } + + public string url { get; set; } + public Dictionary custom_attributes { get; set; } + } + + +} diff --git a/src/Intercom/Data/Articles.cs b/src/Intercom/Data/Articles.cs new file mode 100644 index 0000000..242dd6a --- /dev/null +++ b/src/Intercom/Data/Articles.cs @@ -0,0 +1,95 @@ +using System; +using Intercom.Clients; +using Intercom.Exceptions; +using System.Collections.Generic; +using Intercom.Core; +using System.Collections; +using Newtonsoft.Json; +using Intercom.Converters.AttributeConverters; + +namespace Intercom.Data +{ + public class Articles : Models + { + public List
data { set; get; } + + public string scroll_param { get; set; } + + public Articles () + { + } + public IEnumerator
GetEnumerator() + { + return data.GetEnumerator(); + } + public Article FindExact(string searchString) + { + Article result = new Article(); + + foreach (Article article in data) + { + if (article.title == searchString) + { + result = article; + break; + } + else if (!string.IsNullOrEmpty(article.description) && article.description.Contains(searchString)) + { + result = article; + break; + } + else + result = null; + } + + return result; + } + public void NewOrModifiedArticles(DateTime marker) + { + List
articles = new List
(); + + foreach (Article article in data) + { + if (article.created_at < marker || article.updated_at < marker) + { + data.Remove(article); + } + } + } + public void RemoveDraftArticles() + { + List
articles = new List
(); + + foreach (Article article in data) + { + if (article.state == "draft") + { + articles.Add(article); + } + } + + foreach (Article remove in articles) + { + data.Remove(remove); + } + } + + public void RemovePublishedArticles() + { + List
articles = new List
(); + + foreach (Article article in data) + { + if (article.state == "published") + { + articles.Add(article); + } + } + + foreach (Article remove in articles) + { + data.Remove(remove); + } + } + } +} diff --git a/src/Intercom/Data/ArticlesMultiLanguage.cs b/src/Intercom/Data/ArticlesMultiLanguage.cs new file mode 100644 index 0000000..d904b38 --- /dev/null +++ b/src/Intercom/Data/ArticlesMultiLanguage.cs @@ -0,0 +1,194 @@ +using System; +using Intercom.Clients; +using Intercom.Exceptions; +using System.Collections.Generic; +using Intercom.Core; +using System.Collections; +using Newtonsoft.Json; +using Intercom.Converters.AttributeConverters; +using Intercom.Converters.ClassConverters; + +namespace Intercom.Data +{ + public class ArticlesMultiLanguage : Models + { + public List data {get; set;} + + public ArticlesMultiLanguage() + { + } + public IEnumerator GetEnumerator() + { + return data.GetEnumerator(); + } + public ArticleMultiLanguage FindExact(string searchString) + { + ArticleMultiLanguage result = new ArticleMultiLanguage(); + + foreach (ArticleMultiLanguage article in data) + { + if (article.title == searchString) + { + result = article; + break; + } + else if (!string.IsNullOrEmpty(article.description) && article.description.Contains(searchString)) + { + result = article; + break; + } + else + result = null; + } + + return result; + } + public void Search(string searchString) + { + List emptyArticles = new List(); + foreach (ArticleMultiLanguage article in data) + { + List toRemove = new List(); + foreach (KeyValuePair localised in article.translated_content.data) + { + string feTitle = localised.Value.title ?? ""; + string feDescription = localised.Value.description ?? ""; + string feBody = localised.Value.body ?? ""; + + if (!feTitle.Contains(searchString) && !feDescription.Contains(searchString) && !feBody.Contains(searchString)) + { + toRemove.Add(localised.Key); + } + } + foreach (string key in toRemove) + { + article.translated_content.data.Remove(key); + } + if (article.translated_content.data.Count == 0) + { + emptyArticles.Add(article); + } + } + foreach (ArticleMultiLanguage emptyArticle in emptyArticles) + { + data.Remove(emptyArticle); + } + } + + public void NewOrModifiedArticles(DateTime marker) + { + List emptyArticles = new List(); + foreach (ArticleMultiLanguage article in data) + { + List toRemove = new List(); + foreach(KeyValuePair localised in article.translated_content.data) + { + if (localised.Value.created_at < marker || localised.Value.updated_at < marker) + { + toRemove.Add(localised.Key); + } + } + foreach (string key in toRemove) + { + article.translated_content.data.Remove(key); + } + if (article.translated_content.data.Count == 0) + { + emptyArticles.Add(article); + } + } + foreach (ArticleMultiLanguage emptyArticle in emptyArticles) + { + data.Remove(emptyArticle); + } + } + + public void SingleLocaleArticles(string locale) + { + List emptyArticles = new List(); + foreach (ArticleMultiLanguage article in data) + { + List toRemove = new List(); + foreach (KeyValuePair localised in article.translated_content.data) + { + if (localised.Value.locale != locale && localised.Value.locale != null) + { + toRemove.Add(localised.Key); + } + } + foreach (string key in toRemove) + { + article.translated_content.data.Remove(key); + } + if (article.translated_content.data.Count == 0) + { + emptyArticles.Add(article); + } + } + foreach (ArticleMultiLanguage emptyArticle in emptyArticles) + { + data.Remove(emptyArticle); + } + } + + public void RemoveDraftArticles() + { + List articles = new List(); + + List emptyArticles = new List(); + foreach (ArticleMultiLanguage article in data) + { + List toRemove = new List(); + foreach (KeyValuePair localised in article.translated_content.data) + { + if (localised.Value.state == "draft") + { + toRemove.Add(localised.Key); + } + } + foreach (string key in toRemove) + { + article.translated_content.data.Remove(key); + } + if (article.translated_content.data.Count == 0) + { + emptyArticles.Add(article); + } + } + foreach (ArticleMultiLanguage emptyArticle in emptyArticles) + { + data.Remove(emptyArticle); + } + } + + public void RemovePublishedArticles() + { + List articles = new List(); + + List emptyArticles = new List(); + foreach (ArticleMultiLanguage article in data) + { + List toRemove = new List(); + foreach (KeyValuePair localised in article.translated_content.data) + { + if (localised.Value.state == "published") + { + toRemove.Add(localised.Key); + } + } + foreach (string key in toRemove) + { + article.translated_content.data.Remove(key); + } + if (article.translated_content.data.Count == 0) + { + emptyArticles.Add(article); + } + } + foreach (ArticleMultiLanguage emptyArticle in emptyArticles) + { + data.Remove(emptyArticle); + } + } + } +} diff --git a/src/Intercom/Data/Company.cs b/src/Intercom/Data/Company.cs index 43ecfef..eb4b20d 100644 --- a/src/Intercom/Data/Company.cs +++ b/src/Intercom/Data/Company.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using Intercom.Converters.AttributeConverters; +using Intercom.Converters.ClassConverters; namespace Intercom.Data { @@ -12,10 +13,14 @@ public class Company : Model public string name { get; set; } public Plan plan { get; set; } public string company_id { get; set; } - public long? remote_created_at { get; set; } - public long? created_at { get; set; } - public long? updated_at { get; set; } - public long? last_request_at { get; set; } + public DateTimeOffset? remote_created_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? created_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? updated_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? last_request_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] public decimal? monthly_spend { get; set; } public int? session_count { get; set; } public int? user_count { get; set; } diff --git a/src/Intercom/Data/Contacts.cs b/src/Intercom/Data/Contacts.cs index 653d0e2..dbcb0ef 100644 --- a/src/Intercom/Data/Contacts.cs +++ b/src/Intercom/Data/Contacts.cs @@ -5,11 +5,36 @@ namespace Intercom.Data { public class Contacts : Models { - public List contacts { set; get; } + public List data { set; get; } public string scroll_param { get; set; } public Contacts() { } + + public Contact FindExact(string searchString) + { + Contact result = new Contact(); + + foreach (Contact contact in data) + { + if (contact.id == searchString) + { + result = contact; + } + else if (contact.email == searchString) + { + result = contact; + } + else if (contact.user_id == searchString) + { + result = contact; + } + else + result = null; + } + + return result; + } } } \ No newline at end of file diff --git a/src/Intercom/Data/Conversation.cs b/src/Intercom/Data/Conversation.cs index 37d0f3b..2d81020 100644 --- a/src/Intercom/Data/Conversation.cs +++ b/src/Intercom/Data/Conversation.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Intercom.Clients; using Intercom.Converters.AttributeConverters; +using Intercom.Converters.ClassConverters; using Intercom.Core; using Intercom.Data; using Intercom.Exceptions; @@ -11,8 +12,10 @@ namespace Intercom.Data { public class Conversation : Model { - public long created_at { get; set; } - public long updated_at { get; set; } + public DateTimeOffset? created_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? updated_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] public long? waiting_since { get; set; } public long? snoozed_until { get; set; } public Assignee assignee { get; set; } diff --git a/src/Intercom/Data/ConversationPart.cs b/src/Intercom/Data/ConversationPart.cs index 0dc894b..87c5f02 100644 --- a/src/Intercom/Data/ConversationPart.cs +++ b/src/Intercom/Data/ConversationPart.cs @@ -4,6 +4,8 @@ using Intercom.Clients; using Intercom.Exceptions; using System.Collections.Generic; +using Intercom.Converters.ClassConverters; +using Newtonsoft.Json; namespace Intercom.Data { @@ -12,8 +14,11 @@ public class ConversationPart : Model public string part_type { get; set; } public string body { get; set; } public long created_at { get; set; } - public long updated_at { get; set; } - public long notified_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? updated_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? notified_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] public Assignee assigned_to { get; set; } public Author author { get; set; } public List attachments { get; set; } diff --git a/src/Intercom/Data/Event.cs b/src/Intercom/Data/Event.cs index eae74cb..68d8833 100644 --- a/src/Intercom/Data/Event.cs +++ b/src/Intercom/Data/Event.cs @@ -6,13 +6,15 @@ using System.Collections.Generic; using Newtonsoft.Json; using Intercom.Converters.AttributeConverters; +using Intercom.Converters.ClassConverters; namespace Intercom.Data { public class Event : Model { public string event_name { get; set; } - public long? created_at { get; set; } + public DateTimeOffset? created_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] public string user_id { get; set; } public string email { get; set; } diff --git a/src/Intercom/Data/Note.cs b/src/Intercom/Data/Note.cs index cb72523..caf45e6 100644 --- a/src/Intercom/Data/Note.cs +++ b/src/Intercom/Data/Note.cs @@ -3,12 +3,15 @@ using Intercom.Data; using Intercom.Clients; using Intercom.Exceptions; +using Intercom.Converters.ClassConverters; +using Newtonsoft.Json; namespace Intercom.Data { public class Note : Model { - public long? created_at { get; set; } + public DateTimeOffset? created_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] public string body { get; set; } public Admin author { get; set; } public User user { get; set; } diff --git a/src/Intercom/Data/Segment.cs b/src/Intercom/Data/Segment.cs index 95a6227..f184b8a 100644 --- a/src/Intercom/Data/Segment.cs +++ b/src/Intercom/Data/Segment.cs @@ -1,6 +1,8 @@ using System; using Intercom.Core; using Intercom.Data; +using Intercom.Converters.ClassConverters; +using Newtonsoft.Json; using Intercom.Clients; @@ -13,8 +15,10 @@ namespace Intercom.Data public class Segment : Model { public string name { get; set; } - public long created_at { get; set; } - public long updated_at { get; set; } + public DateTimeOffset? created_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? updated_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] public int? count { get; set; } public Segment() diff --git a/src/Intercom/Data/User.cs b/src/Intercom/Data/User.cs index 4e26805..a76660d 100644 --- a/src/Intercom/Data/User.cs +++ b/src/Intercom/Data/User.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using Intercom.Converters.AttributeConverters; +using Intercom.Converters.ClassConverters; namespace Intercom.Data { @@ -12,12 +13,16 @@ public class User : Model public string email { get; set; } public string phone { get; set; } public string name { get; set; } - public long? updated_at { get; set; } + public DateTimeOffset? updated_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] public string last_seen_ip { get; set; } public bool? unsubscribed_from_emails { get; set; } - public long? last_request_at { get; set; } - public long? signed_up_at { get; set; } - public long? created_at { get; set; } + public DateTimeOffset? last_request_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? signed_up_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public DateTimeOffset? created_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] public int? session_count { get; set; } public bool? new_session { get; set; } public string user_agent_data { get; set; } @@ -35,8 +40,9 @@ public class User : Model [JsonConverter(typeof(ListJsonConverter))] public List tags { get; set; } public string app_id { get; set; } - public long? remote_created_at { get; set; } - public string referrer { get; set; } + public DateTimeOffset? remote_created_at { get; set; } + [JsonConverter(typeof(DateTimeOffsetJsonConverter))] + public string referrer { get; set; } public string utm_campaign { get; set; } public string utm_content { get; set; } public string utm_medium { get; set; } diff --git a/src/Intercom/Intercom.csproj b/src/Intercom/Intercom.csproj index dda6eaf..0ada521 100644 --- a/src/Intercom/Intercom.csproj +++ b/src/Intercom/Intercom.csproj @@ -1,7 +1,7 @@ - netstandard2.0;net452 + netstandard2.1;net452 3.0.0 Intercom.Dotnet.Client https://raw.githubusercontent.com/intercom/intercom-dotnet/master/src/assets/Intercom.png