diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..5e8276638 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Introduction of spotless: https://github.com/cloudbees-oss/zendesk-java-client/pull/602 +b10b28ab9bb2f269795376b7465f8676ed4292db diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 68a799752..cd0295f3c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,4 +5,4 @@ # @cloudbees/team-support will be requested for # review when someone opens a pull request. -* @aheritier @johnou @duemir +* @aheritier @johnou @duemir @PierreBtz diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4c2239364..d06e1afd9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,4 +8,9 @@ updates: - package-ecosystem: "maven" # See documentation for possible values directory: "/" # Location of package manifests schedule: - interval: "weekly" + interval: "daily" + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci-master.yml similarity index 84% rename from .github/workflows/ci.yml rename to .github/workflows/ci-master.yml index a170ab410..c66e52e82 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci-master.yml @@ -6,8 +6,6 @@ name: Java CI with Maven on: push: branches: [ master ] - pull_request: - branches: [ master ] jobs: build: @@ -15,16 +13,17 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 with: # Disabling shallow clone is recommended for improving relevancy of reporting with SonarCloud fetch-depth: 0 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 + - name: Set up JDK 11 + uses: actions/setup-java@v5 with: - java-version: 1.8 + distribution: 'temurin' + java-version: 11 - name: Build with Maven - run: mvn --show-version --no-transfer-progress verify --file pom.xml -Pcoverage + run: mvn --show-version --no-transfer-progress verify --file pom.xml -Pcoverage,cloudbees-oss-release -Dgpg.skip=true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ZENDESK_JAVA_CLIENT_TEST_URL: ${{ secrets.ZENDESK_JAVA_CLIENT_TEST_URL }} @@ -33,10 +32,11 @@ jobs: ZENDESK_JAVA_CLIENT_TEST_TOKEN: ${{ secrets.ZENDESK_JAVA_CLIENT_TEST_TOKEN }} ZENDESK_JAVA_CLIENT_TEST_REQUESTER_EMAIL: ${{ secrets.ZENDESK_JAVA_CLIENT_TEST_REQUESTER_EMAIL }} ZENDESK_JAVA_CLIENT_TEST_REQUESTER_NAME: ${{ secrets.ZENDESK_JAVA_CLIENT_TEST_REQUESTER_NAME }} - - name: Set up JDK 11 - uses: actions/setup-java@v1 + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: - java-version: 11 + distribution: 'temurin' + java-version: 17 - name: Analyze with SonarQube run: mvn --show-version --no-transfer-progress sonar:sonar --file pom.xml -Dsonar.organization=cloudbees -Dsonar.host.url=${SONAR_URL} -Dsonar.login=${SONAR_TOKEN} env: diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml new file mode 100644 index 000000000..46d357674 --- /dev/null +++ b/.github/workflows/ci-pr.yml @@ -0,0 +1,28 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven for PRs + +on: + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + with: + # Disabling shallow clone is recommended for improving relevancy of reporting with SonarCloud + fetch-depth: 0 + - name: Set up JDK 11 + uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: 11 + - name: Build with Maven + run: mvn --show-version --no-transfer-progress verify --file pom.xml -Pcoverage,cloudbees-oss-release -Dgpg.skip=true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/close-stale-items.yml b/.github/workflows/close-stale-items.yml new file mode 100644 index 000000000..1a47e7c94 --- /dev/null +++ b/.github/workflows/close-stale-items.yml @@ -0,0 +1,16 @@ +name: "Close stale issues and PRs" +on: + schedule: + - cron: "0 8 * * *" + +jobs: + default: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v10 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + only-pr-labels: 'needs-fix' + exempt-draft-pr: true + stale-issue-message: 'This issue/PR is stale because it has been opened 60 days with no activity. Remove stale label or comment or this will be closed in 7 days' + diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..2fa8e7332 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,71 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '30 8 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v4 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 7292bee51..9e8df84b7 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -11,6 +11,6 @@ jobs: runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "master" - - uses: release-drafter/release-drafter@v5.11.0 + - uses: release-drafter/release-drafter@v6.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f5c99a7f6..000000000 --- a/.travis.yml +++ /dev/null @@ -1 +0,0 @@ -language: java \ No newline at end of file diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index 76e9a58d6..d3a75ac08 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -26,8 +26,9 @@ Unless code has shipped to users, the initial value of the `serialVersionUID` fi == Indentation -1. **Use spaces.** Tabs are banned. -2. **Java blocks are 4 spaces.** JavaScript blocks as for Java. **XML nesting is 2 spaces** +The project relies on [https://github.com/diffplug/spotless/tree/main/plugin-maven](the Spotless Maven Plugin) to manage its formatting. +The [https://github.com/google/google-java-format](google-java-format) project was chosen, check it, it contains links to plugins for various IDEs. +You can also run the formatter from command line: `mvn spotless:apply`. == Field Naming Conventions @@ -41,42 +42,6 @@ If you need more than three, you are likely doing something wrong and as such yo 6. It is acceptable to use `e` for the exception in a `try...catch` block. 7. You shall never use `l` (i.e. lower case `L`) as a variable name. -== Line Length - -To the greatest extent possible, please wrap lines to ensure that they do not exceed 120 characters. - -== Maven POM file layout - -* The `pom.xml` file shall use the sequencing of elements as defined by the `mvn tidy:pom` command (after any indenting fix-up). -* If you are introducing a property to the `pom.xml` the property must be used in at least two distinct places in the model or a comment justifying the use of a property shall be provided. -* If the `` is in the groupId `org.apache.maven.plugins` you shall omit the ``. -* All `` entries shall have an explicit version defined unless inherited from the parent. - -== Java code style - -=== Modifiers - -* For fields, the order is: - . `public` / `protected` / `private` - . `static` - . `final` - . `transient` - . `volatile` -* For methods, the order is: - . `public` / `protected` / `private` - . `abstract` - . `static` - . `final` - . `synchronized` - . `native` - . `strictfp` -* For classes, the order is: - . `public` / `protected` / `private` - . `abstract` - . `static` - . `final` - . `strictfp` - === Imports * For code in `src/main`: @@ -89,11 +54,6 @@ To the greatest extent possible, please wrap lines to ensure that they do not ex . `import static org.hamcrest.Matchers.*`, `import static org.junit.Assert.*`, `import static org.junit.Assume.*` are expressly encouraged and permitted. Any other `static` `*` imports are discouraged unless code readability is significantly enhanced and the import is restricted to a single class. -=== Annotation placement - -* Annotations on classes, interfaces, annotations, enums, methods, fields and local variables shall be on the lines immediately preceding the line where modifier(s) (e.g. `public` / `protected` / `private` / `final`, etc) would be appropriate. -* Annotations on method arguments shall, to the largest extent possible, be on the same line as the method argument (and, if present, before the `final` modifier) - === Javadoc * Each class shall have a Javadoc comment. @@ -138,31 +98,20 @@ public void setWidgetCount(int widgetCount) { The version shall be `FIXME` to indicate that the person merging the change should replace the `FIXME` with the next release version number. The fields and methods within a class/interface (but not nested classes) will be assumed to have the `@since` annotation of their class/interface unless a different `@since` annotation is present. -=== IDE Configuration - -* Eclipse, by and large the IDE defaults are acceptable with the following changes: -** Tab policy to `Spaces only` -** Indent statements within `switch` body -** Maximum line width `120` -** Line wrapping, ensure all to `wrap where necessary` -** Organize imports alphabetically, no grouping -* NetBeans, by and large the IDE defaults are acceptable with the following changes: -** Tabs and Indents -*** Change Right Margin to `120` -*** Indent case statements in switch -** Wrapping -*** Change all the `Never` values to `If Long` -*** Select the checkbox for Wrap After Assignment Operators -* IntelliJ, by and large the IDE defaults are acceptable with the following changes: -** Wrapping and Braces -*** Change `Do not wrap` to `Wrap if long` -*** Change `Do not force` to `Always` -** Javadoc -*** Disable generating `

` on empty lines -** Imports -*** Class count to use import with '*': `9999` -*** Names count to use static import with '*': `99999` -*** Import Layout -**** import all other imports -**** blank line -**** import static all other imports +== Integration Tests + +This library has a set of integration tests in the class `src/test/java/org/zendesk/client/v2/RealSmokeTest.java`. These tests are used to validate the API calls with a real Zendesk instance. The project never got access to a specific sandbox provided by @zendesk thus we are using the sandbox used by CloudBees. + +Some of these tests are sadly relying on some specific data of this instance (ex: https://github.com/cloudbees-oss/zendesk-java-client/blob/master/src/test/java/org/zendesk/client/v2/RealSmokeTest.java#L102-L103) and thus you cannot expect to execute all of them in a different instance. + +To execute these tests you have to pass several settings in the file `src/test/resources/zendesk.properties`: + +``` +url=#A ZENDESK SANDBOX URL# +username=#A EMAIL OF AN ACCOUNT HAVING ACCESS TO THE INSTANCE# +password=#THE PASSWORD OF THE ACCOUNT# +token=#A TOKEN TO ACCESS TO THE INSTANCE# +requester.email=#A EMAIL - can be like username - TO CREATE THE REPORTER# +requester.name=#A NAME FOR THE REPORTER# +``` + diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index d6c28779a..000000000 --- a/Jenkinsfile +++ /dev/null @@ -1,33 +0,0 @@ -// For Reference Only -pipeline { - environment { - ZENDESK_JAVA_CLIENT_TEST_URL = credentials('ZENDESK_JAVA_CLIENT_TEST_URL') - ZENDESK_JAVA_CLIENT_TEST_USERNAME = credentials('ZENDESK_JAVA_CLIENT_TEST_USERNAME') - ZENDESK_JAVA_CLIENT_TEST_PASSWORD = credentials('ZENDESK_JAVA_CLIENT_TEST_PASSWORD') - ZENDESK_JAVA_CLIENT_TEST_TOKEN = credentials('ZENDESK_JAVA_CLIENT_TEST_TOKEN') - ZENDESK_JAVA_CLIENT_TEST_REQUESTER_EMAIL = credentials('ZENDESK_JAVA_CLIENT_TEST_REQUESTER.EMAIL') - ZENDESK_JAVA_CLIENT_TEST_REQUESTER_NAME = credentials('ZENDESK_JAVA_CLIENT_TEST_REQUESTER.NAME') - } - agent { - label "standard" - } - stages { - stage("Build") { - steps { - withSonarQubeEnv('sonarcloud.io') { - withMaven( - mavenOpts: '-Xmx512m -Djava.awt.headless=true' - ) { - sh "mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent ${env.BRANCH_NAME == 'master' && readMavenPom().version.contains('-SNAPSHOT') ? 'deploy -DdeployAtEnd=true' : 'verify'} sonar:sonar -Dsonar.organization=cloudbees -Dsonar.branch.name=\"${env.BRANCH_NAME}\" -Dmaven.test.failure.ignore=true" - } - } - } - } - } - options { - // Keep 10 builds at a time - buildDiscarder(logRotator(numToKeepStr: '10')) - // Be sure that this build doesn't hang forever - timeout(time: 5, unit: 'MINUTES') - } -} diff --git a/README.md b/README.md index 4304ab1ad..a6c4bd8ea 100644 --- a/README.md +++ b/README.md @@ -33,66 +33,74 @@ all records have been fetched, so e.g. will iterate through *all* tickets. Most likely you will want to implement your own cut-off process to stop iterating when you have got enough data. -Mailing lists +Community ------------- * [Users list](https://groups.google.com/forum/#!forum/zendesk-java-client-users) +* [GitHub discussions](https://github.com/cloudbees-oss/zendesk-java-client/discussions) Status ------ Here is the status of the various API components: -* [Tickets](http://developer.zendesk.com/documentation/rest_api/tickets.html) ✓ -* [Ticket Audits](http://developer.zendesk.com/documentation/rest_api/ticket_audits.html) ✓ -* [Incremental Export](https://developer.zendesk.com/rest_api/docs/core/incremental_export) - Partial (tickets, users, organizations only) -* [Ticket Fields](http://developer.zendesk.com/documentation/rest_api/ticket_fields.html) ✓ -* [Ticket Import](http://developer.zendesk.com/documentation/rest_api/ticket_import.html) ✓ -* [Ticket Metrics](http://developer.zendesk.com/documentation/rest_api/ticket_metrics.html) ✓ -* [Ticket Forms](http://developer.zendesk.com/documentation/rest_api/ticket_forms.html) *getTicketForm() and getTicketForms()* -* [Views](http://developer.zendesk.com/documentation/rest_api/views.html) -* [Users](http://developer.zendesk.com/documentation/rest_api/users.html) ✓ - * [User Related Information](https://developer.zendesk.com/rest_api/docs/core/users#user-related-information) ✓ -* [User Fields](https://developer.zendesk.com/rest_api/docs/core/user_fields.html) - Partial - List User Fields (`getUserField()`) -* [Requests](http://developer.zendesk.com/documentation/rest_api/requests.html) ✓ -* [User Identities](http://developer.zendesk.com/documentation/rest_api/user_identities.html) ✓ -* [Groups](http://developer.zendesk.com/documentation/rest_api/groups.html) ✓ -* [Group Membership](http://developer.zendesk.com/documentation/rest_api/group_memberships.html) ✓ -* [Custom Agent Roles](http://developer.zendesk.com/documentation/rest_api/custom_roles.html) ✓ -* [Organizations](http://developer.zendesk.com/documentation/rest_api/organizations.html) ✓ *except for related info* -* [Search](http://developer.zendesk.com/documentation/rest_api/search.html) ✓ *except for topics and sort ordering* -* [Tags](http://developer.zendesk.com/documentation/rest_api/tags.html) +* [Tickets](https://developer.zendesk.com/api-reference/ticketing/tickets/tickets/) ✓ +* [Ticket Audits](https://developer.zendesk.com/api-reference/ticketing/tickets/ticket_audits/) ✓ +* [Incremental Export](https://developer.zendesk.com/api-reference/ticketing/ticket-management/incremental_exports/) - Partial (tickets, users, organizations only) +* [Ticket Fields](https://developer.zendesk.com/api-reference/ticketing/tickets/ticket_fields/) ✓ +* [Ticket Import](https://developer.zendesk.com/api-reference/ticketing/tickets/ticket_import/) ✓ +* [Ticket Metrics](https://developer.zendesk.com/api-reference/ticketing/tickets/ticket_metrics/) ✓ +* [Ticket Forms](https://developer.zendesk.com/api-reference/ticketing/tickets/ticket_forms/) *getTicketForm() and getTicketForms()* +* [Views](https://developer.zendesk.com/api-reference/ticketing/business-rules/views/) +* [Users](https://developer.zendesk.com/api-reference/ticketing/users/users/) ✓ + * [User Related Information](https://developer.zendesk.com/api-reference/ticketing/users/users/#show-user-related-information) ✓ +* [User Fields](https://developer.zendesk.com/api-reference/ticketing/users/user_fields/) - Partial - List User Fields (`getUserField()`) +* [Requests](https://developer.zendesk.com/api-reference/ticketing/tickets/ticket-requests/) ✓ +* [User Identities](https://developer.zendesk.com/api-reference/ticketing/users/user_identities/) ✓ +* [Groups](https://developer.zendesk.com/api-reference/ticketing/groups/groups/) ✓ +* [Group Membership](https://developer.zendesk.com/api-reference/ticketing/groups/group_memberships/) ✓ +* [Custom Agent Roles](https://developer.zendesk.com/api-reference/ticketing/account-configuration/custom_roles/) ✓ +* [Organizations](https://developer.zendesk.com/api-reference/ticketing/organizations/organizations/) ✓ *except for related info* +* [Search](https://developer.zendesk.com/api-reference/ticketing/ticket-management/search/) ✓ *except for topics and sort ordering* +* [Tags](https://developer.zendesk.com/api-reference/ticketing/ticket-management/tags/) * [Forums](http://developer.zendesk.com/documentation/rest_api/forums.html) ✓ * [Forum Subscriptions](http://developer.zendesk.com/documentation/rest_api/forum_subscriptions.html) * [Categories](http://developer.zendesk.com/documentation/rest_api/categories.html) -* [Topics](http://developer.zendesk.com/documentation/rest_api/topics.html) ✓ -* [Topic Comments](http://developer.zendesk.com/documentation/rest_api/topic_comments.html) -* [Topic Subscriptions](http://developer.zendesk.com/documentation/rest_api/topic_subscriptions.html) +* [Topics](https://developer.zendesk.com/api-reference/help_center/help-center-api/topics/) ✓ +* [Post Comments](https://developer.zendesk.com/api-reference/help_center/help-center-api/post_comments/) +* [Content Subscriptions](https://developer.zendesk.com/api-reference/help_center/help-center-api/content_subscriptions/) * [Help Center Categories](https://developer.zendesk.com/rest_api/docs/help_center/categories) ✓ * [Help Center Sections](https://developer.zendesk.com/rest_api/docs/help_center/sections) ✓ -* [Help Center Articles](https://developer.zendesk.com/rest_api/docs/help_center/articles) ✓ -* [Help Center Translations](https://developer.zendesk.com/rest_api/docs/help_center/translations) - Partial (List Translations, Update Translation, Delete Translation) +* [Help Center Articles](https://developer.zendesk.com/api-reference/help_center/help-center-api/articles/) ✓ +* [Help Center Translations](https://developer.zendesk.com/api-reference/help_center/help-center-api/translations/) - Partial (List Translations, Update Translation, Delete Translation) * [Help Center Subscriptions](https://developer.zendesk.com/rest_api/docs/help_center/subscriptions) * [Help Center Management Permission Groups](https://developer.zendesk.com/rest_api/docs/help_center/permission_groups) * [Help Center User Segments](https://developer.zendesk.com/rest_api/docs/help_center/user_segments) -* [Topic Votes](http://developer.zendesk.com/documentation/rest_api/topic_votes.html) -* [Account Settings](http://developer.zendesk.com/documentation/rest_api/account_settings.html) -* [Activity Stream](http://developer.zendesk.com/documentation/rest_api/activity_stream.html) -* [Attachments](http://developer.zendesk.com/documentation/rest_api/attachments.html) ✓ -* [Autocompletion](http://developer.zendesk.com/documentation/rest_api/autocomplete.html) -* [Automations](http://developer.zendesk.com/documentation/rest_api/automations.html) ✓ -* [Job Statuses](http://developer.zendesk.com/documentation/rest_api/job_statuses.html) -* [Locales](http://developer.zendesk.com/documentation/rest_api/locales.html) -* [Macros](http://developer.zendesk.com/documentation/rest_api/macros.html) ✓ *except for restrictions* -* [Restrictions and Responsibilities](http://developer.zendesk.com/documentation/rest_api/restrictions.html) -* [Satisfaction Ratings](http://developer.zendesk.com/documentation/rest_api/satisfaction_ratings.html) ✓ -* [Sharing Agreements](http://developer.zendesk.com/documentation/rest_api/sharing_agreements.html) -* [Suspended Tickets](http://developer.zendesk.com/documentation/rest_api/suspended_tickets.html) -* [Triggers](http://developer.zendesk.com/documentation/rest_api/triggers.html) ✓ +* [Topic Votes](https://developer.zendesk.com/api-reference/help_center/help-center-api/votes/) +* [Account Settings](https://developer.zendesk.com/api-reference/ticketing/account-configuration/account_settings/) +* [Activity Stream](https://developer.zendesk.com/api-reference/ticketing/tickets/activity_stream/) +* [Attachments](https://developer.zendesk.com/api-reference/ticketing/tickets/ticket-attachments/) ✓ +* [Automations](https://developer.zendesk.com/api-reference/ticketing/business-rules/automations/) ✓ +* [Job Statuses](https://developer.zendesk.com/api-reference/ticketing/ticket-management/job_statuses/) ✓ +* [Locales](https://developer.zendesk.com/api-reference/ticketing/account-configuration/locales/) - Partial (List Locales) +* [Macros](https://developer.zendesk.com/api-reference/ticketing/business-rules/macros/) ✓ *except for restrictions* +* [Satisfaction Ratings](https://developer.zendesk.com/api-reference/ticketing/ticket-management/satisfaction_ratings/) ✓ +* [Sharing Agreements](https://developer.zendesk.com/api-reference/ticketing/account-configuration/sharing_agreements/) +* [Suspended Tickets](https://developer.zendesk.com/api-reference/ticketing/tickets/suspended_tickets/) +* [Triggers](https://developer.zendesk.com/api-reference/ticketing/business-rules/triggers/) ✓ + +JDK Support +------ + +The current version of this project supports Java 11 and above. +It is built on Java 11 and Java 17. +The release is built using Java 11. + +Latest version supporting Java 8: 0.24.3 (https://github.com/cloudbees-oss/zendesk-java-client/releases/tag/zendesk-java-client-0.24.3). History ------- * See [releases](https://github.com/cloudbees/zendesk-java-client/releases) - [zd]: http://zendesk.com + [zd]: https://zendesk.com diff --git a/pom.xml b/pom.xml index ba0f51eba..f00396362 100644 --- a/pom.xml +++ b/pom.xml @@ -1,3 +1,4 @@ + - 4.0.0 com.cloudbees cloudbees-oss-parent - 9 + 2026.1.1 com.cloudbees.thirdparty zendesk-java-client - 0.14.2-SNAPSHOT + 1.4.1-SNAPSHOT zendesk-java-client Java client for the Zendesk API @@ -74,8 +74,8 @@ scm:git:git://github.com/cloudbees-oss/zendesk-java-client.git scm:git:git@github.com:cloudbees-oss/zendesk-java-client.git - http://github.com/cloudbees-oss/zendesk-java-client/tree/master/ HEAD + http://github.com/cloudbees-oss/zendesk-java-client/tree/master/ @@ -88,9 +88,23 @@ https://app.codeship.com/projects/302087 + + + cloudbees-nexus-staging + Nexus Release Repository + https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/ + + + cloudbees-nexus-snapshots + Sonatype Nexus Snapshots + https://ossrh-staging-api.central.sonatype.com/content/repositories/snapshots/ + + + - 1.8 - 1.8 + 11 + 11 + 2.46.1 @@ -98,7 +112,14 @@ com.fasterxml.jackson jackson-bom - 2.12.2 + 2.20.0 + pom + import + + + io.netty + netty-bom + 4.2.9.Final pom import @@ -109,12 +130,12 @@ org.slf4j slf4j-api - 1.7.30 + 2.0.17 org.asynchttpclient async-http-client - 2.12.2 + 3.0.3 com.fasterxml.jackson.core @@ -144,32 +165,32 @@ org.slf4j slf4j-simple - 1.7.30 + 2.0.17 test - com.github.tomakehurst - wiremock - 2.27.2 - test + com.github.tomakehurst + wiremock + 2.27.2 + test org.assertj assertj-core - 3.19.0 + 3.27.6 test org.apache.commons commons-text - 1.9 + 1.14.0 test org.awaitility awaitility - 4.0.3 + 4.3.0 test @@ -179,7 +200,6 @@ maven-enforcer-plugin - 3.0.0-M3 org.apache.maven.plugins @@ -196,6 +216,18 @@ true + + + org.codehaus.mojo + animal-sniffer-enforcer-rule + 1.26 + + + org.codehaus.mojo + extra-enforcer-rules + 1.11.0 + + check-java-compat @@ -223,28 +255,77 @@ - 1.8 + 11 - - - org.codehaus.mojo - animal-sniffer-enforcer-rule - 1.20 - - - org.codehaus.mojo - extra-enforcer-rules - 1.3 - - + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + + + org.zendesk.client.v2 + + + + + + com.diffplug.spotless + spotless-maven-plugin + ${spotless.version} + + + + @formatter:off + @formatter:on + + + + + + + + + false + + + + + + + check + + + + + + org.apache.maven.plugins + maven-release-plugin + + spotless:apply verify + spotless:apply verify + + + + org.sonatype.plugins + nexus-staging-maven-plugin + true + + cloudbees-nexus-snapshots + https://ossrh-staging-api.central.sonatype.com/ + true + - + coverage @@ -253,7 +334,7 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.13 prepare-agent @@ -272,6 +353,6 @@ - + diff --git a/src/main/java/org/zendesk/client/v2/DefaultUserAgent.java b/src/main/java/org/zendesk/client/v2/DefaultUserAgent.java new file mode 100644 index 000000000..bd6f29ad6 --- /dev/null +++ b/src/main/java/org/zendesk/client/v2/DefaultUserAgent.java @@ -0,0 +1,24 @@ +package org.zendesk.client.v2; + +import java.util.regex.Pattern; + +public class DefaultUserAgent { + private static final Pattern VERSION_PATTERN = Pattern.compile("[\\w-.]+"); + private final String userAgent; + + public DefaultUserAgent() { + this(DefaultUserAgent.class.getPackage().getImplementationVersion()); + } + + public DefaultUserAgent(String version) { + StringBuilder sb = new StringBuilder("zendesk-java-client"); + if (version != null && VERSION_PATTERN.matcher(version).matches()) { + sb.append('/').append(version); + } + this.userAgent = sb.toString(); + } + + public String toString() { + return this.userAgent; + } +} diff --git a/src/main/java/org/zendesk/client/v2/FixedUri.java b/src/main/java/org/zendesk/client/v2/FixedUri.java index 004ef59cb..7af1da180 100644 --- a/src/main/java/org/zendesk/client/v2/FixedUri.java +++ b/src/main/java/org/zendesk/client/v2/FixedUri.java @@ -6,14 +6,14 @@ */ class FixedUri extends Uri { - private final String uri; + private final String uri; - FixedUri(String uri) { - this.uri = uri; - } + FixedUri(String uri) { + this.uri = uri; + } - @Override - public String toString() { - return uri; - } + @Override + public String toString() { + return uri; + } } diff --git a/src/main/java/org/zendesk/client/v2/TemplateUri.java b/src/main/java/org/zendesk/client/v2/TemplateUri.java index 81280bf38..f01c5b94f 100644 --- a/src/main/java/org/zendesk/client/v2/TemplateUri.java +++ b/src/main/java/org/zendesk/client/v2/TemplateUri.java @@ -1,7 +1,6 @@ package org.zendesk.client.v2; import com.damnhandy.uri.template.UriTemplate; - import java.util.Date; import java.util.Map; @@ -11,33 +10,33 @@ */ class TemplateUri extends Uri { - private final UriTemplate uri; + private final UriTemplate uri; - public TemplateUri(UriTemplate uri) { - this.uri = uri; - } + public TemplateUri(UriTemplate uri) { + this.uri = uri; + } - public TemplateUri(String uri) { - this.uri = UriTemplate.fromTemplate(uri); - } + public TemplateUri(String uri) { + this.uri = UriTemplate.fromTemplate(uri); + } - public TemplateUri set(Map values) { - uri.set(values); - return this; - } + public TemplateUri set(Map values) { + uri.set(values); + return this; + } - public TemplateUri set(String variableName, Date value) { - uri.set(variableName, value); - return this; - } + public TemplateUri set(String variableName, Date value) { + uri.set(variableName, value); + return this; + } - public TemplateUri set(String variableName, Object value) { - uri.set(variableName, value); - return this; - } + public TemplateUri set(String variableName, Object value) { + uri.set(variableName, value); + return this; + } - @Override - public String toString() { - return uri.expand(); - } + @Override + public String toString() { + return uri.expand(); + } } diff --git a/src/main/java/org/zendesk/client/v2/Uri.java b/src/main/java/org/zendesk/client/v2/Uri.java index aa186304a..70605bca3 100644 --- a/src/main/java/org/zendesk/client/v2/Uri.java +++ b/src/main/java/org/zendesk/client/v2/Uri.java @@ -5,6 +5,6 @@ * @since 05/04/2013 10:05 */ abstract class Uri { - @Override - public abstract String toString(); + @Override + public abstract String toString(); } diff --git a/src/main/java/org/zendesk/client/v2/Zendesk.java b/src/main/java/org/zendesk/client/v2/Zendesk.java index 1de73d221..968bed1e5 100644 --- a/src/main/java/org/zendesk/client/v2/Zendesk.java +++ b/src/main/java/org/zendesk/client/v2/Zendesk.java @@ -6,11 +6,33 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.util.StdDateFormat; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; import org.asynchttpclient.AsyncCompletionHandler; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.DefaultAsyncHttpClient; +import org.asynchttpclient.DefaultAsyncHttpClientConfig; import org.asynchttpclient.ListenableFuture; import org.asynchttpclient.Realm; import org.asynchttpclient.Request; @@ -18,6 +40,7 @@ import org.asynchttpclient.Response; import org.asynchttpclient.request.body.multipart.FilePart; import org.asynchttpclient.request.body.multipart.StringPart; +import org.jetbrains.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zendesk.client.v2.model.AgentRole; @@ -27,13 +50,16 @@ import org.zendesk.client.v2.model.Brand; import org.zendesk.client.v2.model.Comment; import org.zendesk.client.v2.model.ComplianceDeletionStatus; +import org.zendesk.client.v2.model.CustomTicketStatus; import org.zendesk.client.v2.model.DeletedTicket; import org.zendesk.client.v2.model.Field; import org.zendesk.client.v2.model.Forum; import org.zendesk.client.v2.model.Group; import org.zendesk.client.v2.model.GroupMembership; import org.zendesk.client.v2.model.Identity; +import org.zendesk.client.v2.model.JiraLink; import org.zendesk.client.v2.model.JobStatus; +import org.zendesk.client.v2.model.Locale; import org.zendesk.client.v2.model.Macro; import org.zendesk.client.v2.model.Metric; import org.zendesk.client.v2.model.Organization; @@ -45,24 +71,30 @@ import org.zendesk.client.v2.model.Status; import org.zendesk.client.v2.model.SuspendedTicket; import org.zendesk.client.v2.model.Ticket; +import org.zendesk.client.v2.model.TicketCount; import org.zendesk.client.v2.model.TicketForm; import org.zendesk.client.v2.model.TicketImport; +import org.zendesk.client.v2.model.TicketPage; import org.zendesk.client.v2.model.TicketResult; +import org.zendesk.client.v2.model.TimeZone; import org.zendesk.client.v2.model.Topic; import org.zendesk.client.v2.model.Trigger; import org.zendesk.client.v2.model.TwitterMonitor; import org.zendesk.client.v2.model.User; import org.zendesk.client.v2.model.UserField; import org.zendesk.client.v2.model.UserRelatedInfo; +import org.zendesk.client.v2.model.View; import org.zendesk.client.v2.model.dynamic.DynamicContentItem; import org.zendesk.client.v2.model.dynamic.DynamicContentItemVariant; 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.ContentTag; +import org.zendesk.client.v2.model.hc.Locales; +import org.zendesk.client.v2.model.hc.PermissionGroup; import org.zendesk.client.v2.model.hc.Section; import org.zendesk.client.v2.model.hc.Subscription; 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.schedules.Holiday; import org.zendesk.client.v2.model.schedules.Schedule; @@ -73,2972 +105,4303 @@ import org.zendesk.client.v2.model.targets.Target; import org.zendesk.client.v2.model.targets.TwitterTarget; import org.zendesk.client.v2.model.targets.UrlTarget; - -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; +import org.zendesk.client.v2.model.views.ExecutedViewPage; +import org.zendesk.client.v2.model.views.ViewRow; /** * @author stephenc * @since 04/04/2013 13:08 */ public class Zendesk implements Closeable { - private static final String JSON = "application/json; charset=UTF-8"; - private final boolean closeClient; - private final AsyncHttpClient client; - private final Realm realm; - private final String url; - private final String oauthToken; - private final Map headers; - private final ObjectMapper mapper; - private final Logger logger; - private boolean closed = false; - private static final Map> searchResultTypes = searchResultTypes(); - private static final Map> targetTypes = targetTypes(); - - private static Map> searchResultTypes() { - Map> result = new HashMap<>(); - result.put("ticket", Ticket.class); - result.put("user", User.class); - result.put("group", Group.class); - result.put("organization", Organization.class); - result.put("topic", Topic.class); - result.put("article", Article.class); - return Collections.unmodifiableMap(result); - } - - private static Map> targetTypes() { - Map> result = new HashMap<>(); - result.put("url_target", UrlTarget.class); - result.put("email_target",EmailTarget.class); - result.put("basecamp_target", BasecampTarget.class); - result.put("campfire_target", CampfireTarget.class); - result.put("pivotal_target", PivotalTarget.class); - result.put("twitter_target", TwitterTarget.class); - - // TODO: Implement other Target types - //result.put("clickatell_target", ClickatellTarget.class); - //result.put("flowdock_target", FlowdockTarget.class); - //result.put("get_satisfaction_target", GetSatisfactionTarget.class); - //result.put("yammer_target", YammerTarget.class); - - return Collections.unmodifiableMap(result); - } - - private Zendesk(AsyncHttpClient client, String url, String username, String password, Map headers) { - this.logger = LoggerFactory.getLogger(Zendesk.class); - this.closeClient = client == null; - this.oauthToken = null; - this.client = client == null ? new DefaultAsyncHttpClient() : client; - this.url = url.endsWith("/") ? url + "api/v2" : url + "/api/v2"; - if (username != null) { - this.realm = new Realm.Builder(username, password) - .setScheme(Realm.AuthScheme.BASIC) - .setUsePreemptiveAuth(true) - .build(); - } else { - if (password != null) { - throw new IllegalStateException("Cannot specify token or password without specifying username"); - } - this.realm = null; - } - this.headers = Collections.unmodifiableMap(headers); - this.mapper = createMapper(); - } - + private static final String JSON = "application/json; charset=UTF-8"; + private static final String USER_AGENT_HEADER = "User-Agent"; + private static final DefaultAsyncHttpClientConfig DEFAULT_ASYNC_HTTP_CLIENT_CONFIG = + new DefaultAsyncHttpClientConfig.Builder().setFollowRedirect(true).build(); + private final boolean closeClient; + private final AsyncHttpClient client; + private final Realm realm; + private final String url; + private final String oauthToken; + private final Map headers; + private final int cbpPageSize; + private final ObjectMapper mapper; + private final Logger logger; + private boolean closed = false; + private static final Map> searchResultTypes = + searchResultTypes(); + private static final Map> targetTypes = targetTypes(); + + private static Map> searchResultTypes() { + Map> result = new HashMap<>(); + result.put("ticket", Ticket.class); + result.put("user", User.class); + result.put("group", Group.class); + result.put("organization", Organization.class); + result.put("topic", Topic.class); + result.put("article", Article.class); + return Collections.unmodifiableMap(result); + } - private Zendesk(AsyncHttpClient client, String url, String oauthToken, Map headers) { - this.logger = LoggerFactory.getLogger(Zendesk.class); - this.closeClient = client == null; - this.realm = null; - this.client = client == null ? new DefaultAsyncHttpClient() : client; - this.url = url.endsWith("/") ? url + "api/v2" : url + "/api/v2"; - if (oauthToken != null) { - this.oauthToken = oauthToken; - } else { - throw new IllegalStateException("Cannot specify token or password without specifying username"); - } - this.headers = Collections.unmodifiableMap(headers); + private static Map> targetTypes() { + Map> result = new HashMap<>(); + result.put("url_target", UrlTarget.class); + result.put("email_target", EmailTarget.class); + result.put("basecamp_target", BasecampTarget.class); + result.put("campfire_target", CampfireTarget.class); + result.put("pivotal_target", PivotalTarget.class); + result.put("twitter_target", TwitterTarget.class); + + // TODO: Implement other Target types + // result.put("clickatell_target", ClickatellTarget.class); + // result.put("flowdock_target", FlowdockTarget.class); + // result.put("get_satisfaction_target", GetSatisfactionTarget.class); + // result.put("yammer_target", YammerTarget.class); + + return Collections.unmodifiableMap(result); + } - this.mapper = createMapper(); - } + private Zendesk( + AsyncHttpClient client, + String url, + String username, + String password, + Map headers, + int cbpPageSize, + Function objectMapperCustomizer) { + this.logger = LoggerFactory.getLogger(Zendesk.class); + this.closeClient = client == null; + this.oauthToken = null; + this.client = + client == null ? new DefaultAsyncHttpClient(DEFAULT_ASYNC_HTTP_CLIENT_CONFIG) : client; + this.url = url.endsWith("/") ? url + "api/v2" : url + "/api/v2"; + if (username != null) { + this.realm = + new Realm.Builder(username, password) + .setScheme(Realm.AuthScheme.BASIC) + .setUsePreemptiveAuth(true) + .build(); + } else { + if (password != null) { + throw new IllegalStateException( + "Cannot specify token or password without specifying username"); + } + this.realm = null; + } + headers.putIfAbsent(USER_AGENT_HEADER, new DefaultUserAgent().toString()); + this.headers = Collections.unmodifiableMap(headers); + this.cbpPageSize = cbpPageSize; + this.mapper = createMapper(objectMapperCustomizer); + } + private Zendesk( + AsyncHttpClient client, + String url, + String oauthToken, + Map headers, + int cbpPageSize, + Function objectMapperCustomizer) { + this.logger = LoggerFactory.getLogger(Zendesk.class); + this.closeClient = client == null; + this.realm = null; + this.client = + client == null ? new DefaultAsyncHttpClient(DEFAULT_ASYNC_HTTP_CLIENT_CONFIG) : client; + this.url = url.endsWith("/") ? url + "api/v2" : url + "/api/v2"; + if (oauthToken != null) { + this.oauthToken = oauthToken; + } else { + throw new IllegalStateException( + "Cannot specify token or password without specifying username"); + } + headers.putIfAbsent(USER_AGENT_HEADER, new DefaultUserAgent().toString()); + this.headers = Collections.unmodifiableMap(headers); + this.cbpPageSize = cbpPageSize; + this.mapper = createMapper(objectMapperCustomizer); + } - ////////////////////////////////////////////////////////////////////// - // Closeable interface methods - ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + // Closeable interface methods + ////////////////////////////////////////////////////////////////////// - public boolean isClosed() { - return closed || client.isClosed(); - } + public boolean isClosed() { + return closed || client.isClosed(); + } - public void close() { - if (closeClient && !client.isClosed()) { - try { - client.close(); - } catch (IOException e) { - logger.warn("Unexpected error on client close", e); - } - } - closed = true; + public void close() { + if (closeClient && !client.isClosed()) { + try { + client.close(); + } catch (IOException e) { + logger.warn("Unexpected error on client close", e); + } } + closed = true; + } - ////////////////////////////////////////////////////////////////////// - // Action methods - ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + // Action methods + ////////////////////////////////////////////////////////////////////// - public JobStatus getJobStatus(JobStatus status) { - return complete(getJobStatusAsync(status)); - } + public JobStatus getJobStatus(JobStatus status) { + return complete(getJobStatusAsync(status)); + } - public ListenableFuture getJobStatusAsync(JobStatus status) { - return submit(req("GET", tmpl("/job_statuses/{id}.json").set("id", status.getId())), - handleJobStatus()); - } + public ListenableFuture getJobStatusAsync(JobStatus status) { + return submit( + req("GET", tmpl("/job_statuses/{id}.json").set("id", status.getId())), handleJobStatus()); + } - public List getJobStatuses(List statuses) { - return complete(getJobStatusesAsync(statuses)); - } + public List getJobStatuses(List statuses) { + return complete(getJobStatusesAsync(statuses)); + } - public ListenableFuture> getJobStatusesAsync(List statuses) { - List ids = new ArrayList<>(statuses.size()); - for (JobStatus status : statuses) { - ids.add(status.getId()); - } - Class clazz = (Class) (Object) JobStatus.class; - return submit(req("GET", tmpl("/job_statuses/show_many.json{?ids}").set("ids", ids)), - handleList(clazz, "job_statuses")); + public ListenableFuture> getJobStatusesAsync(List statuses) { + List ids = new ArrayList<>(statuses.size()); + for (JobStatus status : statuses) { + ids.add(status.getId()); } + Class clazz = (Class) (Object) JobStatus.class; + return submit( + req("GET", tmpl("/job_statuses/show_many.json{?ids}").set("ids", ids)), + handleList(clazz, "job_statuses")); + } - public List getBrands(){ - return complete(submit(req("GET", cnst("/brands.json")), handleList(Brand.class, - "brands"))); - } + public Iterable getBrands() { + return new PagedIterable<>(cbp("/brands.json"), handleList(Brand.class, "brands")); + } - public TicketForm getTicketForm(long id) { - return complete(submit(req("GET", tmpl("/ticket_forms/{id}.json").set("id", id)), handle(TicketForm.class, - "ticket_form"))); - } + public TicketForm getTicketForm(long id) { + return complete( + submit( + req("GET", tmpl("/ticket_forms/{id}.json").set("id", id)), + handle(TicketForm.class, "ticket_form"))); + } - public List getTicketForms() { - return complete(submit(req("GET", cnst("/ticket_forms.json")), handleList(TicketForm.class, - "ticket_forms"))); - } + public List getTicketForms() { + return complete( + submit( + req("GET", cnst("/ticket_forms.json")), handleList(TicketForm.class, "ticket_forms"))); + } - public TicketForm createTicketForm(TicketForm ticketForm) { - return complete(submit(req("POST", cnst("/ticket_forms.json"), JSON, json( - Collections.singletonMap("ticket_form", ticketForm))), handle(TicketForm.class, "ticket_form"))); - } + public TicketForm createTicketForm(TicketForm ticketForm) { + return complete( + submit( + req( + "POST", + cnst("/ticket_forms.json"), + JSON, + json(Collections.singletonMap("ticket_form", ticketForm))), + handle(TicketForm.class, "ticket_form"))); + } - public Ticket importTicket(TicketImport ticketImport) { - return complete(submit(req("POST", cnst("/imports/tickets.json"), - JSON, json(Collections.singletonMap("ticket", ticketImport))), - handle(Ticket.class, "ticket"))); - } + public TicketForm updateTicketForm(TicketForm ticketForm) { + checkHasId(ticketForm); + return complete( + submit( + req( + "PUT", + tmpl("/ticket_forms/{id}.json").set("id", ticketForm.getId()), + JSON, + json(Collections.singletonMap("ticket_form", ticketForm))), + handle(TicketForm.class, "ticket_form"))); + } - public JobStatus importTickets(TicketImport... ticketImports) { - return importTickets(Arrays.asList(ticketImports)); - } + public void deleteTicketForm(TicketForm ticketForm) { + checkHasId(ticketForm); + deleteTicketForm(ticketForm.getId()); + } - public JobStatus importTickets(List ticketImports) { - return complete(importTicketsAsync(ticketImports)); - } + public void deleteTicketForm(long id) { + complete(submit(req("DELETE", tmpl("/ticket_forms/{id}.json").set("id", id)), handleStatus())); + } - public ListenableFuture importTicketsAsync(List ticketImports) { - return submit(req("POST", cnst("/imports/tickets/create_many.json"), JSON, json( - Collections.singletonMap("tickets", ticketImports))), handleJobStatus()); - } + public Ticket importTicket(TicketImport ticketImport) { + return complete( + submit( + req( + "POST", + cnst("/imports/tickets.json"), + JSON, + json(Collections.singletonMap("ticket", ticketImport))), + handle(Ticket.class, "ticket"))); + } - public Ticket getTicket(long id) { - return complete(submit(req("GET", tmpl("/tickets/{id}.json").set("id", id)), handle(Ticket.class, - "ticket"))); - } + public JobStatus importTickets(TicketImport... ticketImports) { + return importTickets(Arrays.asList(ticketImports)); + } - public List getTicketIncidents(long id) { - return complete(submit(req("GET", tmpl("/tickets/{id}/incidents.json").set("id", id)), - handleList(Ticket.class, "tickets"))); - } + public JobStatus importTickets(List ticketImports) { + return complete(importTicketsAsync(ticketImports)); + } - public List getTicketCollaborators(long id) { - return complete(submit(req("GET", tmpl("/tickets/{id}/collaborators.json").set("id", id)), - handleList(User.class, "users"))); - } + public ListenableFuture importTicketsAsync(List ticketImports) { + return submit( + req( + "POST", + cnst("/imports/tickets/create_many.json"), + JSON, + json(Collections.singletonMap("tickets", ticketImports))), + handleJobStatus()); + } - /** - * https://developer.zendesk.com/rest_api/docs/support/tickets#list-deleted-tickets - */ - public Iterable getDeletedTickets() { - return new PagedIterable<>(cnst("/deleted_tickets.json"), handleList(DeletedTicket.class, "deleted_tickets")); - } + public TicketCount getTicketsCount() { + return complete( + submit(req("GET", cnst("/tickets/count.json")), handle(TicketCount.class, "count"))); + } - /** - * https://developer.zendesk.com/rest_api/docs/support/tickets#list-deleted-tickets - */ - public Iterable getDeletedTickets(String sortBy, SortOrder sortOrder) { - return new PagedIterable<>(tmpl("/deleted_tickets.json?sort_by={sortBy}&sort_order={sortOrder}") - .set("sortBy", sortBy) - .set("sortOrder", sortOrder.getQueryParameter()), - handleList(DeletedTicket.class, "deleted_tickets")); - } + public TicketCount getTicketsCountForOrganization(long id) { + return complete( + submit( + req("GET", tmpl("/organizations/{id}/tickets/count.json").set("id", id)), + handle(TicketCount.class, "count"))); + } - public void deleteTicket(Ticket ticket) { - checkHasId(ticket); - deleteTicket(ticket.getId()); - } + public TicketCount getCcdTicketsCountForUser(long id) { + return complete( + submit( + req("GET", tmpl("/users/{id}/tickets/ccd/count.json").set("id", id)), + handle(TicketCount.class, "count"))); + } - public void deleteTicket(long id) { - complete(submit(req("DELETE", tmpl("/tickets/{id}.json").set("id", id)), handleStatus())); - } + public TicketCount getAssignedTicketsCountForUser(long id) { + return complete( + submit( + req("GET", tmpl("/users/{id}/tickets/assigned/count.json").set("id", id)), + handle(TicketCount.class, "count"))); + } - public JobStatus permanentlyDeleteTicket(Ticket ticket) { - checkHasId(ticket); - return permanentlyDeleteTicket(ticket.getId()); - } + public Ticket getTicket(long id) { + return complete( + submit( + req("GET", tmpl("/tickets/{id}.json").set("id", id)), handle(Ticket.class, "ticket"))); + } - public JobStatus permanentlyDeleteTicket(long id) { - return complete(submit( - req("DELETE", tmpl("/deleted_tickets/{id}.json").set("id", id)), - handleJobStatus()) - ); - } + public Iterable getTicketIncidents(long id) { + return new PagedIterable<>( + cbp("/tickets/{id}/incidents.json").set("id", id), handleList(Ticket.class, "tickets")); + } - public ListenableFuture queueCreateTicketAsync(Ticket ticket) { - return submit(req("POST", cnst("/tickets.json?async=true"), - JSON, json(Collections.singletonMap("ticket", ticket))), - handleJobStatus()); - } + public List getTicketCollaborators(long id) { + return complete( + submit( + req("GET", tmpl("/tickets/{id}/collaborators.json").set("id", id)), + handleList(User.class, "users"))); + } - public ListenableFuture createTicketAsync(Ticket ticket) { - return submit(req("POST", cnst("/tickets.json"), - JSON, json(Collections.singletonMap("ticket", ticket))), - handle(Ticket.class, "ticket")); - } + /** https://developer.zendesk.com/rest_api/docs/support/tickets#list-deleted-tickets */ + public Iterable getDeletedTickets() { + return new PagedIterable<>( + cbp("/deleted_tickets.json"), handleList(DeletedTicket.class, "deleted_tickets")); + } - public Ticket createTicket(Ticket ticket) { - return complete(createTicketAsync(ticket)); - } + /** https://developer.zendesk.com/rest_api/docs/support/tickets#list-deleted-tickets */ + public Iterable getDeletedTickets(String sortBy, SortOrder sortOrder) { + return new PagedIterable<>( + tmpl("/deleted_tickets.json?sort_by={sortBy}&sort_order={sortOrder}") + .set("sortBy", sortBy) + .set("sortOrder", sortOrder.getQueryParameter()), + handleList(DeletedTicket.class, "deleted_tickets")); + } - public JobStatus createTickets(Ticket... tickets) { - return createTickets(Arrays.asList(tickets)); - } + public void deleteTicket(Ticket ticket) { + checkHasId(ticket); + deleteTicket(ticket.getId()); + } - public JobStatus createTickets(List tickets) { - return complete(createTicketsAsync(tickets)); - } + public void deleteTicket(long id) { + complete(submit(req("DELETE", tmpl("/tickets/{id}.json").set("id", id)), handleStatus())); + } - public ListenableFuture createTicketsAsync(List tickets) { - return submit(req("POST", cnst("/tickets/create_many.json"), JSON, json( - Collections.singletonMap("tickets", tickets))), handleJobStatus()); - } + public JobStatus permanentlyDeleteTicket(Ticket ticket) { + checkHasId(ticket); + return permanentlyDeleteTicket(ticket.getId()); + } - public Ticket updateTicket(Ticket ticket) { - checkHasId(ticket); - return complete(submit(req("PUT", tmpl("/tickets/{id}.json").set("id", ticket.getId()), - JSON, json(Collections.singletonMap("ticket", ticket))), - handle(Ticket.class, "ticket"))); - } + public JobStatus permanentlyDeleteTicket(long id) { + return complete( + submit(req("DELETE", tmpl("/deleted_tickets/{id}.json").set("id", id)), handleJobStatus())); + } - public JobStatus updateTickets(Ticket... tickets) { - return updateTickets(Arrays.asList(tickets)); - } + public ListenableFuture queueCreateTicketAsync(Ticket ticket) { + return submit( + req( + "POST", + cnst("/tickets.json?async=true"), + JSON, + json(Collections.singletonMap("ticket", ticket))), + handleJobStatus()); + } - public JobStatus updateTickets(List tickets) { - return complete(updateTicketsAsync(tickets)); - } + public ListenableFuture createTicketAsync(Ticket ticket) { + return submit( + req("POST", cnst("/tickets.json"), JSON, json(Collections.singletonMap("ticket", ticket))), + handle(Ticket.class, "ticket")); + } - public ListenableFuture updateTicketsAsync(List tickets) { - return submit(req("PUT", cnst("/tickets/update_many.json"), JSON, json( - Collections.singletonMap("tickets", tickets))), handleJobStatus()); - } + public Ticket createTicket(Ticket ticket) { + return complete(createTicketAsync(ticket)); + } - public void markTicketAsSpam(Ticket ticket) { - checkHasId(ticket); - markTicketAsSpam(ticket.getId()); - } + public JobStatus createTickets(Ticket... tickets) { + return createTickets(Arrays.asList(tickets)); + } - public void markTicketAsSpam(long id) { - complete(submit(req("PUT", tmpl("/tickets/{id}/mark_as_spam.json").set("id", id)), handleStatus())); - } + public JobStatus createTickets(List tickets) { + return complete(createTicketsAsync(tickets)); + } - public void deleteTickets(long id, long... ids) { - complete(submit(req("DELETE", tmpl("/tickets/destroy_many.json{?ids}").set("ids", idArray(id, ids))), - handleStatus())); - } + public ListenableFuture createTicketsAsync(List tickets) { + return submit( + req( + "POST", + cnst("/tickets/create_many.json"), + JSON, + json(Collections.singletonMap("tickets", tickets))), + handleJobStatus()); + } - public JobStatus permanentlyDeleteTickets(long id, long... ids) { - return complete( - submit( - req("DELETE", tmpl("/deleted_tickets/destroy_many.json{?ids}").set("ids", idArray(id, ids))), - handleJobStatus()) - ); - } + public Ticket updateTicket(Ticket ticket) { + checkHasId(ticket); + return complete( + submit( + req( + "PUT", + tmpl("/tickets/{id}.json").set("id", ticket.getId()), + JSON, + json(Collections.singletonMap("ticket", ticket))), + handle(Ticket.class, "ticket"))); + } - public Iterable getTickets() { - return new PagedIterable<>(cnst("/tickets.json"), handleList(Ticket.class, "tickets")); - } + public JobStatus updateTickets(Ticket... tickets) { + return updateTickets(Arrays.asList(tickets)); + } - /** - * @deprecated This API is no longer available from the vendor. Use the {@link #getTicketsFromSearch(String)} method instead - * @param ticketStatus - * @return - */ - @Deprecated - public Iterable getTicketsByStatus(Status... ticketStatus) { - return new PagedIterable<>(tmpl("/tickets.json{?status}").set("status", statusArray(ticketStatus)), - handleList(Ticket.class, "tickets")); - } + public JobStatus updateTickets(List tickets) { + return complete(updateTicketsAsync(tickets)); + } - public Iterable getTicketsByExternalId(String externalId, boolean includeArchived) { - Iterable results = - new PagedIterable<>(tmpl("/tickets.json{?external_id}").set("external_id", externalId), - handleList(Ticket.class, "tickets")); + public ListenableFuture updateTicketsAsync(List tickets) { + return submit( + req( + "PUT", + cnst("/tickets/update_many.json"), + JSON, + json(Collections.singletonMap("tickets", tickets))), + handleJobStatus()); + } - if (!includeArchived || results.iterator().hasNext()) { - return results; - } - return new PagedIterable<>( - tmpl("/search.json{?query}{&type}").set("query", "external_id:" + externalId).set("type", "ticket"), - handleList(Ticket.class, "results")); - } + public void markTicketAsSpam(Ticket ticket) { + checkHasId(ticket); + markTicketAsSpam(ticket.getId()); + } - public Iterable getTicketsByExternalId(String externalId) { - return getTicketsByExternalId(externalId, false); - } + public void markTicketAsSpam(long id) { + complete( + submit(req("PUT", tmpl("/tickets/{id}/mark_as_spam.json").set("id", id)), handleStatus())); + } - public Iterable getTicketsFromSearch(String searchTerm) { - return new PagedIterable<>(tmpl("/search.json{?query}").set("query", searchTerm + "+type:ticket"), - handleList(Ticket.class, "results")); - } + public JobStatus deleteTickets(long id, long... ids) { + return complete( + submit( + req("DELETE", tmpl("/tickets/destroy_many.json{?ids}").set("ids", idArray(id, ids))), + handleJobStatus())); + } - public Iterable

