diff --git a/src/ZendeskApi_v2/Requests/Organizations.cs b/src/ZendeskApi_v2/Requests/Organizations.cs index 20d986ed..c4a4ab01 100644 --- a/src/ZendeskApi_v2/Requests/Organizations.cs +++ b/src/ZendeskApi_v2/Requests/Organizations.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using ZendeskApi_v2.Extensions; using ZendeskApi_v2.Models.Organizations; using ZendeskApi_v2.Models.Shared; @@ -33,11 +34,14 @@ public interface IOrganizations : ICore /// GroupOrganizationResponse GetMultipleOrganizationsByExternalIds(IEnumerable externalIds); IndividualOrganizationResponse CreateOrganization(Organization organization); + JobStatusResponse CreateMultipleOrganizations(IEnumerable organizations); IndividualOrganizationResponse CreateOrUpdateOrganization(Organization organization); IndividualOrganizationResponse UpdateOrganization(Organization organization); JobStatusResponse UpdateMultipleOrganizations(IEnumerable organizations); bool DeleteOrganization(long id); + JobStatusResponse DeleteMultipleOrganizations(IEnumerable ids); + JobStatusResponse DeleteMultipleOrganizationsByExternalIds(IEnumerable externalIds); GroupOrganizationMembershipResponse GetOrganizationMemberships(int? perPage = null, int? page = null); GroupOrganizationMembershipResponse GetOrganizationMembershipsByUserId(long userId, int? perPage = null, int? page = null); @@ -77,10 +81,13 @@ public interface IOrganizations : ICore /// Task GetMultipleOrganizationsByExternalIdsAsync(IEnumerable externalIds); Task CreateOrganizationAsync(Organization organization); + Task CreateMultipleOrganizationsAsync(IEnumerable organizations); Task CreateOrUpdateOrganizationAsync(Organization organization); Task UpdateOrganizationAsync(Organization organization); Task UpdateMultipleOrganizationsAsync(IEnumerable organizations); Task DeleteOrganizationAsync(long id); + Task DeleteMultipleOrganizationsAsync(IEnumerable ids); + Task DeleteMultipleOrganizationsByExternalIdsAsync(IEnumerable externalIds); Task GetOrganizationMembershipsAsync(int? perPage = null, int? page = null); Task GetOrganizationMembershipsByUserIdAsync(long userId, int? perPage = null, int? page = null); @@ -151,6 +158,11 @@ public IndividualOrganizationResponse CreateOrganization(Organization organizati return GenericPost("organizations.json", body); } + public JobStatusResponse CreateMultipleOrganizations(IEnumerable organizations) + { + return GenericPost("organizations/create_many.json", new { organizations }); + } + public IndividualOrganizationResponse CreateOrUpdateOrganization(Organization organization) { var body = new { organization }; @@ -173,6 +185,17 @@ public bool DeleteOrganization(long id) return GenericDelete($"organizations/{id}.json"); } + public JobStatusResponse DeleteMultipleOrganizations(IEnumerable ids) + { + return GenericDelete($"organizations/destroy_many.json?ids={string.Join(",", ids)}"); + } + + public JobStatusResponse DeleteMultipleOrganizationsByExternalIds(IEnumerable externalIds) + { + var encodedIds = WebUtility.UrlEncode(string.Join(",", externalIds)); + return GenericDelete($"organizations/destroy_many.json?external_ids={encodedIds}"); + } + public GroupOrganizationMembershipResponse GetOrganizationMemberships(int? perPage = null, int? page = null) { return GenericPagedGet("organization_memberships.json", perPage, page); @@ -276,6 +299,11 @@ public async Task GetOrganizationAsync(long id) return await GenericGetAsync($"organizations/{id}.json"); } + public async Task CreateMultipleOrganizationsAsync(IEnumerable organizations) + { + return await GenericPostAsync("organizations/create_many.json", new { organizations }); + } + public async Task GetMultipleOrganizationsAsync(IEnumerable ids) { return await GenericGetAsync($"organizations/show_many.json?ids={ids.ToCsv()}"); @@ -311,6 +339,17 @@ public async Task DeleteOrganizationAsync(long id) return await GenericDeleteAsync($"organizations/{id}.json"); } + public async Task DeleteMultipleOrganizationsAsync(IEnumerable ids) + { + return await GenericDeleteAsync($"organizations/destroy_many.json?ids={string.Join(",", ids)}"); + } + + public async Task DeleteMultipleOrganizationsByExternalIdsAsync(IEnumerable externalIds) + { + var encodedIds = WebUtility.UrlEncode(string.Join(",", externalIds)); + return await GenericDeleteAsync($"organizations/destroy_many.json?external_ids={encodedIds}"); + } + public async Task GetOrganizationMembershipsAsync(int? perPage = null, int? page = null) { return await GenericPagedGetAsync("organization_memberships.json", perPage, page); diff --git a/tests/ZendeskApi_v2.Tests/OrganizationTests.cs b/tests/ZendeskApi_v2.Tests/OrganizationTests.cs index e9c0976e..718e6732 100644 --- a/tests/ZendeskApi_v2.Tests/OrganizationTests.cs +++ b/tests/ZendeskApi_v2.Tests/OrganizationTests.cs @@ -1,6 +1,7 @@ using NUnit.Framework; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Threading; using System.Threading.Tasks; using ZendeskApi_v2.Models.Organizations; @@ -69,7 +70,6 @@ public void CanSearchForOrganizations() { var res = Api.Organizations.GetOrganizationsStartingWith(Organization.Name.Substring(0, 3)); Assert.That(res.Count, Is.GreaterThan(0)); - var search = Api.Organizations.SearchForOrganizationsByExternalId(Organization.ExternalID); Assert.That(search.Count, Is.GreaterThan(0)); } @@ -151,6 +151,70 @@ public void CanCreateOrUpdateOrganizations() Assert.That(Api.Organizations.DeleteOrganization(res.Organization.Id.Value), Is.True); } + [Test] + public void CanCreateMultipleOrganizations() + { + var createJobStatus = Api.Organizations.CreateMultipleOrganizations(new[] + { + new Organization + { + Name = "Create Multiple Test Org 1" + }, + new Organization + { + Name = "Create Multiple Test Org 2" + } + }); + + Assert.That(createJobStatus.JobStatus.Status, Is.EqualTo("queued")); + JobStatusResponse job; + do + { + Thread.Sleep(1000); + job = Api.JobStatuses.GetJobStatus(createJobStatus.JobStatus.Id); + Assert.That(job.JobStatus.Id, Is.EqualTo(createJobStatus.JobStatus.Id)); + + if (job.JobStatus.Status == "completed") break; + } while (true); + + Assert.That(job.JobStatus.Results.Count, Is.EqualTo(2)); + + foreach (var result in job.JobStatus.Results) + Assert.That(result.Id, Is.Not.EqualTo(0)); + } + + [Test] + public async Task CanCreateMultipleOrganizationsAsync() + { + var createJobStatus = await Api.Organizations.CreateMultipleOrganizationsAsync(new[] + { + new Organization + { + Name = "Create Multiple Async Test Org 1" + }, + new Organization + { + Name = "Create Multiple Async Test Org 2" + } + }); + + Assert.That(createJobStatus.JobStatus.Status, Is.EqualTo("queued")); + JobStatusResponse job; + do + { + Thread.Sleep(1000); + job = await Api.JobStatuses.GetJobStatusAsync(createJobStatus.JobStatus.Id); + Assert.That(job.JobStatus.Id, Is.EqualTo(createJobStatus.JobStatus.Id)); + + if (job.JobStatus.Status == "completed") break; + } while (true); + + Assert.That(job.JobStatus.Results.Count, Is.EqualTo(2)); + + foreach (var result in job.JobStatus.Results) + Assert.That(result.Id, Is.Not.EqualTo(0)); + } + [Test] public async Task CanCreateOrUpdateOrganizationsAsync() { @@ -170,7 +234,7 @@ public async Task CanCreateOrUpdateOrganizationsAsync() } [Test] - public void CanCreateUpdateAndDeleteMultipeOrganizations() + public void CanCreateUpdateAndDeleteMultipleOrganizations() { var res1 = Api.Organizations.CreateOrganization(new Organization() { @@ -213,6 +277,114 @@ public void CanCreateUpdateAndDeleteMultipeOrganizations() Api.Organizations.DeleteOrganization(res2.Organization.Id.Value); } + [Test] + public void CanCreateAndDeleteMultipleOrganizationsUsingBulkApis() + { + var createJobStatus = Api.Organizations.CreateMultipleOrganizations(new[] + { + new Organization + { + Name = "Bulk Create Org 1" + }, + new Organization + { + Name = "Bulk Create Org 2" + } + }); + + Assert.That(createJobStatus.JobStatus.Status, Is.EqualTo("queued")); + JobStatusResponse job; + do + { + Thread.Sleep(1000); + job = Api.JobStatuses.GetJobStatus(createJobStatus.JobStatus.Id); + Assert.That(job.JobStatus.Id, Is.EqualTo(createJobStatus.JobStatus.Id)); + + if (job.JobStatus.Status == "completed") break; + } while (true); + + Assert.That(job.JobStatus.Results.Count, Is.EqualTo(2)); + + foreach (var result in job.JobStatus.Results) + Assert.That(result.Id, Is.Not.EqualTo(0)); + + var createdOrgIds = job.JobStatus.Results.Select(r => r.Id).ToList(); + + var deleteJobStatus = Api.Organizations.DeleteMultipleOrganizations(createdOrgIds); + Assert.That(deleteJobStatus.JobStatus.Status, Is.EqualTo("queued")); + do + { + Thread.Sleep(1000); + job = Api.JobStatuses.GetJobStatus(createJobStatus.JobStatus.Id); + Assert.That(job.JobStatus.Id, Is.EqualTo(createJobStatus.JobStatus.Id)); + + if (job.JobStatus.Status == "completed") break; + } while (true); + + //verify they have actually been deleted + foreach (var orgId in createdOrgIds) + { + var exception = Assert.Throws(() => Api.Organizations.GetOrganization(orgId)); + Assert.That((exception?.Response as HttpWebResponse)?.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); + } + } + + [Test] + public void CanDeleteMultipleOrganizationsByExternalIds() + { + var orgs = new[] + { + new Organization + { + Name = "Bulk Create Org 1", + ExternalId = "Test Id 1" + }, + new Organization + { + Name = "Bulk Create Org 2", + ExternalId = "External Id 1" + } + }; + var createJobStatus = Api.Organizations.CreateMultipleOrganizations(orgs); + + Assert.That(createJobStatus.JobStatus.Status, Is.EqualTo("queued")); + JobStatusResponse job; + do + { + Thread.Sleep(1000); + job = Api.JobStatuses.GetJobStatus(createJobStatus.JobStatus.Id); + Assert.That(job.JobStatus.Id, Is.EqualTo(createJobStatus.JobStatus.Id)); + + if (job.JobStatus.Status == "completed") break; + } while (true); + + Assert.That(job.JobStatus.Results.Count, Is.EqualTo(2)); + + foreach (var result in job.JobStatus.Results) + Assert.That(result.Id, Is.Not.EqualTo(0)); + + var createdOrgIds = job.JobStatus.Results.Select(r => r.Id).ToList(); + var externalIds = orgs.Select(o => o.ExternalId.ToString()).ToList(); + + var deleteJobStatus = Api.Organizations.DeleteMultipleOrganizationsByExternalIds(externalIds); + Assert.That(deleteJobStatus.JobStatus.Status, Is.EqualTo("queued")); + do + { + Thread.Sleep(1000); + job = Api.JobStatuses.GetJobStatus(createJobStatus.JobStatus.Id); + Assert.That(job.JobStatus.Id, Is.EqualTo(createJobStatus.JobStatus.Id)); + + if (job.JobStatus.Status == "completed") break; + } while (true); + + //verify they have actually been deleted + foreach (var orgId in createdOrgIds) + { + var exception = Assert.Throws(() => Api.Organizations.GetOrganization(orgId)); + Assert.That((exception?.Response as HttpWebResponse)?.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); + } + } + [Test] public void CanCreateAndDeleteOrganizationMemberships() { @@ -230,7 +402,7 @@ public void CanCreateAndDeleteOrganizationMemberships() var res = Api.Users.CreateUser(user); - var org_membership = new OrganizationMembership() { UserId = res.User.Id, OrganizationId = org.Organization.Id }; + var org_membership = new OrganizationMembership() {UserId = res.User.Id, OrganizationId = org.Organization.Id}; var res2 = Api.Organizations.CreateOrganizationMembership(org_membership); @@ -268,16 +440,16 @@ public async Task CanGetIncrementalOrganizationExportAsync() } [Test] - public async Task CanCreateUpdateAndDeleteMultipeOrganizationsAsync() + public async Task CanCreateUpdateAndDeleteMultipleOrganizationsAsync() { var res1 = await Api.Organizations.CreateOrganizationAsync(new Organization() { - Name = "Test Org 1" + Name = "Test Org 1 Async" }); var res2 = await Api.Organizations.CreateOrganizationAsync(new Organization() { - Name = "Test Org 2" + Name = "Test Org 2 Async" }); Assert.That(res1.Organization.Id, Is.GreaterThan(0)); @@ -310,5 +482,113 @@ public async Task CanCreateUpdateAndDeleteMultipeOrganizationsAsync() await Api.Organizations.DeleteOrganizationAsync(res1.Organization.Id.Value); await Api.Organizations.DeleteOrganizationAsync(res2.Organization.Id.Value); } + + [Test] + public async Task CanCreateAndDeleteMultipleOrganizationsUsingBulkApisAsync() + { + var createJobStatus = await Api.Organizations.CreateMultipleOrganizationsAsync(new[] + { + new Organization + { + Name = "Bulk Create Org 1" + }, + new Organization + { + Name = "Bulk Create Org 2" + } + }); + + Assert.That(createJobStatus.JobStatus.Status, Is.EqualTo("queued")); + JobStatusResponse job; + do + { + await Task.Delay(1000); + job = await Api.JobStatuses.GetJobStatusAsync(createJobStatus.JobStatus.Id); + Assert.That(job.JobStatus.Id, Is.EqualTo(createJobStatus.JobStatus.Id)); + + if (job.JobStatus.Status == "completed") break; + } while (true); + + Assert.That(job.JobStatus.Results.Count, Is.EqualTo(2)); + + foreach (var result in job.JobStatus.Results) + Assert.That(result.Id, Is.Not.EqualTo(0)); + + var createdOrgIds = job.JobStatus.Results.Select(r => r.Id).ToList(); + + var deleteJobStatus = await Api.Organizations.DeleteMultipleOrganizationsAsync(createdOrgIds); + Assert.That(deleteJobStatus.JobStatus.Status, Is.EqualTo("queued")); + do + { + await Task.Delay(1000); + job = await Api.JobStatuses.GetJobStatusAsync(createJobStatus.JobStatus.Id); + Assert.That(job.JobStatus.Id, Is.EqualTo(createJobStatus.JobStatus.Id)); + + if (job.JobStatus.Status == "completed") break; + } while (true); + + //verify they have actually been deleted + foreach (var orgId in createdOrgIds) + { + var exception = Assert.ThrowsAsync(async () => await Api.Organizations.GetOrganizationAsync(orgId)); + Assert.That((exception?.Response as HttpWebResponse)?.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); + } + } + + [Test] + public async Task CanDeleteMultipleOrganizationsByExternalIdsAsync() + { + var orgs = new[] + { + new Organization + { + Name = "Bulk Create Org 1", + ExternalId = "Test Id 1" + }, + new Organization + { + Name = "Bulk Create Org 2", + ExternalId = "External Id 1" + } + }; + var createJobStatus = await Api.Organizations.CreateMultipleOrganizationsAsync(orgs); + + Assert.That(createJobStatus.JobStatus.Status, Is.EqualTo("queued")); + JobStatusResponse job; + do + { + await Task.Delay(1000); + job = await Api.JobStatuses.GetJobStatusAsync(createJobStatus.JobStatus.Id); + Assert.That(job.JobStatus.Id, Is.EqualTo(createJobStatus.JobStatus.Id)); + + if (job.JobStatus.Status == "completed") break; + } while (true); + + Assert.That(job.JobStatus.Results.Count, Is.EqualTo(2)); + + foreach (var result in job.JobStatus.Results) + Assert.That(result.Id, Is.Not.EqualTo(0)); + + var createdOrgIds = job.JobStatus.Results.Select(r => r.Id).ToList(); + var externalIds = orgs.Select(o => o.ExternalId.ToString()).ToList(); + + var deleteJobStatus = await Api.Organizations.DeleteMultipleOrganizationsByExternalIdsAsync(externalIds); + Assert.That(deleteJobStatus.JobStatus.Status, Is.EqualTo("queued")); + do + { + await Task.Delay(1000); + job = await Api.JobStatuses.GetJobStatusAsync(createJobStatus.JobStatus.Id); + Assert.That(job.JobStatus.Id, Is.EqualTo(createJobStatus.JobStatus.Id)); + + if (job.JobStatus.Status == "completed") break; + } while (true); + + //verify they have actually been deleted + foreach (var orgId in createdOrgIds) + { + var exception = Assert.ThrowsAsync(async () => await Api.Organizations.GetOrganizationAsync(orgId)); + Assert.That((exception?.Response as HttpWebResponse)?.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); + } + } } } \ No newline at end of file