diff --git a/src/ZendeskApi_v2/Models/Constants/UserFieldTypes.cs b/src/ZendeskApi_v2/Models/Constants/UserFieldTypes.cs new file mode 100644 index 00000000..d0999bc5 --- /dev/null +++ b/src/ZendeskApi_v2/Models/Constants/UserFieldTypes.cs @@ -0,0 +1,16 @@ +namespace ZendeskApi_v2.Models.Constants +{ + public static class UserFieldTypes + { + public const string Text = "text"; + public const string Textarea = "textarea"; + public const string Checkbox = "checkbox"; + public const string Date = "date"; + public const string Integer = "integer"; + public const string Decimal = "decimal"; + public const string Regexp = "regexp"; + public const string Dropdown = "dropdown"; + public const string Lookup = "lookup"; + public const string Multiselect = "multiselect"; + } +} diff --git a/src/ZendeskApi_v2/Models/Shared/CustomFieldOption.cs b/src/ZendeskApi_v2/Models/Shared/CustomFieldOption.cs new file mode 100644 index 00000000..96ff5f9e --- /dev/null +++ b/src/ZendeskApi_v2/Models/Shared/CustomFieldOption.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace ZendeskApi_v2.Models.Shared +{ + public class CustomFieldOption + { + [JsonProperty("id")] + public long? Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("raw_name")] + public string RawName { get; set; } + + [JsonProperty("value")] + public string Value { get; set; } + } +} diff --git a/src/ZendeskApi_v2/Models/Users/GroupUserFieldResponse.cs b/src/ZendeskApi_v2/Models/Users/GroupUserFieldResponse.cs new file mode 100644 index 00000000..748ab5ed --- /dev/null +++ b/src/ZendeskApi_v2/Models/Users/GroupUserFieldResponse.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace ZendeskApi_v2.Models.Users +{ + public class GroupUserFieldResponse : GroupResponseBase + { + [JsonProperty("user_fields")] + public IList UserFields { get; set; } + } +} diff --git a/src/ZendeskApi_v2/Models/Users/IndividualUserFieldResponse.cs b/src/ZendeskApi_v2/Models/Users/IndividualUserFieldResponse.cs new file mode 100644 index 00000000..9ebed36e --- /dev/null +++ b/src/ZendeskApi_v2/Models/Users/IndividualUserFieldResponse.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace ZendeskApi_v2.Models.Users +{ + public class IndividualUserFieldResponse + { + [JsonProperty("user_field")] + public UserField UserField { get; set; } + } +} diff --git a/src/ZendeskApi_v2/Models/Users/UserField.cs b/src/ZendeskApi_v2/Models/Users/UserField.cs new file mode 100644 index 00000000..ea03b119 --- /dev/null +++ b/src/ZendeskApi_v2/Models/Users/UserField.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using ZendeskApi_v2.Models.Shared; + +namespace ZendeskApi_v2.Models.Users +{ + public class UserField + { + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("id")] + public long? Id { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("key")] + public string Key { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("description")] + public string Description { get; set; } + + [JsonProperty("raw_title")] + public string RawTitle { get; set; } + + [JsonProperty("raw_description")] + public string RawDescription { get; set; } + + [JsonProperty("position")] + public long? Position { get; set; } + + [JsonProperty("active")] + public bool? Active { get; set; } + + [JsonProperty("system")] + public bool? System { get; set; } + + [JsonProperty("regexp_for_validation")] + public string RegexpForValidation { get; set; } + + [JsonProperty("created_at")] + [JsonConverter(typeof(IsoDateTimeConverter))] + public DateTimeOffset? CreatedAt { get; set; } + + [JsonProperty("updated_at")] + [JsonConverter(typeof(IsoDateTimeConverter))] + public DateTimeOffset? UpdatedAt { get; set; } + + [JsonProperty("tag")] + public string Tag { get; set; } + + [JsonProperty("custom_field_options")] + public IList CustomFieldOptions { get; set; } + } +} diff --git a/src/ZendeskApi_v2/Requests/Users.cs b/src/ZendeskApi_v2/Requests/Users.cs index a5c2c88d..235834ba 100644 --- a/src/ZendeskApi_v2/Requests/Users.cs +++ b/src/ZendeskApi_v2/Requests/Users.cs @@ -116,6 +116,12 @@ public interface IUsers : ICore GroupUserExportResponse GetIncrementalUserExportNextPage(string nextPage); GroupSubscriptionsResponse GetSubscriptions(long userId, SubscriptionSideLoadOptions subscriptionSideLoadOptions = SubscriptionSideLoadOptions.None, int? perPage = null, int? page = null); + + GroupUserFieldResponse GetUserFields(); + IndividualUserFieldResponse GetUserFieldById(long id); + IndividualUserFieldResponse CreateUserField(UserField userField, bool replaceNameSpacesWithUnderscore = true); + IndividualUserFieldResponse UpdateUserField(UserField userField, bool replaceNameSpacesWithUnderscore = false); + bool DeleteUserField(long id); #endif #if ASYNC @@ -207,6 +213,12 @@ public interface IUsers : ICore Task GetIncrementalUserExportNextPageAsync(string nextPage); Task GetSubscriptionsAsync(long userId, SubscriptionSideLoadOptions subscriptionSideLoadOptions = SubscriptionSideLoadOptions.None, int? perPage = null, int? page = null); + + Task GetUserFieldsAsync(); + Task GetUserFieldByIdAsync(long id); + Task CreateUserFieldAsync(UserField userField, bool replaceNameSpacesWithUnderscore = true); + Task UpdateUserFieldAsync(UserField userField, bool replaceNameSpacesWithUnderscore = false); + Task DeleteUserFieldAsync(long id); #endif } @@ -214,7 +226,7 @@ public class Users : Core, IUsers { private const string _incremental_export = "incremental/users.json?start_time="; - public Users(string yourZendeskUrl, string user, string password, string apiToken, string p_OAuthToken, Dictionary customHeaders) + public Users(string yourZendeskUrl, string user, string password, string apiToken, string p_OAuthToken, Dictionary customHeaders) : base(yourZendeskUrl, user, password, apiToken, p_OAuthToken, customHeaders) { } @@ -478,6 +490,53 @@ public GroupSubscriptionsResponse GetSubscriptions(long userId, SubscriptionSide { return GenericPagedGet($"help_center/users/{userId}/subscriptions.json".SubscriptionSideloadUri(subscriptionSideLoadOptions), perPage, page); } + + public GroupUserFieldResponse GetUserFields() + { + return GenericGet("user_fields.json"); + } + + public IndividualUserFieldResponse GetUserFieldById(long id) + { + return GenericGet($"user_fields/{id}.json"); + } + public IndividualUserFieldResponse CreateUserField(UserField userField, bool replaceNameSpacesWithUnderscore = true) + { + if (userField.CustomFieldOptions != null) + { + foreach (var field in userField.CustomFieldOptions) + { + if (replaceNameSpacesWithUnderscore) + { + field.Name = field.Name.Replace(' ', '_'); + } + field.Value = field.Value.Replace(' ', '_'); + } + } + return GenericPost($"user_fields.json", new { user_field = userField }); + + } + + public IndividualUserFieldResponse UpdateUserField(UserField userField, bool replaceNameSpacesWithUnderscore = true) + { + if (userField.CustomFieldOptions != null) + { + foreach (var field in userField.CustomFieldOptions) + { + if (replaceNameSpacesWithUnderscore) + { + field.Name = field.Name.Replace(' ', '_'); + } + field.Value = field.Value.Replace(' ', '_'); + } + } + return GenericPut($"user_fields/{userField.Id}.json", new { user_field = userField }); + } + + public bool DeleteUserField(long id) + { + return GenericDelete($"user_fields/{id}.json"); + } #endif #if ASYNC @@ -725,6 +784,53 @@ public Task GetSubscriptionsAsync(long userId, Subsc { return GenericPagedGetAsync($"help_center/users/{userId}/subscriptions.json".SubscriptionSideloadUri(subscriptionSideLoadOptions), perPage, page); } + + public async Task GetUserFieldsAsync() + { + return await GenericGetAsync("user_fields.json"); + } + + public async Task GetUserFieldByIdAsync(long id) + { + return await GenericGetAsync($"user_fields/{id}.json"); + } + public async Task CreateUserFieldAsync(UserField userField, bool replaceNameSpacesWithUnderscore = true) + { + if (userField.CustomFieldOptions != null) + { + foreach (var field in userField.CustomFieldOptions) + { + if (replaceNameSpacesWithUnderscore) + { + field.Name = field.Name.Replace(' ', '_'); + } + field.Value = field.Value.Replace(' ', '_'); + } + } + return await GenericPostAsync($"user_fields.json", new { user_field = userField }); + + } + + public async Task UpdateUserFieldAsync(UserField userField, bool replaceNameSpacesWithUnderscore = true) + { + if (userField.CustomFieldOptions != null) + { + foreach (var field in userField.CustomFieldOptions) + { + if (replaceNameSpacesWithUnderscore) + { + field.Name = field.Name.Replace(' ', '_'); + } + field.Value = field.Value.Replace(' ', '_'); + } + } + return await GenericPutAsync($"user_fields/{userField.Id}.json", new { user_field = userField }); + } + + public async Task DeleteUserFieldAsync(long id) + { + return await GenericDeleteAsync($"user_fields/{id}.json"); + } #endif private string GetResourceStringWithSideLoadOptionsParam(string resource, UserSideLoadOptions sideLoadOptions) { diff --git a/tests/ZendeskApi_v2.Tests/UserTests.cs b/tests/ZendeskApi_v2.Tests/UserTests.cs index 7d6cac98..8e851fa1 100644 --- a/tests/ZendeskApi_v2.Tests/UserTests.cs +++ b/tests/ZendeskApi_v2.Tests/UserTests.cs @@ -895,8 +895,293 @@ public async Task CanBulkCreateUpdateUsersAsync() } } } -} + [OneTimeTearDown] + public async Task TestCleanUp() + { + var response = await Api.Users.GetUserFieldsAsync(); + foreach (var item in response.UserFields) + { + if (item.Title == "My Dropdown") + { + await Api.Users.DeleteUserFieldAsync(item.Id.Value); + } + } + } + + [Test] + public async Task UserField() + { + var tField = new UserField + { + Type = UserFieldTypes.Dropdown, + Title = "My Dropdown", + Description = "Test description", + RawTitle = "My Dropdown", + RawDescription = "Test description", + CustomFieldOptions = new List + { + new CustomFieldOption + { + Name = "test entryA", + Value = "Test2" + }, + new CustomFieldOption + { + Name = "test entryB", + Value = "test3" + } + } + }; + + var res = await Api.Users.CreateUserFieldAsync(tField); + Assert.That(res.UserField, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(res.UserField.Id, Is.Not.Null); + Assert.That(res.UserField.CustomFieldOptions, Has.Count.EqualTo(2)); + }); + } + + [TestCase(true, "test entryA", "test entryA newTitle", "test entryB", "test entryC", "test_entryA", "test_entryA_newTitle", "test_entryB", "test_entryC")] + [TestCase(false, "test entryA", "test entryA newTitle", "test entryB", "test entryC", "test entryA", "test entryA newTitle", "test entryB", "test entryC")] + public void CanCreateUpdateOptionsAndDeleteDropdownUserField(bool replaceNameSpaceWithUnderscore, string name1, string name1_Update, string name2, string name3, string expectedName1, string expectedName1_Update, string expectedName2, string expectedName3) + { + var option1 = "test_value_a"; + var option1_Update = "test value_a_newtag"; + var option2 = "test_value_b"; + var option3 = "test_value_c"; + var tField = new UserField() + { + Type = UserFieldTypes.Dropdown, + Title = "My Dropdown", + Description = "Test description", + RawTitle = "My Dropdown", + RawDescription = "Test description", + CustomFieldOptions = new List() + }; + + tField.CustomFieldOptions.Add(new CustomFieldOption() + { + Name = name1, + Value = option1 + }); + tField.CustomFieldOptions.Add(new CustomFieldOption() + { + Name = name2, + Value = option2 + }); + var res = Api.Users.CreateUserField(tField, replaceNameSpaceWithUnderscore); + Assert.That(res.UserField, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(res.UserField.Id, Is.Not.Null); + Assert.That(res.UserField.CustomFieldOptions, Has.Count.EqualTo(2)); + }); + Assert.That(res.UserField.CustomFieldOptions[0].Value, Is.EqualTo(option1)); + Assert.Multiple(() => + { + Assert.That(res.UserField.CustomFieldOptions[1].Value, Is.EqualTo(option2)); + Assert.That(res.UserField.CustomFieldOptions[0].Name, Is.EqualTo(expectedName1)); + Assert.That(res.UserField.CustomFieldOptions[1].Name, Is.EqualTo(expectedName2)); + }); + var id = res.UserField.Id.Value; + + var tFieldU = new UserField() + { + Id = id, + CustomFieldOptions = new List() + }; + + //update CustomFieldOption A + tFieldU.CustomFieldOptions.Add(new CustomFieldOption() + { + Name = name1_Update, + Value = option1_Update + }); + //delete CustomFieldOption B + //add CustomFieldOption C + tFieldU.CustomFieldOptions.Add(new CustomFieldOption() + { + Name = name3, + Value = option3 + }); + + var resU = Api.Users.UpdateUserField(tFieldU, replaceNameSpaceWithUnderscore); + + Assert.That(resU.UserField.CustomFieldOptions, Has.Count.EqualTo(2)); + Assert.Multiple(() => + { + Assert.That(resU.UserField.CustomFieldOptions[0].Value, Is.EqualTo(option1_Update.Replace(" ", "_"))); + Assert.That(resU.UserField.CustomFieldOptions[1].Value, Is.EqualTo(option3)); + Assert.That(resU.UserField.CustomFieldOptions[0].Name, Is.EqualTo(expectedName1_Update)); + Assert.That(resU.UserField.CustomFieldOptions[1].Name, Is.EqualTo(expectedName3)); + + Assert.That(Api.Users.DeleteUserField(id), Is.True); + }); + } + + [Test] + public async Task CanCreateUpdateOptionsAndDeleteDropdownUserFieldAsync() + { + var option1 = "test_value_a"; + var option1_Update = "test_value_a_newtag"; + var option2 = "test_value_b"; + var option3 = "test_value_c"; + + var tField = new UserField() + { + Type = UserFieldTypes.Dropdown, + Title = "My Dropdown", + Description = "Test description", + RawTitle = "My Dropdown", + RawDescription = "Test description", + CustomFieldOptions = new List() + }; + + tField.CustomFieldOptions.Add(new CustomFieldOption() + { + Name = "test entryA", + Value = option1 + }); + + tField.CustomFieldOptions.Add(new CustomFieldOption() + { + Name = "test entryB", + Value = option2 + }); + + var res = await Api.Users.CreateUserFieldAsync(tField); + Assert.That(res.UserField, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(res.UserField.Id, Is.Not.Null); + Assert.That(res.UserField.CustomFieldOptions, Has.Count.EqualTo(2)); + }); + Assert.Multiple(() => + { + Assert.That(res.UserField.CustomFieldOptions[0].Value, Is.EqualTo(option1)); + Assert.That(res.UserField.CustomFieldOptions[1].Value, Is.EqualTo(option2)); + }); + var id = res.UserField.Id.Value; + + var tFieldU = new UserField() + { + Id = id, + CustomFieldOptions = new List() + }; + + //update CustomFieldOption A + tFieldU.CustomFieldOptions.Add(new CustomFieldOption() + { + Name = "test entryA newTitle", + Value = option1_Update + }); + //delete CustomFieldOption B + //add CustomFieldOption C + tFieldU.CustomFieldOptions.Add(new CustomFieldOption() + { + Name = "test entryC", + Value = option3 + }); + + var resU = await Api.Users.UpdateUserFieldAsync(tFieldU); + + Assert.That(resU.UserField.CustomFieldOptions, Has.Count.EqualTo(2)); + Assert.Multiple(async () => + { + Assert.That(resU.UserField.CustomFieldOptions[0].Value, Is.EqualTo(option1_Update)); + Assert.That(resU.UserField.CustomFieldOptions[1].Value, Is.Not.EqualTo(option2)); + + Assert.That(await Api.Users.DeleteUserFieldAsync(id), Is.True); + }); + } + + [Test] + public void CanGetUserFields() + { + var res = Api.Users.GetUserFields(); + Assert.That(res.UserFields, Is.Not.Empty); + } + + [Test] + public void CanGetUserFieldById() + { + var id = Settings.CustomFieldId; + var userField = Api.Users.GetUserFieldById(id).UserField; + Assert.Multiple(() => + { + Assert.That(userField, Is.Not.Null); + Assert.That(id, Is.EqualTo(userField.Id)); + }); + } + + [Test] + public void CanGetUserFieldByIdAsync() + { + var id = Settings.CustomFieldId; + var userField = Api.Users.GetUserFieldByIdAsync(id).Result.UserField; + Assert.Multiple(() => + { + Assert.That(userField, Is.Not.Null); + Assert.That(id, Is.EqualTo(userField.Id)); + }); + } + + [Test] + public void CanCreateUpdateAndDeleteUserFields() + { + var tField = new UserField() + { + Type = UserFieldTypes.Text, + Title = "MyField", + }; + + var res = Api.Users.CreateUserField(tField); + Assert.That(res.UserField, Is.Not.Null); + + var updatedTF = res.UserField; + updatedTF.Title = "My Custom Field"; + + var updatedRes = Api.Users.UpdateUserField(updatedTF); + Assert.Multiple(() => + { + Assert.That(updatedTF.Title, Is.EqualTo(updatedRes.UserField.Title)); + + Assert.That(Api.Users.DeleteUserField(updatedTF.Id.Value), Is.True); + }); + } + + [TestCase(true, "test entry", "test_entry")] + [TestCase(false, "test entry", "test entry")] + public void CanCreateAndDeleteDropdownUserField(bool replaceNameSpaceWithUnderscore, string name, string expectedName) + { + var tField = new UserField() + { + Type = UserFieldTypes.Dropdown, + Title = "My Dropdown", + Description = "Test description", + RawTitle = "My Dropdown", + RawDescription = "Test description", + CustomFieldOptions = new List() + }; + + tField.CustomFieldOptions.Add(new CustomFieldOption() + { + Name = name, + Value = "test value" + }); + + var res = Api.Users.CreateUserField(tField, replaceNameSpaceWithUnderscore); + Assert.Multiple(() => + { + Assert.That(res.UserField, Is.Not.Null); + Assert.That(expectedName, Is.EqualTo(res.UserField.CustomFieldOptions[0].Name)); + + Assert.That(Api.Users.DeleteUserField(res.UserField.Id.Value), Is.True); + }); + } +} \ No newline at end of file