getArticleFromSearch(String searchTerm) { - return new PagedIterable<>(tmpl("/help_center/articles/search.json{?query}").set("query", searchTerm), - handleList(Article.class, "results")); - } + public JobStatus permanentlyDeleteTickets(long id, long... ids) { + return complete( + submit( + req( + "DELETE", + tmpl("/deleted_tickets/destroy_many.json{?ids}").set("ids", idArray(id, ids))), + handleJobStatus())); + } - public Iterable
getArticleFromSearch(String searchTerm, Long sectionId) { - return new PagedIterable<>(tmpl("/help_center/articles/search.json{?section,query}") - .set("query", searchTerm).set("section", sectionId), handleList(Article.class, "results")); - } + public Iterable getTickets() { + return new PagedIterable<>(cbp("/tickets.json"), handleList(Ticket.class, "tickets")); + } - public Iterable
getArticlesFromAnyLabels(List labels) { - return new PagedIterable<>(tmpl("/help_center/articles/search.json{?label_names}").set("label_names", labels), - handleList(Article.class, "results")); - } + /** + * @deprecated This API is no longer available from the vendor. Use the {@link + * #getTicketsFromSearch(String)} method instead + * @param ticketStatus + * @return + */ + @Deprecated + public Iterable getTicketsByStatus(Status... ticketStatus) { + return new PagedIterable<>( + tmpl("/tickets.json{?status}").set("status", statusArray(ticketStatus)), + handleList(Ticket.class, "tickets")); + } - public Iterable
getArticlesFromAllLabels(List labels) { - return new PagedIterable<>(tmpl("/help_center/en-us/articles.json{?label_names}").set("label_names", labels), - handleList(Article.class, "articles")); - } + public Iterable getTicketsByExternalId(String externalId, boolean includeArchived) { + Iterable results = + new PagedIterable<>( + tmpl("/tickets.json{?external_id}").set("external_id", externalId), + handleList(Ticket.class, "tickets")); - public List getAttachmentsFromArticle(Long articleID) { - return complete(submit(req("GET", tmpl("/help_center/articles/{id}/attachments.json").set("id", articleID)), - handleArticleAttachmentsList("article_attachments"))); + if (!includeArchived || results.iterator().hasNext()) { + return results; } + return new PagedIterable<>( + tmpl("/search.json{?query}{&type}") + .set("query", "external_id:" + externalId) + .set("type", "ticket"), + handleList(Ticket.class, "results")); + } - 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"))); - } + public Iterable getTicketsByExternalId(String externalId) { + return getTicketsByExternalId(externalId, false); + } - public Iterable getRecentTickets() { - return new PagedIterable<>(cnst("/tickets/recent.json"), handleList(Ticket.class, "tickets")); - } + public Iterable getTicketsFromSearch(String searchTerm) { + return new PagedIterable<>( + tmpl("/search.json{?query}").set("query", searchTerm + " type:ticket"), + handleList(Ticket.class, "results")); + } - /** - * https://developer.zendesk.com/rest_api/docs/support/incremental_export - */ - public Iterable getTicketsIncrementally(Date startTime) { - return new PagedIterable<>( - tmpl("/incremental/tickets.json{?start_time}").set("start_time", msToSeconds(startTime.getTime())), - handleIncrementalList(Ticket.class, "tickets")); - } + public Iterable getTicketFromSearchWithExport(String searchTerm) { + return getTicketFromSearchWithExport(searchTerm, cbpPageSize); + } - /** - * https://developer.zendesk.com/rest_api/docs/support/incremental_export - * - * @deprecated incremental export does not support an end_time parameter - */ - @Deprecated - public Iterable getTicketsIncrementally(Date startTime, Date endTime) { - return new PagedIterable<>( - tmpl("/incremental/tickets.json{?start_time,end_time}") - .set("start_time", msToSeconds(startTime.getTime())) - .set("end_time", msToSeconds(endTime.getTime())), - handleIncrementalList(Ticket.class, "tickets")); - } + public Iterable getTicketFromSearchWithExport(String searchTerm, int pageSize) { + return new PagedIterable<>( + tmpl(cbp("/search/export", true, pageSize).toString() + + "&filter[type]=ticket&query={query}") + .set("query", searchTerm + " type:ticket"), + handleList(Ticket.class, "results")); + } - public Iterable getOrganizationTickets(long organizationId) { - return new PagedIterable<>( - tmpl("/organizations/{organizationId}/tickets.json").set("organizationId", organizationId), - handleList(Ticket.class, "tickets")); - } + public Iterable
getArticleFromSearch(String searchTerm) { + return new PagedIterable<>( + tmpl("/help_center/articles/search.json{?query}").set("query", searchTerm), + handleList(Article.class, "results")); + } - public Iterable getUserRequestedTickets(long userId) { - return new PagedIterable<>(tmpl("/users/{userId}/tickets/requested.json").set("userId", userId), - handleList(Ticket.class, "tickets")); - } + public Iterable
getArticleFromSearch(String searchTerm, Long sectionId) { + return new PagedIterable<>( + tmpl("/help_center/articles/search.json{?section,query}") + .set("query", searchTerm) + .set("section", sectionId), + handleList(Article.class, "results")); + } - public Iterable getComplianceDeletionStatuses(long userId) { - return new PagedIterable<>(tmpl("/users/{userId}/compliance_deletion_statuses.json").set("userId", userId), - handleList(ComplianceDeletionStatus.class, "compliance_deletion_statuses")); - } + public Iterable
getArticlesFromAnyLabels(List labels) { + return new PagedIterable<>( + tmpl("/help_center/articles/search.json{?label_names}").set("label_names", labels), + handleList(Article.class, "results")); + } - public Iterable getUserCCDTickets(long userId) { - return new PagedIterable<>(tmpl("/users/{userId}/tickets/ccd.json").set("userId", userId), - handleList(Ticket.class, "tickets")); - } + public Iterable
getArticlesFromAllLabels(List labels) { + return new PagedIterable<>( + tmpl("/help_center/en-us/articles.json{?label_names}").set("label_names", labels), + handleList(Article.class, "articles")); + } - public UserRelatedInfo getUserRelatedInfo(long userId) { - return complete(submit(req("GET", tmpl("/users/{userId}/related.json").set("userId", userId)), - handle(UserRelatedInfo.class, "user_related"))); - } + public List getAttachmentsFromArticle(Long articleID) { + return complete( + submit( + req("GET", tmpl("/help_center/articles/{id}/attachments.json").set("id", articleID)), + handleArticleAttachmentsList("article_attachments"))); + } - public Iterable getTicketMetrics() { - return new PagedIterable<>(cnst("/ticket_metrics.json"), handleList(Metric.class, "ticket_metrics")); - } + 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"))); + } - public Metric getTicketMetricByTicket(long id) { - return complete(submit(req("GET", tmpl("/tickets/{ticketId}/metrics.json").set("ticketId", id)), handle(Metric.class, "ticket_metric"))); - } + public Iterable getRecentTickets() { + return new PagedIterable<>(cnst("/tickets/recent.json"), handleList(Ticket.class, "tickets")); + } - public Metric getTicketMetric(long id) { - return complete(submit(req("GET", tmpl("/ticket_metrics/{ticketMetricId}.json").set("ticketMetricId", id)), handle(Metric.class, "ticket_metric"))); - } + /** https://developer.zendesk.com/rest_api/docs/support/incremental_export */ + public Iterable getTicketsIncrementally(Date startTime) { + return new PagedIterable<>( + tmpl("/incremental/tickets.json{?start_time}") + .set("start_time", msToSeconds(startTime.getTime())), + handleIncrementalList(Ticket.class, "tickets")); + } - public Iterable getTicketAudits(Ticket ticket) { - checkHasId(ticket); - return getTicketAudits(ticket.getId()); - } + /** + * https://developer.zendesk.com/rest_api/docs/support/incremental_export + * + * @deprecated incremental export does not support an end_time parameter + */ + @Deprecated + public Iterable getTicketsIncrementally(Date startTime, Date endTime) { + return new PagedIterable<>( + tmpl("/incremental/tickets.json{?start_time,end_time}") + .set("start_time", msToSeconds(startTime.getTime())) + .set("end_time", msToSeconds(endTime.getTime())), + handleIncrementalList(Ticket.class, "tickets")); + } - public Iterable getTicketAudits(Long id) { - return new PagedIterable<>(tmpl("/tickets/{ticketId}/audits.json").set("ticketId", id), - handleList(Audit.class, "audits")); - } + public Iterable getOrganizationTickets(long organizationId) { + return new PagedIterable<>( + tmpl("/organizations/{organizationId}/tickets.json").set("organizationId", organizationId), + handleList(Ticket.class, "tickets")); + } - public Audit getTicketAudit(Ticket ticket, Audit audit) { - checkHasId(audit); - return getTicketAudit(ticket, audit.getId()); - } + public Iterable getOrganizationRequests( + long organizationId) { + return new PagedIterable<>( + tmpl("/organizations/{organizationId}/requests.json").set("organizationId", organizationId), + handleList(org.zendesk.client.v2.model.Request.class, "requests")); + } - public Audit getTicketAudit(Ticket ticket, long id) { - checkHasId(ticket); - return getTicketAudit(ticket.getId(), id); - } + public Iterable getUserRequestedTickets(long userId) { + return new PagedIterable<>( + tmpl("/users/{userId}/tickets/requested.json").set("userId", userId), + handleList(Ticket.class, "tickets")); + } - public Audit getTicketAudit(long ticketId, long auditId) { - return complete(submit(req("GET", - tmpl("/tickets/{ticketId}/audits/{auditId}.json").set("ticketId", ticketId) - .set("auditId", auditId)), - handle(Audit.class, "audit"))); - } + public Iterable getComplianceDeletionStatuses(long userId) { + return new PagedIterable<>( + tmpl("/users/{userId}/compliance_deletion_statuses.json").set("userId", userId), + handleList(ComplianceDeletionStatus.class, "compliance_deletion_statuses")); + } - public void trustTicketAudit(Ticket ticket, Audit audit) { - checkHasId(audit); - trustTicketAudit(ticket, audit.getId()); - } + public Iterable getCustomTicketStatuses() { + return new PagedIterable<>( + tmpl("/custom_statuses.json"), handleList(CustomTicketStatus.class, "custom_statuses")); + } - public void trustTicketAudit(Ticket ticket, long id) { - checkHasId(ticket); - trustTicketAudit(ticket.getId(), id); - } + public Iterable getUserCCDTickets(long userId) { + return new PagedIterable<>( + tmpl("/users/{userId}/tickets/ccd.json").set("userId", userId), + handleList(Ticket.class, "tickets")); + } - public void trustTicketAudit(long ticketId, long auditId) { - complete(submit(req("PUT", tmpl("/tickets/{ticketId}/audits/{auditId}/trust.json").set("ticketId", ticketId) - .set("auditId", auditId)), handleStatus())); - } + public UserRelatedInfo getUserRelatedInfo(long userId) { + return complete( + submit( + req("GET", tmpl("/users/{userId}/related.json").set("userId", userId)), + handle(UserRelatedInfo.class, "user_related"))); + } - public void makePrivateTicketAudit(Ticket ticket, Audit audit) { - checkHasId(audit); - makePrivateTicketAudit(ticket, audit.getId()); - } + public Iterable getTicketMetrics() { + return new PagedIterable<>( + cbp("/ticket_metrics.json"), handleList(Metric.class, "ticket_metrics")); + } - public void makePrivateTicketAudit(Ticket ticket, long id) { - checkHasId(ticket); - makePrivateTicketAudit(ticket.getId(), id); - } + public Metric getTicketMetricByTicket(long id) { + return complete( + submit( + req("GET", tmpl("/tickets/{ticketId}/metrics.json").set("ticketId", id)), + handle(Metric.class, "ticket_metric"))); + } - public void makePrivateTicketAudit(long ticketId, long auditId) { - complete(submit(req("PUT", - tmpl("/tickets/{ticketId}/audits/{auditId}/make_private.json").set("ticketId", ticketId) - .set("auditId", auditId)), handleStatus())); - } + public Metric getTicketMetric(long id) { + return complete( + submit( + req("GET", tmpl("/ticket_metrics/{ticketMetricId}.json").set("ticketMetricId", id)), + handle(Metric.class, "ticket_metric"))); + } - public List getTicketFields() { - return complete(submit(req("GET", cnst("/ticket_fields.json")), handleList(Field.class, "ticket_fields"))); - } + public Iterable getTicketAudits(Ticket ticket) { + checkHasId(ticket); + return getTicketAudits(ticket.getId()); + } - public Field getTicketField(long id) { - return complete(submit(req("GET", tmpl("/ticket_fields/{id}.json").set("id", id)), handle(Field.class, - "ticket_field"))); - } + public Iterable getTicketAudits(Long id) { + return new PagedIterable<>( + cbp("/tickets/{ticketId}/audits.json").set("ticketId", id), + handleList(Audit.class, "audits")); + } - public Field createTicketField(Field field) { - return complete(submit(req("POST", cnst("/ticket_fields.json"), JSON, json( - Collections.singletonMap("ticket_field", field))), handle(Field.class, "ticket_field"))); - } + public Audit getTicketAudit(Ticket ticket, Audit audit) { + checkHasId(audit); + return getTicketAudit(ticket, audit.getId()); + } - public Field updateTicketField(Field field) { - checkHasId(field); - return complete(submit(req("PUT", tmpl("/ticket_fields/{id}.json").set("id", field.getId()), JSON, - json(Collections.singletonMap("ticket_field", field))), handle(Field.class, "ticket_field"))); - } + public Audit getTicketAudit(Ticket ticket, long id) { + checkHasId(ticket); + return getTicketAudit(ticket.getId(), id); + } - public void deleteTicketField(Field field) { - checkHasId(field); - deleteTicket(field.getId()); - } + public Audit getTicketAudit(long ticketId, long auditId) { + return complete( + submit( + req( + "GET", + tmpl("/tickets/{ticketId}/audits/{auditId}.json") + .set("ticketId", ticketId) + .set("auditId", auditId)), + handle(Audit.class, "audit"))); + } - public void deleteTicketField(long id) { - complete(submit(req("DELETE", tmpl("/ticket_fields/{id}.json").set("id", id)), handleStatus())); - } + public void trustTicketAudit(Ticket ticket, Audit audit) { + checkHasId(audit); + trustTicketAudit(ticket, audit.getId()); + } - public Iterable getSuspendedTickets() { - return new PagedIterable<>(cnst("/suspended_tickets.json"), - handleList(SuspendedTicket.class, "suspended_tickets")); - } + public void trustTicketAudit(Ticket ticket, long id) { + checkHasId(ticket); + trustTicketAudit(ticket.getId(), id); + } - public void deleteSuspendedTicket(SuspendedTicket ticket) { - checkHasId(ticket); - deleteSuspendedTicket(ticket.getId()); - } + public void trustTicketAudit(long ticketId, long auditId) { + complete( + submit( + req( + "PUT", + tmpl("/tickets/{ticketId}/audits/{auditId}/trust.json") + .set("ticketId", ticketId) + .set("auditId", auditId)), + handleStatus())); + } - public void deleteSuspendedTicket(long id) { - complete(submit(req("DELETE", tmpl("/suspended_tickets/{id}.json").set("id", id)), handleStatus())); - } + public void makePrivateTicketAudit(Ticket ticket, Audit audit) { + checkHasId(audit); + makePrivateTicketAudit(ticket, audit.getId()); + } - public Attachment.Upload createUpload(String fileName, byte[] content) { - return createUpload(null, fileName, "application/binary", content); - } + public void makePrivateTicketAudit(Ticket ticket, long id) { + checkHasId(ticket); + makePrivateTicketAudit(ticket.getId(), id); + } - public Attachment.Upload createUpload(String fileName, String contentType, byte[] content) { - return createUpload(null, fileName, contentType, content); - } + public void makePrivateTicketAudit(long ticketId, long auditId) { + complete( + submit( + req( + "PUT", + tmpl("/tickets/{ticketId}/audits/{auditId}/make_private.json") + .set("ticketId", ticketId) + .set("auditId", auditId)), + handleStatus())); + } - public Attachment.Upload createUpload(String token, String fileName, String contentType, byte[] content) { - TemplateUri uri = tmpl("/uploads.json{?filename,token}").set("filename", fileName); - if (token != null) { - uri.set("token", token); - } - return complete( - submit(req("POST", uri, contentType, - content), handle(Attachment.Upload.class, "upload"))); - } + public Iterable getTicketFields() { + return new PagedIterable<>( + cbp("/ticket_fields.json"), handleList(Field.class, "ticket_fields")); + } - public void associateAttachmentsToArticle(String idArticle, List attachments) { - TemplateUri uri = tmpl("/help_center/articles/{article_id}/bulk_attachments.json").set("article_id", idArticle); - List attachmentsIds = new ArrayList<>(); - for(Attachment item : attachments){ - attachmentsIds.add(item.getId()); - } - complete(submit(req("POST", uri, JSON, json(Collections.singletonMap("attachment_ids", attachmentsIds))), handleStatus())); + public Field getTicketField(long id) { + return complete( + submit( + req("GET", tmpl("/ticket_fields/{id}.json").set("id", id)), + handle(Field.class, "ticket_field"))); } - /** - * Create upload article with inline false - */ - public ArticleAttachments createUploadArticle(long articleId, File file) throws IOException { - return createUploadArticle(articleId, file, false); + public Field createTicketField(Field field) { + return complete( + submit( + req( + "POST", + cnst("/ticket_fields.json"), + JSON, + json(Collections.singletonMap("ticket_field", field))), + handle(Field.class, "ticket_field"))); } - public ArticleAttachments createUploadArticle(long articleId, File file, boolean inline) throws IOException { - RequestBuilder builder = reqBuilder("POST", tmpl("/help_center/articles/{id}/attachments.json").set("id", articleId).toString()); - builder.setHeader("Content-Type", "multipart/form-data"); + public Field updateTicketField(Field field) { + checkHasId(field); + return complete( + submit( + req( + "PUT", + tmpl("/ticket_fields/{id}.json").set("id", field.getId()), + JSON, + json(Collections.singletonMap("ticket_field", field))), + handle(Field.class, "ticket_field"))); + } - if (inline) - builder.addBodyPart(new StringPart("inline", "true")); + public void deleteTicketField(Field field) { + checkHasId(field); + deleteTicket(field.getId()); + } - builder.addBodyPart( - new FilePart("file", file, "application/octet-stream", StandardCharsets.UTF_8, file.getName())); - final Request req = builder.build(); - return complete(submit(req, handle(ArticleAttachments.class, "article_attachment"))); - } + public void deleteTicketField(long id) { + complete(submit(req("DELETE", tmpl("/ticket_fields/{id}.json").set("id", id)), handleStatus())); + } - public void deleteUpload(Attachment.Upload upload) { - checkHasToken(upload); - deleteUpload(upload.getToken()); - } + public Iterable getSuspendedTickets() { + return new PagedIterable<>( + cbp("/suspended_tickets.json"), handleList(SuspendedTicket.class, "suspended_tickets")); + } - public void deleteUpload(String token) { - complete(submit(req("DELETE", tmpl("/uploads/{token}.json").set("token", token)), handleStatus())); - } + /** + * Recover Multiple Suspended Tickets. Accepts + * up to 100 ticket ids. + * + * @throws IllegalArgumentException when the number of tickets exceeds 100 + * @param tickets tickets to be recovered + * @return recovered tickets + */ + public Iterable recoverSuspendedTickets(List tickets) { + if (100 < tickets.size()) { + throw new IllegalArgumentException( + "This endpoint accepts up to 100 tickets. Provided " + + tickets.size() + + " tickets.\n" + + "https://developer.zendesk.com/rest_api/docs/support/suspended_tickets#recover-multiple-suspended-tickets"); + } + List ids = new ArrayList<>(); + for (SuspendedTicket ticket : tickets) { + ids.add(ticket.getId()); + } + return complete( + submit( + req("PUT", tmpl("/suspended_tickets/recover_many.json{?ids}").set("ids", ids)), + handleList(Ticket.class, "tickets"))); + } - public Attachment getAttachment(Attachment attachment) { - checkHasId(attachment); - return getAttachment(attachment.getId()); - } + public void deleteSuspendedTicket(SuspendedTicket ticket) { + checkHasId(ticket); + deleteSuspendedTicket(ticket.getId()); + } - public Attachment getAttachment(long id) { - return complete(submit(req("GET", tmpl("/attachments/{id}.json").set("id", id)), handle(Attachment.class, - "attachment"))); - } + public void deleteSuspendedTicket(long id) { + complete( + submit(req("DELETE", tmpl("/suspended_tickets/{id}.json").set("id", id)), handleStatus())); + } - public void deleteAttachment(Attachment attachment) { - checkHasId(attachment); - deleteAttachment(attachment.getId()); - } + public Attachment.Upload createUpload(String fileName, byte[] content) { + return createUpload(null, fileName, "application/binary", content); + } - public void deleteAttachment(long id) { - complete(submit(req("DELETE", tmpl("/attachments/{id}.json").set("id", id)), handleStatus())); - } + public Attachment.Upload createUpload(String fileName, String contentType, byte[] content) { + return createUpload(null, fileName, contentType, content); + } - public Iterable getTargets() { - return new PagedIterable<>(cnst("/targets.json"), handleTargetList("targets")); + public Attachment.Upload createUpload( + String token, String fileName, String contentType, byte[] content) { + TemplateUri uri = tmpl("/uploads.json{?filename,token}").set("filename", fileName); + if (token != null) { + uri.set("token", token); } + return complete( + submit(req("POST", uri, contentType, content), handle(Attachment.Upload.class, "upload"))); + } - public Target getTarget(long id) { - return complete(submit(req("GET", tmpl("/targets/{id}.json").set("id", id)), handle(Target.class, "target"))); - } + public void associateAttachmentsToArticle(String idArticle, List attachments) { + TemplateUri uri = + tmpl("/help_center/articles/{article_id}/bulk_attachments.json") + .set("article_id", idArticle); + List attachmentsIds = new ArrayList<>(); + for (Attachment item : attachments) { + attachmentsIds.add(item.getId()); + } + complete( + submit( + req( + "POST", + uri, + JSON, + json(Collections.singletonMap("attachment_ids", attachmentsIds))), + handleStatus())); + } - public Target createTarget(Target target) { - return complete(submit(req("POST", cnst("/targets.json"), JSON, json(Collections.singletonMap("target", target))), - handle(Target.class, "target"))); - } + /** Create upload article with inline false */ + public ArticleAttachments createUploadArticle(long articleId, File file) throws IOException { + return createUploadArticle(articleId, file, false); + } - public void deleteTarget(long targetId) { - complete(submit(req("DELETE", tmpl("/targets/{id}.json").set("id", targetId)), handleStatus())); - } + public ArticleAttachments createUploadArticle(long articleId, File file, boolean inline) + throws IOException { + RequestBuilder builder = + reqBuilder( + "POST", + tmpl("/help_center/articles/{id}/attachments.json").set("id", articleId).toString()); + builder.setHeader("Content-Type", "multipart/form-data"); + + if (inline) builder.addBodyPart(new StringPart("inline", "true")); + + builder.addBodyPart( + new FilePart( + "file", file, "application/octet-stream", StandardCharsets.UTF_8, file.getName())); + final Request req = builder.build(); + return complete(submit(req, handle(ArticleAttachments.class, "article_attachment"))); + } - public Iterable getTriggers() { - return new PagedIterable<>(cnst("/triggers.json"), handleList(Trigger.class, "triggers")); - } + public void deleteUpload(Attachment.Upload upload) { + checkHasToken(upload); + deleteUpload(upload.getToken()); + } - public Trigger getTrigger(long id) { - return complete(submit(req("GET", tmpl("/triggers/{id}.json").set("id", id)), handle(Trigger.class, "trigger"))); - } + public void deleteUpload(String token) { + complete( + submit(req("DELETE", tmpl("/uploads/{token}.json").set("token", token)), handleStatus())); + } - public Trigger createTrigger(Trigger trigger) { - return complete(submit(req("POST", cnst("/triggers.json"), JSON, json(Collections.singletonMap("trigger", trigger))), - handle(Trigger.class, "trigger"))); - } + public Attachment getAttachment(Attachment attachment) { + checkHasId(attachment); + return getAttachment(attachment.getId()); + } + + public Attachment getAttachment(long id) { + return complete( + submit( + req("GET", tmpl("/attachments/{id}.json").set("id", id)), + handle(Attachment.class, "attachment"))); + } + + public void deleteAttachment(Attachment attachment) { + checkHasId(attachment); + deleteAttachment(attachment.getId()); + } + + public void deleteAttachment(long id) { + complete(submit(req("DELETE", tmpl("/attachments/{id}.json").set("id", id)), handleStatus())); + } + + public Iterable getTargets() { + return new PagedIterable<>(cnst("/targets.json"), handleTargetList("targets")); + } + + public Target getTarget(long id) { + return complete( + submit( + req("GET", tmpl("/targets/{id}.json").set("id", id)), handle(Target.class, "target"))); + } + + public Target createTarget(Target target) { + return complete( + submit( + req( + "POST", + cnst("/targets.json"), + JSON, + json(Collections.singletonMap("target", target))), + handle(Target.class, "target"))); + } + + public void deleteTarget(long targetId) { + complete(submit(req("DELETE", tmpl("/targets/{id}.json").set("id", targetId)), handleStatus())); + } + + public Iterable getTriggers() { + return new PagedIterable<>(cbp("/triggers.json"), handleList(Trigger.class, "triggers")); + } + + public Iterable getTriggers( + String categoryId, boolean active, String sortBy, SortOrder sortOrder) { + return new PagedIterable<>( + tmpl("/triggers.json{?category_id,active,sort_by,sort_order}") + .set("category_id", categoryId) + .set("active", active) + .set("sort_by", sortBy) + .set("sort_order", sortOrder.getQueryParameter()), + handleList(Trigger.class, "triggers")); + } + + public Iterable getActiveTriggers() { + return new PagedIterable<>(cbp("/triggers/active.json"), handleList(Trigger.class, "triggers")); + } + + public Iterable searchTriggers(String query) { + return new PagedIterable<>( + tmpl("/triggers/search.json{?query}").set("query", query), + handleList(Trigger.class, "triggers")); + } + + public Iterable searchTriggers( + String query, boolean active, String sortBy, SortOrder sortOrder) { + return new PagedIterable<>( + tmpl("/triggers/search.json{?query,active,sort_by,sort_order}") + .set("query", query) + .set("active", active) + .set("sort_by", sortBy) + .set("sort_order", sortOrder.getQueryParameter()), + handleList(Trigger.class, "triggers")); + } - public Trigger updateTrigger(Long triggerId, Trigger trigger) { - return complete(submit(req("PUT", tmpl("/triggers/{id}.json").set("id", triggerId), JSON, json(Collections.singletonMap("trigger", trigger))), + public Trigger getTrigger(long id) { + return complete( + submit( + req("GET", tmpl("/triggers/{id}.json").set("id", id)), handle(Trigger.class, "trigger"))); } - public void deleteTrigger(long triggerId) { - complete(submit(req("DELETE", tmpl("/triggers/{id}.json").set("id", triggerId)), handleStatus())); - } + public Trigger createTrigger(Trigger trigger) { + return complete( + submit( + req( + "POST", + cnst("/triggers.json"), + JSON, + json(Collections.singletonMap("trigger", trigger))), + handle(Trigger.class, "trigger"))); + } + + public Trigger updateTrigger(Long triggerId, Trigger trigger) { + return complete( + submit( + req( + "PUT", + tmpl("/triggers/{id}.json").set("id", triggerId), + JSON, + json(Collections.singletonMap("trigger", trigger))), + handle(Trigger.class, "trigger"))); + } + + public void deleteTrigger(long triggerId) { + complete( + submit(req("DELETE", tmpl("/triggers/{id}.json").set("id", triggerId)), handleStatus())); + } + + public Iterable getViews() { + return new PagedIterable<>(cbp("/views.json"), handleList(View.class, "views")); + } + + public Iterable getView(long id) { + return new PagedIterable<>( + tmpl("/views/{id}/tickets.json").set("id", id), handleList(Ticket.class, "tickets")); + } + @SuppressWarnings("unchecked") + public Optional> executeView(long id, Class clazz) { + var objectReader = + mapper.readerFor( + mapper.getTypeFactory().constructParametricType(ExecutedViewPage.class, clazz)); + return Optional.of( + complete( + submit( + req("GET", tmpl("/views/{id}/execute.json").set("id", id)), handle(objectReader)))); + } // Automations public Iterable getAutomations() { - return new PagedIterable<>(cnst("/automations.json"), - handleList(Automation.class, "automations")); + return new PagedIterable<>( + cbp("/automations.json"), handleList(Automation.class, "automations")); } public Automation getAutomation(long id) { - return complete(submit(req("GET", tmpl("/automations/{id}.json").set("id", id)), - handle(Automation.class, "automation"))); + return complete( + submit( + req("GET", tmpl("/automations/{id}.json").set("id", id)), + handle(Automation.class, "automation"))); } public Automation createAutomation(Automation automation) { - return complete(submit( - req("POST", cnst("/automations.json"), JSON, - json(Collections.singletonMap("automation", automation))), - handle(Automation.class, "automation"))); + return complete( + submit( + req( + "POST", + cnst("/automations.json"), + JSON, + json(Collections.singletonMap("automation", automation))), + handle(Automation.class, "automation"))); } public Automation updateAutomation(Long automationId, Automation automation) { - return complete(submit( - req("PUT", tmpl("/automations/{id}.json").set("id", automationId), JSON, - json(Collections.singletonMap("automation", automation))), - handle(Automation.class, "automation"))); + return complete( + submit( + req( + "PUT", + tmpl("/automations/{id}.json").set("id", automationId), + JSON, + json(Collections.singletonMap("automation", automation))), + handle(Automation.class, "automation"))); } public void deleteAutomation(long automationId) { - complete(submit(req("DELETE", tmpl("/automations/{id}.json").set("id", automationId)), - handleStatus())); + complete( + submit( + req("DELETE", tmpl("/automations/{id}.json").set("id", automationId)), handleStatus())); } + public Iterable getTwitterMonitors() { + return new PagedIterable<>( + cnst("/channels/twitter/monitored_twitter_handles.json"), + handleList(TwitterMonitor.class, "monitored_twitter_handles")); + } - public Iterable getTwitterMonitors() { - return new PagedIterable<>(cnst("/channels/twitter/monitored_twitter_handles.json"), - handleList(TwitterMonitor.class, "monitored_twitter_handles")); - } + public Iterable getUsers() { + return new PagedIterable<>(cbp("/users.json"), handleList(User.class, "users")); + } + public Iterable getUsersByRole(String role, String... roles) { + // Going to have to build this URI manually, because the RFC6570 template spec doesn't support + // variables like ?role[]=...role[]=..., which is what Zendesk requires. + // See https://developer.zendesk.com/rest_api/docs/core/users#filters + final StringBuilder uriBuilder = + new StringBuilder("/users.json?page[size]=").append(cbpPageSize); + if (roles.length == 0) { + uriBuilder.append("&role=").append(encodeUrl(role)); + } else { + uriBuilder.append("&role[]=").append(encodeUrl(role)); + } + for (final String curRole : roles) { + uriBuilder.append("&role[]=").append(encodeUrl(curRole)); + } + return new PagedIterable<>(cnst(uriBuilder.toString()), handleList(User.class, "users")); + } - public Iterable getUsers() { - return new PagedIterable<>(cnst("/users.json"), handleList(User.class, "users")); - } + public List getUsers(long id, long... ids) { + return complete( + submit( + req("GET", tmpl("/users/show_many.json{?ids}").set("ids", idArray(id, ids))), + handleList(User.class, "users"))); + } - public Iterable getUsersByRole(String role, String... roles) { - // Going to have to build this URI manually, because the RFC6570 template spec doesn't support - // variables like ?role[]=...role[]=..., which is what Zendesk requires. - // See https://developer.zendesk.com/rest_api/docs/core/users#filters - final StringBuilder uriBuilder = new StringBuilder("/users.json"); - if (roles.length == 0) { - uriBuilder.append("?role=").append(encodeUrl(role)); - } else { - uriBuilder.append("?role[]=").append(encodeUrl(role)); - } - for (final String curRole : roles) { - uriBuilder.append("&role[]=").append(encodeUrl(curRole)); - } - return new PagedIterable<>(cnst(uriBuilder.toString()), handleList(User.class, "users")); - } + /** + * @deprecated - User externalIds are Strings in Zendesk API, not longs. Use {@link + * #getUsersByExternalIds(String, String...)} instead + */ + @Deprecated + public List getUsersByExternalIds(long externalId, long... externalIds) { + return complete( + submit( + req( + "GET", + tmpl("/users/show_many.json{?external_ids}") + .set("external_ids", idArray(externalId, externalIds))), + handleList(User.class, "users"))); + } - public Iterable getUsersIncrementally(Date startTime) { - return new PagedIterable<>( - tmpl("/incremental/users.json{?start_time}").set("start_time", msToSeconds(startTime.getTime())), - handleIncrementalList(User.class, "users")); - } + public List getUsersByExternalIds(String externalId, String... externalIds) { + return complete( + submit( + req( + "GET", + tmpl("/users/show_many.json{?external_ids}") + .set("external_ids", idArray(externalId, externalIds))), + handleList(User.class, "users"))); + } - public Iterable getGroupUsers(long id) { - return new PagedIterable<>(tmpl("/groups/{id}/users.json").set("id", id), handleList(User.class, "users")); - } + public Iterable getUsersIncrementally(Date startTime) { + return new PagedIterable<>( + tmpl("/incremental/users.json{?start_time}") + .set("start_time", msToSeconds(startTime.getTime())), + handleIncrementalList(User.class, "users")); + } - public Iterable getOrganizationUsers(long id) { - return new PagedIterable<>(tmpl("/organizations/{id}/users.json").set("id", id), - handleList(User.class, "users")); - } + public Iterable getGroupUsers(long id) { + return new PagedIterable<>( + cbp("/groups/{id}/users.json").set("id", id), handleList(User.class, "users")); + } - public User getUser(long id) { - return complete(submit(req("GET", tmpl("/users/{id}.json").set("id", id)), handle(User.class, "user"))); - } + public Iterable getOrganizationUsers(long id) { + return new PagedIterable<>( + cbp("/organizations/{id}/users.json").set("id", id), handleList(User.class, "users")); + } - public User getAuthenticatedUser() { - return complete(submit(req("GET", cnst("/users/me.json")), handle(User.class, "user"))); - } + public User getUser(long id) { + return complete( + submit(req("GET", tmpl("/users/{id}.json").set("id", id)), handle(User.class, "user"))); + } - public Iterable getUserFields() { - return complete(submit(req("GET", cnst("/user_fields.json")), - handleList(UserField.class, "user_fields"))); - } + public User getAuthenticatedUser() { + return complete(submit(req("GET", cnst("/users/me.json")), handle(User.class, "user"))); + } - public User createUser(User user) { - return complete(submit(req("POST", cnst("/users.json"), JSON, json( - Collections.singletonMap("user", user))), handle(User.class, "user"))); - } + public Iterable getUserFields() { + return complete( + submit(req("GET", cnst("/user_fields.json")), handleList(UserField.class, "user_fields"))); + } - public User mergeUsers(long userIdThatWillRemain, long userIdThatWillBeMerged) { - return complete(submit(req("PUT", tmpl("/users/{id}/merge.json").set("id", userIdThatWillBeMerged), JSON, json( - Collections.singletonMap("user", Collections.singletonMap("id", userIdThatWillRemain)))), handle(User.class, "user"))); - } + public User createUser(User user) { + return complete( + submit( + req("POST", cnst("/users.json"), JSON, json(Collections.singletonMap("user", user))), + handle(User.class, "user"))); + } - public JobStatus createUsers(User... users) { - return createUsers(Arrays.asList(users)); - } + public User mergeUsers(long userIdThatWillRemain, long userIdThatWillBeMerged) { + return complete( + submit( + req( + "PUT", + tmpl("/users/{id}/merge.json").set("id", userIdThatWillBeMerged), + JSON, + json( + Collections.singletonMap( + "user", Collections.singletonMap("id", userIdThatWillRemain)))), + handle(User.class, "user"))); + } - public JobStatus createUsers(List users) { - return complete(createUsersAsync(users)); - } + public JobStatus createUsers(User... users) { + return createUsers(Arrays.asList(users)); + } - public ListenableFuture createUsersAsync(List users) { - return submit(req("POST", cnst("/users/create_many.json"), JSON, json( - Collections.singletonMap("users", users))), handleJobStatus()); - } + public JobStatus createUsers(List users) { + return complete(createUsersAsync(users)); + } - public User createOrUpdateUser(User user) { - return complete(submit(req("POST", cnst("/users/create_or_update.json"), JSON, json( - Collections.singletonMap("user", user))), handle(User.class, "user"))); - } + public ListenableFuture createUsersAsync(List users) { + return submit( + req( + "POST", + cnst("/users/create_many.json"), + JSON, + json(Collections.singletonMap("users", users))), + handleJobStatus()); + } - public JobStatus createOrUpdateUsers(User... users) { - return createOrUpdateUsers(Arrays.asList(users)); - } + public User createOrUpdateUser(User user) { + return complete( + submit( + req( + "POST", + cnst("/users/create_or_update.json"), + JSON, + json(Collections.singletonMap("user", user))), + handle(User.class, "user"))); + } - public JobStatus createOrUpdateUsers(List users) { - return complete(createOrUpdateUsersAsync(users)); - } + public JobStatus createOrUpdateUsers(User... users) { + return createOrUpdateUsers(Arrays.asList(users)); + } - public ListenableFuture createOrUpdateUsersAsync(List users) { - return submit(req("POST", cnst("/users/create_or_update_many.json"), JSON, json( - Collections.singletonMap("users", users))), handleJobStatus()); - } + public JobStatus createOrUpdateUsers(List users) { + return complete(createOrUpdateUsersAsync(users)); + } - public User updateUser(User user) { - checkHasId(user); - return complete(submit(req("PUT", tmpl("/users/{id}.json").set("id", user.getId()), JSON, json( - Collections.singletonMap("user", user))), handle(User.class, "user"))); - } + public ListenableFuture createOrUpdateUsersAsync(List users) { + return submit( + req( + "POST", + cnst("/users/create_or_update_many.json"), + JSON, + json(Collections.singletonMap("users", users))), + handleJobStatus()); + } - public JobStatus updateUsers(User... users) { - return updateUsers(Arrays.asList(users)); - } + public User updateUser(User user) { + checkHasId(user); + return complete( + submit( + req( + "PUT", + tmpl("/users/{id}.json").set("id", user.getId()), + JSON, + json(Collections.singletonMap("user", user))), + handle(User.class, "user"))); + } - public JobStatus updateUsers(List users) { - return complete(updateUsersAsync(users)); - } + public JobStatus updateUsers(User... users) { + return updateUsers(Arrays.asList(users)); + } - public ListenableFuture updateUsersAsync(List users) { - return submit(req("PUT", cnst("/users/update_many.json"), JSON, json( - Collections.singletonMap("users", users))), handleJobStatus()); - } + public JobStatus updateUsers(List users) { + return complete(updateUsersAsync(users)); + } - public void deleteUser(User user) { - checkHasId(user); - deleteUser(user.getId()); - } + public ListenableFuture updateUsersAsync(List users) { + return submit( + req( + "PUT", + cnst("/users/update_many.json"), + JSON, + json(Collections.singletonMap("users", users))), + handleJobStatus()); + } - public void deleteUser(long id) { - complete(submit(req("DELETE", tmpl("/users/{id}.json").set("id", id)), handleStatus())); - } + public void deleteUser(User user) { + checkHasId(user); + deleteUser(user.getId()); + } - public User permanentlyDeleteUser(User user) { - checkHasId(user); - return permanentlyDeleteUser(user.getId()); - } + public void deleteUser(long id) { + complete(submit(req("DELETE", tmpl("/users/{id}.json").set("id", id)), handleStatus())); + } - public User permanentlyDeleteUser(long id) { - deleteUser(id); - return complete(submit(req("DELETE", tmpl("/deleted_users/{id}.json").set("id", id)), handle(User.class))); - } + public JobStatus deleteUsers(long... ids) { + return complete( + submit( + req("DELETE", tmpl("/users/destroy_many.json{?ids}").set("ids", ids)), + handleJobStatus())); + } - public User suspendUser(long id) { - User user = new User(); - user.setId(id); - user.setSuspended(true); - return updateUser(user); - } + public User permanentlyDeleteUser(User user) { + checkHasId(user); + return permanentlyDeleteUser(user.getId()); + } - public User unsuspendUser(long id) { - User user = new User(); - user.setId(id); - user.setSuspended(false); - return updateUser(user); - } + public User permanentlyDeleteUser(long id) { + deleteUser(id); + return complete( + submit(req("DELETE", tmpl("/deleted_users/{id}.json").set("id", id)), handle(User.class))); + } - public Iterable lookupUserByEmail(String email) { - return new PagedIterable<>(tmpl("/users/search.json{?query}").set("query", email), - handleList(User.class, "users")); - } + public User suspendUser(long id) { + User user = new User(); + user.setId(id); + user.setSuspended(true); + return updateUser(user); + } - public Iterable lookupUserByExternalId(String externalId) { - return new PagedIterable<>(tmpl("/users/search.json{?external_id}").set("external_id", externalId), - handleList(User.class, "users")); - } + public User unsuspendUser(long id) { + User user = new User(); + user.setId(id); + user.setSuspended(false); + return updateUser(user); + } - public User getCurrentUser() { - return complete(submit(req("GET", cnst("/users/me.json")), handle(User.class, "user"))); - } + public Iterable lookupUserByEmail(String email) { + return new PagedIterable<>( + tmpl("/users/search.json{?query}").set("query", email), handleList(User.class, "users")); + } - public void resetUserPassword(User user, String password) { - checkHasId(user); - resetUserPassword(user.getId(), password); - } + public Iterable lookupUserByExternalId(String externalId) { + return new PagedIterable<>( + tmpl("/users/search.json{?external_id}").set("external_id", externalId), + handleList(User.class, "users")); + } - public void resetUserPassword(long id, String password) { - complete(submit(req("POST", tmpl("/users/{id}/password.json").set("id", id), JSON, - json(Collections.singletonMap("password", password))), handleStatus())); - } + public User getCurrentUser() { + return complete(submit(req("GET", cnst("/users/me.json")), handle(User.class, "user"))); + } - public void changeUserPassword(User user, String oldPassword, String newPassword) { - checkHasId(user); - Map req = new HashMap<>(); - req.put("previous_password", oldPassword); - req.put("password", newPassword); - complete(submit(req("PUT", tmpl("/users/{id}/password.json").set("id", user.getId()), JSON, - json(req)), handleStatus())); - } + public void resetUserPassword(User user, String password) { + checkHasId(user); + resetUserPassword(user.getId(), password); + } - public List getUserIdentities(User user) { - checkHasId(user); - return getUserIdentities(user.getId()); - } + public void resetUserPassword(long id, String password) { + complete( + submit( + req( + "POST", + tmpl("/users/{id}/password.json").set("id", id), + JSON, + json(Collections.singletonMap("password", password))), + handleStatus())); + } - public List getUserIdentities(long userId) { - return complete(submit(req("GET", tmpl("/users/{id}/identities.json").set("id", userId)), - handleList(Identity.class, "identities"))); - } + public void changeUserPassword(User user, String oldPassword, String newPassword) { + checkHasId(user); + Map req = new HashMap<>(); + req.put("previous_password", oldPassword); + req.put("password", newPassword); + complete( + submit( + req("PUT", tmpl("/users/{id}/password.json").set("id", user.getId()), JSON, json(req)), + handleStatus())); + } - public Identity getUserIdentity(User user, Identity identity) { - checkHasId(identity); - return getUserIdentity(user, identity.getId()); - } + public List getUserIdentities(User user) { + checkHasId(user); + return getUserIdentities(user.getId()); + } - public Identity getUserIdentity(User user, long identityId) { - checkHasId(user); - return getUserIdentity(user.getId(), identityId); - } + public List getUserIdentities(long userId) { + return complete( + submit( + req("GET", tmpl("/users/{id}/identities.json").set("id", userId)), + handleList(Identity.class, "identities"))); + } - public Identity getUserIdentity(long userId, long identityId) { - return complete(submit(req("GET", tmpl("/users/{userId}/identities/{identityId}.json").set("userId", userId) - .set("identityId", identityId)), handle( - Identity.class, "identity"))); - } + public Identity getUserIdentity(User user, Identity identity) { + checkHasId(identity); + return getUserIdentity(user, identity.getId()); + } - public List setUserPrimaryIdentity(User user, Identity identity) { - checkHasId(identity); - return setUserPrimaryIdentity(user, identity.getId()); - } + public Identity getUserIdentity(User user, long identityId) { + checkHasId(user); + return getUserIdentity(user.getId(), identityId); + } - public List setUserPrimaryIdentity(User user, long identityId) { - checkHasId(user); - return setUserPrimaryIdentity(user.getId(), identityId); - } + public Identity getUserIdentity(long userId, long identityId) { + return complete( + submit( + req( + "GET", + tmpl("/users/{userId}/identities/{identityId}.json") + .set("userId", userId) + .set("identityId", identityId)), + handle(Identity.class, "identity"))); + } - public List setUserPrimaryIdentity(long userId, long identityId) { - return complete(submit(req("PUT", - tmpl("/users/{userId}/identities/{identityId}/make_primary.json").set("userId", userId) - .set("identityId", identityId), JSON, null), - handleList(Identity.class, "identities"))); - } + public List setUserPrimaryIdentity(User user, Identity identity) { + checkHasId(identity); + return setUserPrimaryIdentity(user, identity.getId()); + } - public Identity verifyUserIdentity(User user, Identity identity) { - checkHasId(identity); - return verifyUserIdentity(user, identity.getId()); - } + public List setUserPrimaryIdentity(User user, long identityId) { + checkHasId(user); + return setUserPrimaryIdentity(user.getId(), identityId); + } - public Identity verifyUserIdentity(User user, long identityId) { - checkHasId(user); - return verifyUserIdentity(user.getId(), identityId); - } + public List setUserPrimaryIdentity(long userId, long identityId) { + return complete( + submit( + req( + "PUT", + tmpl("/users/{userId}/identities/{identityId}/make_primary.json") + .set("userId", userId) + .set("identityId", identityId), + JSON, + null), + handleList(Identity.class, "identities"))); + } - public Identity verifyUserIdentity(long userId, long identityId) { - return complete(submit(req("PUT", tmpl("/users/{userId}/identities/{identityId}/verify.json") - .set("userId", userId) - .set("identityId", identityId), JSON, null), handle(Identity.class, "identity"))); - } + public Identity verifyUserIdentity(User user, Identity identity) { + checkHasId(identity); + return verifyUserIdentity(user, identity.getId()); + } - public Identity requestVerifyUserIdentity(User user, Identity identity) { - checkHasId(identity); - return requestVerifyUserIdentity(user, identity.getId()); - } + public Identity verifyUserIdentity(User user, long identityId) { + checkHasId(user); + return verifyUserIdentity(user.getId(), identityId); + } - public Identity requestVerifyUserIdentity(User user, long identityId) { - checkHasId(user); - return requestVerifyUserIdentity(user.getId(), identityId); - } + public Identity verifyUserIdentity(long userId, long identityId) { + return complete( + submit( + req( + "PUT", + tmpl("/users/{userId}/identities/{identityId}/verify.json") + .set("userId", userId) + .set("identityId", identityId), + JSON, + null), + handle(Identity.class, "identity"))); + } - public Identity requestVerifyUserIdentity(long userId, long identityId) { - return complete(submit(req("PUT", tmpl("/users/{userId}/identities/{identityId}/request_verification.json") - .set("userId", userId) - .set("identityId", identityId), JSON, null), handle(Identity.class, "identity"))); - } + public Identity requestVerifyUserIdentity(User user, Identity identity) { + checkHasId(identity); + return requestVerifyUserIdentity(user, identity.getId()); + } - public Identity updateUserIdentity(long userId, Identity identity) { - checkHasId(identity); - return complete(submit(req("PUT", tmpl("/users/{userId}/identities/{identityId}.json") - .set("userId", userId) - .set("identityId", identity.getId()), JSON, json(Collections.singletonMap("identity", identity))), handle(Identity.class, "identity"))); - } + public Identity requestVerifyUserIdentity(User user, long identityId) { + checkHasId(user); + return requestVerifyUserIdentity(user.getId(), identityId); + } - public Identity updateUserIdentity(User user, Identity identity) { - checkHasId(user); - return updateUserIdentity(user.getId(), identity); - } + public Identity requestVerifyUserIdentity(long userId, long identityId) { + return complete( + submit( + req( + "PUT", + tmpl("/users/{userId}/identities/{identityId}/request_verification.json") + .set("userId", userId) + .set("identityId", identityId), + JSON, + null), + handle(Identity.class, "identity"))); + } - public void deleteUserIdentity(User user, Identity identity) { - checkHasId(identity); - deleteUserIdentity(user, identity.getId()); - } + public Identity updateUserIdentity(long userId, Identity identity) { + checkHasId(identity); + return complete( + submit( + req( + "PUT", + tmpl("/users/{userId}/identities/{identityId}.json") + .set("userId", userId) + .set("identityId", identity.getId()), + JSON, + json(Collections.singletonMap("identity", identity))), + handle(Identity.class, "identity"))); + } - public void deleteUserIdentity(User user, long identityId) { - checkHasId(user); - deleteUserIdentity(user.getId(), identityId); - } + public Identity updateUserIdentity(User user, Identity identity) { + checkHasId(user); + return updateUserIdentity(user.getId(), identity); + } - public void deleteUserIdentity(long userId, long identityId) { - complete(submit(req("DELETE", tmpl("/users/{userId}/identities/{identityId}.json") - .set("userId", userId) - .set("identityId", identityId) - ), handleStatus())); - } + public void deleteUserIdentity(User user, Identity identity) { + checkHasId(identity); + deleteUserIdentity(user, identity.getId()); + } - public Identity createUserIdentity(long userId, Identity identity) { - return complete(submit(req("POST", tmpl("/users/{userId}/identities.json").set("userId", userId), JSON, - json(Collections.singletonMap("identity", identity))), handle(Identity.class, "identity"))); - } + public void deleteUserIdentity(User user, long identityId) { + checkHasId(user); + deleteUserIdentity(user.getId(), identityId); + } - public Identity createUserIdentity(User user, Identity identity) { - return complete(submit(req("POST", tmpl("/users/{userId}/identities.json").set("userId", user.getId()), JSON, - json(Collections.singletonMap("identity", identity))), handle(Identity.class, "identity"))); - } + public void deleteUserIdentity(long userId, long identityId) { + complete( + submit( + req( + "DELETE", + tmpl("/users/{userId}/identities/{identityId}.json") + .set("userId", userId) + .set("identityId", identityId)), + handleStatus())); + } - public Iterable getCustomAgentRoles() { - return new PagedIterable<>(cnst("/custom_roles.json"), - handleList(AgentRole.class, "custom_roles")); - } + public Identity createUserIdentity(long userId, Identity identity) { + return complete( + submit( + req( + "POST", + tmpl("/users/{userId}/identities.json").set("userId", userId), + JSON, + json(Collections.singletonMap("identity", identity))), + handle(Identity.class, "identity"))); + } - public Iterable getRequests() { - return new PagedIterable<>(cnst("/requests.json"), - handleList(org.zendesk.client.v2.model.Request.class, "requests")); - } + public Identity createUserIdentity(User user, Identity identity) { + return complete( + submit( + req( + "POST", + tmpl("/users/{userId}/identities.json").set("userId", user.getId()), + JSON, + json(Collections.singletonMap("identity", identity))), + handle(Identity.class, "identity"))); + } - public Iterable getOpenRequests() { - return new PagedIterable<>(cnst("/requests/open.json"), - handleList(org.zendesk.client.v2.model.Request.class, "requests")); - } + public Iterable getCustomAgentRoles() { + return new PagedIterable<>( + cnst("/custom_roles.json"), handleList(AgentRole.class, "custom_roles")); + } - public Iterable getSolvedRequests() { - return new PagedIterable<>(cnst("/requests/solved.json"), - handleList(org.zendesk.client.v2.model.Request.class, "requests")); - } + public Iterable getRequests() { + return new PagedIterable<>( + cbp("/requests.json"), handleList(org.zendesk.client.v2.model.Request.class, "requests")); + } - public Iterable getCCRequests() { - return new PagedIterable<>(cnst("/requests/ccd.json"), - handleList(org.zendesk.client.v2.model.Request.class, "requests")); - } + public Iterable getOpenRequests() { + return new PagedIterable<>( + cbp("/requests/open.json"), + handleList(org.zendesk.client.v2.model.Request.class, "requests")); + } - public Iterable getUserRequests(User user) { - checkHasId(user); - return getUserRequests(user.getId()); - } + public Iterable getSolvedRequests() { + return new PagedIterable<>( + cbp("/requests/solved.json"), + handleList(org.zendesk.client.v2.model.Request.class, "requests")); + } - public Iterable getUserRequests(long id) { - return new PagedIterable<>(tmpl("/users/{id}/requests.json").set("id", id), - handleList(org.zendesk.client.v2.model.Request.class, "requests")); - } + public Iterable getCCRequests() { + return new PagedIterable<>( + cnst("/requests/ccd.json"), + handleList(org.zendesk.client.v2.model.Request.class, "requests")); + } - public org.zendesk.client.v2.model.Request getRequest(long id) { - return complete(submit(req("GET", tmpl("/requests/{id}.json").set("id", id)), - handle(org.zendesk.client.v2.model.Request.class, "request"))); - } + public Iterable getUserRequests(User user) { + checkHasId(user); + return getUserRequests(user.getId()); + } - public org.zendesk.client.v2.model.Request createRequest(org.zendesk.client.v2.model.Request request) { - return complete(submit(req("POST", cnst("/requests.json"), - JSON, json(Collections.singletonMap("request", request))), - handle(org.zendesk.client.v2.model.Request.class, "request"))); - } + public Iterable getUserRequests(long id) { + return new PagedIterable<>( + tmpl("/users/{id}/requests.json").set("id", id), + handleList(org.zendesk.client.v2.model.Request.class, "requests")); + } - public org.zendesk.client.v2.model.Request updateRequest(org.zendesk.client.v2.model.Request request) { - checkHasId(request); - return complete(submit(req("PUT", tmpl("/requests/{id}.json").set("id", request.getId()), - JSON, json(Collections.singletonMap("request", request))), - handle(org.zendesk.client.v2.model.Request.class, "request"))); - } + public org.zendesk.client.v2.model.Request getRequest(long id) { + return complete( + submit( + req("GET", tmpl("/requests/{id}.json").set("id", id)), + handle(org.zendesk.client.v2.model.Request.class, "request"))); + } - public Iterable getRequestComments(org.zendesk.client.v2.model.Request request) { - checkHasId(request); - return getRequestComments(request.getId()); - } + public org.zendesk.client.v2.model.Request createRequest( + org.zendesk.client.v2.model.Request request) { + return complete( + submit( + req( + "POST", + cnst("/requests.json"), + JSON, + json(Collections.singletonMap("request", request))), + handle(org.zendesk.client.v2.model.Request.class, "request"))); + } - public Iterable getRequestComments(long id) { - return new PagedIterable<>(tmpl("/requests/{id}/comments.json").set("id", id), - handleList(Comment.class, "comments")); - } + public org.zendesk.client.v2.model.Request updateRequest( + org.zendesk.client.v2.model.Request request) { + checkHasId(request); + return complete( + submit( + req( + "PUT", + tmpl("/requests/{id}.json").set("id", request.getId()), + JSON, + json(Collections.singletonMap("request", request))), + handle(org.zendesk.client.v2.model.Request.class, "request"))); + } - public Iterable getTicketComments(long id) { - return getTicketComments(id, SortOrder.ASCENDING); - } + public Iterable getRequestComments(org.zendesk.client.v2.model.Request request) { + checkHasId(request); + return getRequestComments(request.getId()); + } - public Iterable getTicketComments(long id, SortOrder order) { - return new PagedIterable<>( - tmpl("/tickets/{id}/comments.json?sort_order={order}") - .set("id", id) - .set("order", order.getQueryParameter()), - handleList(Comment.class, "comments")); - } + public Iterable getRequestComments(long id) { + return new PagedIterable<>( + cbp("/requests/{id}/comments.json").set("id", id), handleList(Comment.class, "comments")); + } - public Comment getRequestComment(org.zendesk.client.v2.model.Request request, Comment comment) { - checkHasId(comment); - return getRequestComment(request, comment.getId()); - } + public Iterable getTicketComments(long id) { + return getTicketComments(id, SortOrder.ASCENDING); + } - public Comment getRequestComment(org.zendesk.client.v2.model.Request request, long commentId) { - checkHasId(request); - return getRequestComment(request.getId(), commentId); - } + public Iterable getTicketComments(long id, SortOrder order) { + return new PagedIterable<>( + tmpl("/tickets/{id}/comments.json?sort_order={order}") + .set("id", id) + .set("order", order.getQueryParameter()), + handleList(Comment.class, "comments")); + } - public Comment getRequestComment(long requestId, long commentId) { - return complete(submit(req("GET", tmpl("/requests/{requestId}/comments/{commentId}.json") - .set("requestId", requestId) - .set("commentId", commentId)), - handle(Comment.class, "comment"))); - } + public Comment getRequestComment(org.zendesk.client.v2.model.Request request, Comment comment) { + checkHasId(comment); + return getRequestComment(request, comment.getId()); + } - public Ticket createComment(long ticketId, Comment comment) { - Ticket ticket = new Ticket(); - ticket.setComment(comment); - return complete(submit(req("PUT", tmpl("/tickets/{id}.json").set("id", ticketId), JSON, - json(Collections.singletonMap("ticket", ticket))), - handle(Ticket.class, "ticket"))); - } + public Comment getRequestComment(org.zendesk.client.v2.model.Request request, long commentId) { + checkHasId(request); + return getRequestComment(request.getId(), commentId); + } - public Ticket createTicketFromTweet(long tweetId, long monitorId) { - Map map = new HashMap<>(); - map.put("twitter_status_message_id", tweetId); - map.put("monitored_twitter_handle_id", monitorId); + public Comment getRequestComment(long requestId, long commentId) { + return complete( + submit( + req( + "GET", + tmpl("/requests/{requestId}/comments/{commentId}.json") + .set("requestId", requestId) + .set("commentId", commentId)), + handle(Comment.class, "comment"))); + } - return complete(submit(req("POST", cnst("/channels/twitter/tickets.json"), JSON, - json(Collections.singletonMap("ticket", map))), - handle(Ticket.class, "ticket"))); - } + public Ticket createComment(long ticketId, Comment comment) { + Ticket ticket = new Ticket(); + ticket.setComment(comment); + return complete( + submit( + req( + "PUT", + tmpl("/tickets/{id}.json").set("id", ticketId), + JSON, + json(Collections.singletonMap("ticket", ticket))), + handle(Ticket.class, "ticket"))); + } - public Iterable getOrganizations() { - return new PagedIterable<>(cnst("/organizations.json"), - handleList(Organization.class, "organizations")); - } + public Ticket createTicketFromTweet(long tweetId, long monitorId) { + Map map = new HashMap<>(); + map.put("twitter_status_message_id", tweetId); + map.put("monitored_twitter_handle_id", monitorId); + + return complete( + submit( + req( + "POST", + cnst("/channels/twitter/tickets.json"), + JSON, + json(Collections.singletonMap("ticket", map))), + handle(Ticket.class, "ticket"))); + } - public Iterable getOrganizationsIncrementally(Date startTime) { - return new PagedIterable<>( - tmpl("/incremental/organizations.json{?start_time}") - .set("start_time", msToSeconds(startTime.getTime())), - handleIncrementalList(Organization.class, "organizations")); - } + public Iterable getOrganizations() { + return new PagedIterable<>( + cbp("/organizations.json"), handleList(Organization.class, "organizations")); + } - public Iterable getOrganizationFields() { - //The organization_fields api doesn't seem to support paging - return complete(submit(req("GET", cnst("/organization_fields.json")), - handleList(OrganizationField.class, "organization_fields"))); - } + public List getOrganizations(long id, long... ids) { + return complete( + submit( + req("GET", tmpl("/organizations/show_many.json{?ids}").set("ids", idArray(id, ids))), + handleList(Organization.class, "organizations"))); + } - public Iterable getAutoCompleteOrganizations(String name) { - if (name == null || name.length() < 2) { - throw new IllegalArgumentException("Name must be at least 2 characters long"); - } - return new PagedIterable<>(tmpl("/organizations/autocomplete.json{?name}").set("name", name), - handleList(Organization.class, "organizations")); - } + public Iterable getOrganizationsIncrementally(Date startTime) { + return new PagedIterable<>( + tmpl("/incremental/organizations.json{?start_time}") + .set("start_time", msToSeconds(startTime.getTime())), + handleIncrementalList(Organization.class, "organizations")); + } - // TODO getOrganizationRelatedInformation + public Iterable getOrganizationFields() { + // The organization_fields api doesn't seem to support paging + return complete( + submit( + req("GET", cnst("/organization_fields.json")), + handleList(OrganizationField.class, "organization_fields"))); + } - public Organization getOrganization(long id) { - return complete(submit(req("GET", tmpl("/organizations/{id}.json").set("id", id)), - handle(Organization.class, "organization"))); + public Iterable getAutoCompleteOrganizations(String name) { + if (name == null || name.length() < 2) { + throw new IllegalArgumentException("Name must be at least 2 characters long"); } + return new PagedIterable<>( + tmpl("/organizations/autocomplete.json{?name}").set("name", name), + handleList(Organization.class, "organizations")); + } - public Organization createOrganization(Organization organization) { - return complete(submit(req("POST", cnst("/organizations.json"), JSON, json( - Collections.singletonMap("organization", organization))), handle(Organization.class, "organization"))); - } + // TODO getOrganizationRelatedInformation - public JobStatus createOrganizations(Organization... organizations) { - return createOrganizations(Arrays.asList(organizations)); - } + public Organization getOrganization(long id) { + return complete( + submit( + req("GET", tmpl("/organizations/{id}.json").set("id", id)), + handle(Organization.class, "organization"))); + } - public JobStatus createOrganizations(List organizations) { - return complete(createOrganizationsAsync(organizations)); - } + public Organization createOrganization(Organization organization) { + return complete( + submit( + req( + "POST", + cnst("/organizations.json"), + JSON, + json(Collections.singletonMap("organization", organization))), + handle(Organization.class, "organization"))); + } - public ListenableFuture createOrganizationsAsync(List organizations) { - return submit(req("POST", cnst("/organizations/create_many.json"), JSON, json( - Collections.singletonMap("organizations", organizations))), handleJobStatus()); - } + public JobStatus createOrganizations(Organization... organizations) { + return createOrganizations(Arrays.asList(organizations)); + } - public Organization updateOrganization(Organization organization) { - checkHasId(organization); - return complete(submit(req("PUT", tmpl("/organizations/{id}.json").set("id", organization.getId()), JSON, json( - Collections.singletonMap("organization", organization))), handle(Organization.class, "organization"))); - } + public JobStatus createOrganizations(List organizations) { + return complete(createOrganizationsAsync(organizations)); + } - public JobStatus updateOrganizations(Organization... organizations) { - return updateOrganizations(Arrays.asList(organizations)); - } + public ListenableFuture createOrganizationsAsync(List organizations) { + return submit( + req( + "POST", + cnst("/organizations/create_many.json"), + JSON, + json(Collections.singletonMap("organizations", organizations))), + handleJobStatus()); + } - public JobStatus updateOrganizations(List organizations) { - return complete(updateOrganizationsAsync(organizations)); - } + public Organization createOrUpdateOrganization(Organization organization) { + return complete( + submit( + req( + "POST", + cnst("/organizations/create_or_update.json"), + JSON, + json(Collections.singletonMap("organization", organization))), + handle(Organization.class, "organization"))); + } - public ListenableFuture updateOrganizationsAsync(List organizations) { - return submit(req("PUT", cnst("/organizations/update_many.json"), JSON, json( - Collections.singletonMap("organizations", organizations))), handleJobStatus()); - } + public Organization updateOrganization(Organization organization) { + checkHasId(organization); + return complete( + submit( + req( + "PUT", + tmpl("/organizations/{id}.json").set("id", organization.getId()), + JSON, + json(Collections.singletonMap("organization", organization))), + handle(Organization.class, "organization"))); + } - public void deleteOrganization(Organization organization) { - checkHasId(organization); - deleteOrganization(organization.getId()); - } + public JobStatus updateOrganizations(Organization... organizations) { + return updateOrganizations(Arrays.asList(organizations)); + } - public void deleteOrganization(long id) { - complete(submit(req("DELETE", tmpl("/organizations/{id}.json").set("id", id)), handleStatus())); - } + public JobStatus updateOrganizations(List organizations) { + return complete(updateOrganizationsAsync(organizations)); + } - public Iterable lookupOrganizationsByExternalId(String externalId) { - if (externalId == null || externalId.length() < 2) { - throw new IllegalArgumentException("Name must be at least 2 characters long"); - } - return new PagedIterable<>( - tmpl("/organizations/search.json{?external_id}").set("external_id", externalId), - handleList(Organization.class, "organizations")); - } + public ListenableFuture updateOrganizationsAsync(List organizations) { + return submit( + req( + "PUT", + cnst("/organizations/update_many.json"), + JSON, + json(Collections.singletonMap("organizations", organizations))), + handleJobStatus()); + } - public Iterable getOrganizationMemberships() { - return new PagedIterable<>(cnst("/organization_memberships.json"), - handleList(OrganizationMembership.class, "organization_memberships")); - } + public void deleteOrganization(Organization organization) { + checkHasId(organization); + deleteOrganization(organization.getId()); + } - public Iterable getOrganizationMembershipsForOrg(long organization_id) { - return new PagedIterable<>(tmpl("/organizations/{organization_id}/organization_memberships.json") - .set("organization_id", organization_id), - handleList(OrganizationMembership.class, "organization_memberships")); - } + public void deleteOrganization(long id) { + complete(submit(req("DELETE", tmpl("/organizations/{id}.json").set("id", id)), handleStatus())); + } - public Iterable getOrganizationMembershipsForUser(long user_id) { - return new PagedIterable<>(tmpl("/users/{user_id}/organization_memberships.json").set("user_id", user_id), - handleList(OrganizationMembership.class, "organization_memberships")); - } + public JobStatus deleteOrganizations(long... ids) { + return complete( + submit( + req("DELETE", tmpl("/organizations/destroy_many.json{?ids}").set("ids", ids)), + handleJobStatus())); + } - public OrganizationMembership getOrganizationMembershipForUser(long user_id, long id) { - return complete(submit(req("GET", - tmpl("/users/{user_id}/organization_memberships/{id}.json").set("user_id", user_id).set("id", id)), - handle(OrganizationMembership.class, "organization_membership"))); + public Iterable lookupOrganizationsByExternalId(String externalId) { + if (externalId == null || externalId.length() == 0) { + throw new IllegalArgumentException("External ID must not be null or length 0"); } + return new PagedIterable<>( + tmpl("/organizations/search.json{?external_id}").set("external_id", externalId), + handleList(Organization.class, "organizations")); + } - public OrganizationMembership getOrganizationMembership(long id) { - return complete(submit(req("GET", - tmpl("/organization_memberships/{id}.json").set("id", id)), - handle(OrganizationMembership.class, "organization_membership"))); - } + public Iterable getOrganizationMemberships() { + return new PagedIterable<>( + cbp("/organization_memberships.json"), + handleList(OrganizationMembership.class, "organization_memberships")); + } - public OrganizationMembership createOrganizationMembership(OrganizationMembership organizationMembership) { - return complete(submit(req("POST", - cnst("/organization_memberships.json"), JSON, json( - Collections.singletonMap("organization_membership", - organizationMembership))), handle(OrganizationMembership.class, "organization_membership"))); - } + public Iterable getOrganizationMembershipsForOrg(long organization_id) { + return new PagedIterable<>( + cbp("/organizations/{organization_id}/organization_memberships.json") + .set("organization_id", organization_id), + handleList(OrganizationMembership.class, "organization_memberships")); + } - /** - * https://developer.zendesk.com/rest_api/docs/support/organization_memberships#create-many-memberships - */ - public JobStatus createOrganizationMemberships(OrganizationMembership... organizationMemberships) { - return createOrganizationMemberships(Arrays.asList(organizationMemberships)); - } + public Iterable getOrganizationMembershipsForUser(long user_id) { + return new PagedIterable<>( + cbp("/users/{user_id}/organization_memberships.json").set("user_id", user_id), + handleList(OrganizationMembership.class, "organization_memberships")); + } - /** - * https://developer.zendesk.com/rest_api/docs/support/organization_memberships#create-many-memberships - */ - public JobStatus createOrganizationMemberships(List organizationMemberships) { - return complete(createOrganizationMembershipsAsync(organizationMemberships)); - } + public OrganizationMembership getOrganizationMembershipForUser(long user_id, long id) { + return complete( + submit( + req( + "GET", + tmpl("/users/{user_id}/organization_memberships/{id}.json") + .set("user_id", user_id) + .set("id", id)), + handle(OrganizationMembership.class, "organization_membership"))); + } - /** - * https://developer.zendesk.com/rest_api/docs/support/organization_memberships#create-many-memberships - */ - public ListenableFuture createOrganizationMembershipsAsync( - List organizationMemberships) { - return submit(req("POST", cnst("/organization_memberships/create_many.json"), JSON, json( - Collections.singletonMap("organization_memberships", organizationMemberships))), handleJobStatus()); - } + public OrganizationMembership getOrganizationMembership(long id) { + return complete( + submit( + req("GET", tmpl("/organization_memberships/{id}.json").set("id", id)), + handle(OrganizationMembership.class, "organization_membership"))); + } - public void deleteOrganizationMembership(long id) { - complete(submit(req("DELETE", tmpl("/organization_memberships/{id}.json").set("id", id)), handleStatus())); - } + public OrganizationMembership createOrganizationMembership( + OrganizationMembership organizationMembership) { + return complete( + submit( + req( + "POST", + cnst("/organization_memberships.json"), + JSON, + json(Collections.singletonMap("organization_membership", organizationMembership))), + handle(OrganizationMembership.class, "organization_membership"))); + } - /** - * https://developer.zendesk.com/rest_api/docs/support/organization_memberships#bulk-delete-memberships - */ - public void deleteOrganizationMemberships(long id, long... ids) { - complete(submit(req("DELETE", tmpl("/organization_memberships/destroy_many.json{?ids}").set("ids", idArray(id, ids))), - handleStatus())); - } + /** + * https://developer.zendesk.com/rest_api/docs/support/organization_memberships#create-many-memberships + */ + public JobStatus createOrganizationMemberships( + OrganizationMembership... organizationMemberships) { + return createOrganizationMemberships(Arrays.asList(organizationMemberships)); + } - public Iterable getGroups() { - return new PagedIterable<>(cnst("/groups.json"), - handleList(Group.class, "groups")); - } + /** + * https://developer.zendesk.com/rest_api/docs/support/organization_memberships#create-many-memberships + */ + public JobStatus createOrganizationMemberships( + List organizationMemberships) { + return complete(createOrganizationMembershipsAsync(organizationMemberships)); + } - public Iterable getAssignableGroups() { - return new PagedIterable<>(cnst("/groups/assignable.json"), - handleList(Group.class, "groups")); - } + /** + * https://developer.zendesk.com/rest_api/docs/support/organization_memberships#create-many-memberships + */ + public ListenableFuture createOrganizationMembershipsAsync( + List organizationMemberships) { + return submit( + req( + "POST", + cnst("/organization_memberships/create_many.json"), + JSON, + json(Collections.singletonMap("organization_memberships", organizationMemberships))), + handleJobStatus()); + } - public Group getGroup(long id) { - return complete(submit(req("GET", tmpl("/groups/{id}.json").set("id", id)), - handle(Group.class, "group"))); - } + public void deleteOrganizationMembership(long id) { + complete( + submit( + req("DELETE", tmpl("/organization_memberships/{id}.json").set("id", id)), + handleStatus())); + } - public Group createGroup(Group group) { - return complete(submit(req("POST", cnst("/groups.json"), JSON, json( - Collections.singletonMap("group", group))), handle(Group.class, "group"))); - } + /** + * https://developer.zendesk.com/rest_api/docs/support/organization_memberships#bulk-delete-memberships + */ + public void deleteOrganizationMemberships(long id, long... ids) { + complete( + submit( + req( + "DELETE", + tmpl("/organization_memberships/destroy_many.json{?ids}") + .set("ids", idArray(id, ids))), + handleStatus())); + } - /** - * This API will be removed in a future release. The API endpoint does not exist. - * Instead, the {@link #createGroup(Group) createGroup} method should be called for each Group - * - * @see Zendesk Java Client Issue #111 - */ - @Deprecated - public List createGroups(Group... groups) { - return createGroups(Arrays.asList(groups)); - } + public Iterable getGroups() { + return new PagedIterable<>(cbp("/groups.json"), handleList(Group.class, "groups")); + } - /** - * This API will be removed in a future release. The API endpoint does not exist. - * Instead, the {@link #createGroup(Group) createGroup} method should be called for each Group - * - * @see Zendesk Java Client Issue #111 - */ - @Deprecated - public List createGroups(List groups) { - throw new ZendeskException("API Endpoint for createGroups does not exist."); - } + public Iterable getAssignableGroups() { + return new PagedIterable<>(cbp("/groups/assignable.json"), handleList(Group.class, "groups")); + } - public Group updateGroup(Group group) { - checkHasId(group); - return complete(submit(req("PUT", tmpl("/groups/{id}.json").set("id", group.getId()), JSON, json( - Collections.singletonMap("group", group))), handle(Group.class, "group"))); - } + public Group getGroup(long id) { + return complete( + submit(req("GET", tmpl("/groups/{id}.json").set("id", id)), handle(Group.class, "group"))); + } - public void deleteGroup(Group group) { - checkHasId(group); - deleteGroup(group.getId()); - } + public Group createGroup(Group group) { + return complete( + submit( + req("POST", cnst("/groups.json"), JSON, json(Collections.singletonMap("group", group))), + handle(Group.class, "group"))); + } - public void deleteGroup(long id) { - complete(submit(req("DELETE", tmpl("/groups/{id}.json").set("id", id)), handleStatus())); - } + /** + * This API will be removed in a future release. The API endpoint does not exist. Instead, the + * {@link #createGroup(Group) createGroup} method should be called for each Group + * + * @see Zendesk Java Client + * Issue #111 + */ + @Deprecated + public List createGroups(Group... groups) { + return createGroups(Arrays.asList(groups)); + } - public Iterable getMacros(){ - return new PagedIterable<>(cnst("/macros.json"), - handleList(Macro.class, "macros")); - } + /** + * This API will be removed in a future release. The API endpoint does not exist. Instead, the + * {@link #createGroup(Group) createGroup} method should be called for each Group + * + * @see Zendesk Java Client + * Issue #111 + */ + @Deprecated + public List createGroups(List groups) { + throw new ZendeskException("API Endpoint for createGroups does not exist."); + } + + public Group updateGroup(Group group) { + checkHasId(group); + return complete( + submit( + req( + "PUT", + tmpl("/groups/{id}.json").set("id", group.getId()), + JSON, + json(Collections.singletonMap("group", group))), + handle(Group.class, "group"))); + } + + public void deleteGroup(Group group) { + checkHasId(group); + deleteGroup(group.getId()); + } + + public void deleteGroup(long id) { + complete(submit(req("DELETE", tmpl("/groups/{id}.json").set("id", id)), handleStatus())); + } - public Macro getMacro(long macroId){ + public Iterable getMacros() { + return new PagedIterable<>(cbp("/macros.json"), handleList(Macro.class, "macros")); + } + + public Macro getMacro(long macroId) { - return complete(submit(req("GET", tmpl("/macros/{id}.json").set("id", macroId)), handle(Macro.class, "macro"))); + return complete( + submit( + req("GET", tmpl("/macros/{id}.json").set("id", macroId)), + handle(Macro.class, "macro"))); } public Macro createMacro(Macro macro) { - return complete(submit( - req("POST", cnst("/macros.json"), JSON, json(Collections.singletonMap("macro", macro))), - handle(Macro.class, "macro"))); + return complete( + submit( + req("POST", cnst("/macros.json"), JSON, json(Collections.singletonMap("macro", macro))), + handle(Macro.class, "macro"))); } public Macro updateMacro(Long macroId, Macro macro) { - return complete(submit(req("PUT", tmpl("/macros/{id}.json").set("id", macroId), JSON, - json(Collections.singletonMap("macro", macro))), handle(Macro.class, "macro"))); + return complete( + submit( + req( + "PUT", + tmpl("/macros/{id}.json").set("id", macroId), + JSON, + json(Collections.singletonMap("macro", macro))), + handle(Macro.class, "macro"))); } + public Ticket macrosShowChangesToTicket(long macroId) { + return complete( + submit( + req("GET", tmpl("/macros/{id}/apply.json").set("id", macroId)), + handle(TicketResult.class, "result"))) + .getTicket(); + } - public Ticket macrosShowChangesToTicket(long macroId) { - return complete(submit(req("GET", tmpl("/macros/{id}/apply.json").set("id", macroId)), - handle(TicketResult.class, "result"))).getTicket(); - } - - public Ticket macrosShowTicketAfterChanges(long ticketId, long macroId) { - return complete(submit(req("GET", tmpl("/tickets/{ticket_id}/macros/{id}/apply.json") + public Ticket macrosShowTicketAfterChanges(long ticketId, long macroId) { + return complete( + submit( + req( + "GET", + tmpl("/tickets/{ticket_id}/macros/{id}/apply.json") .set("ticket_id", ticketId) .set("id", macroId)), - handle(TicketResult.class, "result"))).getTicket(); - } + handle(TicketResult.class, "result"))) + .getTicket(); + } - public List addTagToTicket(long id, String... tags) { - return complete(submit( - req("PUT", tmpl("/tickets/{id}/tags.json").set("id", id), JSON, - json(Collections.singletonMap("tags", tags))), - handle(List.class, "tags"))); - } + public List addTagToTicket(long id, String... tags) { + return complete( + submit( + req( + "PUT", + tmpl("/tickets/{id}/tags.json").set("id", id), + JSON, + json(Collections.singletonMap("tags", tags))), + handle(List.class, "tags"))); + } - public List addTagToTopics(long id, String... tags) { - return complete(submit( - req("PUT", tmpl("/topics/{id}/tags.json").set("id", id), JSON, - json(Collections.singletonMap("tags", tags))), - handle(List.class, "tags"))); - } + public List addTagToTopics(long id, String... tags) { + return complete( + submit( + req( + "PUT", + tmpl("/topics/{id}/tags.json").set("id", id), + JSON, + json(Collections.singletonMap("tags", tags))), + handle(List.class, "tags"))); + } - public List addTagToOrganisations(long id, String... tags) { - return complete(submit( - req("PUT", tmpl("/organizations/{id}/tags.json").set("id", id), - JSON, json(Collections.singletonMap("tags", tags))), - handle(List.class, "tags"))); - } + public List addTagToOrganisations(long id, String... tags) { + return complete( + submit( + req( + "PUT", + tmpl("/organizations/{id}/tags.json").set("id", id), + JSON, + json(Collections.singletonMap("tags", tags))), + handle(List.class, "tags"))); + } - public List setTagOnTicket(long id, String... tags) { - return complete(submit( - req("POST", tmpl("/tickets/{id}/tags.json").set("id", id), - JSON, json(Collections.singletonMap("tags", tags))), - handle(List.class, "tags"))); - } + public List setTagOnTicket(long id, String... tags) { + return complete( + submit( + req( + "POST", + tmpl("/tickets/{id}/tags.json").set("id", id), + JSON, + json(Collections.singletonMap("tags", tags))), + handle(List.class, "tags"))); + } - public List setTagOnTopics(long id, String... tags) { - return complete(submit( - req("POST", tmpl("/topics/{id}/tags.json").set("id", id), JSON, - json(Collections.singletonMap("tags", tags))), - handle(List.class, "tags"))); - } + public List setTagOnTopics(long id, String... tags) { + return complete( + submit( + req( + "POST", + tmpl("/topics/{id}/tags.json").set("id", id), + JSON, + json(Collections.singletonMap("tags", tags))), + handle(List.class, "tags"))); + } - public List setTagOnOrganisations(long id, String... tags) { - return complete(submit( - req("POST", - tmpl("/organizations/{id}/tags.json").set("id", id), - JSON, json(Collections.singletonMap("tags", tags))), - handle(List.class, "tags"))); - } + public List setTagOnOrganisations(long id, String... tags) { + return complete( + submit( + req( + "POST", + tmpl("/organizations/{id}/tags.json").set("id", id), + JSON, + json(Collections.singletonMap("tags", tags))), + handle(List.class, "tags"))); + } - public List removeTagFromTicket(long id, String... tags) { - return complete(submit( - req("DELETE", tmpl("/tickets/{id}/tags.json").set("id", id), - JSON, json(Collections.singletonMap("tags", tags))), - handle(List.class, "tags"))); - } + public List removeTagFromTicket(long id, String... tags) { + return complete( + submit( + req( + "DELETE", + tmpl("/tickets/{id}/tags.json").set("id", id), + JSON, + json(Collections.singletonMap("tags", tags))), + handle(List.class, "tags"))); + } - public List removeTagFromTopics(long id, String... tags) { - return complete(submit( - req("DELETE", tmpl("/topics/{id}/tags.json").set("id", id), - JSON, json(Collections.singletonMap("tags", tags))), - handle(List.class, "tags"))); - } + public List removeTagFromTopics(long id, String... tags) { + return complete( + submit( + req( + "DELETE", + tmpl("/topics/{id}/tags.json").set("id", id), + JSON, + json(Collections.singletonMap("tags", tags))), + handle(List.class, "tags"))); + } - public List removeTagFromOrganisations(long id, String... tags) { - return complete(submit( - req("DELETE", - tmpl("/organizations/{id}/tags.json").set("id", id), - JSON, json(Collections.singletonMap("tags", tags))), - handle(List.class, "tags"))); - } + public List removeTagFromOrganisations(long id, String... tags) { + return complete( + submit( + req( + "DELETE", + tmpl("/organizations/{id}/tags.json").set("id", id), + JSON, + json(Collections.singletonMap("tags", tags))), + handle(List.class, "tags"))); + } - public Map getIncrementalTicketsResult(long unixEpochTime) { - return complete(submit( - req("GET", - tmpl("/exports/tickets.json?start_time={time}").set( - "time", unixEpochTime)), handle(Map.class))); - } + public Map getIncrementalTicketsResult(long unixEpochTime) { + return complete( + submit( + req("GET", tmpl("/exports/tickets.json?start_time={time}").set("time", unixEpochTime)), + handle(Map.class))); + } - public Iterable getGroupMemberships() { - return new PagedIterable<>(cnst("/group_memberships.json"), - handleList(GroupMembership.class, "group_memberships")); - } + public Iterable getGroupMemberships() { + return new PagedIterable<>( + cbp("/group_memberships.json"), handleList(GroupMembership.class, "group_memberships")); + } - public List getGroupMembershipByUser(long user_id) { - return complete(submit(req("GET", tmpl("/users/{user_id}/group_memberships.json").set("user_id", user_id)), - handleList(GroupMembership.class, "group_memberships"))); - } + public Iterable getGroupMembershipByUser(long user_id) { + return new PagedIterable<>( + cbp("/users/{user_id}/group_memberships.json").set("user_id", user_id), + handleList(GroupMembership.class, "group_memberships")); + } - public List getGroupMemberships(long group_id) { - return complete(submit(req("GET", tmpl("/groups/{group_id}/memberships.json").set("group_id", group_id)), - handleList(GroupMembership.class, "group_memberships"))); - } + public Iterable getGroupMemberships(long group_id) { + return new PagedIterable<>( + cbp("/groups/{group_id}/memberships.json").set("group_id", group_id), + handleList(GroupMembership.class, "group_memberships")); + } - public Iterable getAssignableGroupMemberships() { - return new PagedIterable<>(cnst("/group_memberships/assignable.json"), - handleList(GroupMembership.class, "group_memberships")); - } + public Iterable getAssignableGroupMemberships() { + return new PagedIterable<>( + cbp("/group_memberships/assignable.json"), + handleList(GroupMembership.class, "group_memberships")); + } - public List getAssignableGroupMemberships(long group_id) { - return complete(submit(req("GET", - tmpl("/groups/{group_id}/memberships/assignable.json").set("group_id", group_id)), - handleList(GroupMembership.class, "group_memberships"))); - } + public Iterable getAssignableGroupMemberships(long group_id) { + return new PagedIterable<>( + cbp("/groups/{group_id}/memberships/assignable.json").set("group_id", group_id), + handleList(GroupMembership.class, "group_memberships")); + } - public GroupMembership getGroupMembership(long id) { - return complete(submit(req("GET", tmpl("/group_memberships/{id}.json").set("id", id)), - handle(GroupMembership.class, "group_membership"))); - } + public GroupMembership getGroupMembership(long id) { + return complete( + submit( + req("GET", tmpl("/group_memberships/{id}.json").set("id", id)), + handle(GroupMembership.class, "group_membership"))); + } - public GroupMembership getGroupMembership(long user_id, long group_membership_id) { - return complete(submit(req("GET", tmpl("/users/{uid}/group_memberships/{gmid}.json").set("uid", user_id) - .set("gmid", group_membership_id)), - handle(GroupMembership.class, "group_membership"))); - } + public GroupMembership getGroupMembership(long user_id, long group_membership_id) { + return complete( + submit( + req( + "GET", + tmpl("/users/{uid}/group_memberships/{gmid}.json") + .set("uid", user_id) + .set("gmid", group_membership_id)), + handle(GroupMembership.class, "group_membership"))); + } - public GroupMembership createGroupMembership(GroupMembership groupMembership) { - return complete(submit(req("POST", cnst("/group_memberships.json"), JSON, json( - Collections.singletonMap("group_membership", groupMembership))), - handle(GroupMembership.class, "group_membership"))); - } + public GroupMembership createGroupMembership(GroupMembership groupMembership) { + return complete( + submit( + req( + "POST", + cnst("/group_memberships.json"), + JSON, + json(Collections.singletonMap("group_membership", groupMembership))), + handle(GroupMembership.class, "group_membership"))); + } - public GroupMembership createGroupMembership(long user_id, GroupMembership groupMembership) { - return complete(submit(req("POST", tmpl("/users/{id}/group_memberships.json").set("id", user_id), JSON, - json(Collections.singletonMap("group_membership", groupMembership))), - handle(GroupMembership.class, "group_membership"))); - } + public GroupMembership createGroupMembership(long user_id, GroupMembership groupMembership) { + return complete( + submit( + req( + "POST", + tmpl("/users/{id}/group_memberships.json").set("id", user_id), + JSON, + json(Collections.singletonMap("group_membership", groupMembership))), + handle(GroupMembership.class, "group_membership"))); + } - public void deleteGroupMembership(GroupMembership groupMembership) { - checkHasId(groupMembership); - deleteGroupMembership(groupMembership.getId()); - } + public void deleteGroupMembership(GroupMembership groupMembership) { + checkHasId(groupMembership); + deleteGroupMembership(groupMembership.getId()); + } - public void deleteGroupMembership(long id) { - complete(submit(req("DELETE", tmpl("/group_memberships/{id}.json").set("id", id)), handleStatus())); - } + public void deleteGroupMembership(long id) { + complete( + submit(req("DELETE", tmpl("/group_memberships/{id}.json").set("id", id)), handleStatus())); + } - public void deleteGroupMembership(long user_id, GroupMembership groupMembership) { - checkHasId(groupMembership); - deleteGroupMembership(user_id, groupMembership.getId()); - } + public void deleteGroupMembership(long user_id, GroupMembership groupMembership) { + checkHasId(groupMembership); + deleteGroupMembership(user_id, groupMembership.getId()); + } - public void deleteGroupMembership(long user_id, long group_membership_id) { - complete(submit(req("DELETE", tmpl("/users/{uid}/group_memberships/{gmid}.json").set("uid", user_id) - .set("gmid", group_membership_id)), handleStatus())); - } + public void deleteGroupMembership(long user_id, long group_membership_id) { + complete( + submit( + req( + "DELETE", + tmpl("/users/{uid}/group_memberships/{gmid}.json") + .set("uid", user_id) + .set("gmid", group_membership_id)), + handleStatus())); + } - public List setGroupMembershipAsDefault(long user_id, GroupMembership groupMembership) { - checkHasId(groupMembership); - return complete(submit(req("PUT", tmpl("/users/{uid}/group_memberships/{gmid}/make_default.json") - .set("uid", user_id).set("gmid", groupMembership.getId()), JSON, json( - Collections.singletonMap("group_memberships", groupMembership))), - handleList(GroupMembership.class, "results"))); - } + public List setGroupMembershipAsDefault( + long user_id, GroupMembership groupMembership) { + checkHasId(groupMembership); + return complete( + submit( + req( + "PUT", + tmpl("/users/{uid}/group_memberships/{gmid}/make_default.json") + .set("uid", user_id) + .set("gmid", groupMembership.getId()), + JSON, + json(Collections.singletonMap("group_memberships", groupMembership))), + handleList(GroupMembership.class, "results"))); + } - public Iterable getForums() { - return new PagedIterable<>(cnst("/forums.json"), handleList(Forum.class, "forums")); - } + public Iterable getForums() { + return new PagedIterable<>(cnst("/forums.json"), handleList(Forum.class, "forums")); + } - public List getForums(long category_id) { - return complete(submit(req("GET", tmpl("/categories/{id}/forums.json").set("id", category_id)), - handleList(Forum.class, "forums"))); - } + public List getForums(long category_id) { + return complete( + submit( + req("GET", tmpl("/categories/{id}/forums.json").set("id", category_id)), + handleList(Forum.class, "forums"))); + } - public Forum getForum(long id) { - return complete(submit(req("GET", tmpl("/forums/{id}.json").set("id", id)), - handle(Forum.class, "forum"))); - } + public Forum getForum(long id) { + return complete( + submit(req("GET", tmpl("/forums/{id}.json").set("id", id)), handle(Forum.class, "forum"))); + } - public Forum createForum(Forum forum) { - return complete(submit(req("POST", cnst("/forums.json"), JSON, json( - Collections.singletonMap("forum", forum))), handle(Forum.class, "forum"))); - } + public Forum createForum(Forum forum) { + return complete( + submit( + req("POST", cnst("/forums.json"), JSON, json(Collections.singletonMap("forum", forum))), + handle(Forum.class, "forum"))); + } - public Forum updateForum(Forum forum) { - checkHasId(forum); - return complete(submit(req("PUT", tmpl("/forums/{id}.json").set("id", forum.getId()), JSON, json( - Collections.singletonMap("forum", forum))), handle(Forum.class, "forum"))); - } + public Forum updateForum(Forum forum) { + checkHasId(forum); + return complete( + submit( + req( + "PUT", + tmpl("/forums/{id}.json").set("id", forum.getId()), + JSON, + json(Collections.singletonMap("forum", forum))), + handle(Forum.class, "forum"))); + } - public void deleteForum(Forum forum) { - checkHasId(forum); - complete(submit(req("DELETE", tmpl("/forums/{id}.json").set("id", forum.getId())), handleStatus())); - } + public void deleteForum(Forum forum) { + checkHasId(forum); + complete( + submit(req("DELETE", tmpl("/forums/{id}.json").set("id", forum.getId())), handleStatus())); + } - public Iterable getTopics() { - return new PagedIterable<>(cnst("/topics.json"), handleList(Topic.class, "topics")); - } + public Iterable getTopics() { + return new PagedIterable<>(cnst("/topics.json"), handleList(Topic.class, "topics")); + } - public List getTopics(long forum_id) { - return complete(submit(req("GET", tmpl("/forums/{id}/topics.json").set("id", forum_id)), - handleList(Topic.class, "topics"))); - } + public List getTopics(long forum_id) { + return complete( + submit( + req("GET", tmpl("/forums/{id}/topics.json").set("id", forum_id)), + handleList(Topic.class, "topics"))); + } - public List getTopicsByUser(long user_id) { - return complete(submit(req("GET", tmpl("/users/{id}/topics.json").set("id", user_id)), - handleList(Topic.class, "topics"))); - } + public List getTopicsByUser(long user_id) { + return complete( + submit( + req("GET", tmpl("/users/{id}/topics.json").set("id", user_id)), + handleList(Topic.class, "topics"))); + } - public Topic getTopic(long id) { - return complete(submit(req("GET", tmpl("/topics/{id}.json").set("id", id)), - handle(Topic.class, "topic"))); - } + public Topic getTopic(long id) { + return complete( + submit(req("GET", tmpl("/topics/{id}.json").set("id", id)), handle(Topic.class, "topic"))); + } - public Topic createTopic(Topic topic) { - checkHasId(topic); - return complete(submit(req("POST", cnst("/topics.json"), JSON, json( - Collections.singletonMap("topic", topic))), handle(Topic.class, "topic"))); - } + public Topic createTopic(Topic topic) { + checkHasId(topic); + return complete( + submit( + req("POST", cnst("/topics.json"), JSON, json(Collections.singletonMap("topic", topic))), + handle(Topic.class, "topic"))); + } - public Topic importTopic(Topic topic) { - checkHasId(topic); - return complete(submit(req("POST", cnst("/import/topics.json"), JSON, json( - Collections.singletonMap("topic", topic))), handle(Topic.class, "topic"))); - } + public Topic importTopic(Topic topic) { + checkHasId(topic); + return complete( + submit( + req( + "POST", + cnst("/import/topics.json"), + JSON, + json(Collections.singletonMap("topic", topic))), + handle(Topic.class, "topic"))); + } - public List getTopics(long id, long... ids) { - return complete(submit(req("POST", tmpl("/topics/show_many.json{?ids}").set("ids", idArray(id, ids))), - handleList(Topic.class, "topics"))); - } + public List getTopics(long id, long... ids) { + return complete( + submit( + req("POST", tmpl("/topics/show_many.json{?ids}").set("ids", idArray(id, ids))), + handleList(Topic.class, "topics"))); + } - public Topic updateTopic(Topic topic) { - checkHasId(topic); - return complete(submit(req("PUT", tmpl("/topics/{id}.json").set("id", topic.getId()), JSON, json( - Collections.singletonMap("topic", topic))), handle(Topic.class, "topic"))); - } + public Topic updateTopic(Topic topic) { + checkHasId(topic); + return complete( + submit( + req( + "PUT", + tmpl("/topics/{id}.json").set("id", topic.getId()), + JSON, + json(Collections.singletonMap("topic", topic))), + handle(Topic.class, "topic"))); + } - public void deleteTopic(Topic topic) { - checkHasId(topic); - complete(submit(req("DELETE", tmpl("/topics/{id}.json").set("id", topic.getId())), handleStatus())); - } + public void deleteTopic(Topic topic) { + checkHasId(topic); + complete( + submit(req("DELETE", tmpl("/topics/{id}.json").set("id", topic.getId())), handleStatus())); + } - // https://support.zendesk.com/hc/communities/public/posts/203464106-Managing-Organization-Memberships-via-the-Zendesk-API - public List getOrganizationMembershipByUser(long user_id) { - return complete(submit(req("GET", tmpl("/users/{user_id}/organization_memberships.json").set("user_id", user_id)), - handleList(OrganizationMembership.class, "organization_memberships"))); - } + // https://support.zendesk.com/hc/communities/public/posts/203464106-Managing-Organization-Memberships-via-the-Zendesk-API + public Iterable getOrganizationMembershipByUser(long user_id) { + return getOrganizationMembershipsForUser(user_id); + } - public OrganizationMembership getGroupOrganization(long user_id, long organization_membership_id) { - return complete(submit(req("GET", tmpl("/users/{uid}/organization_memberships/{oid}.json").set("uid", user_id) - .set("oid", organization_membership_id)), - handle(OrganizationMembership.class, "organization_membership"))); - } + public OrganizationMembership getGroupOrganization( + long user_id, long organization_membership_id) { + return complete( + submit( + req( + "GET", + tmpl("/users/{uid}/organization_memberships/{oid}.json") + .set("uid", user_id) + .set("oid", organization_membership_id)), + handle(OrganizationMembership.class, "organization_membership"))); + } - public OrganizationMembership createOrganizationMembership(long user_id, OrganizationMembership organizationMembership) { - return complete(submit(req("POST", tmpl("/users/{id}/organization_memberships.json").set("id", user_id), JSON, - json(Collections.singletonMap("organization_membership", organizationMembership))), - handle(OrganizationMembership.class, "organization_membership"))); - } + public OrganizationMembership createOrganizationMembership( + long user_id, OrganizationMembership organizationMembership) { + return complete( + submit( + req( + "POST", + tmpl("/users/{id}/organization_memberships.json").set("id", user_id), + JSON, + json(Collections.singletonMap("organization_membership", organizationMembership))), + handle(OrganizationMembership.class, "organization_membership"))); + } - public void deleteOrganizationMembership(long user_id, OrganizationMembership organizationMembership) { - checkHasId(organizationMembership); - deleteOrganizationMembership(user_id, organizationMembership.getId()); - } + public void deleteOrganizationMembership( + long user_id, OrganizationMembership organizationMembership) { + checkHasId(organizationMembership); + deleteOrganizationMembership(user_id, organizationMembership.getId()); + } + + public void deleteOrganizationMembership(long user_id, long organization_membership_id) { + complete( + submit( + req( + "DELETE", + tmpl("/users/{uid}/organization_memberships/{oid}.json") + .set("uid", user_id) + .set("oid", organization_membership_id)), + handleStatus())); + } + + public void unassignOrganizationMembership(long user_id, long organization_id) { + complete( + submit( + req( + "DELETE", + tmpl("/users/{uid}/organizations/{oid}.json") + .set("uid", user_id) + .set("oid", organization_id)), + handleStatus())); + } + + public List setOrganizationMembershipAsDefault( + long user_id, OrganizationMembership organizationMembership) { + checkHasId(organizationMembership); + return complete( + submit( + req( + "PUT", + tmpl("/users/{uid}/organization_memberships/{omid}/make_default.json") + .set("uid", user_id) + .set("omid", organizationMembership.getId()), + JSON, + json(Collections.singletonMap("organization_memberships", organizationMembership))), + handleList(OrganizationMembership.class, "results"))); + } + + // -- END BETA + + public Iterable getSearchResults(String query) { + return new PagedIterable<>( + tmpl("/search.json{?query}").set("query", query), handleSearchList("results")); + } + + public Iterable getSearchResults(Class type, String query) { + return getSearchResults(type, query, Collections.emptyMap()); + } + + /** + * @deprecated Use {@link #getSearchResults(Class, String, Map)} instead. + */ + @Deprecated + public Iterable getSearchResults( + Class type, String query, String params) { + /* + preserving backwards compatabile logic, this method will continue to do what it did before, which is "wrong" + in that you can't really specify things like sort order via this method + */ + + Map paramsMap = new HashMap<>(1); + paramsMap.put("params", params); + + return getSearchResults(type, query, paramsMap); + } + + public Iterable getSearchResults( + Class type, String query, String sortBy, SortOrder sortOrder) { + Map paramsMap = new HashMap<>(2); + paramsMap.put("sort_by", sortBy); + paramsMap.put("sort_order", sortOrder.getQueryParameter()); - public void deleteOrganizationMembership(long user_id, long organization_membership_id) { - complete(submit(req("DELETE", tmpl("/users/{uid}/organization_memberships/{oid}.json").set("uid", user_id) - .set("oid", organization_membership_id)), handleStatus())); + return getSearchResults(type, query, paramsMap); + } + + public Iterable getSearchResults( + Class type, String query, Map params) { + String typeName = getTypeName(type); + + if (typeName == null) { + return Collections.emptyList(); } - public List setOrganizationMembershipAsDefault(long user_id, OrganizationMembership organizationMembership) { - checkHasId(organizationMembership); - return complete(submit(req("PUT", tmpl("/users/{uid}/organization_memberships/{omid}/make_default.json") - .set("uid", user_id).set("omid", organizationMembership.getId()), JSON, json( - Collections.singletonMap("organization_memberships", organizationMembership))), - handleList(OrganizationMembership.class, "results"))); + TemplateUri templateUri = getSearchUri(params, query, typeName); + + return new PagedIterable<>(templateUri, handleList(type, "results")); + } + + /** + * Search API implementation with pagination support. + * + * @param searchType type of search entity like Ticket, User etc + * @param pageType page return type to which the search result will be deserialized + * @param query string used filter a type given by searchType + * @param queryParams additional parameters other than filter string like per_page, page etc + * @param sortBy name of any field of the searchType + * @param sortOrder sort order + */ + public Optional getSearchResults( + final Class searchType, + final Class pageType, + final String query, + final Map queryParams, + final String sortBy, + final SortOrder sortOrder) { + + String typeName = getTypeName(searchType); + + if (typeName == null) { + return Optional.empty(); } - //-- END BETA - public Iterable getSearchResults(String query) { - return new PagedIterable<>(tmpl("/search.json{?query}").set("query", query), - handleSearchList("results")); + final Map paramsMap = new HashMap<>(); + + if (queryParams != null) { + paramsMap.putAll(queryParams); } - public Iterable getSearchResults(Class type, String query) { - return getSearchResults(type, query, Collections.emptyMap()); + if (sortBy != null && sortOrder != null) { + paramsMap.put("sort_by", sortBy); + paramsMap.put("sort_order", sortOrder.getQueryParameter()); } - /** - * @deprecated Use {@link #getSearchResults(Class, String, Map)} instead. - */ - @Deprecated - public Iterable getSearchResults(Class type, String query, String params) { - /* - preserving backwards compatabile logic, this method will continue to do what it did before, which is "wrong" - in that you can't really specify things like sort order via this method - */ + final TemplateUri templateUri = getSearchUri(paramsMap, query, typeName); - Map paramsMap = new HashMap<>(1); - paramsMap.put("params", params); + return Optional.of(complete(submit(req("GET", templateUri.toString()), handle(pageType)))); + } - return getSearchResults(type, query, paramsMap); - } + /** + * Ticket Search API implementation with pagination support. + * + * @param query string used filter a type given by searchType + * @param queryParams additional parameters other than filter string like per_page, page etc + * @param sortBy name of any field of the searchType + * @param sortOrder sort order + */ + public Optional getSearchTicketResults( + final String query, + final Map queryParams, + final String sortBy, + final SortOrder sortOrder) { + + return getSearchResults(Ticket.class, TicketPage.class, query, queryParams, sortBy, sortOrder); + } + public void notifyApp(String json) { + complete(submit(req("POST", cnst("/apps/notify.json"), JSON, json.getBytes()), handleStatus())); + } - public Iterable getSearchResults(Class type, String query, String sortBy, SortOrder sortOrder) { - Map paramsMap = new HashMap<>(2); - paramsMap.put("sort_by", sortBy); - paramsMap.put("sort_order", sortOrder.getQueryParameter()); + public void updateInstallation(int id, String json) { + complete( + submit( + req("PUT", tmpl("/apps/installations/{id}.json").set("id", id), JSON, json.getBytes()), + handleStatus())); + } - return getSearchResults(type, query, paramsMap); - } + public Iterable getSatisfactionRatings() { + return new PagedIterable<>( + cbp("/satisfaction_ratings.json"), + handleList(SatisfactionRating.class, "satisfaction_ratings")); + } + public SatisfactionRating getSatisfactionRating(long id) { + return complete( + submit( + req("GET", tmpl("/satisfaction_ratings/{id}.json").set("id", id)), + handle(SatisfactionRating.class, "satisfaction_rating"))); + } - public Iterable getSearchResults(Class type, String query, Map params) { - String typeName = null; - for (Map.Entry> entry : searchResultTypes.entrySet()) { - if (type.equals(entry.getValue())) { - typeName = entry.getKey(); - break; - } - } - if (typeName == null) { - return Collections.emptyList(); - } + public SatisfactionRating createSatisfactionRating( + long ticketId, SatisfactionRating satisfactionRating) { + return complete( + submit( + req( + "POST", + tmpl("/tickets/{ticketId}/satisfaction_rating.json").set("ticketId", ticketId), + JSON, + json(Collections.singletonMap("satisfaction_rating", satisfactionRating))), + handle(SatisfactionRating.class, "satisfaction_rating"))); + } - StringBuilder uriTemplate = new StringBuilder("/search.json{?query"); //leave off ending curly brace + public SatisfactionRating createSatisfactionRating( + Ticket ticket, SatisfactionRating satisfactionRating) { + return createSatisfactionRating(ticket.getId(), satisfactionRating); + } - //we have to add each param name to the template so that when we call set() with a map, the entries get put in the uri - for (String paramName : params.keySet()) { - uriTemplate.append(",") - .append(paramName); - } + ////////////////////////////////////////////////////////////////////// + // Action methods for Dynamic Content - Items and Variants + ////////////////////////////////////////////////////////////////////// - uriTemplate.append("}"); + public Iterable getDynamicContentItems() { + return new PagedIterable<>( + cbp("/dynamic_content/items.json"), handleList(DynamicContentItem.class, "items")); + } - TemplateUri templateUri = tmpl(uriTemplate.toString()) - .set("query", query + "+type:" + typeName); + public DynamicContentItem getDynamicContentItem(long id) { + return complete( + submit( + req("GET", tmpl("/dynamic_content/items/{id}.json").set("id", id)), + handle(DynamicContentItem.class, "item"))); + } - if(params != null) { - templateUri.set(params); - } + public DynamicContentItem createDynamicContentItem(DynamicContentItem item) { + return complete( + submit( + req( + "POST", + cnst("/dynamic_content/items.json"), + JSON, + json(Collections.singletonMap("item", item))), + handle(DynamicContentItem.class, "item"))); + } - return new PagedIterable<>(templateUri, handleList(type, "results")); - } + public DynamicContentItem updateDynamicContentItem(DynamicContentItem item) { + checkHasId(item); + return complete( + submit( + req( + "PUT", + tmpl("/dynamic_content/items/{id}.json").set("id", item.getId()), + JSON, + json(Collections.singletonMap("item", item))), + handle(DynamicContentItem.class, "item"))); + } - public void notifyApp(String json) { - complete(submit(req("POST", cnst("/apps/notify.json"), JSON, json.getBytes()), handleStatus())); - } + public void deleteDynamicContentItem(DynamicContentItem item) { + checkHasId(item); + complete( + submit( + req("DELETE", tmpl("/dynamic_content/items/{id}.json").set("id", item.getId())), + handleStatus())); + } - public void updateInstallation(int id, String json) { - complete(submit(req("PUT", tmpl("/apps/installations/{id}.json").set("id", id), JSON, json.getBytes()), handleStatus())); - } + /** VARIANTS */ + public Iterable getDynamicContentItemVariants( + DynamicContentItem item) { + checkHasId(item); + return new PagedIterable<>( + cbp("/dynamic_content/items/{id}/variants.json").set("id", item.getId()), + handleList(DynamicContentItemVariant.class, "variants")); + } - public Iterable getSatisfactionRatings() { - return new PagedIterable<>(cnst("/satisfaction_ratings.json"), - handleList(SatisfactionRating.class, "satisfaction_ratings")); - } + public DynamicContentItemVariant getDynamicContentItemVariant(Long itemId, long id) { + return complete( + submit( + req( + "GET", + tmpl("/dynamic_content/items/{itemId}/variants/{id}.json") + .set("itemId", itemId) + .set("id", id)), + handle(DynamicContentItemVariant.class, "variant"))); + } - public SatisfactionRating getSatisfactionRating(long id) { - return complete(submit(req("GET", tmpl("/satisfaction_ratings/{id}.json").set("id", id)), - handle(SatisfactionRating.class, "satisfaction_rating"))); - } + public DynamicContentItemVariant createDynamicContentItemVariant( + Long itemId, DynamicContentItemVariant variant) { + checkHasItemId(itemId); + return complete( + submit( + req( + "POST", + tmpl("/dynamic_content/items/{id}/variants.json").set("id", itemId), + JSON, + json(Collections.singletonMap("variant", variant))), + handle(DynamicContentItemVariant.class, "variant"))); + } - public SatisfactionRating createSatisfactionRating(long ticketId, SatisfactionRating satisfactionRating) { - return complete(submit(req("POST", tmpl("/tickets/{ticketId}/satisfaction_rating.json") - .set("ticketId", ticketId), JSON, - json(Collections.singletonMap("satisfaction_rating", satisfactionRating))), - handle(SatisfactionRating.class, "satisfaction_rating"))); - } + public DynamicContentItemVariant updateDynamicContentItemVariant( + Long itemId, DynamicContentItemVariant variant) { + checkHasItemId(itemId); + checkHasId(variant); + return complete( + submit( + req( + "PUT", + tmpl("/dynamic_content/items/{itemId}/variants/{id}.json") + .set("itemId", itemId) + .set("id", variant.getId()), + JSON, + json(Collections.singletonMap("variant", variant))), + handle(DynamicContentItemVariant.class, "variant"))); + } - public SatisfactionRating createSatisfactionRating(Ticket ticket, SatisfactionRating satisfactionRating) { - return createSatisfactionRating(ticket.getId(), satisfactionRating); - } + public void deleteDynamicContentItemVariant(Long itemId, DynamicContentItemVariant variant) { + checkHasItemId(itemId); + checkHasId(variant); + complete( + submit( + req( + "DELETE", + tmpl("/dynamic_content/items/{itemId}/variants/{id}.json") + .set("itemId", itemId) + .set("id", variant.getId())), + handleStatus())); + } - ////////////////////////////////////////////////////////////////////// - // Action methods for Dynamic Content - Items and Variants - ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + // Action methods for Locales + ////////////////////////////////////////////////////////////////////// + + /** + * https://developer.zendesk.com/api-reference/ticketing/account-configuration/locales/#list-locales + * + * @return the translation locales available for the account. + * @since FIXME + */ + public Iterable getLocales() { + return new PagedIterable<>(cnst("/locales.json"), handleList(Locale.class, "locales")); + } - public Iterable getDynamicContentItems() { - return new PagedIterable<>(cnst("/dynamic_content/items.json"), handleList(DynamicContentItem.class, "items")); - } + // TODO search with query building API + + ////////////////////////////////////////////////////////////////////// + // Action methods for Help Center + ////////////////////////////////////////////////////////////////////// + /** + * Get all permission groups + * + * @return List of Permission Groups + */ + public Iterable getPermissionGroups() { + return new PagedIterable<>( + cnst("/guide/permission_groups.json"), + handleList(PermissionGroup.class, "permission_groups")); + } - public DynamicContentItem getDynamicContentItem(long id) { - return complete(submit(req("GET", tmpl("/dynamic_content/items/{id}.json").set("id", id)), handle(DynamicContentItem.class, "item"))); - } + /** + * Get permission group by id + * + * @param id + */ + public PermissionGroup getPermissionGroup(long id) { + return complete( + submit( + req("GET", tmpl("/guide/permission_groups/{id}.json").set("id", id)), + handle(PermissionGroup.class, "permission_group"))); + } - public DynamicContentItem createDynamicContentItem(DynamicContentItem item) { - return complete(submit(req("POST", cnst("/dynamic_content/items.json"), JSON, json( - Collections.singletonMap("item", item))), handle(DynamicContentItem.class, "item"))); - } + /** + * Create permission group + * + * @param permissionGroup + */ + public PermissionGroup createPermissionGroup(PermissionGroup permissionGroup) { + return complete( + submit( + req( + "POST", + tmpl("/guide/permission_groups.json"), + JSON, + json(Collections.singletonMap("permission_group", permissionGroup))), + handle(PermissionGroup.class, "permission_group"))); + } - public DynamicContentItem updateDynamicContentItem(DynamicContentItem item) { - checkHasId(item); - return complete(submit(req("PUT", tmpl("/dynamic_content/items/{id}.json").set("id", item.getId()), - JSON, json(Collections.singletonMap("item", item))), handle(DynamicContentItem.class, "item"))); - } + /** + * Update permission group + * + * @param permissionGroup + */ + public PermissionGroup updatePermissionGroup(PermissionGroup permissionGroup) { + checkHasId(permissionGroup); + return complete( + submit( + req( + "PUT", + tmpl("/guide/permission_groups/{id}.json").set("id", permissionGroup.getId()), + JSON, + json(Collections.singletonMap("permission_group", permissionGroup))), + handle(PermissionGroup.class, "permission_group"))); + } - public void deleteDynamicContentItem(DynamicContentItem item) { - checkHasId(item); - complete(submit(req("DELETE", tmpl("/dynamic_content/items/{id}.json").set("id", item.getId())), - handleStatus())); - } + /** + * Delete permission group + * + * @param permissionGroup + */ + public void deletePermissionGroup(PermissionGroup permissionGroup) { + checkHasId(permissionGroup); + deletePermissionGroup(permissionGroup.getId()); + } - /** VARIANTS */ + /** + * Delete permission group + * + * @param id + */ + public void deletePermissionGroup(long id) { + complete( + submit( + req("DELETE", tmpl("/guide/permission_groups/{id}.json").set("id", id)), + handleStatus())); + } - public Iterable getDynamicContentItemVariants(DynamicContentItem item) { - checkHasId(item); - return new PagedIterable<>( - tmpl("/dynamic_content/items/{id}/variants.json").set("id", item.getId()), - handleList(DynamicContentItemVariant.class, "variants")); - } + /** + * Get user segments + * + * @return List of User Segments + */ + public Iterable getUserSegments() { + return new PagedIterable<>( + cnst("/help_center/user_segments.json"), handleList(UserSegment.class, "user_segments")); + } - public DynamicContentItemVariant getDynamicContentItemVariant(Long itemId, long id) { - return complete(submit(req("GET", tmpl("/dynamic_content/items/{itemId}/variants/{id}.json").set("itemId", itemId).set("id", id)), - handle(DynamicContentItemVariant.class, "variant"))); - } + /** + * Returns the list of user segments that a particular user belongs to + * + * @return List of User Segments + */ + public Iterable getUserSegments(long id) { + return new PagedIterable<>( + tmpl("/help_center/users/{id}/user_segments.json").set("id", id), + handleList(UserSegment.class, "user_segments")); + } - public DynamicContentItemVariant createDynamicContentItemVariant(Long itemId, DynamicContentItemVariant variant) { - checkHasItemId(itemId); - return complete(submit(req("POST", tmpl("/dynamic_content/items/{id}/variants.json").set("id", itemId), - JSON, json(Collections.singletonMap("variant", variant))), handle(DynamicContentItemVariant.class, "variant"))); - } + /** + * Request only user segments applicable on the account's current Guide plan + * + * @return List of User Segments + */ + public Iterable getUserSegmentsApplicable() { + return new PagedIterable<>( + cnst("/help_center/user_segments/applicable.json"), + handleList(UserSegment.class, "user_segments")); + } - public DynamicContentItemVariant updateDynamicContentItemVariant(Long itemId, DynamicContentItemVariant variant) { - checkHasItemId(itemId); - checkHasId(variant); - return complete(submit(req("PUT", tmpl("/dynamic_content/items/{itemId}/variants/{id}.json").set("itemId", itemId).set("id", variant.getId()), - JSON, json(Collections.singletonMap("variant", variant))), handle(DynamicContentItemVariant.class, "variant"))); - } + /** + * Get user segment by id + * + * @param id + */ + public UserSegment getUserSegment(long id) { + return complete( + submit( + req("GET", tmpl("/help_center/user_segments/{id}.json").set("id", id)), + handle(UserSegment.class, "user_segment"))); + } - public void deleteDynamicContentItemVariant(Long itemId, DynamicContentItemVariant variant) { - checkHasItemId(itemId); - checkHasId(variant); - complete(submit(req("DELETE", tmpl("/dynamic_content/items/{itemId}/variants/{id}.json").set("itemId", itemId).set("id", variant.getId())), handleStatus())); - } + /** + * List Sections using a User Segment + * + * @param userSegment + * @return List of Sections + */ + public Iterable
getSections(UserSegment userSegment) { + checkHasId(userSegment); + return new PagedIterable<>( + tmpl("/help_center/user_segments/{id}/sections.json").set("id", userSegment.getId()), + handleList(Section.class, "sections")); + } - // TODO search with query building API + /** + * List Topics using a User Segment + * + * @param userSegment + * @return List of Topics + */ + public Iterable getTopics(UserSegment userSegment) { + checkHasId(userSegment); + return new PagedIterable<>( + tmpl("/help_center/user_segments/{id}/topics.json").set("id", userSegment.getId()), + handleList(Topic.class, "topics")); + } - ////////////////////////////////////////////////////////////////////// - // Action methods for Help Center - ////////////////////////////////////////////////////////////////////// - /** - * Get all permission groups - * - * @return List of Permission Groups - */ - public Iterable getPermissionGroups() { - return new PagedIterable<>(cnst("/guide/permission_groups.json"), - handleList(PermissionGroup.class, "permission_groups")); - } - /** - * Get permission group by id - * - * @param id - */ - public PermissionGroup getPermissionGroup(long id) { - return complete(submit(req("GET", tmpl("/guide/permission_groups/{id}.json").set("id", id)), - handle(PermissionGroup.class, "permission_group"))); - } - /** - * Create permission group - * - * @param permissionGroup - */ - public PermissionGroup createPermissionGroup(PermissionGroup permissionGroup) { - return complete(submit(req("POST", tmpl("/guide/permission_groups.json"), - JSON, json(Collections.singletonMap("permission_group", permissionGroup))), handle(PermissionGroup.class, "permission_group"))); - } - /** - * Update permission group - * - * @param permissionGroup - */ - public PermissionGroup updatePermissionGroup(PermissionGroup permissionGroup) { - checkHasId(permissionGroup); - return complete(submit(req("PUT", tmpl("/guide/permission_groups/{id}.json").set("id", permissionGroup.getId()), - JSON, json(Collections.singletonMap("permission_group", permissionGroup))), handle(PermissionGroup.class, "permission_group"))); - } - /** - * Delete permission group - * - * @param permissionGroup - */ - public void deletePermissionGroup(PermissionGroup permissionGroup) { - checkHasId(permissionGroup); - deletePermissionGroup(permissionGroup.getId()); - } - /** - * Delete permission group - * - * @param id - */ - public void deletePermissionGroup(long id) { - complete(submit(req("DELETE", tmpl("/guide/permission_groups/{id}.json").set("id", id)), - handleStatus())); - } - /** - * Get user segments - * - * @return List of User Segments - */ - public Iterable getUserSegments() { - return new PagedIterable<>(cnst("/help_center/user_segments.json"), - handleList(UserSegment.class, "user_segments")); - } - /** - * Returns the list of user segments that a particular user belongs to - * - * @return List of User Segments - */ - public Iterable getUserSegments(long id) { - return new PagedIterable<>(tmpl("/help_center/users/{id}/user_segments.json").set("id", id), - handleList(UserSegment.class, "user_segments")); - } - /** - * Request only user segments applicable on the account's current Guide plan - * - * @return List of User Segments - */ - public Iterable getUserSegmentsApplicable() { - return new PagedIterable<>(cnst("/help_center/user_segments/applicable.json"), - handleList(UserSegment.class, "user_segments")); - } - /** - * Get user segment by id - * - * @param id - */ - public UserSegment getUserSegment(long id) { - return complete(submit(req("GET", tmpl("/help_center/user_segments/{id}.json").set("id", id)), - handle(UserSegment.class, "user_segment"))); - } + /** + * Create User Segment + * + * @param userSegment + */ + public UserSegment createUserSegment(UserSegment userSegment) { + return complete( + submit( + req( + "POST", + tmpl("/help_center/user_segments.json"), + JSON, + json(Collections.singletonMap("user_segment", userSegment))), + handle(UserSegment.class, "user_segment"))); + } - /** - * List Sections using a User Segment - * - * @param userSegment - * @return List of Sections - */ - public Iterable
getSections(UserSegment userSegment) { - checkHasId(userSegment); - return new PagedIterable<>( - tmpl("/help_center/user_segments/{id}/sections.json").set("id", userSegment.getId()), - handleList(Section.class, "sections")); - } + /** + * Update User Segment + * + * @param userSegment + */ + public UserSegment updateUserSegment(UserSegment userSegment) { + checkHasId(userSegment); + return complete( + submit( + req( + "PUT", + tmpl("/help_center/user_segments/{id}.json").set("id", userSegment.getId()), + JSON, + json(Collections.singletonMap("user_segment", userSegment))), + handle(UserSegment.class, "user_segment"))); + } - /** - * List Topics using a User Segment - * - * @param userSegment - * @return List of Topics - */ - public Iterable getTopics(UserSegment userSegment) { - checkHasId(userSegment); - return new PagedIterable<>( - tmpl("/help_center/user_segments/{id}/topics.json").set("id", userSegment.getId()), - handleList(Topic.class, "topics")); - } + /** + * Delete User Segment + * + * @param userSegment + */ + public void deleteUserSegment(UserSegment userSegment) { + checkHasId(userSegment); + deleteUserSegment(userSegment.getId()); + } - /** - * Create User Segment - * - * @param userSegment - */ - public UserSegment createUserSegment(UserSegment userSegment) { - return complete(submit(req("POST", tmpl("/help_center/user_segments.json"), - JSON, json(Collections.singletonMap("user_segment", userSegment))), handle(UserSegment.class, "user_segment"))); - } - /** - * Update User Segment - * - * @param userSegment - */ - public UserSegment updateUserSegment(UserSegment userSegment) { - checkHasId(userSegment); - return complete(submit(req("PUT", tmpl("/help_center/user_segments/{id}.json").set("id", userSegment.getId()), - JSON, json(Collections.singletonMap("user_segment", userSegment))), handle(UserSegment.class, "user_segment"))); - } - /** - * Delete User Segment - * - * @param userSegment - */ - public void deleteUserSegment(UserSegment userSegment) { - checkHasId(userSegment); - deleteUserSegment(userSegment.getId()); - } + /** + * Delete User Segment + * + * @param id + */ + public void deleteUserSegment(long id) { + complete( + submit( + req("DELETE", tmpl("/help_center/user_segments/{id}.json").set("id", id)), + handleStatus())); + } - /** - * Delete User Segment - * - * @param id - */ - public void deleteUserSegment(long id) { - complete(submit(req("DELETE", tmpl("/help_center/user_segments/{id}.json").set("id", id)), - handleStatus())); - } + public Locales listHelpCenterLocales() { + return complete(submit(req("GET", cnst("/help_center/locales.json")), handle(Locales.class))); + } - public List getHelpCenterLocales() { - return complete(submit( - req("GET", cnst("/help_center/locales.json")), - handle(List.class, "locales"))); - } + /** + * @deprecated Use {@link Zendesk#listHelpCenterLocales()} instead + */ + @Deprecated + public List getHelpCenterLocales() { + return listHelpCenterLocales().getLocales(); + } - /** - * Get all articles from help center. - * - * @return List of Articles. - */ - public Iterable
getArticles() { - return new PagedIterable<>(cnst("/help_center/articles.json"), - handleList(Article.class, "articles")); - } + /** + * Get all articles from help center. + * + * @return List of Articles. + */ + public Iterable
getArticles() { + return new PagedIterable<>( + cbp("/help_center/articles.json"), handleList(Article.class, "articles")); + } - public Iterable
getArticles(String locale) { - return new PagedIterable<>(tmpl("/help_center/{locale}/articles.json").set("locale", locale), - handleList(Article.class, "articles")); - } + public Iterable
getArticles(String locale) { + return new PagedIterable<>( + cbp("/help_center/{locale}/articles.json").set("locale", locale), + handleList(Article.class, "articles")); + } - public Iterable
getArticles(Category category) { - checkHasId(category); - return new PagedIterable<>( - tmpl("/help_center/categories/{id}/articles.json").set("id", category.getId()), - handleList(Article.class, "articles")); - } + public Iterable
getArticles(Category category) { + checkHasId(category); + return new PagedIterable<>( + cbp("/help_center/categories/{id}/articles.json").set("id", category.getId()), + handleList(Article.class, "articles")); + } - public Iterable
getArticles(Section section) { - checkHasId(section); - return new PagedIterable<>( - tmpl("/help_center/sections/{id}/articles.json").set("id", section.getId()), - handleList(Article.class, "articles")); - } + public Iterable
getArticles(Category category, String locale) { + checkHasId(category); + return new PagedIterable<>( + cbp("/help_center/{locale}/categories/{id}/articles.json") + .set("id", category.getId()) + .set("locale", locale), + handleList(Article.class, "articles")); + } - public Iterable
getArticlesIncrementally(Date startTime) { - return new PagedIterable<>( - tmpl("/help_center/incremental/articles.json{?start_time}") - .set("start_time", msToSeconds(startTime.getTime())), - handleIncrementalList(Article.class, "articles")); - } + public Iterable
getArticles(Section section) { + checkHasId(section); + return new PagedIterable<>( + cbp("/help_center/sections/{id}/articles.json").set("id", section.getId()), + handleList(Article.class, "articles")); + } - public List
getArticlesFromPage(int page) { - return complete(submit(req("GET", tmpl("/help_center/articles.json?page={page}").set("page", page)), - handleList(Article.class, "articles"))); - } + public Iterable
getArticles(Section section, String locale) { + checkHasId(section); + return new PagedIterable<>( + cbp("/help_center/{locale}/sections/{id}/articles.json") + .set("id", section.getId()) + .set("locale", locale), + 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 Iterable
getArticlesIncrementally(Date startTime) { + return new PagedIterable<>( + tmpl("/help_center/incremental/articles.json{?start_time}") + .set("start_time", msToSeconds(startTime.getTime())), + handleIncrementalList(Article.class, "articles")); + } - public Iterable getArticleTranslations(Long articleId) { - return new PagedIterable<>( - tmpl("/help_center/articles/{articleId}/translations.json").set("articleId", articleId), - handleList(Translation.class, "translations")); - } + public List
getArticlesFromPage(int page) { + return complete( + submit( + req("GET", tmpl("/help_center/articles.json?page={page}").set("page", page)), + handleList(Article.class, "articles"))); + } - public Article createArticle(Article article) { - checkHasSectionId(article); - return complete(submit(req("POST", tmpl("/help_center/sections/{id}/articles.json").set("id", article.getSectionId()), - JSON, json(Collections.singletonMap("article", article))), handle(Article.class, "article"))); - } + 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 createArticle(Article article, boolean notifySubscribers) { - checkHasSectionId(article); + public Iterable getArticleTranslations(Long articleId) { + return new PagedIterable<>( + cbp("/help_center/articles/{articleId}/translations.json").set("articleId", articleId), + handleList(Translation.class, "translations")); + } - Map map = new HashMap(); - map.put("article", article); - map.put("notify_subscribers", notifySubscribers ? String.valueOf("true") : String.valueOf("false")); + public Translation showArticleTranslation(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"))); + } - return complete(submit(req("POST", tmpl("/help_center/sections/{id}/articles.json").set("id", article.getSectionId()), - JSON, json(Collections.unmodifiableMap(map))), handle(Article.class, "article"))); - } + public Article createArticle(Article article) { + checkHasSectionId(article); + return complete( + submit( + req( + "POST", + tmpl("/help_center/sections/{id}/articles.json").set("id", article.getSectionId()), + JSON, + json(Collections.singletonMap("article", article))), + handle(Article.class, "article"))); + } - public Article updateArticle(Article article) { - checkHasId(article); - return complete(submit(req("PUT", tmpl("/help_center/articles/{id}.json").set("id", article.getId()), - JSON, json(Collections.singletonMap("article", article))), handle(Article.class, "article"))); - } + public Article createArticle(Article article, boolean notifySubscribers) { + checkHasSectionId(article); + + Map map = new HashMap(); + map.put("article", article); + map.put( + "notify_subscribers", notifySubscribers ? String.valueOf("true") : String.valueOf("false")); + + return complete( + submit( + req( + "POST", + tmpl("/help_center/sections/{id}/articles.json").set("id", article.getSectionId()), + JSON, + json(Collections.unmodifiableMap(map))), + handle(Article.class, "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"))); - } + public Article updateArticle(Article article) { + checkHasId(article); + return complete( + submit( + req( + "PUT", + tmpl("/help_center/articles/{id}.json").set("id", article.getId()), + JSON, + json(Collections.singletonMap("article", article))), + handle(Article.class, "article"))); + } - 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"))); - } + 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"))); + } - public void deleteArticle(Article article) { - checkHasId(article); - complete(submit(req("DELETE", tmpl("/help_center/articles/{id}.json").set("id", article.getId())), - handleStatus())); - } + 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"))); + } - /** - * Delete translation. - * @param translation - */ - public void deleteTranslation(Translation translation) { - checkHasId(translation); - deleteTranslation(translation.getId()); - } + public void deleteArticle(Article article) { + checkHasId(article); + complete( + submit( + req("DELETE", tmpl("/help_center/articles/{id}.json").set("id", article.getId())), + handleStatus())); + } - /** - * Delete translation. - * @param translationId - */ - public void deleteTranslation(Long translationId) { - complete(submit(req("DELETE", tmpl("/help_center/translations/{id}.json").set("id", translationId)), - handleStatus())); - } + /** + * Delete translation. + * + * @param translation + */ + public void deleteTranslation(Translation translation) { + checkHasId(translation); + deleteTranslation(translation.getId()); + } + + /** + * Delete translation. + * + * @param translationId + */ + public void deleteTranslation(Long translationId) { + complete( + submit( + req("DELETE", tmpl("/help_center/translations/{id}.json").set("id", translationId)), + handleStatus())); + } + + /** + * Delete attachment from article. + * + * @param attachment + */ + public void deleteArticleAttachment(ArticleAttachments attachment) { + checkHasId(attachment); + deleteArticleAttachment(attachment.getId()); + } + + /** + * Delete attachment from article. + * + * @param id attachment identifier. + */ + public void deleteArticleAttachment(long id) { + complete( + submit( + req("DELETE", tmpl("/help_center/articles/attachments/{id}.json").set("id", id)), + handleStatus())); + } + + public Iterable getCategories() { + return new PagedIterable<>( + cnst("/help_center/categories.json"), 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 Iterable getCategoryTranslations(Long categoryId) { + return new PagedIterable<>( + tmpl("/help_center/categories/{categoryId}/translations.json") + .set("categoryId", categoryId), + handleList(Translation.class, "translations")); + } + + public Translation showCategoryTranslation(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"))); + } + + public Category updateCategory(Category category) { + checkHasId(category); + return complete( + submit( + req( + "PUT", + tmpl("/help_center/categories/{id}.json").set("id", category.getId()), + JSON, + json(Collections.singletonMap("category", category))), + handle(Category.class, "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"))); + } + + 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"))); + } + + public void deleteCategory(Category category) { + checkHasId(category); + complete( + submit( + req("DELETE", tmpl("/help_center/categories/{id}.json").set("id", category.getId())), + handleStatus())); + } + + public Iterable
getSections() { + return new PagedIterable<>( + cnst("/help_center/sections.json"), handleList(Section.class, "sections")); + } + + public Iterable
getSections(Category category) { + checkHasId(category); + return new PagedIterable<>( + tmpl("/help_center/categories/{id}/sections.json").set("id", category.getId()), + 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 Iterable getSectionTranslations(Long sectionId) { + return new PagedIterable<>( + tmpl("/help_center/sections/{sectionId}/translations.json").set("sectionId", sectionId), + handleList(Translation.class, "translations")); + } + + public Translation showSectionTranslation(long sectionId, String locale) { + 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()), + JSON, + json(Collections.singletonMap("section", section))), + handle(Section.class, "section"))); + } + + public Section updateSection(Section section) { + checkHasId(section); + return complete( + submit( + req( + "PUT", + tmpl("/help_center/sections/{id}.json").set("id", section.getId()), + JSON, + json(Collections.singletonMap("section", section))), + handle(Section.class, "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"))); + } + + 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"))); + } + + public void deleteSection(Section section) { + checkHasId(section); + complete( + submit( + req("DELETE", tmpl("/help_center/sections/{id}.json").set("id", section.getId())), + handleStatus())); + } + + public Iterable getUserSubscriptions(User user) { + checkHasId(user); + return getUserSubscriptions(user.getId()); + } + + public Iterable getUserSubscriptions(Long userId) { + return new PagedIterable<>( + tmpl("/help_center/users/{userId}/subscriptions.json").set("userId", userId), + handleList(Subscription.class, "subscriptions")); + } + + public Iterable getArticleSubscriptions(Long articleId) { + return getArticleSubscriptions(articleId, null); + } + + public Iterable getArticleSubscriptions(Long articleId, String locale) { + return new PagedIterable<>( + tmpl("/help_center{/locale}/articles/{articleId}/subscriptions.json") + .set("locale", locale) + .set("articleId", articleId), + handleList(Subscription.class, "subscriptions")); + } + + public Iterable getSectionSubscriptions(Long sectionId) { + return getSectionSubscriptions(sectionId, null); + } + + public Iterable getSectionSubscriptions(Long sectionId, String locale) { + return new PagedIterable<>( + tmpl("/help_center{/locale}/sections/{sectionId}/subscriptions.json") + .set("locale", locale) + .set("sectionId", sectionId), + handleList(Subscription.class, "subscriptions")); + } + + /** + * Get a list of the current business hours schedules + * + * @return A List of Schedules + */ + public Iterable getSchedules() { + return complete( + submit( + req("GET", cnst("/business_hours/schedules.json")), + handleList(Schedule.class, "schedules"))); + } + + public Schedule getSchedule(Schedule schedule) { + checkHasId(schedule); + return getSchedule(schedule.getId()); + } - /** - * Delete attachment from article. - * @param attachment - */ - public void deleteArticleAttachment(ArticleAttachments attachment) { - checkHasId(attachment); - deleteArticleAttachment(attachment.getId()); - } + public Schedule getSchedule(Long scheduleId) { + return complete( + submit( + req("GET", tmpl("/business_hours/schedules/{id}.json").set("id", scheduleId)), + handle(Schedule.class, "schedule"))); + } - /** - * Delete attachment from article. - * @param id attachment identifier. - */ - public void deleteArticleAttachment(long id) { - complete(submit(req("DELETE", tmpl("/help_center/articles/attachments/{id}.json").set("id", id)), handleStatus())); - } + public Iterable getHolidaysForSchedule(Schedule schedule) { + checkHasId(schedule); + return getHolidaysForSchedule(schedule.getId()); + } - public Iterable getCategories() { - return new PagedIterable<>(cnst("/help_center/categories.json"), - handleList(Category.class, "categories")); - } + public Iterable getHolidaysForSchedule(Long scheduleId) { + return complete( + submit( + req("GET", tmpl("/business_hours/schedules/{id}/holidays.json").set("id", scheduleId)), + handleList(Holiday.class, "holidays"))); + } - public Category getCategory(long id) { - return complete(submit(req("GET", tmpl("/help_center/categories/{id}.json").set("id", id)), - handle(Category.class, "category"))); - } + public ContentTag getContentTag(String contentTagId) { + return complete( + submit( + req("GET", tmpl("/guide/content_tags/{id}").set("id", contentTagId)), + handle(ContentTag.class, "content_tag"))); + } - public Iterable getCategoryTranslations(Long categoryId) { - return new PagedIterable<>( - tmpl("/help_center/categories/{categoryId}/translations.json").set("categoryId", categoryId), - handleList(Translation.class, "translations")); - } - 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"))); - } + public ContentTag createContentTag(ContentTag contentTag) { + checkHasName(contentTag); + return complete( + submit( + req( + "POST", + cnst("/guide/content_tags"), + JSON, + json(Collections.singletonMap("content_tag", contentTag))), + handle(ContentTag.class, "content_tag"))); + } - public Category updateCategory(Category category) { - checkHasId(category); - return complete(submit(req("PUT", tmpl("/help_center/categories/{id}.json").set("id", category.getId()), - JSON, json(Collections.singletonMap("category", category))), handle(Category.class, "category"))); - } + public ContentTag updateContentTag(ContentTag contentTag) { + checkHasId(contentTag); + checkHasName(contentTag); + return complete( + submit( + req( + "PUT", + tmpl("/guide/content_tags/{id}").set("id", contentTag.getId()), + JSON, + json(Collections.singletonMap("content_tag", contentTag))), + handle(ContentTag.class, "content_tag"))); + } - 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"))); - } + public void deleteContentTag(ContentTag contentTag) { + checkHasId(contentTag); + complete( + submit( + req("DELETE", tmpl("/guide/content_tags/{id}").set("id", contentTag.getId())), + handleStatus())); + } - 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"))); - } + public Iterable getContentTags() { + int defaultPageSize = 10; + return getContentTags(defaultPageSize, null); + } - public void deleteCategory(Category category) { - checkHasId(category); - complete(submit(req("DELETE", tmpl("/help_center/categories/{id}.json").set("id", category.getId())), - handleStatus())); - } + public Iterable getContentTags(int pageSize) { + return getContentTags(pageSize, null); + } - public Iterable
getSections() { - return new PagedIterable<>( - cnst("/help_center/sections.json"), handleList(Section.class, "sections")); - } + public Iterable getContentTags(int pageSize, String namePrefix) { + Function afterCursorUriBuilder = + (String afterCursor) -> buildContentTagsSearchUrl(pageSize, namePrefix, afterCursor); + return new PagedIterable<>( + afterCursorUriBuilder.apply(null), + handleListWithAfterCursorButNoLinks(ContentTag.class, afterCursorUriBuilder, "records")); + } - public Iterable
getSections(Category category) { - checkHasId(category); - return new PagedIterable<>( - tmpl("/help_center/categories/{id}/sections.json").set("id", category.getId()), - handleList(Section.class, "sections")); - } + public Iterable getJiraLinks() { + return new PagedIterable<>(cnst("/jira/links"), handleList(JiraLink.class, "links")); + } - public Section getSection(long id) { - return complete(submit(req("GET", tmpl("/help_center/sections/{id}.json").set("id", id)), - handle(Section.class, "section"))); - } + private Uri buildContentTagsSearchUrl(int pageSize, String namePrefixFilter, String afterCursor) { + final StringBuilder uriBuilder = + new StringBuilder("/guide/content_tags?page[size]=").append(pageSize); - public Iterable getSectionTranslations(Long sectionId) { - return new PagedIterable<>( - tmpl("/help_center/sections/{sectionId}/translations.json").set("sectionId", sectionId), - handleList(Translation.class, "translations")); + if (namePrefixFilter != null) { + uriBuilder.append("&filter[name_prefix]=").append(encodeUrl(namePrefixFilter)); } - public Section createSection(Section section) { - checkHasCategoryId(section); - return complete(submit(req("POST", tmpl("/help_center/categories/{id}/sections.json").set("id", section.getCategoryId()), - JSON, json(Collections.singletonMap("section", section))), handle(Section.class, "section"))); + if (afterCursor != null) { + uriBuilder.append("&page[after]=").append(encodeUrl(afterCursor)); } + return cnst(uriBuilder.toString()); + } - public Section updateSection(Section section) { - checkHasId(section); - return complete(submit(req("PUT", tmpl("/help_center/sections/{id}.json").set("id", section.getId()), - JSON, json(Collections.singletonMap("section", section))), handle(Section.class, "section"))); - } + public List getTimeZones() { + return complete( + submit(req("GET", cnst("/time_zones.json")), handleList(TimeZone.class, "time_zones"))); + } - 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"))); - } + ////////////////////////////////////////////////////////////////////// + // Helper methods + ////////////////////////////////////////////////////////////////////// - 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"))); + private byte[] json(Object object) { + try { + return mapper.writeValueAsBytes(object); + } catch (JsonProcessingException e) { + throw new ZendeskException(e.getMessage(), e); } + } - public void deleteSection(Section section) { - checkHasId(section); - complete(submit(req("DELETE", tmpl("/help_center/sections/{id}.json").set("id", section.getId())), - handleStatus())); - } + private ListenableFuture submit( + Request request, ZendeskAsyncCompletionHandler handler) { + if (logger.isDebugEnabled()) { + if (request.getStringData() != null) { + logger.debug( + "Request {} {}\n{}", request.getMethod(), request.getUrl(), request.getStringData()); + } else if (request.getByteData() != null) { + logger.debug( + "Request {} {} {} {} bytes", + request.getMethod(), + request.getUrl(), + request.getHeaders().get("Content-type"), + request.getByteData().length); + } else { + logger.debug("Request {} {}", request.getMethod(), request.getUrl()); + } + } + return client.executeRequest(request, handler); + } - public Iterable getUserSubscriptions(User user) { - checkHasId(user); - return getUserSubscriptions(user.getId()); + private abstract static class ZendeskAsyncCompletionHandler extends AsyncCompletionHandler { + @Override + public void onThrowable(Throwable t) { + if (t instanceof IOException) { + throw new ZendeskException(t); + } else { + super.onThrowable(t); + } } + } - public Iterable getUserSubscriptions(Long userId) { - return new PagedIterable<>( - tmpl("/help_center/users/{userId}/subscriptions.json").set("userId", userId), - handleList(Subscription.class, "subscriptions")); - } + private Request req(String method, Uri template) { + return req(method, template.toString()); + } - public Iterable getArticleSubscriptions(Long articleId) { - return getArticleSubscriptions(articleId, null); - } + private Request req(String method, String url) { + return reqBuilder(method, url).build(); + } - public Iterable getArticleSubscriptions(Long articleId, String locale) { - return new PagedIterable<>( - tmpl("/help_center{/locale}/articles/{articleId}/subscriptions.json").set("locale", locale) - .set("articleId", articleId), - handleList(Subscription.class, "subscriptions")); - } + private Request req(String method, Uri template, String contentType, byte[] body) { + RequestBuilder builder = reqBuilder(method, template.toString()); + builder.addHeader("Content-type", contentType); + builder.setBody(body); + return builder.build(); + } - public Iterable getSectionSubscriptions(Long sectionId) { - return getSectionSubscriptions(sectionId, null); + private RequestBuilder reqBuilder(String method, String url) { + RequestBuilder builder = new RequestBuilder(method); + if (realm != null) { + builder.setRealm(realm); + } else { + builder.addHeader("Authorization", "Bearer " + oauthToken); } + headers.forEach(builder::setHeader); + return builder.setUrl(url); + } - public Iterable getSectionSubscriptions(Long sectionId, String locale) { - return new PagedIterable<>( - tmpl("/help_center{/locale}/sections/{sectionId}/subscriptions.json").set("locale", locale) - .set("sectionId", sectionId), - handleList(Subscription.class, "subscriptions")); - } + protected ZendeskAsyncCompletionHandler handleStatus() { + return new ZendeskAsyncCompletionHandler() { + @Override + public Void onCompleted(Response response) throws Exception { + logResponse(response); + if (isStatus2xx(response)) { + return null; + } else if (isRateLimitResponse(response)) { + throw new ZendeskResponseRateLimitException(response); + } + throw new ZendeskResponseException(response); + } + }; + } - /** - * Get a list of the current business hours schedules - * @return A List of Schedules - */ - public Iterable getSchedules() { - return complete(submit(req("GET", cnst("/business_hours/schedules.json")), - handleList(Schedule.class, "schedules"))); - } + @SuppressWarnings("unchecked") + protected ZendeskAsyncCompletionHandler handle(final Class clazz) { + return handle(mapper.readerFor(clazz)); + } - public Schedule getSchedule(Schedule schedule) { - checkHasId(schedule); - return getSchedule(schedule.getId()); - } + @SuppressWarnings("unchecked") + protected ZendeskAsyncCompletionHandler handle(ObjectReader reader) { + return new ZendeskAsyncCompletionHandler() { + @Override + public T onCompleted(Response response) throws Exception { + logResponse(response); + if (isStatus2xx(response)) { + return reader.readValue(response.getResponseBodyAsStream()); + } else if (isRateLimitResponse(response)) { + throw new ZendeskResponseRateLimitException(response); + } + if (response.getStatusCode() == 404) { + return null; + } + throw new ZendeskResponseException(response); + } + }; + } - public Schedule getSchedule(Long scheduleId) { - return complete(submit(req("GET", tmpl("/business_hours/schedules/{id}.json").set("id", scheduleId)), - handle(Schedule.class, "schedule"))); + private class BasicAsyncCompletionHandler extends ZendeskAsyncCompletionHandler { + private final Class clazz; + private final String name; + private final Class[] typeParams; + + public BasicAsyncCompletionHandler(Class clazz, String name, Class... typeParams) { + this.clazz = clazz; + this.name = name; + this.typeParams = typeParams; + } + + @Override + public T onCompleted(Response response) throws Exception { + logResponse(response); + if (isStatus2xx(response)) { + if (typeParams.length > 0) { + JavaType type = mapper.getTypeFactory().constructParametricType(clazz, typeParams); + return mapper.convertValue( + mapper.readTree(response.getResponseBodyAsStream()).get(name), type); + } + return mapper.convertValue( + mapper.readTree(response.getResponseBodyAsStream()).get(name), clazz); + } else if (isRateLimitResponse(response)) { + throw new ZendeskResponseRateLimitException(response); + } + if (response.getStatusCode() == 404) { + return null; + } + throw new ZendeskResponseException(response); } + } - public Iterable getHolidaysForSchedule(Schedule schedule) { - checkHasId(schedule); - return getHolidaysForSchedule(schedule.getId()); - } + protected ZendeskAsyncCompletionHandler handle( + final Class clazz, final String name, final Class... typeParams) { + return new BasicAsyncCompletionHandler<>(clazz, name, typeParams); + } - public Iterable getHolidaysForSchedule(Long scheduleId) { - return complete(submit(req("GET", - tmpl("/business_hours/schedules/{id}/holidays.json").set("id", scheduleId)), - handleList(Holiday.class, "holidays"))); - } + protected ZendeskAsyncCompletionHandler handleJobStatus() { + return new BasicAsyncCompletionHandler(JobStatus.class, "job_status") { + @Override + public JobStatus onCompleted(Response response) throws Exception { + JobStatus result = super.onCompleted(response); + if (result == null) { + // null is when we receive a 404 response. + // For an async job we trigger an error + throw new ZendeskResponseException(response); + } + return result; + } + }; + } - ////////////////////////////////////////////////////////////////////// - // Helper methods - ////////////////////////////////////////////////////////////////////// + private static final String CURSOR_LINKS = "links"; + private static final String CURSOR_NEXT_PAGE = "next"; + private static final String NEXT_PAGE = "next_page"; + private static final String END_TIME = "end_time"; + private static final String COUNT = "count"; + private static final int INCREMENTAL_EXPORT_MAX_COUNT_BY_REQUEST = 1000; - private byte[] json(Object object) { - try { - return mapper.writeValueAsBytes(object); - } catch (JsonProcessingException e) { - throw new ZendeskException(e.getMessage(), e); - } - } + private abstract class PagedAsyncCompletionHandler extends ZendeskAsyncCompletionHandler { + private String nextPage; + + public void setPagedProperties(JsonNode responseNode, Class clazz) { + JsonNode node = responseNode.get(CURSOR_LINKS); - private ListenableFuture submit(Request request, ZendeskAsyncCompletionHandler handler) { + // Attempt to use cursor pagination if possible + if (node != null) { + node = node.get(CURSOR_NEXT_PAGE); + } else { + node = responseNode.get(NEXT_PAGE); + } + + if (node == null) { + this.nextPage = null; if (logger.isDebugEnabled()) { - if (request.getStringData() != null) { - logger.debug("Request {} {}\n{}", request.getMethod(), request.getUrl(), request.getStringData()); - } else if (request.getByteData() != null) { - logger.debug("Request {} {} {} {} bytes", request.getMethod(), request.getUrl(), - request.getHeaders().get("Content-type"), request.getByteData().length); - } else { - logger.debug("Request {} {}", request.getMethod(), request.getUrl()); - } + logger.debug( + NEXT_PAGE + + " property not found, pagination not supported" + + (clazz != null ? " for " + clazz.getName() : "")); } - return client.executeRequest(request, handler); + } else { + this.nextPage = node.asText(); + } } - private static abstract class ZendeskAsyncCompletionHandler extends AsyncCompletionHandler { - @Override - public void onThrowable(Throwable t) { - if (t instanceof IOException) { - throw new ZendeskException(t); - } else { - super.onThrowable(t); - } - } + public String getNextPage() { + return nextPage; } - private Request req(String method, Uri template) { - return req(method, template.toString()); + public void setNextPage(String nextPage) { + this.nextPage = nextPage; } + } - private static final Pattern RESTRICTED_PATTERN = Pattern.compile("%2B", Pattern.LITERAL); - - private Request req(String method, String url) { - return reqBuilder(method, url).build(); - } + private class PagedAsyncListCompletionHandler extends PagedAsyncCompletionHandler> { + private final Class clazz; + private final String name; - private Request req(String method, Uri template, String contentType, byte[] body) { - RequestBuilder builder = reqBuilder(method, template.toString()); - builder.addHeader("Content-type", contentType); - builder.setBody(body); - return builder.build(); + public PagedAsyncListCompletionHandler(Class clazz, String name) { + this.clazz = clazz; + this.name = name; } - private RequestBuilder reqBuilder(String method, String url) { - RequestBuilder builder = new RequestBuilder(method); - if (realm != null) { - builder.setRealm(realm); - } else { - builder.addHeader("Authorization", "Bearer " + oauthToken); + @Override + public List onCompleted(Response response) throws Exception { + logResponse(response); + if (isStatus2xx(response)) { + JsonNode responseNode = mapper.readTree(response.getResponseBodyAsBytes()); + setPagedProperties(responseNode, clazz); + List values = new ArrayList<>(); + for (JsonNode node : responseNode.get(name)) { + values.add(mapper.convertValue(node, clazz)); } - headers.forEach(builder::setHeader); - return builder.setUrl(RESTRICTED_PATTERN.matcher(url).replaceAll("+")); // replace out %2B with + due to API restriction - } - - protected ZendeskAsyncCompletionHandler handleStatus() { - return new ZendeskAsyncCompletionHandler() { - @Override - public Void onCompleted(Response response) throws Exception { - logResponse(response); - if (isStatus2xx(response)) { - return null; - } else if (isRateLimitResponse(response)) { - throw new ZendeskResponseRateLimitException(response); - } - throw new ZendeskResponseException(response); - } - }; - } - - @SuppressWarnings("unchecked") - protected ZendeskAsyncCompletionHandler handle(final Class clazz) { - return new ZendeskAsyncCompletionHandler() { - @Override - public T onCompleted(Response response) throws Exception { - logResponse(response); - if (isStatus2xx(response)) { - return (T) mapper.readerFor(clazz).readValue(response.getResponseBodyAsStream()); - } else if (isRateLimitResponse(response)) { - throw new ZendeskResponseRateLimitException(response); - } - if (response.getStatusCode() == 404) { - return null; - } - throw new ZendeskResponseException(response); - } - }; + return values; + } else if (isRateLimitResponse(response)) { + throw new ZendeskResponseRateLimitException(response); + } + throw new ZendeskResponseException(response); } + } - private class BasicAsyncCompletionHandler extends ZendeskAsyncCompletionHandler { - private final Class clazz; - private final String name; - private final Class[] typeParams; + protected PagedAsyncCompletionHandler> handleList( + final Class clazz, final String name) { + return new PagedAsyncListCompletionHandler<>(clazz, name); + } - public BasicAsyncCompletionHandler(Class clazz, String name, Class... typeParams) { - this.clazz = clazz; - this.name = name; - this.typeParams = typeParams; + private static final long FIVE_MINUTES = TimeUnit.MINUTES.toMillis(5); + + protected PagedAsyncCompletionHandler> handleIncrementalList( + final Class clazz, final String name) { + return new PagedAsyncListCompletionHandler(clazz, name) { + @Override + public void setPagedProperties(JsonNode responseNode, Class clazz) { + JsonNode node = responseNode.get(NEXT_PAGE); + if (node == null) { + if (logger.isDebugEnabled()) { + logger.debug( + NEXT_PAGE + + " property not found, pagination not supported" + + (clazz != null ? " for " + clazz.getName() : "")); + } + setNextPage(null); + return; } - - @Override - public T onCompleted(Response response) throws Exception { - logResponse(response); - if (isStatus2xx(response)) { - if (typeParams.length > 0) { - JavaType type = mapper.getTypeFactory().constructParametricType(clazz, typeParams); - return mapper.convertValue(mapper.readTree(response.getResponseBodyAsStream()).get(name), type); - } - return mapper.convertValue(mapper.readTree(response.getResponseBodyAsStream()).get(name), clazz); - } else if (isRateLimitResponse(response)) { - throw new ZendeskResponseRateLimitException(response); - } - if (response.getStatusCode() == 404) { - return null; - } - throw new ZendeskResponseException(response); + JsonNode endTimeNode = responseNode.get(END_TIME); + if (endTimeNode == null || endTimeNode.asLong() == 0) { + if (logger.isDebugEnabled()) { + logger.debug( + END_TIME + + " property not found, incremental export pagination not supported" + + (clazz != null ? " for " + clazz.getName() : "")); + } + setNextPage(null); + return; } - } - - protected ZendeskAsyncCompletionHandler handle(final Class clazz, final String name, final Class... typeParams) { - return new BasicAsyncCompletionHandler<>(clazz, name, typeParams); - } - - protected ZendeskAsyncCompletionHandler handleJobStatus() { - return new BasicAsyncCompletionHandler(JobStatus.class, "job_status") { - @Override - public JobStatus onCompleted(Response response) throws Exception { - JobStatus result = super.onCompleted(response); - if (result == null) { - // null is when we receive a 404 response. - // For an async job we trigger an error - throw new ZendeskResponseException(response); - } - return result; - } - }; - } - - private static final String NEXT_PAGE = "next_page"; - private static final String END_TIME = "end_time"; - private static final String COUNT = "count"; - private static final int INCREMENTAL_EXPORT_MAX_COUNT_BY_REQUEST = 1000; - - private abstract class PagedAsyncCompletionHandler extends ZendeskAsyncCompletionHandler { - private String nextPage; - - public void setPagedProperties(JsonNode responseNode, Class clazz) { - JsonNode node = responseNode.get(NEXT_PAGE); - if (node == null) { - this.nextPage = null; - if (logger.isDebugEnabled()) { - logger.debug(NEXT_PAGE + " property not found, pagination not supported" + - (clazz != null ? " for " + clazz.getName() : "")); - } - } else { - this.nextPage = node.asText(); + /* + A request after five minutes ago will result in a 422 responds from Zendesk. + Therefore, we stop pagination. + */ + if (TimeUnit.SECONDS.toMillis(endTimeNode.asLong()) + > System.currentTimeMillis() - FIVE_MINUTES) { + setNextPage(null); + } else { + // Taking into account documentation found at + // https://developer.zendesk.com/rest_api/docs/core/incremental_export#polling-strategy + JsonNode countNode = responseNode.get(COUNT); + if (countNode == null) { + if (logger.isDebugEnabled()) { + logger.debug( + COUNT + + " property not found, incremental export pagination not supported" + + (clazz != null ? " for " + clazz.getName() : "")); } + setNextPage(null); + return; + } + + if (countNode.asInt() < INCREMENTAL_EXPORT_MAX_COUNT_BY_REQUEST) { + setNextPage(null); + } else { + setNextPage(node.asText()); + } } + } + }; + } - public String getNextPage() { - return nextPage; + protected PagedAsyncCompletionHandler> handleSearchList( + final String name) { + return new PagedAsyncCompletionHandler>() { + @Override + public List onCompleted(Response response) throws Exception { + logResponse(response); + if (isStatus2xx(response)) { + JsonNode responseNode = mapper.readTree(response.getResponseBodyAsStream()).get(name); + setPagedProperties(responseNode, null); + List values = new ArrayList<>(); + for (JsonNode node : responseNode) { + Class clazz = + searchResultTypes.get(node.get("result_type").asText()); + if (clazz != null) { + values.add(mapper.convertValue(node, clazz)); + } + } + return values; + } else if (isRateLimitResponse(response)) { + throw new ZendeskResponseRateLimitException(response); } + throw new ZendeskResponseException(response); + } + }; + } - public void setNextPage(String nextPage) { - this.nextPage = nextPage; + protected PagedAsyncCompletionHandler> handleTargetList(final String name) { + return new PagedAsyncCompletionHandler>() { + @Override + public List onCompleted(Response response) throws Exception { + logResponse(response); + if (isStatus2xx(response)) { + JsonNode responseNode = mapper.readTree(response.getResponseBodyAsBytes()); + setPagedProperties(responseNode, null); + List values = new ArrayList<>(); + for (JsonNode node : responseNode.get(name)) { + Class clazz = targetTypes.get(node.get("type").asText()); + if (clazz != null) { + values.add(mapper.convertValue(node, clazz)); + } + } + return values; + } else if (isRateLimitResponse(response)) { + throw new ZendeskResponseRateLimitException(response); } - } + throw new ZendeskResponseException(response); + } + }; + } - private class PagedAsyncListCompletionHandler extends PagedAsyncCompletionHandler> { - private final Class clazz; - private final String name; - public PagedAsyncListCompletionHandler(Class clazz, String name) { - this.clazz = clazz; - this.name = name; + protected PagedAsyncCompletionHandler> handleArticleAttachmentsList( + final String name) { + return new PagedAsyncCompletionHandler>() { + @Override + public List onCompleted(Response response) throws Exception { + logResponse(response); + if (isStatus2xx(response)) { + JsonNode responseNode = mapper.readTree(response.getResponseBodyAsBytes()); + List values = new ArrayList<>(); + for (JsonNode node : responseNode.get(name)) { + values.add(mapper.convertValue(node, ArticleAttachments.class)); + } + return values; + } else if (isRateLimitResponse(response)) { + throw new ZendeskResponseRateLimitException(response); } + throw new ZendeskResponseException(response); + } + }; + } - @Override - public List onCompleted(Response response) throws Exception { - logResponse(response); - if (isStatus2xx(response)) { - JsonNode responseNode = mapper.readTree(response.getResponseBodyAsBytes()); - setPagedProperties(responseNode, clazz); - List values = new ArrayList<>(); - for (JsonNode node : responseNode.get(name)) { - values.add(mapper.convertValue(node, clazz)); - } - return values; - } else if (isRateLimitResponse(response)) { - throw new ZendeskResponseRateLimitException(response); + /** + * For a resource (e.g. ContentTag) which supports cursor based pagination for multiple results, + * but where the response does not have a `links.next` node (which would hold the URL of the next + * page) So we need to build the next page URL from the original URL and the meta.after_cursor + * node value + * + * @param The class of the resource + * @param afterCursorUriBuilder a function to build the URL for the next page + * `fn(after_cursor_value) => URL_of_next_page` + * @param name the name of the Json node that contains the resources entities (e.g. 'records' for + * ContentTag) + */ + private PagedAsyncCompletionHandler> handleListWithAfterCursorButNoLinks( + Class clazz, Function afterCursorUriBuilder, String name) { + + return new PagedAsyncListCompletionHandler(clazz, name) { + @Override + public void setPagedProperties(JsonNode responseNode, Class clazz) { + JsonNode metaNode = responseNode.get("meta"); + String nextPage = null; + if (metaNode == null) { + if (logger.isDebugEnabled()) { + logger.debug( + "meta" + + " property not found, pagination not supported" + + (clazz != null ? " for " + clazz.getName() : "")); + } + } else { + JsonNode afterCursorNode = metaNode.get("after_cursor"); + if (afterCursorNode != null) { + JsonNode hasMoreNode = metaNode.get("has_more"); + if (hasMoreNode != null && hasMoreNode.asBoolean()) { + nextPage = afterCursorUriBuilder.apply(afterCursorNode.asText()).toString(); } - throw new ZendeskResponseException(response); + } } - } + setNextPage(nextPage); + } + }; + } - protected PagedAsyncCompletionHandler> handleList(final Class clazz, final String name) { - return new PagedAsyncListCompletionHandler<>(clazz, name); - } - - private static final long FIVE_MINUTES = TimeUnit.MINUTES.toMillis(5); - - protected PagedAsyncCompletionHandler> handleIncrementalList(final Class clazz, final String name) { - return new PagedAsyncListCompletionHandler(clazz, name) { - @Override - public void setPagedProperties(JsonNode responseNode, Class clazz) { - JsonNode node = responseNode.get(NEXT_PAGE); - if (node == null) { - if (logger.isDebugEnabled()) { - logger.debug(NEXT_PAGE + " property not found, pagination not supported" + - (clazz != null ? " for " + clazz.getName() : "")); - } - setNextPage(null); - return; - } - JsonNode endTimeNode = responseNode.get(END_TIME); - if (endTimeNode == null || endTimeNode.asLong() == 0) { - if (logger.isDebugEnabled()) { - logger.debug(END_TIME + " property not found, incremental export pagination not supported" + - (clazz != null ? " for " + clazz.getName() : "")); - } - setNextPage(null); - return; - } - /* - A request after five minutes ago will result in a 422 responds from Zendesk. - Therefore, we stop pagination. - */ - if (TimeUnit.SECONDS.toMillis(endTimeNode.asLong()) > System.currentTimeMillis() - FIVE_MINUTES) { - setNextPage(null); - } else { - // Taking into account documentation found at https://developer.zendesk.com/rest_api/docs/core/incremental_export#polling-strategy - JsonNode countNode = responseNode.get(COUNT); - if (countNode == null) { - if (logger.isDebugEnabled()) { - logger.debug(COUNT + " property not found, incremental export pagination not supported" + - (clazz != null ? " for " + clazz.getName() : "")); - } - setNextPage(null); - return; - } - - if (countNode.asInt() < INCREMENTAL_EXPORT_MAX_COUNT_BY_REQUEST) { - setNextPage(null); - } else { - setNextPage(node.asText()); - } - } - } - }; - } - - protected PagedAsyncCompletionHandler> handleSearchList(final String name) { - return new PagedAsyncCompletionHandler>() { - @Override - public List onCompleted(Response response) throws Exception { - logResponse(response); - if (isStatus2xx(response)) { - JsonNode responseNode = mapper.readTree(response.getResponseBodyAsStream()).get(name); - setPagedProperties(responseNode, null); - List values = new ArrayList<>(); - for (JsonNode node : responseNode) { - Class clazz = searchResultTypes.get(node.get("result_type").asText()); - if (clazz != null) { - values.add(mapper.convertValue(node, clazz)); - } - } - return values; - } else if (isRateLimitResponse(response)) { - throw new ZendeskResponseRateLimitException(response); - } - throw new ZendeskResponseException(response); - } - }; - } - - protected PagedAsyncCompletionHandler> handleTargetList(final String name) { - return new PagedAsyncCompletionHandler>() { - @Override - public List onCompleted(Response response) throws Exception { - logResponse(response); - if (isStatus2xx(response)) { - JsonNode responseNode = mapper.readTree(response.getResponseBodyAsBytes()); - setPagedProperties(responseNode, null); - List values = new ArrayList<>(); - for (JsonNode node : responseNode.get(name)) { - Class clazz = targetTypes.get(node.get("type").asText()); - if (clazz != null) { - values.add(mapper.convertValue(node, clazz)); - } - } - return values; - } else if (isRateLimitResponse(response)) { - throw new ZendeskResponseRateLimitException(response); - } - throw new ZendeskResponseException(response); - } - }; - } - - - protected PagedAsyncCompletionHandler> handleArticleAttachmentsList(final String name) { - return new PagedAsyncCompletionHandler>() { - @Override - public List onCompleted(Response response) throws Exception { - logResponse(response); - if (isStatus2xx(response)) { - JsonNode responseNode = mapper.readTree(response.getResponseBodyAsBytes()); - List values = new ArrayList<>(); - for (JsonNode node : responseNode.get(name)) { - values.add(mapper.convertValue(node, ArticleAttachments.class)); - } - return values; - } else if (isRateLimitResponse(response)) { - throw new ZendeskResponseRateLimitException(response); - } - throw new ZendeskResponseException(response); - } - }; - } + private TemplateUri tmpl(String template) { + return new TemplateUri(url + template); + } - private TemplateUri tmpl(String template) { - return new TemplateUri(url + template); + private TemplateUri cbp(String path) { + return cbp(path, false, cbpPageSize); + } + + private TemplateUri cbp(String path, boolean noDomain, int pageSize) { + Objects.requireNonNull(path, "Path cannot be null"); + if (path.indexOf('?') != -1) { + throw new IllegalArgumentException("Path cannot contain a query string"); } + return new TemplateUri((noDomain ? "" : url) + path + "?page[size]={pageSize}") + .set("pageSize", pageSize); + } + + private Uri cnst(String template) { + return new FixedUri(url + template); + } - private Uri cnst(String template) { - return new FixedUri(url + template); + private void logResponse(Response response) throws IOException { + if (logger.isDebugEnabled()) { + logger.debug( + "Response HTTP/{} {}\n{}", + response.getStatusCode(), + response.getStatusText(), + response.getResponseBody()); + } + if (logger.isTraceEnabled()) { + logger.trace("Response headers {}", response.getHeaders()); } + } - private void logResponse(Response response) throws IOException { - if (logger.isDebugEnabled()) { - logger.debug("Response HTTP/{} {}\n{}", response.getStatusCode(), response.getStatusText(), - response.getResponseBody()); - } - if (logger.isTraceEnabled()) { - logger.trace("Response headers {}", response.getHeaders()); - } + private static final String UTF_8 = "UTF-8"; + + private static String encodeUrl(String input) { + try { + return URLEncoder.encode(input, UTF_8); + } catch (UnsupportedEncodingException impossible) { + return input; } + } - private static final String UTF_8 = "UTF-8"; + private static long msToSeconds(long millis) { + return TimeUnit.MILLISECONDS.toSeconds(millis); + } + + private boolean isStatus2xx(Response response) { + return response.getStatusCode() / 100 == 2; + } + + private boolean isRateLimitResponse(Response response) { + return response.getStatusCode() == 429; + } - private static String encodeUrl(String input) { - try { - return URLEncoder.encode(input, UTF_8); - } catch (UnsupportedEncodingException impossible) { - return input; + ////////////////////////////////////////////////////////////////////// + // Static helper methods + ////////////////////////////////////////////////////////////////////// + + private static T complete(ListenableFuture future) { + try { + return future.get(); + } catch (InterruptedException e) { + throw new ZendeskException(e.getMessage(), e); + } catch (ExecutionException e) { + if (e.getCause() instanceof ZendeskException) { + if (e.getCause() instanceof ZendeskResponseRateLimitException) { + throw new ZendeskResponseRateLimitException( + (ZendeskResponseRateLimitException) e.getCause()); + } + if (e.getCause() instanceof ZendeskResponseException) { + throw new ZendeskResponseException((ZendeskResponseException) e.getCause()); } + throw new ZendeskException(e.getCause()); + } + throw new ZendeskException(e.getMessage(), e); } + } - private static long msToSeconds(long millis) { - return TimeUnit.MILLISECONDS.toSeconds(millis); + private static void checkHasId(Ticket ticket) { + if (ticket.getId() == null) { + throw new IllegalArgumentException("Ticket requires id"); } + } - private boolean isStatus2xx(Response response) { - return response.getStatusCode() / 100 == 2; + private static void checkHasId(TicketForm ticketForm) { + if (ticketForm.getId() == null) { + throw new IllegalArgumentException("TicketForm requires id"); } + } - private boolean isRateLimitResponse(Response response) { - return response.getStatusCode() == 429; + private static void checkHasId(org.zendesk.client.v2.model.Request request) { + if (request.getId() == null) { + throw new IllegalArgumentException("Request requires id"); } + } - ////////////////////////////////////////////////////////////////////// - // Static helper methods - ////////////////////////////////////////////////////////////////////// + private static void checkHasId(Audit audit) { + if (audit.getId() == null) { + throw new IllegalArgumentException("Audit requires id"); + } + } - private static T complete(ListenableFuture future) { - try { - return future.get(); - } catch (InterruptedException e) { - throw new ZendeskException(e.getMessage(), e); - } catch (ExecutionException e) { - if (e.getCause() instanceof ZendeskException) { - if (e.getCause() instanceof ZendeskResponseRateLimitException) { - throw new ZendeskResponseRateLimitException((ZendeskResponseRateLimitException) e.getCause()); - } - if (e.getCause() instanceof ZendeskResponseException) { - throw new ZendeskResponseException((ZendeskResponseException)e.getCause()); - } - throw new ZendeskException(e.getCause()); - } - throw new ZendeskException(e.getMessage(), e); - } + private static void checkHasId(Comment comment) { + if (comment.getId() == null) { + throw new IllegalArgumentException("Comment requires id"); } + } - private static void checkHasId(Ticket ticket) { - if (ticket.getId() == null) { - throw new IllegalArgumentException("Ticket requires id"); - } + private static void checkHasId(Field field) { + if (field.getId() == null) { + throw new IllegalArgumentException("Field requires id"); } + } - private static void checkHasId(org.zendesk.client.v2.model.Request request) { - if (request.getId() == null) { - throw new IllegalArgumentException("Request requires id"); - } + private static void checkHasId(Attachment attachment) { + if (attachment.getId() == null) { + throw new IllegalArgumentException("Attachment requires id"); } + } - private static void checkHasId(Audit audit) { - if (audit.getId() == null) { - throw new IllegalArgumentException("Audit requires id"); - } + private static void checkHasId(ArticleAttachments attachments) { + if (attachments.getId() == null) { + throw new IllegalArgumentException("Attachment requires id"); } + } - private static void checkHasId(Comment comment) { - if (comment.getId() == null) { - throw new IllegalArgumentException("Comment requires id"); - } + private static void checkHasId(User user) { + if (user.getId() == null) { + throw new IllegalArgumentException("User requires id"); } + } - private static void checkHasId(Field field) { - if (field.getId() == null) { - throw new IllegalArgumentException("Field requires id"); - } + private static void checkHasId(Identity identity) { + if (identity.getId() == null) { + throw new IllegalArgumentException("Identity requires id"); } + } - private static void checkHasId(Attachment attachment) { - if (attachment.getId() == null) { - throw new IllegalArgumentException("Attachment requires id"); - } + private static void checkHasId(Organization organization) { + if (organization.getId() == null) { + throw new IllegalArgumentException("Organization requires id"); } + } - private static void checkHasId(ArticleAttachments attachments) { - if (attachments.getId() == null) { - throw new IllegalArgumentException("Attachment requires id"); - } + private static void checkHasId(Group group) { + if (group.getId() == null) { + throw new IllegalArgumentException("Group requires id"); } + } - private static void checkHasId(User user) { - if (user.getId() == null) { - throw new IllegalArgumentException("User requires id"); - } + private static void checkHasId(GroupMembership groupMembership) { + if (groupMembership.getId() == null) { + throw new IllegalArgumentException("GroupMembership requires id"); } + } - private static void checkHasId(Identity identity) { - if (identity.getId() == null) { - throw new IllegalArgumentException("Identity requires id"); - } + private void checkHasId(Forum forum) { + if (forum.getId() == null) { + throw new IllegalArgumentException("Forum requires id"); } + } - private static void checkHasId(Organization organization) { - if (organization.getId() == null) { - throw new IllegalArgumentException("Organization requires id"); - } + private void checkHasId(Topic topic) { + if (topic.getId() == null) { + throw new IllegalArgumentException("Topic requires id"); } + } - private static void checkHasId(Group group) { - if (group.getId() == null) { - throw new IllegalArgumentException("Group requires id"); - } + private static void checkHasId(OrganizationMembership organizationMembership) { + if (organizationMembership.getId() == null) { + throw new IllegalArgumentException("OrganizationMembership requires id"); } + } - private static void checkHasId(GroupMembership groupMembership) { - if (groupMembership.getId() == null) { - throw new IllegalArgumentException("GroupMembership requires id"); - } + private static void checkHasId(Article article) { + if (article.getId() == null) { + throw new IllegalArgumentException("Article requires id"); } + } - private void checkHasId(Forum forum) { - if (forum.getId() == null) { - throw new IllegalArgumentException("Forum requires id"); - } + private static void checkHasId(DynamicContentItem item) { + if (item.getId() == null) { + throw new IllegalArgumentException("Item requires id"); } + } - private void checkHasId(Topic topic) { - if (topic.getId() == null) { - throw new IllegalArgumentException("Topic requires id"); - } + private static void checkHasId(DynamicContentItemVariant variant) { + if (variant.getId() == null) { + throw new IllegalArgumentException("Variant requires id"); } + } - private static void checkHasId(OrganizationMembership organizationMembership) { - if (organizationMembership.getId() == null) { - throw new IllegalArgumentException("OrganizationMembership requires id"); - } + private static void checkHasItemId(Long itemId) { + if (itemId == null) { + throw new IllegalArgumentException("Variant requires item id"); } + } - private static void checkHasId(Article article) { - if (article.getId() == null) { - throw new IllegalArgumentException("Article requires id"); - } + private static void checkHasSectionId(Article article) { + if (article.getSectionId() == null) { + throw new IllegalArgumentException("Article requires section id"); } + } - private static void checkHasId(DynamicContentItem item) { - if (item.getId() == null) { - throw new IllegalArgumentException("Item requires id"); - } + private static void checkHasArticleId(Long articleId) { + if (articleId == null) { + throw new IllegalArgumentException("Translation requires article id"); } + } - private static void checkHasId(DynamicContentItemVariant variant) { - if (variant.getId() == null) { - throw new IllegalArgumentException("Variant requires id"); - } + private static void checkHasSectionId(Long articleId) { + if (articleId == null) { + throw new IllegalArgumentException("Translation requires section id"); } + } - private static void checkHasItemId(Long itemId) { - if (itemId == null) { - throw new IllegalArgumentException("Variant requires item id"); - } + private static void checkHasCategoryId(Long articleId) { + if (articleId == null) { + throw new IllegalArgumentException("Translation requires category id"); } + } - private static void checkHasSectionId(Article article) { - if (article.getSectionId() == null) { - throw new IllegalArgumentException("Article requires section id"); - } + private static void checkHasCategoryId(Section section) { + if (section.getCategoryId() == null) { + throw new IllegalArgumentException("Section requires category id"); } + } - private static void checkHasArticleId(Long articleId) { - if (articleId == null) { - throw new IllegalArgumentException("Translation requires article id"); - } + private static void checkHasId(Category category) { + if (category.getId() == null) { + throw new IllegalArgumentException("Category requires id"); } + } - private static void checkHasSectionId(Long articleId) { - if (articleId == null) { - throw new IllegalArgumentException("Translation requires section id"); - } + private static void checkHasId(Section section) { + if (section.getId() == null) { + throw new IllegalArgumentException("Section requires id"); } + } - private static void checkHasCategoryId(Long articleId) { - if (articleId == null) { - throw new IllegalArgumentException("Translation requires category id"); - } + private static void checkHasId(SuspendedTicket ticket) { + if (ticket == null || ticket.getId() == null) { + throw new IllegalArgumentException("SuspendedTicket requires id"); } + } - private static void checkHasCategoryId(Section section) { - if (section.getCategoryId() == null) { - throw new IllegalArgumentException("Section requires category id"); - } + private static void checkHasId(Translation translation) { + if (translation.getId() == null) { + throw new IllegalArgumentException("Translation requires id"); } + } - private static void checkHasId(Category category) { - if (category.getId() == null) { - throw new IllegalArgumentException("Category requires id"); - } + private static void checkHasId(Schedule schedule) { + if (schedule == null || schedule.getId() == null) { + throw new IllegalArgumentException("Schedule requires id"); } + } - private static void checkHasId(Section section) { - if (section.getId() == null) { - throw new IllegalArgumentException("Section requires id"); - } + private static void checkHasId(Holiday holiday) { + if (holiday == null || holiday.getId() == null) { + throw new IllegalArgumentException("Holiday requires id"); } + } - private static void checkHasId(SuspendedTicket ticket) { - if (ticket == null || ticket.getId() == null) { - throw new IllegalArgumentException("SuspendedTicket requires id"); - } + private static void checkHasId(PermissionGroup permissionGroup) { + if (permissionGroup.getId() == null) { + throw new IllegalArgumentException("PermissionGroup requires id"); } + } - private static void checkHasId(Translation translation) { - if (translation.getId() == null) { - throw new IllegalArgumentException("Translation requires id"); - } + private static void checkHasId(UserSegment userSegment) { + if (userSegment.getId() == null) { + throw new IllegalArgumentException("UserSegment requires id"); } + } - private static void checkHasId(Schedule schedule) { - if (schedule == null || schedule.getId() == null) { - throw new IllegalArgumentException("Schedule requires id"); - } + private static void checkHasId(ContentTag contentTag) { + if (contentTag.getId() == null) { + throw new IllegalArgumentException("Content Tag requires id"); } + } - private static void checkHasId(Holiday holiday) { - if (holiday == null || holiday.getId() == null) { - throw new IllegalArgumentException("Holiday requires id"); - } + private static void checkHasName(ContentTag contentTag) { + if (contentTag.getName() == null || contentTag.getName().trim().isEmpty()) { + throw new IllegalArgumentException("Content Tag requires name"); } + } - private static void checkHasId(PermissionGroup permissionGroup) { - if (permissionGroup.getId() == null) { - throw new IllegalArgumentException("PermissionGroup requires id"); - } + private static void checkHasToken(Attachment.Upload upload) { + if (upload.getToken() == null) { + throw new IllegalArgumentException("Upload requires token"); } + } - private static void checkHasId(UserSegment userSegment) { - if (userSegment.getId() == null) { - throw new IllegalArgumentException("UserSegment requires id"); - } + private static List idArray(long id, long... ids) { + List result = new ArrayList<>(ids.length + 1); + result.add(id); + for (long i : ids) { + result.add(i); } + return result; + } - private static void checkHasToken(Attachment.Upload upload) { - if (upload.getToken() == null) { - throw new IllegalArgumentException("Upload requires token"); - } + private static List idArray(String id, String... ids) { + List result = new ArrayList<>(ids.length + 1); + result.add(id); + for (String i : ids) { + result.add(i); } + return result; + } - private static List idArray(long id, long... ids) { - List result = new ArrayList<>(ids.length + 1); - result.add(id); - for (long i : ids) { - result.add(i); - } - return result; + private static List statusArray(Status... statuses) { + List result = new ArrayList<>(statuses.length); + for (Status s : statuses) { + result.add(s.toString()); } + return result; + } - private static List statusArray(Status... statuses) { - List result = new ArrayList<>(statuses.length); - for (Status s : statuses) { - result.add(s.toString()); - } - return result; + private static String getTypeName(final Class type) { + String typeName = null; + for (final Map.Entry> entry : + searchResultTypes.entrySet()) { + if (type.equals(entry.getValue())) { + typeName = entry.getKey(); + break; + } } + return typeName; + } + + private TemplateUri getSearchUri(Map params, String query, String typeName) { + + StringBuilder uriTemplate = + new StringBuilder("/search.json{?query"); // leave off ending curly brace - public static ObjectMapper createMapper() { - ObjectMapper mapper = new ObjectMapper(); - mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); - mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); - mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - mapper.setDateFormat(new StdDateFormat()); - mapper.enable(DeserializationFeature.USE_LONG_FOR_INTS); - return mapper; + // we have to add each param name to the template so that when we call set() with a map, the + // entries get put in the uri + for (String paramName : params.keySet()) { + uriTemplate.append(",").append(paramName); } - ////////////////////////////////////////////////////////////////////// - // Helper classes - ////////////////////////////////////////////////////////////////////// + uriTemplate.append("}"); - private class PagedIterable implements Iterable { + TemplateUri templateUri = + tmpl(uriTemplate.toString()).set("query", query + " type:" + typeName); - private final Uri url; - private final PagedAsyncCompletionHandler> handler; + if (params != null) { + templateUri.set(params); + } - private PagedIterable(Uri url, PagedAsyncCompletionHandler> handler) { - this.handler = handler; - this.url = url; - } + return templateUri; + } - public Iterator iterator() { - return new PagedIterator(url); - } + /** + * This method did not allow for any customization and its public visibility was likely only for + * tests. If you rely on it for some reason, please report a new issue to discuss options. To + * customize the ObjectMapper, use the {@link Builder#customizeObjectMapper(Function)} method. + */ + @Deprecated(since = "1.3.0", forRemoval = true) + public static ObjectMapper createMapper() { + return createMapper(Function.identity()); + } - private class PagedIterator implements Iterator { + @VisibleForTesting + public static ObjectMapper createMapper( + Function objectMapperCustomizer) { + ObjectMapper mapper = new ObjectMapper(); + mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); + mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + mapper.setDateFormat(new StdDateFormat()); + mapper.enable(DeserializationFeature.USE_LONG_FOR_INTS); + return objectMapperCustomizer.apply(mapper); + } - private Iterator current; - private String nextPage; + @VisibleForTesting + ObjectMapper getMapper() { + return mapper; + } - public PagedIterator(Uri url) { - this.nextPage = url.toString(); - } + ////////////////////////////////////////////////////////////////////// + // Helper classes + ////////////////////////////////////////////////////////////////////// - public boolean hasNext() { - if (current == null || !current.hasNext()) { - if (nextPage == null || nextPage.equalsIgnoreCase("null")) { - return false; - } - List values = complete(submit(req("GET", nextPage), handler)); - nextPage = handler.getNextPage(); - current = values.iterator(); - } - return current.hasNext(); - } + private class PagedIterable implements Iterable { - public T next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return current.next(); - } + private final Uri url; + private final PagedAsyncCompletionHandler> handler; - public void remove() { - throw new UnsupportedOperationException(); - } - } + private PagedIterable(Uri url, PagedAsyncCompletionHandler> handler) { + this.handler = handler; + this.url = url; + } + public Iterator iterator() { + return new PagedIterator(url); } - public static class Builder { - private AsyncHttpClient client = null; - private final String url; - private String username = null; - private String password = null; - private String token = null; - private String oauthToken = null; - private final Map headers; + private class PagedIterator implements Iterator { - public Builder(String url) { - this.url = url; - this.headers = new HashMap<>(); - } + private Iterator current; + private String nextPage; - public Builder setClient(AsyncHttpClient client) { - this.client = client; - return this; - } + public PagedIterator(Uri url) { + this.nextPage = url.toString(); + } - public Builder setUsername(String username) { - this.username = username; - return this; + public boolean hasNext() { + if (current == null || !current.hasNext()) { + if (nextPage == null || nextPage.equalsIgnoreCase("null")) { + return false; + } + List values = complete(submit(req("GET", nextPage), handler)); + nextPage = handler.getNextPage(); + current = values.iterator(); } + return current.hasNext(); + } - public Builder setPassword(String password) { - this.password = password; - if (password != null) { - this.token = null; - this.oauthToken = null; - } - return this; + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); } + return current.next(); + } - public Builder setToken(String token) { - this.token = token; - if (token != null) { - this.password = null; - this.oauthToken = null; - } - return this; - } + public void remove() { + throw new UnsupportedOperationException(); + } + } + } + public static class Builder { + private static final Integer DEFAULT_CBP_PAGE_SIZE = 100; + private AsyncHttpClient client = null; + private final String url; + private String username = null; + private String password = null; + private String token = null; + private String oauthToken = null; + private int cbpPageSize = DEFAULT_CBP_PAGE_SIZE; + private Function objectMapperCustomizer; + private final Map headers; - public Builder setOauthToken(String oauthToken) { - this.oauthToken = oauthToken; - if (oauthToken != null) { - this.password = null; - this.token = null; - } - return this; - } + public Builder(String url) { + this.url = url; + this.headers = new HashMap<>(); + objectMapperCustomizer = Function.identity(); + } + public Builder setClient(AsyncHttpClient client) { + this.client = client; + return this; + } - public Builder setRetry(boolean retry) { - return this; - } + public Builder setUsername(String username) { + this.username = username; + return this; + } - public Builder addHeader(String name, String value) { - Objects.requireNonNull(name, "Header name cannot be null"); - Objects.requireNonNull(value, "Header value cannot be null"); - headers.put(name, value); - return this; - } + public Builder setPassword(String password) { + this.password = password; + if (password != null) { + this.token = null; + this.oauthToken = null; + } + return this; + } - public Zendesk build() { - if (token != null) { - return new Zendesk(client, url, username + "/token", token, headers); - } else if (oauthToken != null) { - return new Zendesk(client, url, oauthToken, headers); - } - return new Zendesk(client, url, username, password, headers); - } + public Builder setToken(String token) { + this.token = token; + if (token != null) { + this.password = null; + this.oauthToken = null; + } + return this; + } + + public Builder setOauthToken(String oauthToken) { + this.oauthToken = oauthToken; + if (oauthToken != null) { + this.password = null; + this.token = null; + } + return this; + } + + public Builder setRetry(boolean retry) { + return this; + } + + public Builder addHeader(String name, String value) { + Objects.requireNonNull(name, "Header name cannot be null"); + Objects.requireNonNull(value, "Header value cannot be null"); + headers.put(name, value); + return this; } + + public Builder setCbpPageSize(int cbpPageSize) { + this.cbpPageSize = cbpPageSize; + return this; + } + + /** + * Customize the ObjectMapper used by this Zendesk client. Careful, the customizer function will + * be applied after the default configuration for this library. + * + * @param customizer a function that takes an ObjectMapper and returns a customized ObjectMapper + * @return this builder instance + * @since 1.3.0 + */ + public Builder customizeObjectMapper(Function customizer) { + if (customizer != null) { + this.objectMapperCustomizer = customizer; + } + return this; + } + + public Zendesk build() { + if (token != null) { + return new Zendesk( + client, url, username + "/token", token, headers, cbpPageSize, objectMapperCustomizer); + } else if (oauthToken != null) { + return new Zendesk(client, url, oauthToken, headers, cbpPageSize, objectMapperCustomizer); + } + return new Zendesk( + client, url, username, password, headers, cbpPageSize, objectMapperCustomizer); + } + } } diff --git a/src/main/java/org/zendesk/client/v2/ZendeskException.java b/src/main/java/org/zendesk/client/v2/ZendeskException.java index 477c3f5c0..82cd3ce68 100644 --- a/src/main/java/org/zendesk/client/v2/ZendeskException.java +++ b/src/main/java/org/zendesk/client/v2/ZendeskException.java @@ -6,20 +6,19 @@ */ public class ZendeskException extends RuntimeException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public ZendeskException(String message) { - super(message); - } + public ZendeskException(String message) { + super(message); + } - public ZendeskException() { - } + public ZendeskException() {} - public ZendeskException(Throwable cause) { - super(cause); - } + public ZendeskException(Throwable cause) { + super(cause); + } - public ZendeskException(String message, Throwable cause) { - super(message, cause); - } + public ZendeskException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/main/java/org/zendesk/client/v2/ZendeskResponseException.java b/src/main/java/org/zendesk/client/v2/ZendeskResponseException.java index 8b5dcfe33..195e66a99 100644 --- a/src/main/java/org/zendesk/client/v2/ZendeskResponseException.java +++ b/src/main/java/org/zendesk/client/v2/ZendeskResponseException.java @@ -1,48 +1,45 @@ package org.zendesk.client.v2; -import org.asynchttpclient.Response; - import java.io.IOException; import java.text.MessageFormat; +import org.asynchttpclient.Response; -/** - * {@link ZendeskException} specialisation for HTTP non-2xx responses - */ +/** {@link ZendeskException} specialisation for HTTP non-2xx responses */ public class ZendeskResponseException extends ZendeskException { - private static final long serialVersionUID = 1L; - - private int statusCode; - private String statusText; - private String body; - - public ZendeskResponseException(Response resp) throws IOException { - this(resp.getStatusCode(), resp.getStatusText(), resp.getResponseBody()); - } - - public ZendeskResponseException(int statusCode, String statusText, String body) { - super(MessageFormat.format("HTTP/{0}: {1} - {2}", statusCode, statusText, body)); - this.statusCode = statusCode; - this.statusText = statusText; - this.body = body; - } - - public ZendeskResponseException(ZendeskResponseException cause) { - super(cause.getMessage(), cause); - this.statusCode = cause.getStatusCode(); - this.statusText = cause.getStatusText(); - this.body = cause.getBody(); - } - - public int getStatusCode() { - return statusCode; - } - - public String getStatusText() { - return statusText; - } - - public String getBody() { - return body; - } + private static final long serialVersionUID = 1L; + + private int statusCode; + private String statusText; + private String body; + + public ZendeskResponseException(Response resp) throws IOException { + this(resp.getStatusCode(), resp.getStatusText(), resp.getResponseBody()); + } + + public ZendeskResponseException(int statusCode, String statusText, String body) { + super(MessageFormat.format("HTTP/{0}: {1} - {2}", statusCode, statusText, body)); + this.statusCode = statusCode; + this.statusText = statusText; + this.body = body; + } + + public ZendeskResponseException(ZendeskResponseException cause) { + super(cause.getMessage(), cause); + this.statusCode = cause.getStatusCode(); + this.statusText = cause.getStatusText(); + this.body = cause.getBody(); + } + + public int getStatusCode() { + return statusCode; + } + + public String getStatusText() { + return statusText; + } + + public String getBody() { + return body; + } } diff --git a/src/main/java/org/zendesk/client/v2/ZendeskResponseRateLimitException.java b/src/main/java/org/zendesk/client/v2/ZendeskResponseRateLimitException.java index a19bc7f77..c4dacb7c8 100644 --- a/src/main/java/org/zendesk/client/v2/ZendeskResponseRateLimitException.java +++ b/src/main/java/org/zendesk/client/v2/ZendeskResponseRateLimitException.java @@ -1,32 +1,31 @@ package org.zendesk.client.v2; -import org.asynchttpclient.Response; - import java.io.IOException; +import org.asynchttpclient.Response; public class ZendeskResponseRateLimitException extends ZendeskResponseException { - private static final long serialVersionUID = 1L; - private static final String RETRY_AFTER_HEADER = "Retry-After"; - private static final long DEFAULT_RETRY_AFTER = 60L; + private static final long serialVersionUID = 1L; + private static final String RETRY_AFTER_HEADER = "Retry-After"; + private static final long DEFAULT_RETRY_AFTER = 60L; - private Long retryAfter = DEFAULT_RETRY_AFTER; + private Long retryAfter = DEFAULT_RETRY_AFTER; - public ZendeskResponseRateLimitException(Response resp) throws IOException { - super(resp); - try { - this.retryAfter = Long.valueOf(resp.getHeader(RETRY_AFTER_HEADER)); - } catch (NumberFormatException e) { - // Ignore, use the default value already set - } + public ZendeskResponseRateLimitException(Response resp) throws IOException { + super(resp); + try { + this.retryAfter = Long.valueOf(resp.getHeader(RETRY_AFTER_HEADER)); + } catch (NumberFormatException e) { + // Ignore, use the default value already set } + } - protected ZendeskResponseRateLimitException(ZendeskResponseRateLimitException e) { - super(e); - this.retryAfter = e.getRetryAfter(); - } + protected ZendeskResponseRateLimitException(ZendeskResponseRateLimitException e) { + super(e); + this.retryAfter = e.getRetryAfter(); + } - public Long getRetryAfter() { - return retryAfter; - } + public Long getRetryAfter() { + return retryAfter; + } } diff --git a/src/main/java/org/zendesk/client/v2/model/Action.java b/src/main/java/org/zendesk/client/v2/model/Action.java index cfdcda8be..0c9d49eea 100644 --- a/src/main/java/org/zendesk/client/v2/model/Action.java +++ b/src/main/java/org/zendesk/client/v2/model/Action.java @@ -1,7 +1,6 @@ package org.zendesk.client.v2.model; import com.fasterxml.jackson.annotation.JsonFormat; - import java.util.Arrays; /** @@ -10,41 +9,40 @@ */ public class Action { - private String field; + private String field; - @JsonFormat(with = {JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, - JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED}) - private Object[] value; + @JsonFormat( + with = { + JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, + JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED + }) + private Object[] value; - public Action() { - } + public Action() {} - public Action(String field, String[] value) { - this.field = field; - this.value = value; - } + public Action(String field, String[] value) { + this.field = field; + this.value = value; + } - public String getField() { - return field; - } + public String getField() { + return field; + } - public void setField(String field) { - this.field = field; - } + public void setField(String field) { + this.field = field; + } - public Object[] getValue() { - return value; - } + public Object[] getValue() { + return value; + } - public void setValue(Object[] value) { - this.value = value; - } + public void setValue(Object[] value) { + this.value = value; + } - @Override - public String toString() { - return "Action{" + - "field='" + field + '\'' + - ", value=" + Arrays.toString(value) + - '}'; - } + @Override + public String toString() { + return "Action{" + "field='" + field + '\'' + ", value=" + Arrays.toString(value) + '}'; + } } diff --git a/src/main/java/org/zendesk/client/v2/model/AgentRole.java b/src/main/java/org/zendesk/client/v2/model/AgentRole.java index 831109ced..4afade872 100644 --- a/src/main/java/org/zendesk/client/v2/model/AgentRole.java +++ b/src/main/java/org/zendesk/client/v2/model/AgentRole.java @@ -1,70 +1,67 @@ package org.zendesk.client.v2.model; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Date; import java.util.Map; public class AgentRole { - private Long id; - private String name; - private String description; - private Date createdAt; - private Date updatedAt; - - /** - * A series of permissions granted to agents in this role - */ - private Map configuration; - - public Long getId() { - return id; - } - - public void setId( Long id ) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName( String name ) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription( String description ) { - this.description = description; - } - - @JsonProperty("created_at") - public Date getCreatedAt() { - return createdAt; - } - - public void setCreatedAt( Date createdAt ) { - this.createdAt = createdAt; - } - - @JsonProperty("updated_at") - public Date getUpdatedAt() { - return updatedAt; - } - - public void setUpdatedAt( Date updatedAt ) { - this.updatedAt = updatedAt; - } - - public Map getConfiguration() { - return configuration; - } - - public void setConfiguration( Map configuration ) { - this.configuration = configuration; - } + private Long id; + private String name; + private String description; + private Date createdAt; + private Date updatedAt; + + /** A series of permissions granted to agents in this role */ + private Map configuration; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @JsonProperty("created_at") + public Date getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } + + @JsonProperty("updated_at") + public Date getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(Date updatedAt) { + this.updatedAt = updatedAt; + } + + public Map getConfiguration() { + return configuration; + } + + public void setConfiguration(Map configuration) { + this.configuration = configuration; + } } diff --git a/src/main/java/org/zendesk/client/v2/model/Attachment.java b/src/main/java/org/zendesk/client/v2/model/Attachment.java index b31c7248b..da0eca989 100644 --- a/src/main/java/org/zendesk/client/v2/model/Attachment.java +++ b/src/main/java/org/zendesk/client/v2/model/Attachment.java @@ -8,56 +8,62 @@ */ public class Attachment extends Photo { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private List thumbnails; + private List thumbnails; - public List getThumbnails() { - return thumbnails; + public List getThumbnails() { + return thumbnails; + } + + public void setThumbnails(List thumbnails) { + this.thumbnails = thumbnails; + } + + @Override + public String toString() { + return "Attachment" + + "{id=" + + getId() + + ", fileName='" + + getFileName() + + '\'' + + ", contentType='" + + getContentType() + + '\'' + + ", contentUrl='" + + getContentUrl() + + '\'' + + ", size=" + + getSize() + + ", thumbnails=" + + thumbnails + + '}'; + } + + public static class Upload { + private String token; + private List attachments; + + public String getToken() { + return token; } - public void setThumbnails(List thumbnails) { - this.thumbnails = thumbnails; + public void setToken(String token) { + this.token = token; } - @Override - public String toString() { - return "Attachment" + - "{id=" + getId() + - ", fileName='" + getFileName() + '\'' + - ", contentType='" + getContentType() + '\'' + - ", contentUrl='" + getContentUrl() + '\'' + - ", size=" + getSize() + - ", thumbnails=" + thumbnails + - '}'; + public List getAttachments() { + return attachments; + } + + public void setAttachments(List attachments) { + this.attachments = attachments; } - public static class Upload { - private String token; - private List attachments; - - public String getToken() { - return token; - } - - public void setToken(String token) { - this.token = token; - } - - public List getAttachments() { - return attachments; - } - - public void setAttachments(List attachments) { - this.attachments = attachments; - } - - @Override - public String toString() { - return "Upload" + - "{token='" + token + '\'' + - ", attachments=" + attachments + - '}'; - } + @Override + public String toString() { + return "Upload" + "{token='" + token + '\'' + ", attachments=" + attachments + '}'; } + } } diff --git a/src/main/java/org/zendesk/client/v2/model/Audit.java b/src/main/java/org/zendesk/client/v2/model/Audit.java index e7af123be..2ab6090e6 100644 --- a/src/main/java/org/zendesk/client/v2/model/Audit.java +++ b/src/main/java/org/zendesk/client/v2/model/Audit.java @@ -1,12 +1,11 @@ package org.zendesk.client.v2.model; import com.fasterxml.jackson.annotation.JsonProperty; -import org.zendesk.client.v2.model.events.Event; - import java.io.Serializable; import java.util.Date; import java.util.List; import java.util.Map; +import org.zendesk.client.v2.model.events.Event; /** * @author stephenc @@ -14,86 +13,92 @@ */ public class Audit implements Serializable { - private static final long serialVersionUID = 1L; - - private Long id; - private Long ticketId; - private Map metadata; - private Via via; - private Date createdAt; - private Long authorId; - private List events; - - @JsonProperty("author_id") - public Long getAuthorId() { - return authorId; - } - - public void setAuthorId(Long authorId) { - this.authorId = authorId; - } - - @JsonProperty("created_at") - public Date getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(Date createdAt) { - this.createdAt = createdAt; - } - - public List getEvents() { - return events; - } - - public void setEvents(List events) { - this.events = events; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public Map getMetadata() { - return metadata; - } - - public void setMetadata(Map metadata) { - this.metadata = metadata; - } - - @JsonProperty("ticket_id") - public Long getTicketId() { - return ticketId; - } - - public void setTicketId(Long ticketId) { - this.ticketId = ticketId; - } - - public Via getVia() { - return via; - } - - public void setVia(Via via) { - this.via = via; - } - - @Override - public String toString() { - return "Audit" + - "{authorId=" + authorId + - ", id=" + id + - ", ticketId=" + ticketId + - ", metadata=" + metadata + - ", via=" + via + - ", createdAt=" + createdAt + - ", events=" + events + - '}'; - } - + private static final long serialVersionUID = 1L; + + private Long id; + private Long ticketId; + private Map metadata; + private Via via; + private Date createdAt; + private Long authorId; + private List events; + + @JsonProperty("author_id") + public Long getAuthorId() { + return authorId; + } + + public void setAuthorId(Long authorId) { + this.authorId = authorId; + } + + @JsonProperty("created_at") + public Date getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } + + public List getEvents() { + return events; + } + + public void setEvents(List events) { + this.events = events; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + + @JsonProperty("ticket_id") + public Long getTicketId() { + return ticketId; + } + + public void setTicketId(Long ticketId) { + this.ticketId = ticketId; + } + + public Via getVia() { + return via; + } + + public void setVia(Via via) { + this.via = via; + } + + @Override + public String toString() { + return "Audit" + + "{authorId=" + + authorId + + ", id=" + + id + + ", ticketId=" + + ticketId + + ", metadata=" + + metadata + + ", via=" + + via + + ", createdAt=" + + createdAt + + ", events=" + + events + + '}'; + } } diff --git a/src/main/java/org/zendesk/client/v2/model/Automation.java b/src/main/java/org/zendesk/client/v2/model/Automation.java index 8e4257317..e72b10b40 100644 --- a/src/main/java/org/zendesk/client/v2/model/Automation.java +++ b/src/main/java/org/zendesk/client/v2/model/Automation.java @@ -4,12 +4,10 @@ import java.util.Date; import java.util.List; - /** * https://developer.zendesk.com/rest_api/docs/core/automations - * - * @author Sandeep Kaul (sandeep.kaul@olacabs.com) * + * @author Sandeep Kaul (sandeep.kaul@olacabs.com) */ public class Automation implements Serializable { @@ -23,62 +21,89 @@ public class Automation implements Serializable { private int position; private Date createdAt; private Date updatedAt; - + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + public String getTitle() { return title; } + public void setTitle(String title) { this.title = title; } + public Boolean getActive() { return active; } + public void setActive(Boolean active) { this.active = active; } + public List getActions() { return actions; } + public void setActions(List actions) { this.actions = actions; } + public Conditions getConditions() { return conditions; } + public void setConditions(Conditions conditions) { this.conditions = conditions; } + 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; } + public int getPosition() { return position; } + public void setPosition(int position) { this.position = position; } + @Override public String toString() { - return "Automation [id=" + id + ", title=" + title + ", active=" + active + ", actions=" - + actions + ", conditions=" + conditions + ", position=" + position + ", createdAt=" - + createdAt + ", updatedAt=" + updatedAt + "]"; + return "Automation [id=" + + id + + ", title=" + + title + + ", active=" + + active + + ", actions=" + + actions + + ", conditions=" + + conditions + + ", position=" + + position + + ", createdAt=" + + createdAt + + ", updatedAt=" + + updatedAt + + "]"; } - - - } diff --git a/src/main/java/org/zendesk/client/v2/model/Brand.java b/src/main/java/org/zendesk/client/v2/model/Brand.java index 49e6ab4e9..a88378578 100644 --- a/src/main/java/org/zendesk/client/v2/model/Brand.java +++ b/src/main/java/org/zendesk/client/v2/model/Brand.java @@ -1,7 +1,6 @@ package org.zendesk.client.v2.model; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Date; import java.util.List; @@ -11,154 +10,163 @@ */ public class Brand { - private String url; - private Long id; - private String name; - @JsonProperty("brand_url") - private String brandUrl; - @JsonProperty("has_help_center") - private boolean hasHelpCenter; - @JsonProperty("help_center_state") - private String helpCenterState; - @JsonProperty("active") - private boolean isActive; - @JsonProperty("default") - private boolean isDefault; - private Attachment logo; - @JsonProperty("ticket_form_ids") - private List ticketFormIds; - @JsonProperty("created_at") - private Date createdAt; - @JsonProperty("updated_at") - private Date updatedAt; - private String subdomain; - @JsonProperty("host_mapping") - private String hostMapping; - - public Brand() { - } - - 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; - } - - public String getSubdomain() { - return subdomain; - } - - public void setSubdomain(String subdomain) { - this.subdomain = subdomain; - } - - public String getHostMapping() { - return hostMapping; - } - - public void setHostMapping(String hostMapping) { - this.hostMapping = hostMapping; - } - - public String getSignatureTemplate() { - return signatureTemplate; - } - - public void setSignatureTemplate(String signatureTemplate) { - this.signatureTemplate = signatureTemplate; - } - - @JsonProperty("signature_template") - private String signatureTemplate; - - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getBrandUrl() { - return brandUrl; - } - - public void setBrandUrl(String brandUrl) { - this.brandUrl = brandUrl; - } - - public boolean isHasHelpCenter() { - return hasHelpCenter; - } - - public void setHasHelpCenter(boolean hasHelpCenter) { - this.hasHelpCenter = hasHelpCenter; - } - - public String getHelpCenterState() { - return helpCenterState; - } - - public void setHelpCenterState(String helpCenterState) { - this.helpCenterState = helpCenterState; - } - - public boolean isActive() { - return isActive; - } - - public void setActive(boolean active) { - isActive = active; - } + private String url; + private Long id; + private String name; + + @JsonProperty("brand_url") + private String brandUrl; + + @JsonProperty("has_help_center") + private boolean hasHelpCenter; + + @JsonProperty("help_center_state") + private String helpCenterState; + + @JsonProperty("active") + private boolean isActive; + + @JsonProperty("default") + private boolean isDefault; + + private Attachment logo; + + @JsonProperty("ticket_form_ids") + private List ticketFormIds; + + @JsonProperty("created_at") + private Date createdAt; + + @JsonProperty("updated_at") + private Date updatedAt; + + private String subdomain; + + @JsonProperty("host_mapping") + private String hostMapping; + + public Brand() {} + + 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; + } + + public String getSubdomain() { + return subdomain; + } + + public void setSubdomain(String subdomain) { + this.subdomain = subdomain; + } + + public String getHostMapping() { + return hostMapping; + } + + public void setHostMapping(String hostMapping) { + this.hostMapping = hostMapping; + } + + public String getSignatureTemplate() { + return signatureTemplate; + } + + public void setSignatureTemplate(String signatureTemplate) { + this.signatureTemplate = signatureTemplate; + } + + @JsonProperty("signature_template") + private String signatureTemplate; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getBrandUrl() { + return brandUrl; + } + + public void setBrandUrl(String brandUrl) { + this.brandUrl = brandUrl; + } + + public boolean isHasHelpCenter() { + return hasHelpCenter; + } + + public void setHasHelpCenter(boolean hasHelpCenter) { + this.hasHelpCenter = hasHelpCenter; + } + + public String getHelpCenterState() { + return helpCenterState; + } + + public void setHelpCenterState(String helpCenterState) { + this.helpCenterState = helpCenterState; + } + + public boolean isActive() { + return isActive; + } + + public void setActive(boolean active) { + isActive = active; + } - public boolean isDefault() { - return isDefault; - } + public boolean isDefault() { + return isDefault; + } - public void setDefault(boolean aDefault) { - isDefault = aDefault; - } + public void setDefault(boolean aDefault) { + isDefault = aDefault; + } - public Attachment getLogo() { - return logo; - } + public Attachment getLogo() { + return logo; + } - public void setLogo(Attachment logo) { - this.logo = logo; - } + public void setLogo(Attachment logo) { + this.logo = logo; + } - public List getTicketFormIds() { - return ticketFormIds; - } + public List getTicketFormIds() { + return ticketFormIds; + } - public void setTicketFormIds(List ticketFormIds) { - this.ticketFormIds = ticketFormIds; - } + public void setTicketFormIds(List ticketFormIds) { + this.ticketFormIds = ticketFormIds; + } - public String getUrl() { - return url; - } + public String getUrl() { + return url; + } - public void setUrl(String url) { - this.url = url; - } + public void setUrl(String url) { + this.url = url; + } } diff --git a/src/main/java/org/zendesk/client/v2/model/Collaborator.java b/src/main/java/org/zendesk/client/v2/model/Collaborator.java index 472d3e802..8cd24306c 100644 --- a/src/main/java/org/zendesk/client/v2/model/Collaborator.java +++ b/src/main/java/org/zendesk/client/v2/model/Collaborator.java @@ -1,42 +1,40 @@ package org.zendesk.client.v2.model; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - import java.io.Serializable; @JsonIgnoreProperties(ignoreUnknown = true) public class Collaborator implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private String name; - private String email; + private String name; + private String email; - public Collaborator() { - } + public Collaborator() {} - protected Collaborator(String name) { - this.name = name; - } + protected Collaborator(String name) { + this.name = name; + } - public Collaborator(String name, String email) { - this.name = name; - this.email = email; - } + public Collaborator(String name, String email) { + this.name = name; + this.email = email; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } - public String getEmail() { - return email; - } + public String getEmail() { + return email; + } - public void setEmail(String email) { - this.email = email; - } + public void setEmail(String email) { + this.email = email; + } } diff --git a/src/main/java/org/zendesk/client/v2/model/Comment.java b/src/main/java/org/zendesk/client/v2/model/Comment.java index 4ec6c2b46..3be296bab 100644 --- a/src/main/java/org/zendesk/client/v2/model/Comment.java +++ b/src/main/java/org/zendesk/client/v2/model/Comment.java @@ -1,117 +1,157 @@ package org.zendesk.client.v2.model; -import com.fasterxml.jackson.annotation.JsonProperty; +import static com.fasterxml.jackson.annotation.JsonTypeInfo.As.EXTERNAL_PROPERTY; +import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import java.io.Serializable; import java.util.Arrays; import java.util.Date; import java.util.List; +import org.zendesk.client.v2.model.comments.VoiceComment; /** * @author stephenc * @since 09/04/2013 15:09 */ +@JsonTypeInfo(use = NAME, include = EXTERNAL_PROPERTY, property = "type", visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type(value = Comment.class, name = "Comment"), + @JsonSubTypes.Type(value = VoiceComment.class, name = "VoiceComment"), + @JsonSubTypes.Type(value = VoiceComment.class, name = "TpeVoiceComment") +}) public class Comment implements Serializable { - private static final long serialVersionUID = 1L; - - private Long id; - private String body; - private String htmlBody; - private Long authorId; - private List uploads; - private List attachments; - private Date createdAt; - private Boolean publicComment; - - public Comment() { - } - - public Comment(String body) { - this.body = body; - } - - public Comment(String body, String... uploads) { - this.body = body; - this.uploads = uploads.length == 0 ? null : Arrays.asList(uploads); - } - - public String getBody() { - return body; - } - - public void setBody(String body) { - this.body = body; - } - - @JsonProperty("html_body") - public String getHtmlBody() { - return htmlBody; - } - - public void setHtmlBody(String htmlBody) { - this.htmlBody = htmlBody; - } - - public List getUploads() { - return uploads; - } - - public void setUploads(List uploads) { - this.uploads = uploads; - } - - public List getAttachments() { - return attachments; - } - - public void setAttachments(List attachments) { - this.attachments = attachments; - } - - @JsonProperty("author_id") - public Long getAuthorId() { - return authorId; - } - - public void setAuthorId(Long authorId) { - this.authorId = authorId; - } - - @JsonProperty("created_at") - public Date getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(Date createdAt) { - this.createdAt = createdAt; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - @JsonProperty("public") - public Boolean isPublic() { - return publicComment; - } - - public void setPublic(Boolean isPublic) { - this.publicComment = isPublic; - } - - @Override - public String toString() { - return "Comment{" + "id=" + id + - ", body='" + body + '\'' + - ", authorId=" + authorId + - ", attachments=" + attachments + - ", createdAt=" + createdAt + - ", uploads=" + uploads + - '}'; - } + private static final long serialVersionUID = 1L; + + private Long id; + private String body; + private String htmlBody; + private Long authorId; + private List uploads; + private List attachments; + private Date createdAt; + private Boolean publicComment; + private CommentType type; + private Via via; + + public Comment() {} + + public Comment(String body) { + this.body = body; + } + + public Comment(String body, String... uploads) { + this.body = body; + this.uploads = uploads.length == 0 ? null : Arrays.asList(uploads); + } + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + @JsonProperty("html_body") + public String getHtmlBody() { + return htmlBody; + } + + public void setHtmlBody(String htmlBody) { + this.htmlBody = htmlBody; + } + + public List getUploads() { + return uploads; + } + + public void setUploads(List uploads) { + this.uploads = uploads; + } + + public List getAttachments() { + return attachments; + } + + public void setAttachments(List attachments) { + this.attachments = attachments; + } + + @JsonProperty("author_id") + public Long getAuthorId() { + return authorId; + } + + public void setAuthorId(Long authorId) { + this.authorId = authorId; + } + + @JsonProperty("created_at") + public Date getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + @JsonProperty("public") + public Boolean isPublic() { + return publicComment; + } + + public void setPublic(Boolean isPublic) { + this.publicComment = isPublic; + } + + @JsonProperty("type") + public CommentType getType() { + return type; + } + + public void setType(CommentType type) { + this.type = type; + } + + @JsonProperty("via") + public Via getVia() { + return via; + } + + public void setVia(Via via) { + this.via = via; + } + + @Override + public String toString() { + return "Comment{" + + "id=" + + id + + ", body='" + + body + + '\'' + + ", authorId=" + + authorId + + ", attachments=" + + attachments + + ", createdAt=" + + createdAt + + ", uploads=" + + uploads + + ", via=" + + via + + '}'; + } } diff --git a/src/main/java/org/zendesk/client/v2/model/CommentType.java b/src/main/java/org/zendesk/client/v2/model/CommentType.java new file mode 100644 index 000000000..e5b13159e --- /dev/null +++ b/src/main/java/org/zendesk/client/v2/model/CommentType.java @@ -0,0 +1,20 @@ +package org.zendesk.client.v2.model; + +/** https://developer.zendesk.com/api-reference/ticketing/tickets/ticket_comments/ */ +public enum CommentType { + COMMENT("Comment"), + VOICE_COMMENT("VoiceComment"), + // Talk Partner Edition + TPE_VOICE_COMMENT("TpeVoiceComment"); + + private final String name; + + CommentType(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} diff --git a/src/main/java/org/zendesk/client/v2/model/ComplianceDeletionStatus.java b/src/main/java/org/zendesk/client/v2/model/ComplianceDeletionStatus.java index 2fca472d4..aa827ea0f 100644 --- a/src/main/java/org/zendesk/client/v2/model/ComplianceDeletionStatus.java +++ b/src/main/java/org/zendesk/client/v2/model/ComplianceDeletionStatus.java @@ -1,100 +1,112 @@ package org.zendesk.client.v2.model; import com.fasterxml.jackson.annotation.JsonProperty; - import java.io.Serializable; import java.util.Date; -/** - * https://developer.zendesk.com/rest_api/docs/core/users#show-compliance-deletion-statuses - */ +/** https://developer.zendesk.com/rest_api/docs/core/users#show-compliance-deletion-statuses */ public class ComplianceDeletionStatus implements Serializable { - private static final long serialVersionUID = 1L; - - private String action; - private String application; - @JsonProperty("account_subdomain") - private String accountSubdomain; - @JsonProperty("executer_id") - private long executerId; - @JsonProperty("user_id") - private long userId; - @JsonProperty("account_id") - private long accountId; - @JsonProperty("created_at") - private Date createdAt; - - public static long getSerialVersionUID() { - return serialVersionUID; - } - - public String getAction() { - return action; - } - - public void setAction(String action) { - this.action = action; - } - - public String getApplication() { - return application; - } - - public void setApplication(String application) { - this.application = application; - } - - public String getAccountSubdomain() { - return accountSubdomain; - } - - public void setAccountSubdomain(String accountSubdomain) { - this.accountSubdomain = accountSubdomain; - } - - public long getExecuterId() { - return executerId; - } - - public void setExecuterId(long executerId) { - this.executerId = executerId; - } - - public long getUserId() { - return userId; - } - - public void setUserId(long userId) { - this.userId = userId; - } - - public long getAccountId() { - return accountId; - } - - public void setAccountId(long accountId) { - this.accountId = accountId; - } - - public Date getCreatedAt() { - return new Date(createdAt.getTime()); - } - - public void setCreatedAt(Date createdAt) { - this.createdAt = createdAt; - } - - @Override - public String toString() { - return "ComplianceDeletionStatus{" + - "action='" + action + '\'' + - ", application='" + application + '\'' + - ", accountSubdomain='" + accountSubdomain + '\'' + - ", executerId=" + executerId + - ", userId=" + userId + - ", accountId=" + accountId + - ", createdAt=" + createdAt + - '}'; - } -} \ No newline at end of file + private static final long serialVersionUID = 1L; + + private String action; + private String application; + + @JsonProperty("account_subdomain") + private String accountSubdomain; + + @JsonProperty("executer_id") + private long executerId; + + @JsonProperty("user_id") + private long userId; + + @JsonProperty("account_id") + private long accountId; + + @JsonProperty("created_at") + private Date createdAt; + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public String getApplication() { + return application; + } + + public void setApplication(String application) { + this.application = application; + } + + public String getAccountSubdomain() { + return accountSubdomain; + } + + public void setAccountSubdomain(String accountSubdomain) { + this.accountSubdomain = accountSubdomain; + } + + public long getExecuterId() { + return executerId; + } + + public void setExecuterId(long executerId) { + this.executerId = executerId; + } + + public long getUserId() { + return userId; + } + + public void setUserId(long userId) { + this.userId = userId; + } + + public long getAccountId() { + return accountId; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + public Date getCreatedAt() { + return new Date(createdAt.getTime()); + } + + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } + + @Override + public String toString() { + return "ComplianceDeletionStatus{" + + "action='" + + action + + '\'' + + ", application='" + + application + + '\'' + + ", accountSubdomain='" + + accountSubdomain + + '\'' + + ", executerId=" + + executerId + + ", userId=" + + userId + + ", accountId=" + + accountId + + ", createdAt=" + + createdAt + + '}'; + } +} diff --git a/src/main/java/org/zendesk/client/v2/model/Condition.java b/src/main/java/org/zendesk/client/v2/model/Condition.java index 79c12b6be..805e0dbbf 100644 --- a/src/main/java/org/zendesk/client/v2/model/Condition.java +++ b/src/main/java/org/zendesk/client/v2/model/Condition.java @@ -4,7 +4,6 @@ /** * @author Sandeep Kaul (sandeep.kaul@olacabs.com) - * */ public class Condition implements Serializable { @@ -48,11 +47,6 @@ public void setValue(String value) { @Override public String toString() { - return "Condition" + - "{field=" + field + - ", operator=" + operator + - ", value=" + value + - '}'; + return "Condition" + "{field=" + field + ", operator=" + operator + ", value=" + value + '}'; } - } diff --git a/src/main/java/org/zendesk/client/v2/model/Conditions.java b/src/main/java/org/zendesk/client/v2/model/Conditions.java index 3233471e3..83daf0473 100644 --- a/src/main/java/org/zendesk/client/v2/model/Conditions.java +++ b/src/main/java/org/zendesk/client/v2/model/Conditions.java @@ -4,10 +4,8 @@ import java.util.ArrayList; import java.util.List; - /** * @author Sandeep Kaul(sandeep.kaul@olacabs.com) - * */ public class Conditions implements Serializable { @@ -34,10 +32,6 @@ public void setAny(List any) { @Override public String toString() { - return "Conditions" + - "{all=" + all + - ", any=" + any + - '}'; + return "Conditions" + "{all=" + all + ", any=" + any + '}'; } - } diff --git a/src/main/java/org/zendesk/client/v2/model/CustomFieldValue.java b/src/main/java/org/zendesk/client/v2/model/CustomFieldValue.java index 4e9ea9c0a..74ef37252 100644 --- a/src/main/java/org/zendesk/client/v2/model/CustomFieldValue.java +++ b/src/main/java/org/zendesk/client/v2/model/CustomFieldValue.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - import java.io.Serializable; import java.util.Arrays; @@ -13,43 +12,42 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class CustomFieldValue implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private Long id; + private Long id; - @JsonFormat(with = {JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, - JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED}) - private String[] value; + @JsonFormat( + with = { + JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, + JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED + }) + private String[] value; - public CustomFieldValue() { - } + public CustomFieldValue() {} - public CustomFieldValue(Long id, String[] value) { - this.id = id; - this.value = value; - } + public CustomFieldValue(Long id, String[] value) { + this.id = id; + this.value = value; + } - public Long getId() { - return id; - } + public Long getId() { + return id; + } - public void setId(Long id) { - this.id = id; - } + public void setId(Long id) { + this.id = id; + } - public String[] getValue() { - return value; - } + public String[] getValue() { + return value; + } - public void setValue(String[] value) { - this.value = value; - } + public void setValue(String[] value) { + this.value = value; + } - @Override - public String toString() { - return "CustomFieldValue{" + - "id=" + id + - ", value=" + Arrays.toString(value) + - '}'; - } + @Override + public String toString() { + return "CustomFieldValue{" + "id=" + id + ", value=" + Arrays.toString(value) + '}'; + } } diff --git a/src/main/java/org/zendesk/client/v2/model/CustomTicketStatus.java b/src/main/java/org/zendesk/client/v2/model/CustomTicketStatus.java new file mode 100644 index 000000000..f811524ac --- /dev/null +++ b/src/main/java/org/zendesk/client/v2/model/CustomTicketStatus.java @@ -0,0 +1,192 @@ +package org.zendesk.client.v2.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.Serializable; +import java.util.Date; + +/** https://developer.zendesk.com/api-reference/ticketing/tickets/custom_ticket_statuses/ */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class CustomTicketStatus implements Serializable { + + private static final long serialVersionUID = 1L; + + private Long id; + private boolean active; + private String description; + + @JsonProperty("agent_label") + private String agentLabel; + + @JsonProperty("created_at") + private Date createdAt; + + @JsonProperty("end_user_description") + private String endUserDescription; + + @JsonProperty("end_user_label") + private String endUserLabel; + + @JsonProperty("raw_agent_label") + private String rawAgentLabel; + + @JsonProperty("raw_description") + private String rawDescription; + + @JsonProperty("raw_end_user_description") + private String rawEndUserDescription; + + @JsonProperty("raw_end_user_label") + private String rawEndUserLabel; + + @JsonProperty("status_category") + private String statusCategory; + + @JsonProperty("updated_at") + private Date updatedAt; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getAgentLabel() { + return agentLabel; + } + + public void setAgentLabel(String agentLabel) { + this.agentLabel = agentLabel; + } + + public Date getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } + + public String getEndUserDescription() { + return endUserDescription; + } + + public void setEndUserDescription(String endUserDescription) { + this.endUserDescription = endUserDescription; + } + + public String getEndUserLabel() { + return endUserLabel; + } + + public void setEndUserLabel(String endUserLabel) { + this.endUserLabel = endUserLabel; + } + + public String getRawAgentLabel() { + return rawAgentLabel; + } + + public void setRawAgentLabel(String rawAgentLabel) { + this.rawAgentLabel = rawAgentLabel; + } + + public String getRawDescription() { + return rawDescription; + } + + public void setRawDescription(String rawDescription) { + this.rawDescription = rawDescription; + } + + public String getRawEndUserDescription() { + return rawEndUserDescription; + } + + public void setRawEndUserDescription(String rawEndUserDescription) { + this.rawEndUserDescription = rawEndUserDescription; + } + + public String getRawEndUserLabel() { + return rawEndUserLabel; + } + + public void setRawEndUserLabel(String rawEndUserLabel) { + this.rawEndUserLabel = rawEndUserLabel; + } + + public String getStatusCategory() { + return statusCategory; + } + + public void setStatusCategory(String statusCategory) { + this.statusCategory = statusCategory; + } + + public Date getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(Date updatedAt) { + this.updatedAt = updatedAt; + } + + @Override + public String toString() { + return "CustomTicketStatus{" + + "id=" + + id + + ", active=" + + active + + ", description='" + + description + + '\'' + + ", agentLabel='" + + agentLabel + + '\'' + + ", createdAt=" + + createdAt + + ", endUserDescription='" + + endUserDescription + + '\'' + + ", endUserLabel='" + + endUserLabel + + '\'' + + ", rawAgentLabel='" + + rawAgentLabel + + '\'' + + ", rawDescription='" + + rawDescription + + '\'' + + ", rawEndUserDescription='" + + rawEndUserDescription + + '\'' + + ", rawEndUserLabel='" + + rawEndUserLabel + + '\'' + + ", statusCategory='" + + statusCategory + + '\'' + + ", updatedAt=" + + updatedAt + + '}'; + } +} diff --git a/src/main/java/org/zendesk/client/v2/model/DeletedTicket.java b/src/main/java/org/zendesk/client/v2/model/DeletedTicket.java index 6d62e18b1..9dc4dc15c 100644 --- a/src/main/java/org/zendesk/client/v2/model/DeletedTicket.java +++ b/src/main/java/org/zendesk/client/v2/model/DeletedTicket.java @@ -2,135 +2,142 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; - import java.io.Serializable; import java.util.Date; @JsonIgnoreProperties(ignoreUnknown = true) public class DeletedTicket implements Serializable { - private static final long serialVersionUID = -1245555299753747844L; - - protected Long id; - protected String subject; - protected String description; - protected Actor actor; - protected Status previousState; - protected Date deletedAt; - - public DeletedTicket() { - } - - public DeletedTicket(Long id, String subject, String description, Actor actor, - Status previousState, Date deletedAt) { - this.id = id; - this.subject = subject; - this.description = description; - this.actor = actor; - this.previousState = previousState; - this.deletedAt = deletedAt; + private static final long serialVersionUID = -1245555299753747844L; + + protected Long id; + protected String subject; + protected String description; + protected Actor actor; + protected Status previousState; + protected Date deletedAt; + + public DeletedTicket() {} + + public DeletedTicket( + Long id, + String subject, + String description, + Actor actor, + Status previousState, + Date deletedAt) { + this.id = id; + this.subject = subject; + this.description = description; + this.actor = actor; + this.previousState = previousState; + this.deletedAt = deletedAt; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Actor getActor() { + return actor; + } + + public void setActor(Actor actor) { + this.actor = actor; + } + + @JsonProperty("previous_state") + public Status getPreviousState() { + return previousState; + } + + public void setPreviousState(Status previousState) { + this.previousState = previousState; + } + + @JsonProperty("deleted_at") + public Date getDeletedAt() { + return deletedAt; + } + + public void setDeletedAt(Date deletedAt) { + this.deletedAt = deletedAt; + } + + @Override + public String toString() { + return "DeletedTicket{" + + "id=" + + id + + ", subject='" + + subject + + '\'' + + ", description='" + + description + + '\'' + + ", actor=" + + actor + + ", previousState=" + + previousState + + ", deletedAt=" + + deletedAt + + '}'; + } + + public static class Actor implements Serializable { + private static final long serialVersionUID = 6945229807147073769L; + private Long id; + private String name; + + public Actor() {} + + public Actor(Long id) { + this.id = id; + } + + public Actor(Long id, String name) { + this.id = id; + this.name = name; } public Long getId() { - return id; + return id; } public void setId(Long id) { - this.id = id; - } - - public String getSubject() { - return subject; - } - - public void setSubject(String subject) { - this.subject = subject; + this.id = id; } - public String getDescription() { - return description; + public String getName() { + return name; } - public void setDescription(String description) { - this.description = description; - } - - public Actor getActor() { - return actor; - } - - public void setActor(Actor actor) { - this.actor = actor; - } - - @JsonProperty("previous_state") - public Status getPreviousState() { - return previousState; - } - - public void setPreviousState(Status previousState) { - this.previousState = previousState; - } - - @JsonProperty("deleted_at") - public Date getDeletedAt() { - return deletedAt; - } - - public void setDeletedAt(Date deletedAt) { - this.deletedAt = deletedAt; + public void setName(String name) { + this.name = name; } @Override public String toString() { - return "DeletedTicket{" + - "id=" + id + - ", subject='" + subject + '\'' + - ", description='" + description + '\'' + - ", actor=" + actor + - ", previousState=" + previousState + - ", deletedAt=" + deletedAt + - '}'; - } - - public static class Actor implements Serializable { - private static final long serialVersionUID = 6945229807147073769L; - private Long id; - private String name; - - public Actor() { - } - - public Actor(Long id) { - this.id = id; - } - - public Actor(Long id, String name) { - this.id = id; - this.name = name; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public String toString() { - return "Actor" + - "{id=" + id + - ", name='" + name + '\'' + - '}'; - } + return "Actor" + "{id=" + id + ", name='" + name + '\'' + '}'; } + } } diff --git a/src/main/java/org/zendesk/client/v2/model/Field.java b/src/main/java/org/zendesk/client/v2/model/Field.java index 0dc4c723a..9f8093166 100644 --- a/src/main/java/org/zendesk/client/v2/model/Field.java +++ b/src/main/java/org/zendesk/client/v2/model/Field.java @@ -1,7 +1,6 @@ package org.zendesk.client.v2.model; import com.fasterxml.jackson.annotation.JsonProperty; - import java.io.Serializable; import java.util.Date; import java.util.List; @@ -12,288 +11,309 @@ */ public class Field implements Serializable { - private static final long serialVersionUID = 1L; - - private Long id; - private String url; - private String type; - private String title; - private String rawTitle; - private String description; - private String rawDescription; - private Integer position; - private Boolean active; - private Boolean required; - private Boolean collapsedForAgents; - private String regexpForValidation; - private String titleInPortal; - private String rawTitleInPortal; - private Boolean visibleInPortal; - private Boolean editableInPortal; - private Boolean requiredInPortal; - private String tag; - private Date createdAt; - private Date updatedAt; - private List