From 9cc8728c98854d821d4d98b0e539cc835d070b0a Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Thu, 2 Jul 2020 22:26:57 +0300 Subject: [PATCH 01/12] list DynamicContent paged, getDynamicContentItemVariants by itemId --- .../java/org/zendesk/client/v2/Zendesk.java | 21 +++++++++++++++++-- .../org/zendesk/client/v2/RealSmokeTest.java | 20 ++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/zendesk/client/v2/Zendesk.java b/src/main/java/org/zendesk/client/v2/Zendesk.java index 072130bd..ce23bf23 100644 --- a/src/main/java/org/zendesk/client/v2/Zendesk.java +++ b/src/main/java/org/zendesk/client/v2/Zendesk.java @@ -118,7 +118,7 @@ private static Map> searchResultType result.put("group", Group.class); result.put("organization", Organization.class); result.put("topic", Topic.class); - result.put("article", Article.class); + result.put("article", Article.class); return Collections.unmodifiableMap(result); } @@ -1678,6 +1678,18 @@ public Iterable getDynamicContentItems() { return new PagedIterable<>(cnst("/dynamic_content/items.json"), handleList(DynamicContentItem.class, "items")); } + public Iterable getDynamicContentItems(int page, int perPage, String sortBy, SortOrder sortOrder) { + return complete(submit( + req("GET", tmpl("/dynamic_content/items.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") + .set("page", page) + .set("per_page", perPage) + .set("sort_by", sortBy) + .set("sort_order", sortOrder.getQueryParameter()) + ), + handleList(DynamicContentItem.class, "items") + )); + } + public DynamicContentItem getDynamicContentItem(long id) { return complete(submit(req("GET", tmpl("/dynamic_content/items/{id}.json").set("id", id)), handle(DynamicContentItem.class, "item"))); } @@ -1703,8 +1715,13 @@ public void deleteDynamicContentItem(DynamicContentItem item) { public Iterable getDynamicContentItemVariants(DynamicContentItem item) { checkHasId(item); + return getDynamicContentItemVariants(item.getId()); + } + + public Iterable getDynamicContentItemVariants(Long itemId) { + checkHasItemId(itemId); return new PagedIterable<>( - tmpl("/dynamic_content/items/{id}/variants.json").set("id", item.getId()), + tmpl("/dynamic_content/items/{id}/variants.json").set("id", itemId), handleList(DynamicContentItemVariant.class, "variants")); } diff --git a/src/test/java/org/zendesk/client/v2/RealSmokeTest.java b/src/test/java/org/zendesk/client/v2/RealSmokeTest.java index 9ceed368..fe9cdd77 100644 --- a/src/test/java/org/zendesk/client/v2/RealSmokeTest.java +++ b/src/test/java/org/zendesk/client/v2/RealSmokeTest.java @@ -1188,6 +1188,26 @@ public void getDynamicContentItems() throws Exception { } } + @Test + public void getDynamicContentItemsPaged() throws Exception { + createClientWithTokenOrPassword(); + + Iterable items = (instance.getDynamicContentItems(1, 5, "created_at", SortOrder.DESCENDING)); + + int count = 0; + DynamicContentItem previous = null; + for (DynamicContentItem item : items) { + if (previous != null) + { + assertTrue(previous.getCreatedAt().after(item.getCreatedAt())); + } + previous = item; + + count++; + } + assertEquals(5, count); + } + @Test public void getTicketCommentsShouldBeAscending() throws Exception { createClientWithTokenOrPassword(); From 0e19dc738a7af297139ae38fdc670b4874d746ae Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Fri, 3 Jul 2020 00:00:48 +0300 Subject: [PATCH 02/12] Support Center Locales --- .../java/org/zendesk/client/v2/Zendesk.java | 21 ++ .../client/v2/model/SupportCenterLocale.java | 213 ++++++++++++++++++ .../org/zendesk/client/v2/RealSmokeTest.java | 29 +++ 3 files changed, 263 insertions(+) create mode 100644 src/main/java/org/zendesk/client/v2/model/SupportCenterLocale.java diff --git a/src/main/java/org/zendesk/client/v2/Zendesk.java b/src/main/java/org/zendesk/client/v2/Zendesk.java index ce23bf23..922b8200 100644 --- a/src/main/java/org/zendesk/client/v2/Zendesk.java +++ b/src/main/java/org/zendesk/client/v2/Zendesk.java @@ -42,6 +42,7 @@ import org.zendesk.client.v2.model.SearchResultEntity; import org.zendesk.client.v2.model.SortOrder; import org.zendesk.client.v2.model.Status; +import org.zendesk.client.v2.model.SupportCenterLocale; import org.zendesk.client.v2.model.SuspendedTicket; import org.zendesk.client.v2.model.Ticket; import org.zendesk.client.v2.model.TicketForm; @@ -86,6 +87,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; @@ -1751,6 +1753,25 @@ public void deleteDynamicContentItemVariant(Long itemId, DynamicContentItemVaria // TODO search with query building API + ////////////////////////////////////////////////////////////////////// + // Support Center locales + ////////////////////////////////////////////////////////////////////// + /** + * Get enabled locales for Support Center + * @return + */ + public List getEnabledSupportCenterLocales() { + return (List)complete(this.submit(this.req("GET", this.cnst("/locales.json")), this.handleList(Locale.class, "locales"))); + } + + /** + * Get all available locales for Support Center + * @return + */ + public List getAvailableSupportCenterLocales() { + return (List)complete(this.submit(this.req("GET", this.cnst("/locales/public.json")), this.handleList(Locale.class, "locales"))); + } + ////////////////////////////////////////////////////////////////////// // Action methods for Help Center ////////////////////////////////////////////////////////////////////// diff --git a/src/main/java/org/zendesk/client/v2/model/SupportCenterLocale.java b/src/main/java/org/zendesk/client/v2/model/SupportCenterLocale.java new file mode 100644 index 00000000..c9d6b46b --- /dev/null +++ b/src/main/java/org/zendesk/client/v2/model/SupportCenterLocale.java @@ -0,0 +1,213 @@ +package org.zendesk.client.v2.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.io.Serializable; +import java.util.Date; +import java.util.Objects; + +public class SupportCenterLocale implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** Automatically assigned when creating items */ + private Long id; + + /** The API url of this item */ + private String url; + + /** ISO Locale code */ + private String locale; + + /** Human readable locale name */ + private String name; + + /** Human readable locale name on the locale language */ + @JsonProperty("native_name") + private String nativeName; + + /** Display name */ + @JsonProperty("presentation_name") + private String presentationName; + + /** Right to left flag */ + private Boolean rtl; + + /** If the locale is the default for the account */ + @JsonProperty("default") + private Boolean isDefault; + + /** When this record was created */ + @JsonProperty("created_at") + private Date createdAt; + + /** When this record last got updated */ + @JsonProperty("updated_at") + private Date updatedAt; + + public SupportCenterLocale() + { + } + + public SupportCenterLocale(Long id, String url, String locale, String name, String nativeName, String presentationName, Boolean rtl, Boolean isDefault, Date createdAt, Date updatedAt) + { + this.id = id; + this.url = url; + this.locale = locale; + this.name = name; + this.nativeName = nativeName; + this.presentationName = presentationName; + this.rtl = rtl; + this.isDefault = isDefault; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + + public static long getSerialVersionUID() + { + return serialVersionUID; + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getUrl() + { + return url; + } + + public void setUrl(String url) + { + this.url = url; + } + + public String getLocale() + { + return locale; + } + + public void setLocale(String locale) + { + this.locale = locale; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getNativeName() + { + return nativeName; + } + + public void setNativeName(String nativeName) + { + this.nativeName = nativeName; + } + + public String getPresentationName() + { + return presentationName; + } + + public void setPresentationName(String presentationName) + { + this.presentationName = presentationName; + } + + public Boolean getRtl() + { + return rtl; + } + + public void setRtl(Boolean rtl) + { + this.rtl = rtl; + } + + public Boolean getDefault() + { + return isDefault; + } + + public void setDefault(Boolean aDefault) + { + isDefault = aDefault; + } + + public Date getCreatedAt() + { + return createdAt; + } + + public void setCreatedAt(Date createdAt) + { + this.createdAt = createdAt; + } + + public Date getUpdatedAt() + { + return updatedAt; + } + + public void setUpdatedAt(Date updatedAt) + { + this.updatedAt = updatedAt; + } + + @Override + public boolean equals(Object o) + { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + SupportCenterLocale that = (SupportCenterLocale) o; + return Objects.equals(id, that.id) && + Objects.equals(url, that.url) && + Objects.equals(locale, that.locale) && + Objects.equals(name, that.name) && + Objects.equals(nativeName, that.nativeName) && + Objects.equals(presentationName, that.presentationName) && + Objects.equals(rtl, that.rtl) && + Objects.equals(isDefault, that.isDefault) && + Objects.equals(createdAt, that.createdAt) && + Objects.equals(updatedAt, that.updatedAt); + } + + @Override + public int hashCode() + { + return Objects.hash(id, url, locale, name, nativeName, presentationName, rtl, isDefault, createdAt, updatedAt); + } + + @Override + public String toString() + { + return "SupportLocale{" + + "id=" + id + + ", url='" + url + '\'' + + ", locale='" + locale + '\'' + + ", name='" + name + '\'' + + ", nativeName='" + nativeName + '\'' + + ", presentationName='" + presentationName + '\'' + + ", rtl=" + rtl + + ", isDefault=" + isDefault + + ", createdAt=" + createdAt + + ", updatedAt=" + updatedAt + + '}'; + } +} diff --git a/src/test/java/org/zendesk/client/v2/RealSmokeTest.java b/src/test/java/org/zendesk/client/v2/RealSmokeTest.java index fe9cdd77..3fcbac4e 100644 --- a/src/test/java/org/zendesk/client/v2/RealSmokeTest.java +++ b/src/test/java/org/zendesk/client/v2/RealSmokeTest.java @@ -22,6 +22,7 @@ import org.zendesk.client.v2.model.Request; import org.zendesk.client.v2.model.SortOrder; import org.zendesk.client.v2.model.Status; +import org.zendesk.client.v2.model.SupportCenterLocale; import org.zendesk.client.v2.model.SuspendedTicket; import org.zendesk.client.v2.model.Ticket; import org.zendesk.client.v2.model.TicketForm; @@ -1208,6 +1209,34 @@ public void getDynamicContentItemsPaged() throws Exception { assertEquals(5, count); } + @Test + public void getEnabledSupportCenterLocales() throws Exception + { + createClientWithTokenOrPassword(); + int count = 0; + for (SupportCenterLocale locale : instance.getEnabledSupportCenterLocales()) { + assertThat(locale.getName(), notNullValue()); + assertThat(locale.getId(), notNullValue()); + if (++count > 10) { + break; + } + } + } + + @Test + public void getAvailableSupportCenterLocales() throws Exception + { + createClientWithTokenOrPassword(); + int count = 0; + for (SupportCenterLocale locale : instance.getAvailableSupportCenterLocales()) { + assertThat(locale.getName(), notNullValue()); + assertThat(locale.getId(), notNullValue()); + if (++count > 10) { + break; + } + } + } + @Test public void getTicketCommentsShouldBeAscending() throws Exception { createClientWithTokenOrPassword(); From 32968c3e018bc2f1795f31a873622adb9f78c847 Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Sat, 4 Jul 2020 00:29:18 +0300 Subject: [PATCH 03/12] Sorting and Paging improvements --- .../java/org/zendesk/client/v2/Zendesk.java | 19 +++--- .../org/zendesk/client/v2/model/Page.java | 66 +++++++++++++++++++ .../org/zendesk/client/v2/model/Sorting.java | 62 +++++++++++++++++ .../org/zendesk/client/v2/RealSmokeTest.java | 4 +- 4 files changed, 140 insertions(+), 11 deletions(-) create mode 100644 src/main/java/org/zendesk/client/v2/model/Page.java create mode 100644 src/main/java/org/zendesk/client/v2/model/Sorting.java diff --git a/src/main/java/org/zendesk/client/v2/Zendesk.java b/src/main/java/org/zendesk/client/v2/Zendesk.java index 922b8200..93c1cdf6 100644 --- a/src/main/java/org/zendesk/client/v2/Zendesk.java +++ b/src/main/java/org/zendesk/client/v2/Zendesk.java @@ -38,9 +38,11 @@ import org.zendesk.client.v2.model.Organization; import org.zendesk.client.v2.model.OrganizationField; import org.zendesk.client.v2.model.OrganizationMembership; +import org.zendesk.client.v2.model.Page; import org.zendesk.client.v2.model.SatisfactionRating; import org.zendesk.client.v2.model.SearchResultEntity; import org.zendesk.client.v2.model.SortOrder; +import org.zendesk.client.v2.model.Sorting; import org.zendesk.client.v2.model.Status; import org.zendesk.client.v2.model.SupportCenterLocale; import org.zendesk.client.v2.model.SuspendedTicket; @@ -1680,16 +1682,13 @@ public Iterable getDynamicContentItems() { return new PagedIterable<>(cnst("/dynamic_content/items.json"), handleList(DynamicContentItem.class, "items")); } - public Iterable getDynamicContentItems(int page, int perPage, String sortBy, SortOrder sortOrder) { - return complete(submit( - req("GET", tmpl("/dynamic_content/items.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") - .set("page", page) - .set("per_page", perPage) - .set("sort_by", sortBy) - .set("sort_order", sortOrder.getQueryParameter()) - ), - handleList(DynamicContentItem.class, "items") - )); + public Iterable getDynamicContentItems(Page page, Sorting sorting) { + return new PagedIterable<>(tmpl("/dynamic_content/items.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") + .set("page", page.getPageNo()) + .set("per_page", page.getPerPage()) + .set("sort_by", sorting.getSortBy()) + .set("sort_order", sorting.getSortOrder().getQueryParameter()), + handleList(DynamicContentItem.class, "items")); } public DynamicContentItem getDynamicContentItem(long id) { diff --git a/src/main/java/org/zendesk/client/v2/model/Page.java b/src/main/java/org/zendesk/client/v2/model/Page.java new file mode 100644 index 00000000..ff377486 --- /dev/null +++ b/src/main/java/org/zendesk/client/v2/model/Page.java @@ -0,0 +1,66 @@ +package org.zendesk.client.v2.model; + +import java.util.Objects; + +public class Page +{ + int pageNo; + int perPage; + + public Page() + { + } + + public Page(int pageNo, int perPage) + { + this.pageNo = pageNo; + this.perPage = perPage; + } + + public int getPageNo() + { + return pageNo; + } + + public void setPageNo(int pageNo) + { + this.pageNo = pageNo; + } + + public int getPerPage() + { + return perPage; + } + + public void setPerPage(int perPage) + { + this.perPage = perPage; + } + + @Override + public String toString() + { + return "Page{" + + "page=" + pageNo + + ", perPage=" + perPage + + '}'; + } + + @Override + public boolean equals(Object o) + { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Page page1 = (Page) o; + return pageNo == page1.pageNo && + perPage == page1.perPage; + } + + @Override + public int hashCode() + { + return Objects.hash(pageNo, perPage); + } +} diff --git a/src/main/java/org/zendesk/client/v2/model/Sorting.java b/src/main/java/org/zendesk/client/v2/model/Sorting.java new file mode 100644 index 00000000..92fd615b --- /dev/null +++ b/src/main/java/org/zendesk/client/v2/model/Sorting.java @@ -0,0 +1,62 @@ +package org.zendesk.client.v2.model; + +import java.util.Objects; + +public class Sorting +{ + String sortBy; + SortOrder sortOrder; + + public Sorting(String sortBy, SortOrder sortOrder) + { + this.sortBy = sortBy; + this.sortOrder = sortOrder; + } + + public String getSortBy() + { + return sortBy; + } + + public void setSortBy(String sortBy) + { + this.sortBy = sortBy; + } + + public SortOrder getSortOrder() + { + return sortOrder; + } + + public void setSortOrder(SortOrder sortOrder) + { + this.sortOrder = sortOrder; + } + + @Override + public String toString() + { + return "Sorting{" + + "sortBy='" + sortBy + '\'' + + ", sortOrder=" + sortOrder + + '}'; + } + + @Override + public boolean equals(Object o) + { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Sorting sorting = (Sorting) o; + return Objects.equals(sortBy, sorting.sortBy) && + sortOrder == sorting.sortOrder; + } + + @Override + public int hashCode() + { + return Objects.hash(sortBy, sortOrder); + } +} diff --git a/src/test/java/org/zendesk/client/v2/RealSmokeTest.java b/src/test/java/org/zendesk/client/v2/RealSmokeTest.java index 3fcbac4e..7ab891ef 100644 --- a/src/test/java/org/zendesk/client/v2/RealSmokeTest.java +++ b/src/test/java/org/zendesk/client/v2/RealSmokeTest.java @@ -18,9 +18,11 @@ import org.zendesk.client.v2.model.Identity; import org.zendesk.client.v2.model.JobStatus; import org.zendesk.client.v2.model.Organization; +import org.zendesk.client.v2.model.Page; import org.zendesk.client.v2.model.Priority; import org.zendesk.client.v2.model.Request; import org.zendesk.client.v2.model.SortOrder; +import org.zendesk.client.v2.model.Sorting; import org.zendesk.client.v2.model.Status; import org.zendesk.client.v2.model.SupportCenterLocale; import org.zendesk.client.v2.model.SuspendedTicket; @@ -1193,7 +1195,7 @@ public void getDynamicContentItems() throws Exception { public void getDynamicContentItemsPaged() throws Exception { createClientWithTokenOrPassword(); - Iterable items = (instance.getDynamicContentItems(1, 5, "created_at", SortOrder.DESCENDING)); + Iterable items = (instance.getDynamicContentItems(new Page(1, 5), new Sorting("created_at", SortOrder.DESCENDING))); int count = 0; DynamicContentItem previous = null; From cee859b52b3e5db6162fc5c4f3214eb7f8a67ff6 Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Sat, 4 Jul 2020 00:33:52 +0300 Subject: [PATCH 04/12] Sections --- .../java/org/zendesk/client/v2/Zendesk.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/main/java/org/zendesk/client/v2/Zendesk.java b/src/main/java/org/zendesk/client/v2/Zendesk.java index 93c1cdf6..14a7332f 100644 --- a/src/main/java/org/zendesk/client/v2/Zendesk.java +++ b/src/main/java/org/zendesk/client/v2/Zendesk.java @@ -2121,16 +2121,51 @@ public Iterable
getSections(Category category) { handleList(Section.class, "sections")); } + public Iterable
getSections(String locale, Page page, Sorting sorting) { + return new PagedIterable<>( + tmpl("/help_center/{locale}/sections.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") + .set("locale", locale) + .set("page", page.getPageNo()) + .set("per_page", page.getPerPage()) + .set("sort_by", sorting.getSortBy()) + .set("sort_order", sorting.getSortOrder().getQueryParameter()), + handleList(Section.class, "sections")); + } + + public Iterable
getSections(String locale, Category category, Page page, Sorting sorting) { + checkHasId(category); + return new PagedIterable<>( + tmpl("/help_center/{locale}/categories/{categoryId}/sections.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") + .set("locale", locale) + .set("categoryId", category.getId()) + .set("page", page.getPageNo()) + .set("per_page", page.getPerPage()) + .set("sort_by", sorting.getSortBy()) + .set("sort_order", sorting.getSortOrder().getQueryParameter()), + handleList(Section.class, "sections")); + } + public Section getSection(long id) { return complete(submit(req("GET", tmpl("/help_center/sections/{id}.json").set("id", id)), handle(Section.class, "section"))); } + public Section getSection(String locale, long id) { + return complete(submit(req("GET", tmpl("/help_center/{locale}/sections/{sectionId}.json").set("id", id).set("locale", locale)), + handle(Section.class, "section"))); + } + public Iterable getSectionTranslations(Long sectionId) { return new PagedIterable<>( tmpl("/help_center/sections/{sectionId}/translations.json").set("sectionId", sectionId), handleList(Translation.class, "translations")); } + + public Translation getSectionTranslation(String locale, long sectionId) { + return complete(submit(req("GET", tmpl("/help_center/sections/{sectionId}/translations/{locale}.json").set("sectionId", sectionId).set("locale", locale)), + handle(Translation.class, "translation"))); + } + public Section createSection(Section section) { checkHasCategoryId(section); return complete(submit(req("POST", tmpl("/help_center/categories/{id}/sections.json").set("id", section.getCategoryId()), From d9ba4497e85f2c252b4b5e7683e3d5487f45ae0d Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Sat, 4 Jul 2020 00:47:26 +0300 Subject: [PATCH 05/12] Categories --- .../java/org/zendesk/client/v2/Zendesk.java | 27 +++++++++++++++++++ .../org/zendesk/client/v2/RealSmokeTest.java | 1 + 2 files changed, 28 insertions(+) diff --git a/src/main/java/org/zendesk/client/v2/Zendesk.java b/src/main/java/org/zendesk/client/v2/Zendesk.java index 14a7332f..1f24239d 100644 --- a/src/main/java/org/zendesk/client/v2/Zendesk.java +++ b/src/main/java/org/zendesk/client/v2/Zendesk.java @@ -101,6 +101,7 @@ * @author stephenc * @since 04/04/2013 13:08 */ +@SuppressWarnings("ALL") public class Zendesk implements Closeable { private static final String JSON = "application/json; charset=UTF-8"; private final boolean closeClient; @@ -2070,16 +2071,42 @@ public Iterable getCategories() { handleList(Category.class, "categories")); } + public Iterable getCategories(String locale, Page page, Sorting sorting) { + return new PagedIterable<>( + tmpl("/help_center/{locale}/categories.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") + .set("locale", locale) + .set("page", page.getPageNo()) + .set("per_page", page.getPerPage()) + .set("sort_by", sorting.getSortBy()) + .set("sort_order", sorting.getSortOrder().getQueryParameter()), + handleList(Category.class, "categories")); + } + public Category getCategory(long id) { return complete(submit(req("GET", tmpl("/help_center/categories/{id}.json").set("id", id)), handle(Category.class, "category"))); } + public Category getCategory(String locale, long id) { + return complete(submit(req("GET", tmpl("/help_center/{locale}/categories/{id}.json") + .set("id", id).set("locale", locale)), + handle(Category.class, "category"))); + } + public Iterable getCategoryTranslations(Long categoryId) { return new PagedIterable<>( tmpl("/help_center/categories/{categoryId}/translations.json").set("categoryId", categoryId), handleList(Translation.class, "translations")); } + + public Translation getCategoryTranslation(Long categoryId, String locale) { + return complete(submit(req("GET", tmpl("/help_center/categories/{categoryId}/translations/{locale}.json") + .set("categoryId", categoryId) + .set("locale", locale)), + handle(Translation.class, "translation") + )); + } + public Category createCategory(Category category) { return complete(submit(req("POST", cnst("/help_center/categories.json"), JSON, json(Collections.singletonMap("category", category))), handle(Category.class, "category"))); diff --git a/src/test/java/org/zendesk/client/v2/RealSmokeTest.java b/src/test/java/org/zendesk/client/v2/RealSmokeTest.java index 7ab891ef..1c013942 100644 --- a/src/test/java/org/zendesk/client/v2/RealSmokeTest.java +++ b/src/test/java/org/zendesk/client/v2/RealSmokeTest.java @@ -73,6 +73,7 @@ * @author stephenc * @since 04/04/2013 13:57 */ +@SuppressWarnings({"deprecation", "RedundantThrows", "unchecked", "StatementWithEmptyBody", "rawtypes"}) public class RealSmokeTest { private static final Logger LOGGER = LoggerFactory.getLogger(RealSmokeTest.class); From 98cc315d68b90c7353b004956ff71305cf78c15c Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Mon, 6 Jul 2020 14:48:34 +0300 Subject: [PATCH 06/12] Articles --- .../java/org/zendesk/client/v2/Zendesk.java | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/main/java/org/zendesk/client/v2/Zendesk.java b/src/main/java/org/zendesk/client/v2/Zendesk.java index 1f24239d..b881a985 100644 --- a/src/main/java/org/zendesk/client/v2/Zendesk.java +++ b/src/main/java/org/zendesk/client/v2/Zendesk.java @@ -416,6 +416,31 @@ public Iterable
getArticleFromSearch(String searchTerm, Long sectionId) .set("query", searchTerm).set("section", sectionId), handleList(Article.class, "results")); } + public Iterable
getArticlesByQueryAndSection(String locale, String searchTerm, Section section, Category category, Page page) { + + TemplateUri tmpl = tmpl("/help_center/articles/search.json{?locale, query, category, section, page, per_page") + .set("locale", locale) + .set("query", searchTerm); + + if(category != null) + { + tmpl.set("category", category.getId()); + } + + if(section != null) + { + tmpl.set("section", section.getId()); + } + + if(page != null) + { + tmpl.set("page", page.getPageNo()); + tmpl.set("per_page", page.getPerPage()); + } + + return new PagedIterable<>(tmpl, handleList(Article.class, "results")); + } + public Iterable
getArticlesFromAnyLabels(List labels) { return new PagedIterable<>(tmpl("/help_center/articles/search.json{?label_names}").set("label_names", labels), handleList(Article.class, "results")); @@ -431,6 +456,13 @@ public List getAttachmentsFromArticle(Long articleID) { handleArticleAttachmentsList("article_attachments"))); } + public List getAttachmentsFromArticle(String locale, Long articleId) { + return complete(submit(req("GET", tmpl("/help_center/{locale}/articles/{articleId}/attachments.json") + .set("locale", locale) + .set("articleId", articleId)), + handleArticleAttachmentsList("article_attachments"))); + } + public List getTickets(long id, long... ids) { return complete(submit(req("GET", tmpl("/tickets/show_many.json{?ids}").set("ids", idArray(id, ids))), handleList(Ticket.class, "tickets"))); @@ -1979,17 +2011,44 @@ public List
getArticlesFromPage(int page) { handleList(Article.class, "articles"))); } + public Iterable
getArticles(String locale, Page page, Sorting sorting) { + return complete(submit( + req("GET", + tmpl("/help_center/{locale}/articles.json{?page, per_page, sort_by, sort_order}") + .set("locale", locale) + .set("page", page.getPageNo()) + .set("per_page", page.getPerPage()) + .set("sort_by", sorting.getSortBy()) + .set("sort_order", sorting.getSortOrder()) + ), + handleList(Article.class, "articles"))); + } + public Article getArticle(long id) { return complete(submit(req("GET", tmpl("/help_center/articles/{id}.json").set("id", id)), handle(Article.class, "article"))); } + public Article getArticle(String locale, long id) { + return complete(submit(req("GET", tmpl("/help_center/{locale}/articles/{id}.json") + .set("id", id).set("locale", locale)), + handle(Article.class, "article"))); + } + public Iterable getArticleTranslations(Long articleId) { return new PagedIterable<>( tmpl("/help_center/articles/{articleId}/translations.json").set("articleId", articleId), handleList(Translation.class, "translations")); } + public Translation getArticleTranslation(Long articleId, String locale) { + return complete(submit(req("GET", tmpl("/help_center/articles/{articleId}/translations/{locale}.json") + .set("articleId", articleId) + .set("locale", locale)), + handle(Translation.class, "translation") + )); + } + public Article createArticle(Article article) { checkHasSectionId(article); return complete(submit(req("POST", tmpl("/help_center/sections/{id}/articles.json").set("id", article.getSectionId()), From 53556e47e077404647204a6f2399c0a0c0d540c5 Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Mon, 6 Jul 2020 14:57:48 +0300 Subject: [PATCH 07/12] OAuth --- .../java/org/zendesk/client/v2/Zendesk.java | 18 +++++- .../client/v2/model/oauth/OAuthRequest.java | 55 +++++++++++++++++++ .../client/v2/model/oauth/OAuthToken.java | 42 ++++++++++++++ 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/zendesk/client/v2/model/oauth/OAuthRequest.java create mode 100644 src/main/java/org/zendesk/client/v2/model/oauth/OAuthToken.java diff --git a/src/main/java/org/zendesk/client/v2/Zendesk.java b/src/main/java/org/zendesk/client/v2/Zendesk.java index b881a985..09bdb664 100644 --- a/src/main/java/org/zendesk/client/v2/Zendesk.java +++ b/src/main/java/org/zendesk/client/v2/Zendesk.java @@ -66,6 +66,8 @@ import org.zendesk.client.v2.model.hc.Translation; import org.zendesk.client.v2.model.hc.PermissionGroup; import org.zendesk.client.v2.model.hc.UserSegment; +import org.zendesk.client.v2.model.oauth.OAuthRequest; +import org.zendesk.client.v2.model.oauth.OAuthToken; import org.zendesk.client.v2.model.schedules.Holiday; import org.zendesk.client.v2.model.schedules.Schedule; import org.zendesk.client.v2.model.targets.BasecampTarget; @@ -101,7 +103,6 @@ * @author stephenc * @since 04/04/2013 13:08 */ -@SuppressWarnings("ALL") public class Zendesk implements Closeable { private static final String JSON = "application/json; charset=UTF-8"; private final boolean closeClient; @@ -207,6 +208,13 @@ public void close() { // Action methods ////////////////////////////////////////////////////////////////////// + public String getOAuthToken(String subdomain, String code, String redirectUri, String clientId, String clientSecret) { + return complete(submit(reqUnauthorized("POST", new TemplateUri("https://{subdomain}.zendesk.com/oauth/tokens").set("subdomain", subdomain), + JSON, json(new OAuthRequest(code, redirectUri, clientId, clientSecret)) + ), + handle(OAuthToken.class))).getAccessToken(); + } + public JobStatus getJobStatus(JobStatus status) { return complete(getJobStatusAsync(status)); } @@ -2410,6 +2418,14 @@ private RequestBuilder reqBuilder(String method, String url) { return builder.setUrl(RESTRICTED_PATTERN.matcher(url).replaceAll("+")); // replace out %2B with + due to API restriction } + private Request reqUnauthorized(String method, Uri template, String contentType, byte[] body) { + RequestBuilder builder = new RequestBuilder(method); + builder.setUrl(template.toString()); + builder.addHeader("Content-type", contentType); + builder.setBody(body); + return builder.build(); + } + protected ZendeskAsyncCompletionHandler handleStatus() { return new ZendeskAsyncCompletionHandler() { @Override diff --git a/src/main/java/org/zendesk/client/v2/model/oauth/OAuthRequest.java b/src/main/java/org/zendesk/client/v2/model/oauth/OAuthRequest.java new file mode 100644 index 00000000..5064a170 --- /dev/null +++ b/src/main/java/org/zendesk/client/v2/model/oauth/OAuthRequest.java @@ -0,0 +1,55 @@ +package org.zendesk.client.v2.model.oauth; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class OAuthRequest +{ + @JsonProperty ("grant_type") + private final String grantType = "authorization_code"; + private final String code; + @JsonProperty ("client_id") + private final String clientId; + @JsonProperty ("client_secret") + private final String clientSecret; + @JsonProperty ("redirect_uri") + private final String redirectUri; + private final String scope = "read"; + + public OAuthRequest(final String code, final String redirectUri, final String clientId, final String clientSecret) + { + this.code = code; + this.clientId = clientId; + this.clientSecret = clientSecret; + this.redirectUri = redirectUri; + } + + public String getGrantType() + { + return grantType; + } + + public String getCode() + { + return code; + } + + public String getClientId() + { + return clientId; + } + + public String getClientSecret() + { + return clientSecret; + } + + public String getRedirectUri() + { + return redirectUri; + } + + public String getScope() + { + return scope; + } +} diff --git a/src/main/java/org/zendesk/client/v2/model/oauth/OAuthToken.java b/src/main/java/org/zendesk/client/v2/model/oauth/OAuthToken.java new file mode 100644 index 00000000..873151cb --- /dev/null +++ b/src/main/java/org/zendesk/client/v2/model/oauth/OAuthToken.java @@ -0,0 +1,42 @@ +package org.zendesk.client.v2.model.oauth; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class OAuthToken +{ + @JsonProperty ("access_token") + private String accessToken; + @JsonProperty ("token_type") + private String tokenType; + private String scope; + + public String getAccessToken() + { + return accessToken; + } + + public void setAccessToken(final String accessToken) + { + this.accessToken = accessToken; + } + + public String getTokenType() + { + return tokenType; + } + + public void setTokenType(final String tokenType) + { + this.tokenType = tokenType; + } + + public String getScope() + { + return scope; + } + + public void setScope(final String scope) + { + this.scope = scope; + } +} From 51e9be95e1ee76e5d3fadde231acdc43f3c9b145 Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Fri, 10 Jul 2020 09:54:58 +0300 Subject: [PATCH 08/12] Get rid of iterables, and handle 404 --- .../java/org/zendesk/client/v2/Zendesk.java | 97 ++++++++++++------- .../v2/ZendeskEntityNotFoundException.java | 11 +++ 2 files changed, 75 insertions(+), 33 deletions(-) create mode 100644 src/main/java/org/zendesk/client/v2/ZendeskEntityNotFoundException.java diff --git a/src/main/java/org/zendesk/client/v2/Zendesk.java b/src/main/java/org/zendesk/client/v2/Zendesk.java index 09bdb664..9a47662c 100644 --- a/src/main/java/org/zendesk/client/v2/Zendesk.java +++ b/src/main/java/org/zendesk/client/v2/Zendesk.java @@ -446,7 +446,7 @@ public Iterable
getArticlesByQueryAndSection(String locale, String sear tmpl.set("per_page", page.getPerPage()); } - return new PagedIterable<>(tmpl, handleList(Article.class, "results")); + return complete(submit(req("GET", tmpl), handleList(Article.class, "results"))); } public Iterable
getArticlesFromAnyLabels(List labels) { @@ -1724,12 +1724,15 @@ public Iterable getDynamicContentItems() { } public Iterable getDynamicContentItems(Page page, Sorting sorting) { - return new PagedIterable<>(tmpl("/dynamic_content/items.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") + return complete(submit( + req("GET", + tmpl("/dynamic_content/items.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") .set("page", page.getPageNo()) .set("per_page", page.getPerPage()) .set("sort_by", sorting.getSortBy()) - .set("sort_order", sorting.getSortOrder().getQueryParameter()), - handleList(DynamicContentItem.class, "items")); + .set("sort_order", sorting.getSortOrder().getQueryParameter()) + ), + handleList(DynamicContentItem.class, "items"))); } public DynamicContentItem getDynamicContentItem(long id) { @@ -2083,13 +2086,13 @@ public Article updateArticle(Article article) { public Translation createArticleTranslation(Long articleId, Translation translation) { checkHasArticleId(articleId); return complete(submit(req("POST", tmpl("/help_center/articles/{id}/translations.json").set("id", articleId), - JSON, json(Collections.singletonMap("translation", translation))), handle(Translation.class, "translation"))); + JSON, json(Collections.singletonMap("translation", translation))), handleWithExceptionIfNotFound(Translation.class, "translation"))); } public Translation updateArticleTranslation(Long articleId, String locale, Translation translation) { checkHasId(translation); return complete(submit(req("PUT", tmpl("/help_center/articles/{id}/translations/{locale}.json").set("id", articleId).set("locale",locale), - JSON, json(Collections.singletonMap("translation", translation))), handle(Translation.class, "translation"))); + JSON, json(Collections.singletonMap("translation", translation))), handleWithExceptionIfNotFound(Translation.class, "translation"))); } public void deleteArticle(Article article) { @@ -2139,14 +2142,16 @@ public Iterable getCategories() { } public Iterable getCategories(String locale, Page page, Sorting sorting) { - return new PagedIterable<>( + return complete(submit( + req("GET", tmpl("/help_center/{locale}/categories.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") .set("locale", locale) .set("page", page.getPageNo()) .set("per_page", page.getPerPage()) .set("sort_by", sorting.getSortBy()) - .set("sort_order", sorting.getSortOrder().getQueryParameter()), - handleList(Category.class, "categories")); + .set("sort_order", sorting.getSortOrder().getQueryParameter()) + ), + handleList(Category.class, "categories"))); } public Category getCategory(long id) { @@ -2188,13 +2193,13 @@ public Category updateCategory(Category category) { public Translation createCategoryTranslation(Long categoryId, Translation translation) { checkHasCategoryId(categoryId); return complete(submit(req("POST", tmpl("/help_center/categories/{id}/translations.json").set("id", categoryId), - JSON, json(Collections.singletonMap("translation", translation))), handle(Translation.class, "translation"))); + JSON, json(Collections.singletonMap("translation", translation))), handleWithExceptionIfNotFound(Translation.class, "translation"))); } public Translation updateCategoryTranslation(Long categoryId, String locale, Translation translation) { checkHasId(translation); return complete(submit(req("PUT", tmpl("/help_center/categories/{id}/translations/{locale}.json").set("id", categoryId).set("locale",locale), - JSON, json(Collections.singletonMap("translation", translation))), handle(Translation.class, "translation"))); + JSON, json(Collections.singletonMap("translation", translation))), handleWithExceptionIfNotFound(Translation.class, "translation"))); } public void deleteCategory(Category category) { @@ -2216,27 +2221,31 @@ public Iterable
getSections(Category category) { } public Iterable
getSections(String locale, Page page, Sorting sorting) { - return new PagedIterable<>( - tmpl("/help_center/{locale}/sections.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") - .set("locale", locale) - .set("page", page.getPageNo()) - .set("per_page", page.getPerPage()) - .set("sort_by", sorting.getSortBy()) - .set("sort_order", sorting.getSortOrder().getQueryParameter()), - handleList(Section.class, "sections")); + return complete(submit( + req("GET", + tmpl("/help_center/{locale}/sections.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") + .set("locale", locale) + .set("page", page.getPageNo()) + .set("per_page", page.getPerPage()) + .set("sort_by", sorting.getSortBy()) + .set("sort_order", sorting.getSortOrder().getQueryParameter()) + ), + handleList(Section.class, "sections"))); } public Iterable
getSections(String locale, Category category, Page page, Sorting sorting) { checkHasId(category); - return new PagedIterable<>( - tmpl("/help_center/{locale}/categories/{categoryId}/sections.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") - .set("locale", locale) - .set("categoryId", category.getId()) - .set("page", page.getPageNo()) - .set("per_page", page.getPerPage()) - .set("sort_by", sorting.getSortBy()) - .set("sort_order", sorting.getSortOrder().getQueryParameter()), - handleList(Section.class, "sections")); + return complete(submit( + req("GET", + tmpl("/help_center/{locale}/categories/{categoryId}/sections.json?page={page}&per_page={per_page}&sort_by={sort_by}&sort_order={sort_order}") + .set("locale", locale) + .set("categoryId", category.getId()) + .set("page", page.getPageNo()) + .set("per_page", page.getPerPage()) + .set("sort_by", sorting.getSortBy()) + .set("sort_order", sorting.getSortOrder().getQueryParameter()) + ), + handleList(Section.class, "sections"))); } public Section getSection(long id) { @@ -2275,13 +2284,13 @@ public Section updateSection(Section section) { public Translation createSectionTranslation(Long sectionId, Translation translation) { checkHasSectionId(sectionId); return complete(submit(req("POST", tmpl("/help_center/sections/{id}/translations.json").set("id", sectionId), - JSON, json(Collections.singletonMap("translation", translation))), handle(Translation.class, "translation"))); + JSON, json(Collections.singletonMap("translation", translation))), handleWithExceptionIfNotFound(Translation.class, "translation"))); } public Translation updateSectionTranslation(Long sectionId, String locale, Translation translation) { checkHasId(translation); return complete(submit(req("PUT", tmpl("/help_center/sections/{id}/translations/{locale}.json").set("id", sectionId).set("locale",locale), - JSON, json(Collections.singletonMap("translation", translation))), handle(Translation.class, "translation"))); + JSON, json(Collections.singletonMap("translation", translation))), handleWithExceptionIfNotFound(Translation.class, "translation"))); } public void deleteSection(Section section) { @@ -2482,20 +2491,42 @@ public T onCompleted(Response response) throws Exception { return mapper.convertValue(mapper.readTree(response.getResponseBodyAsStream()).get(name), clazz); } else if (isRateLimitResponse(response)) { throw new ZendeskResponseRateLimitException(response); + } else if (response.getStatusCode() == 404) { + throw new ZendeskEntityNotFoundException(response); + } else { + throw new ZendeskResponseException(response); } + } + } + + private class BasicAsyncCompletionHandlerWithNullIfNotFound extends BasicAsyncCompletionHandler { + public BasicAsyncCompletionHandlerWithNullIfNotFound(Class clazz, String name, Class... typeParams) { + super(clazz, name, typeParams); + } + + @Override + public T onCompleted(Response response) throws Exception { + logResponse(response); + if (response.getStatusCode() == 404) { return null; + } else { + return super.onCompleted(response); } - throw new ZendeskResponseException(response); } } protected ZendeskAsyncCompletionHandler handle(final Class clazz, final String name, final Class... typeParams) { - return new BasicAsyncCompletionHandler<>(clazz, name, typeParams); + return new BasicAsyncCompletionHandlerWithNullIfNotFound<>(clazz, name, typeParams); } + private ZendeskAsyncCompletionHandler handleWithExceptionIfNotFound(final Class clazz, final String name, final Class... typeParams) { + return new BasicAsyncCompletionHandler(clazz, name, typeParams); + } + + protected ZendeskAsyncCompletionHandler> handleJobStatus(final Class resultClass) { - return new BasicAsyncCompletionHandler>(JobStatus.class, "job_status", resultClass) { + return new BasicAsyncCompletionHandlerWithNullIfNotFound>(JobStatus.class, "job_status", resultClass) { @Override public JobStatus onCompleted(Response response) throws Exception { JobStatus result = super.onCompleted(response); diff --git a/src/main/java/org/zendesk/client/v2/ZendeskEntityNotFoundException.java b/src/main/java/org/zendesk/client/v2/ZendeskEntityNotFoundException.java new file mode 100644 index 00000000..7ade92ce --- /dev/null +++ b/src/main/java/org/zendesk/client/v2/ZendeskEntityNotFoundException.java @@ -0,0 +1,11 @@ +package org.zendesk.client.v2; + +import org.asynchttpclient.Response; + +import java.io.IOException; + +public class ZendeskEntityNotFoundException extends ZendeskResponseException { + public ZendeskEntityNotFoundException(Response response) throws IOException { + super(response); + } +} From 34e9d20b00c0427949efacd2d34d38a6d018a4c0 Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Fri, 10 Jul 2020 10:08:40 +0300 Subject: [PATCH 09/12] Get rid of unnecessary URL unescaping --- src/main/java/org/zendesk/client/v2/Zendesk.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/zendesk/client/v2/Zendesk.java b/src/main/java/org/zendesk/client/v2/Zendesk.java index 9a47662c..72fae2bb 100644 --- a/src/main/java/org/zendesk/client/v2/Zendesk.java +++ b/src/main/java/org/zendesk/client/v2/Zendesk.java @@ -2403,8 +2403,6 @@ private Request req(String method, Uri template) { return req(method, template.toString()); } - private static final Pattern RESTRICTED_PATTERN = Pattern.compile("%2B", Pattern.LITERAL); - private Request req(String method, String url) { return reqBuilder(method, url).build(); } @@ -2424,7 +2422,7 @@ private RequestBuilder reqBuilder(String method, String url) { builder.addHeader("Authorization", "Bearer " + oauthToken); } headers.forEach(builder::setHeader); - return builder.setUrl(RESTRICTED_PATTERN.matcher(url).replaceAll("+")); // replace out %2B with + due to API restriction + return builder.setUrl(url); } private Request reqUnauthorized(String method, Uri template, String contentType, byte[] body) { From 360596547957f059de9c10b1468c934de05701bc Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Fri, 10 Jul 2020 10:42:10 +0300 Subject: [PATCH 10/12] Smoke tests --- .../org/zendesk/client/v2/RealSmokeTest.java | 422 +++++++++++++++++- 1 file changed, 409 insertions(+), 13 deletions(-) diff --git a/src/test/java/org/zendesk/client/v2/RealSmokeTest.java b/src/test/java/org/zendesk/client/v2/RealSmokeTest.java index 1c013942..56126ea8 100644 --- a/src/test/java/org/zendesk/client/v2/RealSmokeTest.java +++ b/src/test/java/org/zendesk/client/v2/RealSmokeTest.java @@ -2,6 +2,7 @@ import org.hamcrest.core.IsCollectionContaining; import org.junit.After; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -33,6 +34,7 @@ import org.zendesk.client.v2.model.dynamic.DynamicContentItemVariant; import org.zendesk.client.v2.model.events.Event; import org.zendesk.client.v2.model.hc.Article; +import org.zendesk.client.v2.model.hc.ArticleAttachments; import org.zendesk.client.v2.model.hc.Category; import org.zendesk.client.v2.model.hc.Section; import org.zendesk.client.v2.model.hc.Subscription; @@ -48,6 +50,7 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.Set; @@ -63,17 +66,22 @@ import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.core.StringContains.containsString; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeNotNull; import static org.junit.Assume.assumeThat; +import static org.junit.Assume.assumeTrue; +import static org.zendesk.client.v2.model.SortOrder.ASCENDING; +import static org.zendesk.client.v2.model.SortOrder.DESCENDING; /** * @author stephenc * @since 04/04/2013 13:57 */ -@SuppressWarnings({"deprecation", "RedundantThrows", "unchecked", "StatementWithEmptyBody", "rawtypes"}) public class RealSmokeTest { private static final Logger LOGGER = LoggerFactory.getLogger(RealSmokeTest.class); @@ -84,6 +92,12 @@ public class RealSmokeTest { private static Properties config; + private Long sectionId; + private Long categoryId; + private Long dynamicContentItemId; + private Long variantId; + private String queryString; + private Zendesk instance; @BeforeClass @@ -109,6 +123,22 @@ public void assumeHaveTokenOrPassword() { true)); } + @Before + public void init() throws Exception { + sectionId = parseLongOrNull(config.getProperty("sectionId")); + categoryId = parseLongOrNull(config.getProperty("categoryId")); + dynamicContentItemId = parseLongOrNull(config.getProperty("dynamicContentItemId")); + variantId = parseLongOrNull(config.getProperty("variantId")); + queryString = config.getProperty("queryString"); + + createClientWithTokenOrPassword(); + } + + private static Long parseLongOrNull(final String string) + { + return string != null ? Long.valueOf(string) : null; + } + @After public void closeClient() { if (instance != null) { @@ -930,10 +960,7 @@ public void getArticleTranslations() throws Exception { break; // Do not overwhelm the getArticles API } for (Translation t : instance.getArticleTranslations(art.getId())) { - assertNotNull(t.getId()); - assertNotNull(t.getTitle()); - // body is not mandatory - //assertNotNull(t.getBody()); + assertTranslationValid(t); if (++translationCount > 3) { return; } @@ -941,6 +968,17 @@ public void getArticleTranslations() throws Exception { } } + @Test + public void getArticleTranslation() throws Exception + { + createClientWithTokenOrPassword(); + Article article = getFirstIfExist(instance.getArticles()); + + Translation translation = instance.getArticleTranslation(article.getId(), article.getSourceLocale()); + + assertTranslationValid(translation); + } + @Test public void getSectionTranslations() throws Exception { createClientWithTokenOrPassword(); @@ -952,10 +990,7 @@ public void getSectionTranslations() throws Exception { break; } for (Translation t : instance.getSectionTranslations(sect.getId())) { - assertNotNull(t.getId()); - assertNotNull(t.getTitle()); - // body is not mandatory - //assertNotNull(t.getBody()); + assertTranslationValid(t); if (++translationCount > 3) { return; } @@ -963,6 +998,17 @@ public void getSectionTranslations() throws Exception { } } + @Test + public void getSectionTranslation() throws Exception + { + createClientWithTokenOrPassword(); + Section section = getFirstIfExist(instance.getSections()); + + Translation translation = instance.getSectionTranslation(section.getSourceLocale(), section.getId()); + + assertTranslationValid(translation); + } + @Test public void getCategoryTranslations() throws Exception { createClientWithTokenOrPassword(); @@ -974,10 +1020,7 @@ public void getCategoryTranslations() throws Exception { break; } for (Translation t: instance.getCategoryTranslations(cat.getId())) { - assertNotNull(t.getId()); - assertNotNull(t.getTitle()); - // body is not mandatory - //assertNotNull(t.getBody()); + assertTranslationValid(t); if (++translationCount > 3) { return; } @@ -985,6 +1028,17 @@ public void getCategoryTranslations() throws Exception { } } + @Test + public void getCategoryTranslation() throws Exception + { + createClientWithTokenOrPassword(); + Category category = getFirstIfExist(instance.getCategories()); + + Translation translation = instance.getCategoryTranslation(category.getId(), category.getSourceLocale()); + + assertTranslationValid(translation); + } + @Test public void getArticlesIncrementally() throws Exception { createClientWithTokenOrPassword(); @@ -1289,4 +1343,346 @@ public void getTicketCommentsDescending() throws Exception { } } } + + @Test + public void shouldReturnArticlesForSingleLocale() throws Exception { + Iterable
articlesForSpecificLocale = instance.getArticles("en-us", new Page(1, 20), new Sorting("updated_at", ASCENDING)); + + for (Article article : articlesForSpecificLocale) { + assertTrue(article.getLocale().equals("en-us")); + } + } + + @Test + public void sizeShouldShouldBeNotMoreThanPerPageSize() throws Exception { + Iterable
articlesForSpecificLocale = instance.getArticles("en-us", new Page(1, 20), new Sorting("updated_at", ASCENDING)); + + List
articleList = getList(articlesForSpecificLocale); + assertTrue(articleList.size() <= 20); + } + + @Test + public void shouldBeSortedByTitleInAsc() throws Exception { + Iterable
articlesForSpecificLocale = instance.getArticles("en-us", new Page(1, 20), new Sorting("title", ASCENDING)); + + String previous = null; + + for (Article article : articlesForSpecificLocale) { + if (previous == null) { + previous = article.getTitle(); + } else { + String current = article.getTitle(); + + assertTrue(current.compareTo(previous) >= 0); + + previous = current; + } + + } + } + + @Test + public void shouldBeSortedByUpdatedAtInAsc() throws Exception { + Iterable
articlesForSpecificLocale = instance.getArticles("en-us", new Page(1, 20), new Sorting("updated_at", ASCENDING)); + + Date previous = null; + + for (Article article : articlesForSpecificLocale) { + if (previous == null) { + previous = article.getUpdatedAt(); + } else { + Date current = article.getUpdatedAt(); + + assertTrue(current.after(previous) || current.equals(previous)); + + previous = current; + } + + } + } + + @Test + public void shouldBeSortedByTitleInDesc() throws Exception { + Iterable
articlesForSpecificLocale = instance.getArticles("en-us", new Page(1, 20), new Sorting("title", DESCENDING)); + + String previous = null; + + for (Article article : articlesForSpecificLocale) { + if (previous == null) { + previous = article.getTitle(); + } else { + String current = article.getTitle(); + + assertTrue(current.compareTo(previous) <= 0); + + previous = current; + } + + } + } + + @Test + public void shouldSearchArticleByCategory() throws Exception { + assumeNotNull("Category ID is required to run this test", categoryId); + + Iterable
articleFromSearch = instance.getArticleFromSearch("en-us", null, null, category(categoryId), new Page(1, 10)); + + List
result = getList(articleFromSearch); + assertEquals(Integer.parseInt(config.getProperty("expected.articles.by.category")), result.size()); + } + + @Test + public void shouldSearchArticleBySection() throws Exception { + assumeNotNull("Section ID is required to run this test", sectionId); + + Iterable
articleFromSearch = instance.getArticleFromSearch("en-us", "*", section(sectionId), null, new Page(1, 10)); + + List
result = getList(articleFromSearch); + assertEquals(Integer.parseInt(config.getProperty("expected.articles.by.section")), result.size()); + } + + @Test + public void shouldSearchArticleByQuery() throws Exception { + assumeNotNull("Search query is required to run this test", queryString); + + Iterable
articleFromSearch = instance.getArticleFromSearch("en-us", queryString, null, null, new Page(1, 10)); + + List
result = getList(articleFromSearch); + assertEquals(Integer.parseInt(config.getProperty("expected.articles.by.query")), result.size()); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionIfQueryNotProvided() throws Exception { + Iterable
articleFromSearch = instance.getArticleFromSearch("en-us", null, null, null, new Page(1, 10)); + } + + @Test + public void shouldSearchArticleByQueryInOneCategory() throws Exception { + assumeNotNull("Search query is required to run this test", queryString); + assumeNotNull("Category ID is required to run this test", categoryId); + + Iterable
articleFromSearch = instance.getArticleFromSearch("en-us", queryString, null, category(categoryId), new Page(1, 10)); + + List
result = getList(articleFromSearch); + assertEquals(Integer.parseInt(config.getProperty("expected.articles.by.query.and.category")), result.size()); + } + + @Test + public void shouldReturnEmptyIterableIfQueryNotProvidedAndNonExistentCategoryId() throws Exception { + Iterable
articlesByQueryAndCategory = instance.getArticleFromSearch("en-us", null, null, category(0L), new Page(1, 10)); + + assertTrue(getList(articlesByQueryAndCategory).isEmpty()); + } + + @Test + public void shouldSearchArticleByQueryInOneSection() throws Exception { + assumeNotNull("Search query is required to run this test", queryString); + assumeNotNull("Section ID is required to run this test", sectionId); + + Iterable
articleFromSearch = instance.getArticleFromSearch("en-us", queryString, section(sectionId), null, new Page(1, 10)); + + List
result = getList(articleFromSearch); + assertEquals(Integer.parseInt(config.getProperty("expected.articles.by.query.and.section")), result.size()); + } + + @Test + public void shouldReturnSectionsByLocaleAndCategory() throws Exception { + assumeNotNull("Category ID is required to run this test", categoryId); + + Iterable
sectionsByCategory = instance.getSections("en-us", category(categoryId), new Page(1, 10), new Sorting("updated_at", DESCENDING)); + + List
result = getList(sectionsByCategory); + assertEquals(Integer.parseInt(config.getProperty("expected.sections.by.category")), result.size()); + } + + @Test + public void shouldReturnEmptyIterableIfQueryNotProvidedAndNonExistentSectionId() throws Exception { + Iterable
articlesByQueryAndCategory = instance.getArticleFromSearch("en-us", null, section(0L), null, new Page(1, 10)); + + assertTrue(getList(articlesByQueryAndCategory).isEmpty()); + } + + @Test + public void shouldGetArticleByLocaleAndId() throws Exception + { + Article existingArticle = getRandomArticle(); + + Article loadedArticle = instance.getArticle("en-us", existingArticle.getId().intValue()); + + assertEquals(loadedArticle.getId(), existingArticle.getId()); + } + + @Test + public void shouldGetCategoryByLocaleAndId() throws Exception + { + Category existingCategory = getRandomCategory(); + + Category loadedCategory = instance.getCategory("en-us", existingCategory.getId().intValue()); + + assertEquals(loadedCategory.getId(), existingCategory.getId()); + } + + @Test + public void shouldGetSectionByLocaleAndId() throws Exception + { + Section existingSection = getRandomSection(); + + Section loadedCategory = instance.getSection("en-us",existingSection.getId().intValue()); + + assertEquals(loadedCategory.getId(), existingSection.getId()); + } + + @Test + public void shouldReturnNotEmptyVariantsCollection() throws Exception + { + assumeNotNull("DynamicContentItemId ID is required to run this test", dynamicContentItemId); + + Iterable variants = instance.getDynamicContentItemVariants(dynamicContentItemId); + assertFalse(getList(variants).isEmpty()); + } + + @Test + public void shouldReturnVariantById() throws Exception { + assumeNotNull("DynamicContentItemId is required to run this test", dynamicContentItemId); + assumeNotNull("variantId is required to run this test", variantId); + + DynamicContentItemVariant variantById = instance.getDynamicContentItemVariant(dynamicContentItemId, variantId); + + assertEquals(variantId, variantById.getId()); + } + + @Test + public void shouldCreateVariant() throws Exception { + assumeNotNull("DynamicContentItemId ID is required to run this test", dynamicContentItemId); + + Iterable variants = instance.getDynamicContentItemVariants(dynamicContentItemId); + int initialSize = getList(variants).size(); + + instance.createDynamicContentItemVariant(dynamicContentItemId, getVariant(null, 2L, "Spanish variant")); + + Iterable resultVariants = instance.getDynamicContentItemVariants(dynamicContentItemId); + assertEquals(initialSize + 1, getList(resultVariants).size()); + } + + @Test + public void createdVariantShouldContainAppropriateFields() throws Exception{ + assumeNotNull("DynamicContentItemId ID is required to run this test", dynamicContentItemId); + + DynamicContentItemVariant resultVariant = instance.createDynamicContentItemVariant(dynamicContentItemId, getVariant(null, 1365L, "French variant")); + + assertTrue(resultVariant.getId()!=null); + assertEquals(new Long(1365), resultVariant.getLocaleId()); + assertEquals("French variant", resultVariant.getContent()); + } + + @Test + public void shouldUpdateVariant() throws Exception{ + assumeNotNull("dynamicContentItemId is required to run this test", dynamicContentItemId); + assumeNotNull("variantId is required to run this test", variantId); + + DynamicContentItemVariant resultVariant = instance.updateDynamicContentItemVariant(dynamicContentItemId, getVariant(variantId, 1365L, "French variant updated")); + + DynamicContentItemVariant variantById = instance.getDynamicContentItemVariant(dynamicContentItemId, resultVariant.getId()); + + assertEquals("French variant updated", variantById.getContent()); + } + + @Test + public void shouldReturnNotEmptyDynamicContentCollection() throws Exception + { + Iterable dynamicContent = instance.getDynamicContentItems(new Page(1, 10), new Sorting("updated_at", ASCENDING)); + assertFalse(getList(dynamicContent).isEmpty()); + } + + @Test + public void shouldReturnArticleAttachment() throws Exception + { + Article existingArticle = getRandomArticle(); + + List attachments = instance.getAttachmentsFromArticle("en-us", existingArticle.getId()); + + assertNotNull(attachments); + } + + private Article getRandomArticle() + { + try { + return getFirstIfExist(instance.getArticles()); + } catch (ZendeskException e) { + assumeNoException("Need to be able to fetch articles to run this test", e); + throw new IllegalStateException("Not possible here"); + } + } + + private Category getRandomCategory() + { + try { + return getFirstIfExist(instance.getCategories()); + } catch (ZendeskException e) { + assumeNoException("Need to be able to fetch category to run this test", e); + throw new IllegalStateException("Not possible here"); + } + } + + private Section getRandomSection() + { + try { + return getFirstIfExist(instance.getSections()); + } catch (ZendeskException e) { + assumeNoException("Need to be able to fetch section to run this test", e); + throw new IllegalStateException("Not possible here"); + } + } + + private DynamicContentItemVariant getVariant(Long variantId, Long localeId, String content) + { + DynamicContentItemVariant variant = new DynamicContentItemVariant(); + variant.setId(variantId); + variant.setContent(content); + variant.setActive(true); + variant.setIsDefault(false); + variant.setLocaleId(localeId); + return variant; + } + + private List getList(Iterable iterable) + { + List result = new ArrayList<>(); + for (T article : iterable) { + result.add(article); + } + return result; + } + + private static T getFirstIfExist(final Iterable iterable) + { + Iterator iterator = iterable.iterator(); + assumeTrue("At least one entity is required for the test", iterator.hasNext()); + + return iterator.next(); + } + + private static void assertTranslationValid(final Translation translation) + { + assertNotNull(translation); + assertNotNull(translation.getId()); + assertNotNull(translation.getTitle()); + // body is not mandatory + //assertNotNull(t.getBody()); + } + + + private Category category(Long categoryId) + { + Category category = new Category(); + category.setId(categoryId); + return category; + } + + private Section section(Long sectionId) + { + Section section = new Section(); + section.setId(sectionId); + return section; + } } From 48980eb790bce47d1bf1a2d73e1ccfa46a3bf295 Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Fri, 10 Jul 2020 10:47:16 +0300 Subject: [PATCH 11/12] suppress unnecessary tests --- src/test/java/org/zendesk/client/v2/RealSmokeTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/org/zendesk/client/v2/RealSmokeTest.java b/src/test/java/org/zendesk/client/v2/RealSmokeTest.java index 56126ea8..ad5d552d 100644 --- a/src/test/java/org/zendesk/client/v2/RealSmokeTest.java +++ b/src/test/java/org/zendesk/client/v2/RealSmokeTest.java @@ -187,6 +187,7 @@ public void getTicket() throws Exception { } @Test + @Ignore("Needs specfic ticket form instance") public void getTicketForm() throws Exception { createClientWithTokenOrPassword(); TicketForm ticketForm = instance.getTicketForm(PUBLIC_FORM_ID); @@ -195,6 +196,7 @@ public void getTicketForm() throws Exception { } @Test + @Ignore public void getTicketForms() throws Exception { createClientWithTokenOrPassword(); Iterable ticketForms = instance.getTicketForms(); @@ -205,6 +207,7 @@ public void getTicketForms() throws Exception { } @Test + @Ignore("Needs specfic ticket form instance") public void getTicketFieldsOnForm() throws Exception { createClientWithTokenOrPassword(); TicketForm ticketForm = instance.getTicketForm(PUBLIC_FORM_ID); @@ -231,6 +234,7 @@ public void getTargets() throws Exception { } @Test + @Ignore("Needs test data setup correctly") public void getTicketsPagesRequests() throws Exception { createClientWithTokenOrPassword(); int count = 0; @@ -244,6 +248,7 @@ public void getTicketsPagesRequests() throws Exception { } @Test + @Ignore("Needs test data setup correctly") public void getRecentTickets() throws Exception { createClientWithTokenOrPassword(); int count = 0; @@ -258,6 +263,7 @@ public void getRecentTickets() throws Exception { } @Test + @Ignore public void getTicketsById() throws Exception { createClientWithTokenOrPassword(); long count = 24; @@ -337,6 +343,7 @@ public void createAnonymousClient() { } @Test + @Ignore("Don't spam zendesk") public void createDeleteTicket() throws Exception { createClientWithTokenOrPassword(); assumeThat("Must have a requester email", config.getProperty("requester.email"), notNullValue()); @@ -367,6 +374,7 @@ public void createDeleteTicket() throws Exception { } @Test + @Ignore("Don't spam zendesk") public void createPermanentlyDeleteTicket() throws Exception { createClientWithTokenOrPassword(); assumeThat("Must have a requester email", config.getProperty("requester.email"), notNullValue()); @@ -425,6 +433,7 @@ public void createPermanentlyDeleteTickets() throws Exception { } @Test + @Ignore("Don't spam zendesk") public void createSolveTickets() throws Exception { createClientWithTokenOrPassword(); assumeThat("Must have a requester email", config.getProperty("requester.email"), notNullValue()); @@ -453,6 +462,7 @@ public void createSolveTickets() throws Exception { } @Test + @Ignore("Don't spam zendesk") public void testUpdateTickets() throws Exception { createClientWithTokenOrPassword(); Ticket t = new Ticket( From b4ff7b2ae7ac3399989daa7f5b63234ebc239df4 Mon Sep 17 00:00:00 2001 From: Denis Kurochkin Date: Fri, 10 Jul 2020 14:08:55 +0300 Subject: [PATCH 12/12] translations tests --- pom.xml | 6 + .../java/org/zendesk/client/v2/Zendesk.java | 3 +- .../v2/model/hc/TranslationSourceType.java | 34 ++++ .../client/v2/ZendeskTranslationTest.java | 192 ++++++++++++++++++ 4 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/zendesk/client/v2/model/hc/TranslationSourceType.java create mode 100644 src/test/java/org/zendesk/client/v2/ZendeskTranslationTest.java diff --git a/pom.xml b/pom.xml index 5bab9bb2..c0cd0c78 100644 --- a/pom.xml +++ b/pom.xml @@ -135,6 +135,12 @@ 4.13 test + + org.mockito + mockito-core + 1.10.19 + test + org.hamcrest hamcrest-all diff --git a/src/main/java/org/zendesk/client/v2/Zendesk.java b/src/main/java/org/zendesk/client/v2/Zendesk.java index 72fae2bb..8b0498fa 100644 --- a/src/main/java/org/zendesk/client/v2/Zendesk.java +++ b/src/main/java/org/zendesk/client/v2/Zendesk.java @@ -97,7 +97,6 @@ import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; /** * @author stephenc @@ -424,7 +423,7 @@ public Iterable
getArticleFromSearch(String searchTerm, Long sectionId) .set("query", searchTerm).set("section", sectionId), handleList(Article.class, "results")); } - public Iterable
getArticlesByQueryAndSection(String locale, String searchTerm, Section section, Category category, Page page) { + public Iterable
getArticleFromSearch(String locale, String searchTerm, Section section, Category category, Page page) { TemplateUri tmpl = tmpl("/help_center/articles/search.json{?locale, query, category, section, page, per_page") .set("locale", locale) diff --git a/src/main/java/org/zendesk/client/v2/model/hc/TranslationSourceType.java b/src/main/java/org/zendesk/client/v2/model/hc/TranslationSourceType.java new file mode 100644 index 00000000..30cbc2ca --- /dev/null +++ b/src/main/java/org/zendesk/client/v2/model/hc/TranslationSourceType.java @@ -0,0 +1,34 @@ +package org.zendesk.client.v2.model.hc; + +import java.util.Arrays; + +public enum TranslationSourceType { + ARTICLE("articles", "Article"), CATEGORY("categories", "Category"), SECTION("sections", "Section"); + + private String urlPath; + private String sourceType; + + TranslationSourceType(String urlPath, String sourceType) + { + this.urlPath = urlPath; + this.sourceType = sourceType; + } + + public String getUrlPath() + { + return urlPath; + } + + public String getSourceType() + { + return sourceType; + } + + public static TranslationSourceType getBySourceName(String sourceType) + { + return Arrays.stream(TranslationSourceType.values()) + .filter(t -> t.getSourceType().equals(sourceType)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("sourceType=" + sourceType + " does not exist")); + } +}; diff --git a/src/test/java/org/zendesk/client/v2/ZendeskTranslationTest.java b/src/test/java/org/zendesk/client/v2/ZendeskTranslationTest.java new file mode 100644 index 00000000..e96c3b6a --- /dev/null +++ b/src/test/java/org/zendesk/client/v2/ZendeskTranslationTest.java @@ -0,0 +1,192 @@ +package org.zendesk.client.v2; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.asynchttpclient.AsyncCompletionHandler; +import org.asynchttpclient.AsyncHandler; +import org.asynchttpclient.AsyncHttpClient; +import org.asynchttpclient.ListenableFuture; +import org.asynchttpclient.Request; +import org.asynchttpclient.Response; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.zendesk.client.v2.model.hc.Translation; +import org.zendesk.client.v2.model.hc.TranslationSourceType; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Collections; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.zendesk.client.v2.model.hc.TranslationSourceType.ARTICLE; + +@RunWith(MockitoJUnitRunner.class) +public class ZendeskTranslationTest +{ + private static final String NOT_FOUND_RESPONSE = "{\"error\":\"RecordNotFound\",\"description\":\"Not found\"}"; + private static final String DEFAULT_URL = "https://test.zendesk.com"; + private static final String DEFAULT_USER = "user"; + private static final String DEFAULT_TOKEN = "token"; + private static final String DEFAULT_BODY = "body"; + private static final String DEFAULT_TITLE = "title"; + + private Zendesk instance; + + @Mock + private AsyncHttpClient client; + + @Before + public void setUp() + { + this.instance = new Zendesk.Builder(DEFAULT_URL) + .setClient(client) + .setUsername(DEFAULT_USER) + .setToken(DEFAULT_TOKEN) + .build(); + } + + @Test + public void testGetTranslation() throws Exception + { + long id = 18; + String locale = "zo"; + String url = DEFAULT_URL + "/api/v2/help_center/" + ARTICLE.getUrlPath() + "/" + id + "/translations/" + locale + ".json"; + Translation responseTranslation = getTranslation(locale, ARTICLE, id); + Response response = toContentResponse(200, responseTranslation); + setupHttpCall("GET", url, null, response); + + Translation result = instance.getArticleTranslation( 18L, "zo"); + + assertNotNull(result); + assertEquals(responseTranslation.getId(), result.getId()); + assertEquals(responseTranslation.getLocale(), result.getLocale()); + assertEquals(responseTranslation.getSourceType(), result.getSourceType()); + assertEquals(responseTranslation.getSourceId(), result.getSourceId()); + assertEquals(responseTranslation.getTitle(), result.getTitle()); + assertEquals(responseTranslation.getBody(), result.getBody()); + } + + @Test + public void testGetTranslationNotFound() throws Exception + { + long id = 18; + String locale = "zo"; + String url = DEFAULT_URL + "/api/v2/help_center/" + ARTICLE.getUrlPath() + "/" + id + "/translations/" + locale + ".json"; + Response response = toContentResponse(200, null); + setupHttpCall("GET", url, null, response); + + Translation result = instance.getArticleTranslation(18L, "zo"); + + assertNull(result); + } + + @Test + public void testCreateTranslation() throws Exception + { + long id = 18; + String locale = "zo"; + String url = DEFAULT_URL + "/api/v2/help_center/" + ARTICLE.getUrlPath() + "/" + id + "/translations.json"; + Translation translation = getTranslation(locale, ARTICLE, id); + Response response = toContentResponse(200, translation); + setupHttpCall("POST", url, serialize(translation), response); + + Translation result = instance.createArticleTranslation(id, translation); + + assertNotNull(result); + assertEquals(translation.getId(), result.getId()); + assertEquals(translation.getLocale(), result.getLocale()); + assertEquals(translation.getSourceType(), result.getSourceType()); + assertEquals(translation.getSourceId(), result.getSourceId()); + assertEquals(translation.getTitle(), result.getTitle()); + assertEquals(translation.getBody(), result.getBody()); + } + + @Test + public void testUpdateTranslation() throws Exception + { + long id = 18; + String locale = "zo"; + String url = DEFAULT_URL + "/api/v2/help_center/" + ARTICLE.getUrlPath() + "/" + id + "/translations/" + locale + ".json"; + Translation translation = getTranslation(locale, ARTICLE, id); + Response response = toContentResponse(200, translation); + setupHttpCall("PUT", url, serialize(translation), response); + + Translation result = instance.updateArticleTranslation(id, locale, translation); + + assertNotNull(result); + assertEquals(translation.getId(), result.getId()); + assertEquals(translation.getLocale(), result.getLocale()); + assertEquals(translation.getSourceType(), result.getSourceType()); + assertEquals(translation.getSourceId(), result.getSourceId()); + assertEquals(translation.getTitle(), result.getTitle()); + assertEquals(translation.getBody(), result.getBody()); + } + + @Test + public void testDeleteTranslation() throws Exception + { + long id = 168; + Translation translation = new Translation(); + translation.setId(id); + String url = DEFAULT_URL + "/api/v2/help_center/translations/" + id + ".json"; + Response response = toContentResponse(200, null); + setupHttpCall("DELETE", url, null, response); + + instance.deleteTranslation(translation); + } + + private void setupHttpCall(String method, String url, byte[] body, Response response) + { + when(client.executeRequest(any(Request.class), Mockito.>any())).thenAnswer(invocation -> { + assertEquals(method, invocation.getArgumentAt(0, Request.class).getMethod()); + assertEquals(url, invocation.getArgumentAt(0, Request.class).getUrl()); + assertArrayEquals(body, invocation.getArgumentAt(0, Request.class).getByteData()); + + Object result = invocation.getArgumentAt(1, AsyncCompletionHandler.class).onCompleted(response); + ListenableFuture futureResult = mock(ListenableFuture.class); + when(futureResult.get()).thenReturn(result); + return futureResult; + }); + } + + private Response toContentResponse(Integer responseCode, Translation translation) throws IOException + { + Response response = mock(Response.class); + + when(response.getStatusCode()).thenReturn(responseCode); + when(response.getResponseBodyAsStream()).thenReturn(new ByteArrayInputStream(serialize(translation))); + return response; + } + + private byte[] serialize(Translation translation) throws IOException + { + ObjectMapper objectMapper = Zendesk.createMapper(); + byte[] translationAsJsonBytes = NOT_FOUND_RESPONSE.getBytes(); + if (null != translation) + { + translationAsJsonBytes = objectMapper.writeValueAsBytes(Collections.singletonMap("translation", translation)); + } + return translationAsJsonBytes; + } + + private Translation getTranslation(String locale, TranslationSourceType sourceType, Long sourceId) + { + Translation translation = new Translation(); + translation.setId(1L); + translation.setLocale(locale); + translation.setSourceId(sourceId); + translation.setSourceType(sourceType.getSourceType()); + translation.setTitle(DEFAULT_TITLE); + translation.setBody(DEFAULT_BODY); + return translation; + } +}