|
63 | 63 | import org.zendesk.client.v2.model.hc.Article;
|
64 | 64 | import org.zendesk.client.v2.model.hc.ArticleAttachments;
|
65 | 65 | import org.zendesk.client.v2.model.hc.Category;
|
| 66 | +import org.zendesk.client.v2.model.hc.ContentTag; |
66 | 67 | import org.zendesk.client.v2.model.hc.Locales;
|
67 | 68 | import org.zendesk.client.v2.model.hc.PermissionGroup;
|
68 | 69 | import org.zendesk.client.v2.model.hc.Section;
|
|
98 | 99 | import java.util.Optional;
|
99 | 100 | import java.util.concurrent.ExecutionException;
|
100 | 101 | import java.util.concurrent.TimeUnit;
|
101 |
| -import java.util.regex.Pattern; |
| 102 | +import java.util.function.Function; |
102 | 103 |
|
103 | 104 | /**
|
104 | 105 | * @author stephenc
|
@@ -2534,6 +2535,59 @@ public Iterable<Holiday> getHolidaysForSchedule(Long scheduleId) {
|
2534 | 2535 | handleList(Holiday.class, "holidays")));
|
2535 | 2536 | }
|
2536 | 2537 |
|
| 2538 | + public ContentTag getContentTag(String contentTagId) { |
| 2539 | + return complete(submit(req("GET", tmpl("/guide/content_tags/{id}").set("id", contentTagId)), |
| 2540 | + handle(ContentTag.class, "content_tag"))); |
| 2541 | + } |
| 2542 | + |
| 2543 | + public ContentTag createContentTag(ContentTag contentTag) { |
| 2544 | + checkHasName(contentTag); |
| 2545 | + return complete(submit(req("POST", cnst("/guide/content_tags"), |
| 2546 | + JSON, json(Collections.singletonMap("content_tag", contentTag))), |
| 2547 | + handle(ContentTag.class, "content_tag"))); |
| 2548 | + } |
| 2549 | + |
| 2550 | + public ContentTag updateContentTag(ContentTag contentTag) { |
| 2551 | + checkHasId(contentTag); |
| 2552 | + checkHasName(contentTag); |
| 2553 | + return complete(submit(req("PUT", tmpl("/guide/content_tags/{id}").set("id", contentTag.getId()), |
| 2554 | + JSON, json(Collections.singletonMap("content_tag", contentTag))), |
| 2555 | + handle(ContentTag.class, "content_tag"))); |
| 2556 | + } |
| 2557 | + |
| 2558 | + public void deleteContentTag(ContentTag contentTag) { |
| 2559 | + checkHasId(contentTag); |
| 2560 | + complete(submit(req("DELETE", tmpl("/guide/content_tags/{id}").set("id", contentTag.getId())), |
| 2561 | + handleStatus())); |
| 2562 | + } |
| 2563 | + |
| 2564 | + public Iterable<ContentTag> getContentTags() { |
| 2565 | + int defaultPageSize = 10; |
| 2566 | + return getContentTags(defaultPageSize, null); |
| 2567 | + } |
| 2568 | + |
| 2569 | + public Iterable<ContentTag> getContentTags(int pageSize) { |
| 2570 | + return getContentTags(pageSize, null); |
| 2571 | + } |
| 2572 | + |
| 2573 | + public Iterable<ContentTag> getContentTags(int pageSize, String namePrefix) { |
| 2574 | + Function<String, Uri> afterCursorUriBuilder = (String afterCursor) -> buildContentTagsSearchUrl(pageSize, namePrefix, afterCursor); |
| 2575 | + return new PagedIterable<>(afterCursorUriBuilder.apply(null), |
| 2576 | + handleListWithAfterCursorButNoLinks(ContentTag.class, afterCursorUriBuilder, "records")); |
| 2577 | + } |
| 2578 | + |
| 2579 | + private Uri buildContentTagsSearchUrl(int pageSize, String namePrefixFilter, String afterCursor) { |
| 2580 | + final StringBuilder uriBuilder = new StringBuilder("/guide/content_tags?page[size]=").append(pageSize); |
| 2581 | + |
| 2582 | + if (namePrefixFilter != null) { |
| 2583 | + uriBuilder.append("&filter[name_prefix]=").append(encodeUrl(namePrefixFilter)); |
| 2584 | + } |
| 2585 | + if (afterCursor != null) { |
| 2586 | + uriBuilder.append("&page[after]=").append(encodeUrl(afterCursor)); |
| 2587 | + } |
| 2588 | + return cnst(uriBuilder.toString()); |
| 2589 | + } |
| 2590 | + |
2537 | 2591 | //////////////////////////////////////////////////////////////////////
|
2538 | 2592 | // Helper methods
|
2539 | 2593 | //////////////////////////////////////////////////////////////////////
|
@@ -2687,6 +2741,7 @@ public JobStatus onCompleted(Response response) throws Exception {
|
2687 | 2741 | private static final String COUNT = "count";
|
2688 | 2742 | private static final int INCREMENTAL_EXPORT_MAX_COUNT_BY_REQUEST = 1000;
|
2689 | 2743 |
|
| 2744 | + |
2690 | 2745 | private abstract class PagedAsyncCompletionHandler<T> extends ZendeskAsyncCompletionHandler<T> {
|
2691 | 2746 | private String nextPage;
|
2692 | 2747 |
|
@@ -2871,6 +2926,42 @@ public List<ArticleAttachments> onCompleted(Response response) throws Exception
|
2871 | 2926 | };
|
2872 | 2927 | }
|
2873 | 2928 |
|
| 2929 | + /** |
| 2930 | + * For a resource (e.g. ContentTag) which supports cursor based pagination for multiple results, |
| 2931 | + * but where the response does not have a `links.next` node (which would hold the URL of the next page) |
| 2932 | + * So we need to build the next page URL from the original URL and the meta.after_cursor node value |
| 2933 | + * |
| 2934 | + * @param <T> The class of the resource |
| 2935 | + * @param afterCursorUriBuilder a function to build the URL for the next page `fn(after_cursor_value) => URL_of_next_page` |
| 2936 | + * @param name the name of the Json node that contains the resources entities (e.g. 'records' for ContentTag) |
| 2937 | + */ |
| 2938 | + private <T> PagedAsyncCompletionHandler<List<T>> handleListWithAfterCursorButNoLinks( |
| 2939 | + Class<T> clazz, Function<String, Uri> afterCursorUriBuilder, String name) { |
| 2940 | + |
| 2941 | + return new PagedAsyncListCompletionHandler<T>(clazz, name) { |
| 2942 | + @Override |
| 2943 | + public void setPagedProperties(JsonNode responseNode, Class<?> clazz) { |
| 2944 | + JsonNode metaNode = responseNode.get("meta"); |
| 2945 | + String nextPage = null; |
| 2946 | + if (metaNode == null) { |
| 2947 | + if (logger.isDebugEnabled()) { |
| 2948 | + logger.debug("meta" + " property not found, pagination not supported" + |
| 2949 | + (clazz != null ? " for " + clazz.getName() : "")); |
| 2950 | + } |
| 2951 | + } else { |
| 2952 | + JsonNode afterCursorNode = metaNode.get("after_cursor"); |
| 2953 | + if (afterCursorNode != null) { |
| 2954 | + JsonNode hasMoreNode = metaNode.get("has_more"); |
| 2955 | + if (hasMoreNode != null && hasMoreNode.asBoolean()) { |
| 2956 | + nextPage = afterCursorUriBuilder.apply(afterCursorNode.asText()).toString(); |
| 2957 | + } |
| 2958 | + } |
| 2959 | + } |
| 2960 | + setNextPage(nextPage); |
| 2961 | + } |
| 2962 | + }; |
| 2963 | + } |
| 2964 | + |
2874 | 2965 | private TemplateUri tmpl(String template) {
|
2875 | 2966 | return new TemplateUri(url + template);
|
2876 | 2967 | }
|
@@ -3132,6 +3223,18 @@ private static void checkHasId(UserSegment userSegment) {
|
3132 | 3223 | }
|
3133 | 3224 | }
|
3134 | 3225 |
|
| 3226 | + private static void checkHasId(ContentTag contentTag) { |
| 3227 | + if (contentTag.getId() == null) { |
| 3228 | + throw new IllegalArgumentException("Content Tag requires id"); |
| 3229 | + } |
| 3230 | + } |
| 3231 | + |
| 3232 | + private static void checkHasName(ContentTag contentTag) { |
| 3233 | + if (contentTag.getName() == null || contentTag.getName().trim().isEmpty()) { |
| 3234 | + throw new IllegalArgumentException("Content Tag requires name"); |
| 3235 | + } |
| 3236 | + } |
| 3237 | + |
3135 | 3238 | private static void checkHasToken(Attachment.Upload upload) {
|
3136 | 3239 | if (upload.getToken() == null) {
|
3137 | 3240 | throw new IllegalArgumentException("Upload requires token");
|
|
0 commit comments