diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d4e261..2981058 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,14 @@ name: CI on: push: - branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'stl-preview-head/**' - - 'stl-preview-base/**' + branches: + - '**' + - '!integrated/**' + - '!stl-preview-head/**' + - '!stl-preview-base/**' + - '!generated' + - '!codegen/**' + - 'codegen/stl/**' pull_request: branches-ignore: - 'stl-preview-head/**' @@ -17,13 +19,13 @@ jobs: timeout-minutes: 15 name: lint runs-on: ${{ github.repository == 'stainless-sdks/phoebe-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: temurin java-version: | @@ -40,14 +42,17 @@ jobs: build: timeout-minutes: 15 name: build + permissions: + contents: read + id-token: write runs-on: ${{ github.repository == 'stainless-sdks/phoebe-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: temurin java-version: | @@ -61,16 +66,35 @@ jobs: - name: Build SDK run: ./scripts/build + - name: Get GitHub OIDC Token + if: |- + github.repository == 'stainless-sdks/phoebe-java' && + !startsWith(github.ref, 'refs/heads/stl/') + id: github-oidc + uses: actions/github-script@v8 + with: + script: core.setOutput('github_token', await core.getIDToken()); + + - name: Build and upload Maven artifacts + if: |- + github.repository == 'stainless-sdks/phoebe-java' && + !startsWith(github.ref, 'refs/heads/stl/') + env: + URL: https://pkg.stainless.com/s + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + PROJECT: phoebe-java + run: ./scripts/upload-artifacts test: timeout-minutes: 15 name: test runs-on: ${{ github.repository == 'stainless-sdks/phoebe-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: temurin java-version: | diff --git a/.github/workflows/publish-sonatype.yml b/.github/workflows/publish-sonatype.yml new file mode 100644 index 0000000..3a4ee30 --- /dev/null +++ b/.github/workflows/publish-sonatype.yml @@ -0,0 +1,41 @@ +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to Sonatype in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/phoebe-bird/phoebe-java/actions/workflows/publish-sonatype.yml +name: Publish Sonatype +on: + workflow_dispatch: + + release: + types: [published] + +jobs: + publish: + name: publish + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Set up Java + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: | + 8 + 21 + cache: gradle + + - name: Set up Gradle + uses: gradle/gradle-build-action@v2 + + - name: Publish to Sonatype + run: |- + export -- GPG_SIGNING_KEY_ID + printenv -- GPG_SIGNING_KEY | gpg --batch --passphrase-fd 3 --import 3<<< "$GPG_SIGNING_PASSWORD" + GPG_SIGNING_KEY_ID="$(gpg --with-colons --list-keys | awk -F : -- '/^pub:/ { getline; print "0x" substr($10, length($10) - 7) }')" + ./gradlew publish --no-configuration-cache + env: + SONATYPE_USERNAME: ${{ secrets.PHOEBE_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.PHOEBE_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }} + GPG_SIGNING_KEY: ${{ secrets.PHOEBE_SONATYPE_GPG_SIGNING_KEY || secrets.GPG_SIGNING_KEY }} + GPG_SIGNING_PASSWORD: ${{ secrets.PHOEBE_SONATYPE_GPG_SIGNING_PASSWORD || secrets.GPG_SIGNING_PASSWORD }} \ No newline at end of file diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 0000000..3791b5c --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,24 @@ +name: Release Doctor +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + if: github.repository == 'phoebe-bird/phoebe-java' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v6 + + - name: Check release environment + run: | + bash ./bin/check-release-environment + env: + SONATYPE_USERNAME: ${{ secrets.PHOEBE_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.PHOEBE_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }} + GPG_SIGNING_KEY: ${{ secrets.PHOEBE_SONATYPE_GPG_SIGNING_KEY || secrets.GPG_SIGNING_KEY }} + GPG_SIGNING_PASSWORD: ${{ secrets.PHOEBE_SONATYPE_GPG_SIGNING_PASSWORD || secrets.GPG_SIGNING_PASSWORD }} diff --git a/.gitignore b/.gitignore index b1346e6..90b85e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .prism.log +.stdy.log .gradle .idea .kotlin diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..ba6c348 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.1.0-alpha.1" +} \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 87b8e22..8c14db5 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 25 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/personal-ijyk2f%2Fphoebe-3974e6147f98a3ce3fc4684fda2671e7a7dc7476ecdf42af7a5dc88725cc8f04.yml openapi_spec_hash: 9808c815a0b204fdee17a0abca108ab5 -config_hash: 90be8240e7a9b625b33329bb412c0bea +config_hash: 17a571ff53308f9c391515734db807d2 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d847ddd --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,99 @@ +# Changelog + +## 0.1.0-alpha.1 (2026-04-01) + +Full Changelog: [v0.0.1-alpha.0...v0.1.0-alpha.1](https://github.com/phoebe-bird/phoebe-java/compare/v0.0.1-alpha.0...v0.1.0-alpha.1) + +### ⚠ BREAKING CHANGES + +* **client:** change precision of some numeric types +* **client:** extract auto pagination to shared classes +* **client:** **Migration:** - If you were referencing the `AutoPager` class on a specific `*Page` or `*PageAsync` type, then you should instead reference the shared `AutoPager` and `AutoPagerAsync` types, under the `core` package + - `AutoPagerAsync` now has different usage. You can call `.subscribe(...)` on the returned object instead to get called back each page item. You can also call `onCompleteFuture()` to get a future that completes when all items have been processed. Finally, you can call `.close()` on the returned object to stop auto-paginating early + - If you were referencing `getNextPage` or `getNextPageParams`: + - Swap to `nextPage()` and `nextPageParams()` + - Note that these both now return non-optional types (use `hasNextPage()` before calling these, since they will throw if it's impossible to get another page) + +### Features + +* **api:** manual updates ([d712abb](https://github.com/phoebe-bird/phoebe-java/commit/d712abbfc3f4e8e54e8391ae34616bae45510b27)) +* **client:** add `{QueryParams,Headers}#put(String, JsonValue)` methods ([31f7acd](https://github.com/phoebe-bird/phoebe-java/commit/31f7acd90ff3c222f4db984e29ef8a090ce557d6)) +* **client:** add `HttpRequest#url()` method ([a145b94](https://github.com/phoebe-bird/phoebe-java/commit/a145b94467e13633c59889cccd0c26b0b3789a44)) +* **client:** add a `withOptions` method ([008f2ee](https://github.com/phoebe-bird/phoebe-java/commit/008f2ee04e6216121f6bfdf31616a69f0143d55e)) +* **client:** add https config options ([74d088e](https://github.com/phoebe-bird/phoebe-java/commit/74d088e2486ecc1d43474dc0e928905c7eecdfe8)) +* **client:** allow configuring dispatcher executor service ([ef7c266](https://github.com/phoebe-bird/phoebe-java/commit/ef7c2660841ba59c269caa78b58f9a9f59fa162c)) +* **client:** allow configuring env via system properties ([f1de279](https://github.com/phoebe-bird/phoebe-java/commit/f1de279091ccdbf3ace91ed860f1d00345fb6c71)) +* **client:** allow providing some params positionally ([b24d6c0](https://github.com/phoebe-bird/phoebe-java/commit/b24d6c0cd3ecc5a55a382454f96bbf89a84fd3d7)) +* **client:** extract auto pagination to shared classes ([13f571f](https://github.com/phoebe-bird/phoebe-java/commit/13f571f7baf0caef24ce9c554b244476ed3e812b)) +* **client:** implement per-endpoint base URL support ([f493836](https://github.com/phoebe-bird/phoebe-java/commit/f4938363ced1c863b72b10c787c73c56ab6aac51)) +* **client:** send `X-Stainless-Kotlin-Version` header ([d9f18bd](https://github.com/phoebe-bird/phoebe-java/commit/d9f18bd66b86423c4ae710ea0987fe35f53ed457)) + + +### Bug Fixes + +* **client:** bump max requests per host to max requests (5 -> 64) ([73b4311](https://github.com/phoebe-bird/phoebe-java/commit/73b4311a77e11406a4c591ef9bf111ef84970605)) +* **client:** cancel okhttp call when future cancelled ([5823aba](https://github.com/phoebe-bird/phoebe-java/commit/5823abafce2a92fbb69a0f6c23db79a78b78f2a8)) +* **client:** disallow coercion from float to int ([d00944f](https://github.com/phoebe-bird/phoebe-java/commit/d00944fd323009658808104c8a4b3954211b3559)) +* **client:** don't close client on `withOptions` usage when original is gc'd ([3d3f673](https://github.com/phoebe-bird/phoebe-java/commit/3d3f673ecda4ce417d1ec954ea670cf629ae373a)) +* **client:** ensure error handling always occurs ([c4327a3](https://github.com/phoebe-bird/phoebe-java/commit/c4327a3c088df1b9398337114f3316f5e355a7f3)) +* **client:** fully respect max retries ([7c71f9d](https://github.com/phoebe-bird/phoebe-java/commit/7c71f9dc52f85d1a055e25f5d940ac1fc6d807b0)) +* **client:** preserve time zone in lenient date-time parsing ([50bb540](https://github.com/phoebe-bird/phoebe-java/commit/50bb540673b947b6f5b2da24bb3cac46e3ca498a)) +* **client:** remove `@MustBeClosed` for future returning methods ([a6e5c78](https://github.com/phoebe-bird/phoebe-java/commit/a6e5c78fcb9b3107791323d3c3da12791634939a)) +* **client:** send retry count header for max retries 0 ([7c71f9d](https://github.com/phoebe-bird/phoebe-java/commit/7c71f9dc52f85d1a055e25f5d940ac1fc6d807b0)) +* date time deserialization leniency ([9d5f042](https://github.com/phoebe-bird/phoebe-java/commit/9d5f042b85f9bdaec3a56ad6ea180a32f6a5f454)) + + +### Chores + +* **ci:** enable for pull requests ([30e9e4a](https://github.com/phoebe-bird/phoebe-java/commit/30e9e4af5c35b09de9c58934e263d3a6b667d3fa)) +* **ci:** only run for pushes and fork pull requests ([a3e1190](https://github.com/phoebe-bird/phoebe-java/commit/a3e119093c0f9c11ee28bef632aca42deb6635b2)) +* **ci:** skip uploading artifacts on stainless-internal branches ([5945140](https://github.com/phoebe-bird/phoebe-java/commit/5945140f250d2b0ae6c2d9cd3fc3ee386a65ffc8)) +* **ci:** upgrade `actions/github-script` ([43107c0](https://github.com/phoebe-bird/phoebe-java/commit/43107c0efbf857c052a1ce799f14a45add565375)) +* **ci:** upgrade `actions/setup-java` ([75d7ae3](https://github.com/phoebe-bird/phoebe-java/commit/75d7ae3dc40b246a7fe1e4a0316122c579779edd)) +* configure new SDK language ([30ab2b2](https://github.com/phoebe-bird/phoebe-java/commit/30ab2b26361936249dbea4db64787cfc7c6b7e2d)) +* **docs:** grammar improvements ([33fa7c5](https://github.com/phoebe-bird/phoebe-java/commit/33fa7c53610ea31ad671e5c5437fa6d4d02290a9)) +* **internal:** allow running specific example from cli ([56695c9](https://github.com/phoebe-bird/phoebe-java/commit/56695c9e55d80e78608e41f4f90297f13d100c22)) +* **internal:** clean up maven repo artifact script and add html documentation to repo root ([6a0f2ca](https://github.com/phoebe-bird/phoebe-java/commit/6a0f2cab131804296e2b29cc2e29c58de3603a2b)) +* **internal:** codegen related update ([354d517](https://github.com/phoebe-bird/phoebe-java/commit/354d5177e6816e06d62e4ab27e032efa7bfa528d)) +* **internal:** codegen related update ([f24ff7d](https://github.com/phoebe-bird/phoebe-java/commit/f24ff7de7b1830d92d031d6482e506b1a5806101)) +* **internal:** codegen related update ([4349d53](https://github.com/phoebe-bird/phoebe-java/commit/4349d5372ce68b707353a71d8db12dffcbb0475f)) +* **internal:** codegen related update ([8beb9ab](https://github.com/phoebe-bird/phoebe-java/commit/8beb9ab95768a177d22b20aa3d4c1291b516cd31)) +* **internal:** codegen related update ([d13e25d](https://github.com/phoebe-bird/phoebe-java/commit/d13e25d23ef3cf150699d27a46026d78c64ecc9d)) +* **internal:** codegen related update ([98d74bf](https://github.com/phoebe-bird/phoebe-java/commit/98d74bf8a9919494cd924d1a35c43e76effb9128)) +* **internal:** codegen related update ([8d85b73](https://github.com/phoebe-bird/phoebe-java/commit/8d85b734b31a8fbf779fb80be8e6a12ac0a97e12)) +* **internal:** codegen related update ([a76719e](https://github.com/phoebe-bird/phoebe-java/commit/a76719edc99a3a43bb71f9d7399d7fcc19fa4327)) +* **internal:** codegen related update ([102eb54](https://github.com/phoebe-bird/phoebe-java/commit/102eb54ea114aa3741df602701d022c2bf7f038f)) +* **internal:** codegen related update ([ea5500a](https://github.com/phoebe-bird/phoebe-java/commit/ea5500a64b0688e57f2ca9f3dee0eaaa74d9f860)) +* **internal:** codegen related update ([9b9b899](https://github.com/phoebe-bird/phoebe-java/commit/9b9b899e5b9186438c76ddc90a0b2fe65a21ef17)) +* **internal:** codegen related update ([b07058d](https://github.com/phoebe-bird/phoebe-java/commit/b07058d9f6e68617a2c01194deabd50547848f87)) +* **internal:** codegen related update ([26cd70c](https://github.com/phoebe-bird/phoebe-java/commit/26cd70c6547384e153a6abf88178fbed6e8c2951)) +* **internal:** codegen related update ([2082664](https://github.com/phoebe-bird/phoebe-java/commit/20826641ea001336618b19ceaf63acd87ed82d9f)) +* **internal:** codegen related update ([17f6183](https://github.com/phoebe-bird/phoebe-java/commit/17f6183f74d414255db7fe82cbc6e73b09430d06)) +* **internal:** codegen related update ([fa74474](https://github.com/phoebe-bird/phoebe-java/commit/fa744740ab0421e1a3ed33aad5952fde2e80c4df)) +* **internal:** codegen related update ([78f6424](https://github.com/phoebe-bird/phoebe-java/commit/78f64241e6e1a7f11b3004ea3b40e282688e473d)) +* **internal:** codegen related update ([704a4e1](https://github.com/phoebe-bird/phoebe-java/commit/704a4e190b022602ad545cf926ef21ce4bac5d96)) +* **internal:** codegen related update ([e57fa55](https://github.com/phoebe-bird/phoebe-java/commit/e57fa556588b355f889962a6e57cf041a2ab7b5c)) +* **internal:** codegen related update ([44125db](https://github.com/phoebe-bird/phoebe-java/commit/44125db5522d48fb720c0753aeada71cab6e3105)) +* **internal:** correct cache invalidation for `SKIP_MOCK_TESTS` ([adfd7da](https://github.com/phoebe-bird/phoebe-java/commit/adfd7da05a09ef5622316700c031b6ac6ef4d05f)) +* **internal:** depend on packages directly in example ([7c71f9d](https://github.com/phoebe-bird/phoebe-java/commit/7c71f9dc52f85d1a055e25f5d940ac1fc6d807b0)) +* **internal:** improve maven repo docs ([da49cb7](https://github.com/phoebe-bird/phoebe-java/commit/da49cb77739c9becf5d45b12095298950f1e32f9)) +* **internal:** refactor delegating from client to options ([4ea1ff5](https://github.com/phoebe-bird/phoebe-java/commit/4ea1ff5ee41e8e81d62000b0bac199c47d645b8e)) +* **internal:** remove unnecessary `[...]` in `[@see](https://github.com/see)` ([bed463e](https://github.com/phoebe-bird/phoebe-java/commit/bed463e241505d386c28f49a7c0edbfaa9c4e3be)) +* **internal:** support uploading Maven repo artifacts to stainless package server ([37c89f8](https://github.com/phoebe-bird/phoebe-java/commit/37c89f8c816525461bce82b5550d796ff1ff3023)) +* **internal:** update `actions/checkout` version ([3a146bb](https://github.com/phoebe-bird/phoebe-java/commit/3a146bbdb3ab84f558391f3b8e6bc7e2c8fd77f6)) +* **internal:** update maven repo doc to include authentication ([cf7eb23](https://github.com/phoebe-bird/phoebe-java/commit/cf7eb239f388de322581a476a6e6845cebd5e019)) +* test on Jackson 2.14.0 to avoid encountering FasterXML/jackson-databind[#3240](https://github.com/phoebe-bird/phoebe-java/issues/3240) in tests ([9d5f042](https://github.com/phoebe-bird/phoebe-java/commit/9d5f042b85f9bdaec3a56ad6ea180a32f6a5f454)) + + +### Documentation + +* fix missing readme comment ([4fd91a7](https://github.com/phoebe-bird/phoebe-java/commit/4fd91a77d9e1291a5d81ee0cc950d4a83e2ab753)) +* more code comments ([f8420f4](https://github.com/phoebe-bird/phoebe-java/commit/f8420f4dfd2e03fae3e61c5b7884f425c92b69d8)) +* prominently feature MCP server setup in root SDK readmes ([7754ce1](https://github.com/phoebe-bird/phoebe-java/commit/7754ce1495022f309ff26d5c2f06f3c4c8e913b7)) +* remove `$` for better copy-pasteabality ([fe8a020](https://github.com/phoebe-bird/phoebe-java/commit/fe8a02007be585c2c162442e968a4fe8909eb39e)) + + +### Refactors + +* **client:** change precision of some numeric types ([c593d52](https://github.com/phoebe-bird/phoebe-java/commit/c593d52bf58fc1324aef2f61d1411a0923b15a2e)) +* **internal:** minor `ClientOptionsTest` change ([b9a8ca8](https://github.com/phoebe-bird/phoebe-java/commit/b9a8ca82aa21e634734848084170f7930b906684)) diff --git a/LICENSE b/LICENSE index aa84a46..a69fada 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2025 Phoebe + Copyright 2026 Phoebe Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 9a45101..ef9ad85 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,39 @@ # Phoebe Java API Library -[![Maven Central](https://img.shields.io/maven-central/v/com.phoebe.api/phoebe-java)](https://central.sonatype.com/artifact/com.phoebe.api/phoebe-java/0.0.1-alpha.0) -[![javadoc](https://javadoc.io/badge2/com.phoebe.api/phoebe-java/0.0.1-alpha.0/javadoc.svg)](https://javadoc.io/doc/com.phoebe.api/phoebe-java/0.0.1-alpha.0) + + +[![Maven Central](https://img.shields.io/maven-central/v/com.phoebe.api/phoebe-java)](https://central.sonatype.com/artifact/com.phoebe.api/phoebe-java/0.1.0-alpha.1) +[![javadoc](https://javadoc.io/badge2/com.phoebe.api/phoebe-java/0.1.0-alpha.1/javadoc.svg)](https://javadoc.io/doc/com.phoebe.api/phoebe-java/0.1.0-alpha.1) + + The Phoebe Java SDK provides convenient access to the [Phoebe REST API](https://science.ebird.org/en/use-ebird-data/download-ebird-data-products) from applications written in Java. It is generated with [Stainless](https://www.stainless.com/). -The REST API documentation can be found on [science.ebird.org](https://science.ebird.org/en/use-ebird-data/download-ebird-data-products). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.phoebe.api/phoebe-java/0.0.1-alpha.0). +## MCP Server + +Use the Phoebe MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application. + +[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=phoebe-ebird-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsInBob2ViZS1lYmlyZC1tY3AiXSwiZW52Ijp7IkVCSVJEX0FQSV9LRVkiOiJNeSBBUEkgS2V5In19) +[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22phoebe-ebird-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22phoebe-ebird-mcp%22%5D%2C%22env%22%3A%7B%22EBIRD_API_KEY%22%3A%22My%20API%20Key%22%7D%7D) + +> Note: You may need to set environment variables in your MCP client. + + + +The REST API documentation can be found on [science.ebird.org](https://science.ebird.org/en/use-ebird-data/download-ebird-data-products). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.phoebe.api/phoebe-java/0.1.0-alpha.1). + + ## Installation + + ### Gradle ```kotlin -implementation("com.phoebe.api:phoebe-java:0.0.1-alpha.0") +implementation("com.phoebe.api:phoebe-java:0.1.0-alpha.1") ``` ### Maven @@ -23,10 +42,12 @@ implementation("com.phoebe.api:phoebe-java:0.0.1-alpha.0") com.phoebe.api phoebe-java - 0.0.1-alpha.0 + 0.1.0-alpha.1 ``` + + ## Requirements This library requires Java 8 or later. @@ -220,13 +241,13 @@ The SDK uses the standard [OkHttp logging interceptor](https://github.com/square Enable logging by setting the `PHOEBE_LOG` environment variable to `info`: ```sh -$ export PHOEBE_LOG=info +export PHOEBE_LOG=info ``` Or to `debug` for more verbose logging: ```sh -$ export PHOEBE_LOG=debug +export PHOEBE_LOG=debug ``` ## ProGuard and R8 @@ -246,6 +267,8 @@ If the SDK threw an exception, but you're _certain_ the version is compatible, t > [!CAUTION] > We make no guarantee that the SDK works correctly when the Jackson version check is disabled. +Also note that there are bugs in older Jackson versions that can affect the SDK. We don't work around all Jackson bugs ([example](https://github.com/FasterXML/jackson-databind/issues/3240)) and expect users to upgrade Jackson for those instead. + ## Network options ### Retries @@ -319,6 +342,25 @@ PhoebeClient client = PhoebeOkHttpClient.builder() .build(); ``` +### Connection pooling + +To customize the underlying OkHttp connection pool, configure the client using the `maxIdleConnections` and `keepAliveDuration` methods: + +```java +import com.phoebe.api.client.PhoebeClient; +import com.phoebe.api.client.okhttp.PhoebeOkHttpClient; +import java.time.Duration; + +PhoebeClient client = PhoebeOkHttpClient.builder() + .fromEnv() + // If `maxIdleConnections` is set, then `keepAliveDuration` must be set, and vice versa. + .maxIdleConnections(10) + .keepAliveDuration(Duration.ofMinutes(2)) + .build(); +``` + +If both options are unset, OkHttp's default connection pool settings are used. + ### HTTPS > [!NOTE] @@ -583,4 +625,4 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. -We are keen for your feedback; please open an [issue](https://www.github.com/stainless-sdks/phoebe-java/issues) with questions, bugs, or suggestions. +We are keen for your feedback; please open an [issue](https://www.github.com/phoebe-bird/phoebe-java/issues) with questions, bugs, or suggestions. diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 0000000..3a6a7b4 --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +errors=() + +if [ -z "${SONATYPE_USERNAME}" ]; then + errors+=("The SONATYPE_USERNAME secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +if [ -z "${SONATYPE_PASSWORD}" ]; then + errors+=("The SONATYPE_PASSWORD secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +if [ -z "${GPG_SIGNING_KEY}" ]; then + errors+=("The GPG_SIGNING_KEY secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +if [ -z "${GPG_SIGNING_PASSWORD}" ]; then + errors+=("The GPG_SIGNING_PASSWORD secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +lenErrors=${#errors[@]} + +if [[ lenErrors -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" diff --git a/build.gradle.kts b/build.gradle.kts index 95d3c75..dd5d657 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ repositories { allprojects { group = "com.phoebe.api" - version = "0.0.1-alpha.0" + version = "0.1.0-alpha.1" // x-release-please-version } subprojects { diff --git a/buildSrc/src/main/kotlin/phoebe.java.gradle.kts b/buildSrc/src/main/kotlin/phoebe.java.gradle.kts index 81d5d32..a3cfe28 100644 --- a/buildSrc/src/main/kotlin/phoebe.java.gradle.kts +++ b/buildSrc/src/main/kotlin/phoebe.java.gradle.kts @@ -54,7 +54,7 @@ tasks.withType().configureEach { val palantir by configurations.creating dependencies { - palantir("com.palantir.javaformat:palantir-java-format:2.73.0") + palantir("com.palantir.javaformat:palantir-java-format:2.89.0") } fun registerPalantir( diff --git a/buildSrc/src/main/kotlin/phoebe.kotlin.gradle.kts b/buildSrc/src/main/kotlin/phoebe.kotlin.gradle.kts index 40e3ce8..fcff407 100644 --- a/buildSrc/src/main/kotlin/phoebe.kotlin.gradle.kts +++ b/buildSrc/src/main/kotlin/phoebe.kotlin.gradle.kts @@ -33,11 +33,14 @@ kotlin { tasks.withType().configureEach { systemProperty("junit.jupiter.execution.parallel.enabled", true) systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent") + + // `SKIP_MOCK_TESTS` affects which tests run so it must be added as input for proper cache invalidation. + inputs.property("skipMockTests", System.getenv("SKIP_MOCK_TESTS")).optional(true) } val ktfmt by configurations.creating dependencies { - ktfmt("com.facebook:ktfmt:0.56") + ktfmt("com.facebook:ktfmt:0.61") } fun registerKtfmt( diff --git a/buildSrc/src/main/kotlin/phoebe.publish.gradle.kts b/buildSrc/src/main/kotlin/phoebe.publish.gradle.kts index dd8ea61..a6386d7 100644 --- a/buildSrc/src/main/kotlin/phoebe.publish.gradle.kts +++ b/buildSrc/src/main/kotlin/phoebe.publish.gradle.kts @@ -27,9 +27,9 @@ configure { } scm { - connection.set("scm:git:git://github.com/stainless-sdks/phoebe-java.git") - developerConnection.set("scm:git:git://github.com/stainless-sdks/phoebe-java.git") - url.set("https://github.com/stainless-sdks/phoebe-java") + connection.set("scm:git:git://github.com/phoebe-bird/phoebe-java.git") + developerConnection.set("scm:git:git://github.com/phoebe-bird/phoebe-java.git") + url.set("https://github.com/phoebe-bird/phoebe-java") } versionMapping { @@ -40,6 +40,14 @@ configure { } } } + repositories { + if (project.hasProperty("publishLocal")) { + maven { + name = "LocalFileSystem" + url = uri("${rootProject.layout.buildDirectory.get()}/local-maven-repo") + } + } + } } signing { diff --git a/phoebe-java-client-okhttp/build.gradle.kts b/phoebe-java-client-okhttp/build.gradle.kts index fc320fc..6ae5611 100644 --- a/phoebe-java-client-okhttp/build.gradle.kts +++ b/phoebe-java-client-okhttp/build.gradle.kts @@ -10,5 +10,6 @@ dependencies { implementation("com.squareup.okhttp3:logging-interceptor:4.12.0") testImplementation(kotlin("test")) - testImplementation("org.assertj:assertj-core:3.25.3") + testImplementation("org.assertj:assertj-core:3.27.7") + testImplementation("com.github.tomakehurst:wiremock-jre8:2.35.2") } diff --git a/phoebe-java-client-okhttp/src/main/kotlin/com/phoebe/api/client/okhttp/OkHttpClient.kt b/phoebe-java-client-okhttp/src/main/kotlin/com/phoebe/api/client/okhttp/OkHttpClient.kt index ad75691..755adb8 100644 --- a/phoebe-java-client-okhttp/src/main/kotlin/com/phoebe/api/client/okhttp/OkHttpClient.kt +++ b/phoebe-java-client-okhttp/src/main/kotlin/com/phoebe/api/client/okhttp/OkHttpClient.kt @@ -13,12 +13,17 @@ import java.io.IOException import java.io.InputStream import java.net.Proxy import java.time.Duration +import java.util.concurrent.CancellationException import java.util.concurrent.CompletableFuture +import java.util.concurrent.ExecutorService +import java.util.concurrent.TimeUnit import javax.net.ssl.HostnameVerifier import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509TrustManager import okhttp3.Call import okhttp3.Callback +import okhttp3.ConnectionPool +import okhttp3.Dispatcher import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaType @@ -29,8 +34,8 @@ import okhttp3.Response import okhttp3.logging.HttpLoggingInterceptor import okio.BufferedSink -class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpClient) : - HttpClient { +class OkHttpClient +internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClient) : HttpClient { override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse { val call = newCall(request, requestOptions) @@ -50,20 +55,25 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC ): CompletableFuture { val future = CompletableFuture() - request.body?.run { future.whenComplete { _, _ -> close() } } - - newCall(request, requestOptions) - .enqueue( - object : Callback { - override fun onResponse(call: Call, response: Response) { - future.complete(response.toResponse()) - } + val call = newCall(request, requestOptions) + call.enqueue( + object : Callback { + override fun onResponse(call: Call, response: Response) { + future.complete(response.toResponse()) + } - override fun onFailure(call: Call, e: IOException) { - future.completeExceptionally(PhoebeIoException("Request failed", e)) - } + override fun onFailure(call: Call, e: IOException) { + future.completeExceptionally(PhoebeIoException("Request failed", e)) } - ) + } + ) + + future.whenComplete { _, e -> + if (e is CancellationException) { + call.cancel() + } + request.body?.close() + } return future } @@ -194,6 +204,9 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC private var timeout: Timeout = Timeout.default() private var proxy: Proxy? = null + private var maxIdleConnections: Int? = null + private var keepAliveDuration: Duration? = null + private var dispatcherExecutorService: ExecutorService? = null private var sslSocketFactory: SSLSocketFactory? = null private var trustManager: X509TrustManager? = null private var hostnameVerifier: HostnameVerifier? = null @@ -204,6 +217,32 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC fun proxy(proxy: Proxy?) = apply { this.proxy = proxy } + /** + * Sets the maximum number of idle connections kept by the underlying [ConnectionPool]. + * + * If this is set, then [keepAliveDuration] must also be set. + * + * If unset, then OkHttp's default is used. + */ + fun maxIdleConnections(maxIdleConnections: Int?) = apply { + this.maxIdleConnections = maxIdleConnections + } + + /** + * Sets the keep-alive duration for idle connections in the underlying [ConnectionPool]. + * + * If this is set, then [maxIdleConnections] must also be set. + * + * If unset, then OkHttp's default is used. + */ + fun keepAliveDuration(keepAliveDuration: Duration?) = apply { + this.keepAliveDuration = keepAliveDuration + } + + fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply { + this.dispatcherExecutorService = dispatcherExecutorService + } + fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply { this.sslSocketFactory = sslSocketFactory } @@ -219,12 +258,32 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC fun build(): OkHttpClient = OkHttpClient( okhttp3.OkHttpClient.Builder() + // `RetryingHttpClient` handles retries if the user enabled them. + .retryOnConnectionFailure(false) .connectTimeout(timeout.connect()) .readTimeout(timeout.read()) .writeTimeout(timeout.write()) .callTimeout(timeout.request()) .proxy(proxy) .apply { + dispatcherExecutorService?.let { dispatcher(Dispatcher(it)) } + + val maxIdleConnections = maxIdleConnections + val keepAliveDuration = keepAliveDuration + if (maxIdleConnections != null && keepAliveDuration != null) { + connectionPool( + ConnectionPool( + maxIdleConnections, + keepAliveDuration.toNanos(), + TimeUnit.NANOSECONDS, + ) + ) + } else { + check((maxIdleConnections != null) == (keepAliveDuration != null)) { + "Both or none of `maxIdleConnections` and `keepAliveDuration` must be set, but only one was set" + } + } + val sslSocketFactory = sslSocketFactory val trustManager = trustManager if (sslSocketFactory != null && trustManager != null) { diff --git a/phoebe-java-client-okhttp/src/main/kotlin/com/phoebe/api/client/okhttp/PhoebeOkHttpClient.kt b/phoebe-java-client-okhttp/src/main/kotlin/com/phoebe/api/client/okhttp/PhoebeOkHttpClient.kt index 57aa49e..948b72b 100644 --- a/phoebe-java-client-okhttp/src/main/kotlin/com/phoebe/api/client/okhttp/PhoebeOkHttpClient.kt +++ b/phoebe-java-client-okhttp/src/main/kotlin/com/phoebe/api/client/okhttp/PhoebeOkHttpClient.kt @@ -16,6 +16,7 @@ import java.net.Proxy import java.time.Clock import java.time.Duration import java.util.Optional +import java.util.concurrent.ExecutorService import javax.net.ssl.HostnameVerifier import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509TrustManager @@ -44,16 +45,78 @@ class PhoebeOkHttpClient private constructor() { class Builder internal constructor() { private var clientOptions: ClientOptions.Builder = ClientOptions.builder() + private var dispatcherExecutorService: ExecutorService? = null private var proxy: Proxy? = null + private var maxIdleConnections: Int? = null + private var keepAliveDuration: Duration? = null private var sslSocketFactory: SSLSocketFactory? = null private var trustManager: X509TrustManager? = null private var hostnameVerifier: HostnameVerifier? = null + /** + * The executor service to use for running HTTP requests. + * + * Defaults to OkHttp's + * [default executor service](https://github.com/square/okhttp/blob/ace792f443b2ffb17974f5c0d1cecdf589309f26/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt#L98-L104). + * + * This class takes ownership of the executor service and shuts it down when closed. + */ + fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply { + this.dispatcherExecutorService = dispatcherExecutorService + } + + /** + * Alias for calling [Builder.dispatcherExecutorService] with + * `dispatcherExecutorService.orElse(null)`. + */ + fun dispatcherExecutorService(dispatcherExecutorService: Optional) = + dispatcherExecutorService(dispatcherExecutorService.getOrNull()) + fun proxy(proxy: Proxy?) = apply { this.proxy = proxy } /** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */ fun proxy(proxy: Optional) = proxy(proxy.getOrNull()) + /** + * The maximum number of idle connections kept by the underlying OkHttp connection pool. + * + * If this is set, then [keepAliveDuration] must also be set. + * + * If unset, then OkHttp's default is used. + */ + fun maxIdleConnections(maxIdleConnections: Int?) = apply { + this.maxIdleConnections = maxIdleConnections + } + + /** + * Alias for [Builder.maxIdleConnections]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxIdleConnections(maxIdleConnections: Int) = + maxIdleConnections(maxIdleConnections as Int?) + + /** + * Alias for calling [Builder.maxIdleConnections] with `maxIdleConnections.orElse(null)`. + */ + fun maxIdleConnections(maxIdleConnections: Optional) = + maxIdleConnections(maxIdleConnections.getOrNull()) + + /** + * The keep-alive duration for idle connections in the underlying OkHttp connection pool. + * + * If this is set, then [maxIdleConnections] must also be set. + * + * If unset, then OkHttp's default is used. + */ + fun keepAliveDuration(keepAliveDuration: Duration?) = apply { + this.keepAliveDuration = keepAliveDuration + } + + /** Alias for calling [Builder.keepAliveDuration] with `keepAliveDuration.orElse(null)`. */ + fun keepAliveDuration(keepAliveDuration: Optional) = + keepAliveDuration(keepAliveDuration.getOrNull()) + /** * The socket factory used to secure HTTPS connections. * @@ -296,6 +359,9 @@ class PhoebeOkHttpClient private constructor() { OkHttpClient.builder() .timeout(clientOptions.timeout()) .proxy(proxy) + .maxIdleConnections(maxIdleConnections) + .keepAliveDuration(keepAliveDuration) + .dispatcherExecutorService(dispatcherExecutorService) .sslSocketFactory(sslSocketFactory) .trustManager(trustManager) .hostnameVerifier(hostnameVerifier) diff --git a/phoebe-java-client-okhttp/src/main/kotlin/com/phoebe/api/client/okhttp/PhoebeOkHttpClientAsync.kt b/phoebe-java-client-okhttp/src/main/kotlin/com/phoebe/api/client/okhttp/PhoebeOkHttpClientAsync.kt index e133879..a110af7 100644 --- a/phoebe-java-client-okhttp/src/main/kotlin/com/phoebe/api/client/okhttp/PhoebeOkHttpClientAsync.kt +++ b/phoebe-java-client-okhttp/src/main/kotlin/com/phoebe/api/client/okhttp/PhoebeOkHttpClientAsync.kt @@ -16,6 +16,7 @@ import java.net.Proxy import java.time.Clock import java.time.Duration import java.util.Optional +import java.util.concurrent.ExecutorService import javax.net.ssl.HostnameVerifier import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509TrustManager @@ -44,16 +45,78 @@ class PhoebeOkHttpClientAsync private constructor() { class Builder internal constructor() { private var clientOptions: ClientOptions.Builder = ClientOptions.builder() + private var dispatcherExecutorService: ExecutorService? = null private var proxy: Proxy? = null + private var maxIdleConnections: Int? = null + private var keepAliveDuration: Duration? = null private var sslSocketFactory: SSLSocketFactory? = null private var trustManager: X509TrustManager? = null private var hostnameVerifier: HostnameVerifier? = null + /** + * The executor service to use for running HTTP requests. + * + * Defaults to OkHttp's + * [default executor service](https://github.com/square/okhttp/blob/ace792f443b2ffb17974f5c0d1cecdf589309f26/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt#L98-L104). + * + * This class takes ownership of the executor service and shuts it down when closed. + */ + fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply { + this.dispatcherExecutorService = dispatcherExecutorService + } + + /** + * Alias for calling [Builder.dispatcherExecutorService] with + * `dispatcherExecutorService.orElse(null)`. + */ + fun dispatcherExecutorService(dispatcherExecutorService: Optional) = + dispatcherExecutorService(dispatcherExecutorService.getOrNull()) + fun proxy(proxy: Proxy?) = apply { this.proxy = proxy } /** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */ fun proxy(proxy: Optional) = proxy(proxy.getOrNull()) + /** + * The maximum number of idle connections kept by the underlying OkHttp connection pool. + * + * If this is set, then [keepAliveDuration] must also be set. + * + * If unset, then OkHttp's default is used. + */ + fun maxIdleConnections(maxIdleConnections: Int?) = apply { + this.maxIdleConnections = maxIdleConnections + } + + /** + * Alias for [Builder.maxIdleConnections]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxIdleConnections(maxIdleConnections: Int) = + maxIdleConnections(maxIdleConnections as Int?) + + /** + * Alias for calling [Builder.maxIdleConnections] with `maxIdleConnections.orElse(null)`. + */ + fun maxIdleConnections(maxIdleConnections: Optional) = + maxIdleConnections(maxIdleConnections.getOrNull()) + + /** + * The keep-alive duration for idle connections in the underlying OkHttp connection pool. + * + * If this is set, then [maxIdleConnections] must also be set. + * + * If unset, then OkHttp's default is used. + */ + fun keepAliveDuration(keepAliveDuration: Duration?) = apply { + this.keepAliveDuration = keepAliveDuration + } + + /** Alias for calling [Builder.keepAliveDuration] with `keepAliveDuration.orElse(null)`. */ + fun keepAliveDuration(keepAliveDuration: Optional) = + keepAliveDuration(keepAliveDuration.getOrNull()) + /** * The socket factory used to secure HTTPS connections. * @@ -296,6 +359,9 @@ class PhoebeOkHttpClientAsync private constructor() { OkHttpClient.builder() .timeout(clientOptions.timeout()) .proxy(proxy) + .maxIdleConnections(maxIdleConnections) + .keepAliveDuration(keepAliveDuration) + .dispatcherExecutorService(dispatcherExecutorService) .sslSocketFactory(sslSocketFactory) .trustManager(trustManager) .hostnameVerifier(hostnameVerifier) diff --git a/phoebe-java-client-okhttp/src/test/kotlin/com/phoebe/api/client/okhttp/OkHttpClientTest.kt b/phoebe-java-client-okhttp/src/test/kotlin/com/phoebe/api/client/okhttp/OkHttpClientTest.kt new file mode 100644 index 0000000..5ba90f8 --- /dev/null +++ b/phoebe-java-client-okhttp/src/test/kotlin/com/phoebe/api/client/okhttp/OkHttpClientTest.kt @@ -0,0 +1,44 @@ +package com.phoebe.api.client.okhttp + +import com.github.tomakehurst.wiremock.client.WireMock.* +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo +import com.github.tomakehurst.wiremock.junit5.WireMockTest +import com.phoebe.api.core.http.HttpMethod +import com.phoebe.api.core.http.HttpRequest +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.parallel.ResourceLock + +@WireMockTest +@ResourceLock("https://github.com/wiremock/wiremock/issues/169") +internal class OkHttpClientTest { + + private lateinit var baseUrl: String + private lateinit var httpClient: OkHttpClient + + @BeforeEach + fun beforeEach(wmRuntimeInfo: WireMockRuntimeInfo) { + baseUrl = wmRuntimeInfo.httpBaseUrl + httpClient = OkHttpClient.builder().build() + } + + @Test + fun executeAsync_whenFutureCancelled_cancelsUnderlyingCall() { + stubFor(post(urlPathEqualTo("/something")).willReturn(ok())) + val responseFuture = + httpClient.executeAsync( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build() + ) + val call = httpClient.okHttpClient.dispatcher.runningCalls().single() + + responseFuture.cancel(false) + + // Should have cancelled the underlying call + assertThat(call.isCanceled()).isTrue() + } +} diff --git a/phoebe-java-core/build.gradle.kts b/phoebe-java-core/build.gradle.kts index a65d57c..ce7620e 100644 --- a/phoebe-java-core/build.gradle.kts +++ b/phoebe-java-core/build.gradle.kts @@ -5,14 +5,16 @@ plugins { configurations.all { resolutionStrategy { - // Compile and test against a lower Jackson version to ensure we're compatible with it. - // We publish with a higher version (see below) to ensure users depend on a secure version by default. - force("com.fasterxml.jackson.core:jackson-core:2.13.4") - force("com.fasterxml.jackson.core:jackson-databind:2.13.4") - force("com.fasterxml.jackson.core:jackson-annotations:2.13.4") - force("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.4") - force("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4") - force("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.4") + // Compile and test against a lower Jackson version to ensure we're compatible with it. Note that + // we generally support 2.13.4, but test against 2.14.0 because 2.13.4 has some annoying (but + // niche) bugs (users should upgrade if they encounter them). We publish with a higher version + // (see below) to ensure users depend on a secure version by default. + force("com.fasterxml.jackson.core:jackson-core:2.14.0") + force("com.fasterxml.jackson.core:jackson-databind:2.14.0") + force("com.fasterxml.jackson.core:jackson-annotations:2.14.0") + force("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.0") + force("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.0") + force("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.0") } } @@ -25,13 +27,11 @@ dependencies { implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2") implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2") - implementation("org.apache.httpcomponents.core5:httpcore5:5.2.4") - implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1") testImplementation(kotlin("test")) testImplementation(project(":phoebe-java-client-okhttp")) testImplementation("com.github.tomakehurst:wiremock-jre8:2.35.2") - testImplementation("org.assertj:assertj-core:3.25.3") + testImplementation("org.assertj:assertj-core:3.27.7") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3") testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.3") testImplementation("org.junit-pioneer:junit-pioneer:1.9.1") diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/Check.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/Check.kt index 295fcbc..e8c3e22 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/Check.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/Check.kt @@ -77,7 +77,7 @@ This can happen if you are either: Double-check that you are depending on compatible Jackson versions. -See https://www.github.com/stainless-sdks/phoebe-java#jackson for more information. +See https://www.github.com/phoebe-bird/phoebe-java#jackson for more information. """ .trimIndent() } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/ClientOptions.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/ClientOptions.kt index cadc2c5..a5d109f 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/ClientOptions.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/ClientOptions.kt @@ -402,13 +402,15 @@ private constructor( headers.put("X-Stainless-Package-Version", getPackageVersion()) headers.put("X-Stainless-Runtime", "JRE") headers.put("X-Stainless-Runtime-Version", getJavaVersion()) + headers.put("X-Stainless-Kotlin-Version", KotlinVersion.CURRENT.toString()) + // We replace after all the default headers to allow end-users to overwrite them. + headers.replaceAll(this.headers.build()) + queryParams.replaceAll(this.queryParams.build()) apiKey.let { if (!it.isEmpty()) { - headers.put("X-eBirdApiToken", it) + headers.replace("X-eBirdApiToken", it) } } - headers.replaceAll(this.headers.build()) - queryParams.replaceAll(this.queryParams.build()) return ClientOptions( httpClient, diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/ObjectMappers.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/ObjectMappers.kt index 8c8d7e2..db714d1 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/ObjectMappers.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/ObjectMappers.kt @@ -24,7 +24,8 @@ import java.io.InputStream import java.time.DateTimeException import java.time.LocalDate import java.time.LocalDateTime -import java.time.ZonedDateTime +import java.time.OffsetDateTime +import java.time.ZoneId import java.time.format.DateTimeFormatter import java.time.temporal.ChronoField @@ -36,7 +37,7 @@ fun jsonMapper(): JsonMapper = .addModule( SimpleModule() .addSerializer(InputStreamSerializer) - .addDeserializer(LocalDateTime::class.java, LenientLocalDateTimeDeserializer()) + .addDeserializer(OffsetDateTime::class.java, LenientOffsetDateTimeDeserializer()) ) .withCoercionConfig(LogicalType.Boolean) { it.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) @@ -47,6 +48,7 @@ fun jsonMapper(): JsonMapper = } .withCoercionConfig(LogicalType.Integer) { it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) .setCoercion(CoercionInputShape.String, CoercionAction.Fail) .setCoercion(CoercionInputShape.Array, CoercionAction.Fail) .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) @@ -64,6 +66,12 @@ fun jsonMapper(): JsonMapper = .setCoercion(CoercionInputShape.Array, CoercionAction.Fail) .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) } + .withCoercionConfig(LogicalType.DateTime) { + it.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Array, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } .withCoercionConfig(LogicalType.Array) { it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) @@ -124,10 +132,10 @@ private object InputStreamSerializer : BaseSerializer(InputStream:: } /** - * A deserializer that can deserialize [LocalDateTime] from datetimes, dates, and zoned datetimes. + * A deserializer that can deserialize [OffsetDateTime] from datetimes, dates, and zoned datetimes. */ -private class LenientLocalDateTimeDeserializer : - StdDeserializer(LocalDateTime::class.java) { +private class LenientOffsetDateTimeDeserializer : + StdDeserializer(OffsetDateTime::class.java) { companion object { @@ -141,7 +149,7 @@ private class LenientLocalDateTimeDeserializer : override fun logicalType(): LogicalType = LogicalType.DateTime - override fun deserialize(p: JsonParser, context: DeserializationContext?): LocalDateTime { + override fun deserialize(p: JsonParser, context: DeserializationContext): OffsetDateTime { val exceptions = mutableListOf() for (formatter in DATE_TIME_FORMATTERS) { @@ -150,17 +158,20 @@ private class LenientLocalDateTimeDeserializer : return when { !temporal.isSupported(ChronoField.HOUR_OF_DAY) -> - LocalDate.from(temporal).atStartOfDay() + LocalDate.from(temporal) + .atStartOfDay() + .atZone(ZoneId.of("UTC")) + .toOffsetDateTime() !temporal.isSupported(ChronoField.OFFSET_SECONDS) -> - LocalDateTime.from(temporal) - else -> ZonedDateTime.from(temporal).toLocalDateTime() + LocalDateTime.from(temporal).atZone(ZoneId.of("UTC")).toOffsetDateTime() + else -> OffsetDateTime.from(temporal) } } catch (e: DateTimeException) { exceptions.add(e) } } - throw JsonParseException(p, "Cannot parse `LocalDateTime` from value: ${p.text}").apply { + throw JsonParseException(p, "Cannot parse `OffsetDateTime` from value: ${p.text}").apply { exceptions.forEach { addSuppressed(it) } } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/Properties.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/Properties.kt index a132d4f..c5b53a3 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/Properties.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/Properties.kt @@ -34,9 +34,9 @@ fun getOsName(): String { } } -fun getOsVersion(): String = System.getProperty("os.version", "unknown") +fun getOsVersion(): String = System.getProperty("os.version", "unknown") ?: "unknown" fun getPackageVersion(): String = - PhoebeClient::class.java.`package`.implementationVersion ?: "unknown" + PhoebeClient::class.java.`package`?.implementationVersion ?: "unknown" -fun getJavaVersion(): String = System.getProperty("java.version", "unknown") +fun getJavaVersion(): String = System.getProperty("java.version", "unknown") ?: "unknown" diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/http/HttpRequest.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/http/HttpRequest.kt index e398c05..6ddee7f 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/http/HttpRequest.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/http/HttpRequest.kt @@ -2,6 +2,7 @@ package com.phoebe.api.core.http import com.phoebe.api.core.checkRequired import com.phoebe.api.core.toImmutable +import java.net.URLEncoder class HttpRequest private constructor( @@ -13,6 +14,35 @@ private constructor( @get:JvmName("body") val body: HttpRequestBody?, ) { + fun url(): String = buildString { + append(baseUrl) + + pathSegments.forEach { segment -> + if (!endsWith("/")) { + append("/") + } + append(URLEncoder.encode(segment, "UTF-8")) + } + + if (queryParams.isEmpty()) { + return@buildString + } + + append("?") + var isFirst = true + queryParams.keys().forEach { key -> + queryParams.values(key).forEach { value -> + if (!isFirst) { + append("&") + } + append(URLEncoder.encode(key, "UTF-8")) + append("=") + append(URLEncoder.encode(value, "UTF-8")) + isFirst = false + } + } + } + fun toBuilder(): Builder = Builder().from(this) override fun toString(): String = diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/http/HttpRequestBodies.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/http/HttpRequestBodies.kt index d274acd..bd6fcdb 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/http/HttpRequestBodies.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/http/HttpRequestBodies.kt @@ -8,13 +8,13 @@ import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.node.JsonNodeType import com.phoebe.api.core.MultipartField +import com.phoebe.api.core.toImmutable import com.phoebe.api.errors.PhoebeInvalidDataException +import java.io.ByteArrayInputStream import java.io.InputStream import java.io.OutputStream +import java.util.UUID import kotlin.jvm.optionals.getOrNull -import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder -import org.apache.hc.core5.http.ContentType -import org.apache.hc.core5.http.HttpEntity @JvmSynthetic internal inline fun json(jsonMapper: JsonMapper, value: T): HttpRequestBody = @@ -37,92 +37,231 @@ internal fun multipartFormData( jsonMapper: JsonMapper, fields: Map>, ): HttpRequestBody = - object : HttpRequestBody { - private val entity: HttpEntity by lazy { - MultipartEntityBuilder.create() - .apply { - fields.forEach { (name, field) -> - val knownValue = field.value.asKnown().getOrNull() - val parts = - if (knownValue is InputStream) { - // Read directly from the `InputStream` instead of reading it all - // into memory due to the `jsonMapper` serialization below. - sequenceOf(name to knownValue) - } else { - val node = jsonMapper.valueToTree(field.value) - serializePart(name, node) + MultipartBody.Builder() + .apply { + fields.forEach { (name, field) -> + val knownValue = field.value.asKnown().getOrNull() + val parts = + if (knownValue is InputStream) { + // Read directly from the `InputStream` instead of reading it all + // into memory due to the `jsonMapper` serialization below. + sequenceOf(name to knownValue) + } else { + val node = jsonMapper.valueToTree(field.value) + serializePart(name, node) + } + + parts.forEach { (name, bytes) -> + val partBody = + if (bytes is ByteArrayInputStream) { + val byteArray = bytes.readBytes() + + object : HttpRequestBody { + + override fun writeTo(outputStream: OutputStream) { + outputStream.write(byteArray) + } + + override fun contentType(): String = field.contentType + + override fun contentLength(): Long = byteArray.size.toLong() + + override fun repeatable(): Boolean = true + + override fun close() {} } + } else { + object : HttpRequestBody { + + override fun writeTo(outputStream: OutputStream) { + bytes.copyTo(outputStream) + } + + override fun contentType(): String = field.contentType + + override fun contentLength(): Long = -1L - parts.forEach { (name, bytes) -> - addBinaryBody( - name, - bytes, - ContentType.parseLenient(field.contentType), - field.filename().getOrNull(), - ) + override fun repeatable(): Boolean = false + + override fun close() = bytes.close() + } } - } + + addPart( + MultipartBody.Part.create( + name, + field.filename().getOrNull(), + field.contentType, + partBody, + ) + ) } - .build() + } } + .build() - private fun serializePart( - name: String, - node: JsonNode, - ): Sequence> = - when (node.nodeType) { - JsonNodeType.MISSING, - JsonNodeType.NULL -> emptySequence() - JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream()) - JsonNodeType.STRING -> sequenceOf(name to node.textValue().inputStream()) - JsonNodeType.BOOLEAN -> - sequenceOf(name to node.booleanValue().toString().inputStream()) - JsonNodeType.NUMBER -> - sequenceOf(name to node.numberValue().toString().inputStream()) - JsonNodeType.ARRAY -> - sequenceOf( - name to - node - .elements() - .asSequence() - .mapNotNull { element -> - when (element.nodeType) { - JsonNodeType.MISSING, - JsonNodeType.NULL -> null - JsonNodeType.STRING -> node.textValue() - JsonNodeType.BOOLEAN -> node.booleanValue().toString() - JsonNodeType.NUMBER -> node.numberValue().toString() - null, - JsonNodeType.BINARY, - JsonNodeType.ARRAY, - JsonNodeType.OBJECT, - JsonNodeType.POJO -> - throw PhoebeInvalidDataException( - "Unexpected JsonNode type in array: ${node.nodeType}" - ) - } - } - .joinToString(",") - .inputStream() - ) - JsonNodeType.OBJECT -> - node.fields().asSequence().flatMap { (key, value) -> - serializePart("$name[$key]", value) - } - JsonNodeType.POJO, - null -> - throw PhoebeInvalidDataException("Unexpected JsonNode type: ${node.nodeType}") +private fun serializePart(name: String, node: JsonNode): Sequence> = + when (node.nodeType) { + JsonNodeType.MISSING, + JsonNodeType.NULL -> emptySequence() + JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream()) + JsonNodeType.STRING -> sequenceOf(name to node.textValue().byteInputStream()) + JsonNodeType.BOOLEAN -> sequenceOf(name to node.booleanValue().toString().byteInputStream()) + JsonNodeType.NUMBER -> sequenceOf(name to node.numberValue().toString().byteInputStream()) + JsonNodeType.ARRAY -> + sequenceOf( + name to + node + .elements() + .asSequence() + .mapNotNull { element -> + when (element.nodeType) { + JsonNodeType.MISSING, + JsonNodeType.NULL -> null + JsonNodeType.STRING -> element.textValue() + JsonNodeType.BOOLEAN -> element.booleanValue().toString() + JsonNodeType.NUMBER -> element.numberValue().toString() + null, + JsonNodeType.BINARY, + JsonNodeType.ARRAY, + JsonNodeType.OBJECT, + JsonNodeType.POJO -> + throw PhoebeInvalidDataException( + "Unexpected JsonNode type in array: ${element.nodeType}" + ) + } + } + .joinToString(",") + .byteInputStream() + ) + JsonNodeType.OBJECT -> + node.fields().asSequence().flatMap { (key, value) -> + serializePart("$name[$key]", value) + } + JsonNodeType.POJO, + null -> throw PhoebeInvalidDataException("Unexpected JsonNode type: ${node.nodeType}") + } + +private class MultipartBody +private constructor(private val boundary: String, private val parts: List) : HttpRequestBody { + private val boundaryBytes: ByteArray = boundary.toByteArray() + private val contentType = "multipart/form-data; boundary=$boundary" + + // This must remain in sync with `contentLength`. + override fun writeTo(outputStream: OutputStream) { + parts.forEach { part -> + outputStream.write(DASHDASH) + outputStream.write(boundaryBytes) + outputStream.write(CRLF) + + outputStream.write(CONTENT_DISPOSITION) + outputStream.write(part.contentDisposition.toByteArray()) + outputStream.write(CRLF) + + outputStream.write(CONTENT_TYPE) + outputStream.write(part.contentType.toByteArray()) + outputStream.write(CRLF) + + outputStream.write(CRLF) + part.body.writeTo(outputStream) + outputStream.write(CRLF) + } + + outputStream.write(DASHDASH) + outputStream.write(boundaryBytes) + outputStream.write(DASHDASH) + outputStream.write(CRLF) + } + + override fun contentType(): String = contentType + + // This must remain in sync with `writeTo`. + override fun contentLength(): Long { + var byteCount = 0L + + parts.forEach { part -> + val contentLength = part.body.contentLength() + if (contentLength == -1L) { + return -1L } - private fun String.inputStream(): InputStream = toByteArray().inputStream() + byteCount += + DASHDASH.size + + boundaryBytes.size + + CRLF.size + + CONTENT_DISPOSITION.size + + part.contentDisposition.toByteArray().size + + CRLF.size + + CONTENT_TYPE.size + + part.contentType.toByteArray().size + + CRLF.size + + CRLF.size + + contentLength + + CRLF.size + } - override fun writeTo(outputStream: OutputStream) = entity.writeTo(outputStream) + byteCount += DASHDASH.size + boundaryBytes.size + DASHDASH.size + CRLF.size + return byteCount + } - override fun contentType(): String = entity.contentType + override fun repeatable(): Boolean = parts.all { it.body.repeatable() } - override fun contentLength(): Long = entity.contentLength + override fun close() { + parts.forEach { it.body.close() } + } - override fun repeatable(): Boolean = entity.isRepeatable + class Builder { + private val boundary = UUID.randomUUID().toString() + private val parts: MutableList = mutableListOf() - override fun close() = entity.close() + fun addPart(part: Part) = apply { parts.add(part) } + + fun build() = MultipartBody(boundary, parts.toImmutable()) + } + + class Part + private constructor( + val contentDisposition: String, + val contentType: String, + val body: HttpRequestBody, + ) { + companion object { + fun create( + name: String, + filename: String?, + contentType: String, + body: HttpRequestBody, + ): Part { + val disposition = buildString { + append("form-data; name=") + appendQuotedString(name) + if (filename != null) { + append("; filename=") + appendQuotedString(filename) + } + } + return Part(disposition, contentType, body) + } + } + } + + companion object { + private val CRLF = byteArrayOf('\r'.code.toByte(), '\n'.code.toByte()) + private val DASHDASH = byteArrayOf('-'.code.toByte(), '-'.code.toByte()) + private val CONTENT_DISPOSITION = "Content-Disposition: ".toByteArray() + private val CONTENT_TYPE = "Content-Type: ".toByteArray() + + private fun StringBuilder.appendQuotedString(key: String) { + append('"') + for (ch in key) { + when (ch) { + '\n' -> append("%0A") + '\r' -> append("%0D") + '"' -> append("%22") + else -> append(ch) + } + } + append('"') + } } +} diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/http/RetryingHttpClient.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/http/RetryingHttpClient.kt index d6041fe..362969b 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/http/RetryingHttpClient.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/core/http/RetryingHttpClient.kt @@ -1,3 +1,5 @@ +// File generated from our OpenAPI spec by Stainless. + package com.phoebe.api.core.http import com.phoebe.api.core.DefaultSleeper @@ -31,10 +33,6 @@ private constructor( ) : HttpClient { override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse { - if (!isRetryable(request) || maxRetries <= 0) { - return httpClient.execute(request, requestOptions) - } - var modifiedRequest = maybeAddIdempotencyHeader(request) // Don't send the current retry count in the headers if the caller set their own value. @@ -48,6 +46,10 @@ private constructor( modifiedRequest = setRetryCountHeader(modifiedRequest, retries) } + if (!isRetryable(modifiedRequest)) { + return httpClient.execute(modifiedRequest, requestOptions) + } + val response = try { val response = httpClient.execute(modifiedRequest, requestOptions) @@ -75,10 +77,6 @@ private constructor( request: HttpRequest, requestOptions: RequestOptions, ): CompletableFuture { - if (!isRetryable(request) || maxRetries <= 0) { - return httpClient.executeAsync(request, requestOptions) - } - val modifiedRequest = maybeAddIdempotencyHeader(request) // Don't send the current retry count in the headers if the caller set their own value. @@ -94,8 +92,12 @@ private constructor( val requestWithRetryCount = if (shouldSendRetryCount) setRetryCountHeader(request, retries) else request - return httpClient - .executeAsync(requestWithRetryCount, requestOptions) + val responseFuture = httpClient.executeAsync(requestWithRetryCount, requestOptions) + if (!isRetryable(requestWithRetryCount)) { + return responseFuture + } + + return responseFuture .handleAsync( fun( response: HttpResponse?, @@ -199,7 +201,7 @@ private constructor( ?: headers.values("Retry-After").getOrNull(0)?.let { retryAfter -> retryAfter.toFloatOrNull()?.times(TimeUnit.SECONDS.toNanos(1)) ?: try { - ChronoUnit.MILLIS.between( + ChronoUnit.NANOS.between( OffsetDateTime.now(clock), OffsetDateTime.parse( retryAfter, @@ -212,13 +214,8 @@ private constructor( } } ?.let { retryAfterNanos -> - // If the API asks us to wait a certain amount of time (and it's a reasonable - // amount), just - // do what it says. - val retryAfter = Duration.ofNanos(retryAfterNanos.toLong()) - if (retryAfter in Duration.ofNanos(0)..Duration.ofMinutes(1)) { - return retryAfter - } + // If the API asks us to wait a certain amount of time, do what it says. + return Duration.ofNanos(retryAfterNanos.toLong()) } // Apply exponential backoff, but not more than the max. diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ProductServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ProductServiceAsync.kt index 1eb0c21..c26d846 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ProductServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ProductServiceAsync.kt @@ -24,14 +24,45 @@ interface ProductServiceAsync { */ fun withOptions(modifier: Consumer): ProductServiceAsync + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun lists(): ListServiceAsync + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ fun top100(): Top100ServiceAsync + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ fun stats(): StatServiceAsync + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ fun speciesList(): SpeciesListServiceAsync + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ fun checklist(): ChecklistServiceAsync /** @@ -48,14 +79,45 @@ interface ProductServiceAsync { modifier: Consumer ): ProductServiceAsync.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun lists(): ListServiceAsync.WithRawResponse + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ fun top100(): Top100ServiceAsync.WithRawResponse + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ fun stats(): StatServiceAsync.WithRawResponse + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ fun speciesList(): SpeciesListServiceAsync.WithRawResponse + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ fun checklist(): ChecklistServiceAsync.WithRawResponse } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ProductServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ProductServiceAsyncImpl.kt index 2cce22f..1e9219e 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ProductServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ProductServiceAsyncImpl.kt @@ -41,14 +41,45 @@ class ProductServiceAsyncImpl internal constructor(private val clientOptions: Cl override fun withOptions(modifier: Consumer): ProductServiceAsync = ProductServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun lists(): ListServiceAsync = lists + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ override fun top100(): Top100ServiceAsync = top100 + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ override fun stats(): StatServiceAsync = stats + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ override fun speciesList(): SpeciesListServiceAsync = speciesList + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ override fun checklist(): ChecklistServiceAsync = checklist class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : @@ -81,14 +112,45 @@ class ProductServiceAsyncImpl internal constructor(private val clientOptions: Cl clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun lists(): ListServiceAsync.WithRawResponse = lists + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ override fun top100(): Top100ServiceAsync.WithRawResponse = top100 + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ override fun stats(): StatServiceAsync.WithRawResponse = stats + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ override fun speciesList(): SpeciesListServiceAsync.WithRawResponse = speciesList + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ override fun checklist(): ChecklistServiceAsync.WithRawResponse = checklist } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/RefServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/RefServiceAsync.kt index 50c145d..f3dd9b7 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/RefServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/RefServiceAsync.kt @@ -24,6 +24,10 @@ interface RefServiceAsync { fun region(): RegionServiceAsync + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or + * nearby hotspots + */ fun hotspot(): HotspotServiceAsync fun taxonomy(): TaxonomyServiceAsync @@ -40,6 +44,10 @@ interface RefServiceAsync { fun region(): RegionServiceAsync.WithRawResponse + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region + * or nearby hotspots + */ fun hotspot(): HotspotServiceAsync.WithRawResponse fun taxonomy(): TaxonomyServiceAsync.WithRawResponse diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/RefServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/RefServiceAsyncImpl.kt index ef3b52d..e8650fa 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/RefServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/RefServiceAsyncImpl.kt @@ -31,6 +31,10 @@ class RefServiceAsyncImpl internal constructor(private val clientOptions: Client override fun region(): RegionServiceAsync = region + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or + * nearby hotspots + */ override fun hotspot(): HotspotServiceAsync = hotspot override fun taxonomy(): TaxonomyServiceAsync = taxonomy @@ -59,6 +63,10 @@ class RefServiceAsyncImpl internal constructor(private val clientOptions: Client override fun region(): RegionServiceAsync.WithRawResponse = region + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region + * or nearby hotspots + */ override fun hotspot(): HotspotServiceAsync.WithRawResponse = hotspot override fun taxonomy(): TaxonomyServiceAsync.WithRawResponse = taxonomy diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/ObservationServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/ObservationServiceAsync.kt index 80b23e6..4c9b6ee 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/ObservationServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/ObservationServiceAsync.kt @@ -22,6 +22,13 @@ interface ObservationServiceAsync { */ fun withOptions(modifier: Consumer): ObservationServiceAsync + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun recent(): RecentServiceAsync fun geo(): GeoServiceAsync @@ -43,6 +50,13 @@ interface ObservationServiceAsync { modifier: Consumer ): ObservationServiceAsync.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun recent(): RecentServiceAsync.WithRawResponse fun geo(): GeoServiceAsync.WithRawResponse diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/ObservationServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/ObservationServiceAsyncImpl.kt index 908e9d7..51c9f59 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/ObservationServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/ObservationServiceAsyncImpl.kt @@ -29,6 +29,13 @@ class ObservationServiceAsyncImpl internal constructor(private val clientOptions override fun withOptions(modifier: Consumer): ObservationServiceAsync = ObservationServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun recent(): RecentServiceAsync = recent override fun geo(): GeoServiceAsync = geo @@ -57,6 +64,13 @@ class ObservationServiceAsyncImpl internal constructor(private val clientOptions clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun recent(): RecentServiceAsync.WithRawResponse = recent override fun geo(): GeoServiceAsync.WithRawResponse = geo diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/GeoServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/GeoServiceAsync.kt index 7e66a63..6843a2e 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/GeoServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/GeoServiceAsync.kt @@ -20,6 +20,13 @@ interface GeoServiceAsync { */ fun withOptions(modifier: Consumer): GeoServiceAsync + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun recent(): RecentServiceAsync /** A view of [GeoServiceAsync] that provides access to raw HTTP responses for each method. */ @@ -32,6 +39,13 @@ interface GeoServiceAsync { */ fun withOptions(modifier: Consumer): GeoServiceAsync.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun recent(): RecentServiceAsync.WithRawResponse } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/GeoServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/GeoServiceAsyncImpl.kt index 9fedf02..6095989 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/GeoServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/GeoServiceAsyncImpl.kt @@ -21,6 +21,13 @@ class GeoServiceAsyncImpl internal constructor(private val clientOptions: Client override fun withOptions(modifier: Consumer): GeoServiceAsync = GeoServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun recent(): RecentServiceAsync = recent class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : @@ -37,6 +44,13 @@ class GeoServiceAsyncImpl internal constructor(private val clientOptions: Client clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun recent(): RecentServiceAsync.WithRawResponse = recent } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/NearestServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/NearestServiceAsync.kt index 68e8bcd..1e647ad 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/NearestServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/NearestServiceAsync.kt @@ -20,6 +20,13 @@ interface NearestServiceAsync { */ fun withOptions(modifier: Consumer): NearestServiceAsync + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun geoSpecies(): GeoSpecieServiceAsync /** @@ -36,6 +43,13 @@ interface NearestServiceAsync { modifier: Consumer ): NearestServiceAsync.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun geoSpecies(): GeoSpecieServiceAsync.WithRawResponse } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/NearestServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/NearestServiceAsyncImpl.kt index d9bb0b2..d9c5827 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/NearestServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/NearestServiceAsyncImpl.kt @@ -23,6 +23,13 @@ class NearestServiceAsyncImpl internal constructor(private val clientOptions: Cl override fun withOptions(modifier: Consumer): NearestServiceAsync = NearestServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun geoSpecies(): GeoSpecieServiceAsync = geoSpecies class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : @@ -39,6 +46,13 @@ class NearestServiceAsyncImpl internal constructor(private val clientOptions: Cl clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun geoSpecies(): GeoSpecieServiceAsync.WithRawResponse = geoSpecies } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/RecentServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/RecentServiceAsync.kt index ed439c1..5b2a563 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/RecentServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/RecentServiceAsync.kt @@ -13,6 +13,12 @@ import com.phoebe.api.services.async.data.observations.recent.SpecieServiceAsync import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface RecentServiceAsync { /** @@ -27,10 +33,31 @@ interface RecentServiceAsync { */ fun withOptions(modifier: Consumer): RecentServiceAsync + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun notable(): NotableServiceAsync + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun species(): SpecieServiceAsync + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun historic(): HistoricServiceAsync /** @@ -86,10 +113,31 @@ interface RecentServiceAsync { modifier: Consumer ): RecentServiceAsync.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun notable(): NotableServiceAsync.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun species(): SpecieServiceAsync.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun historic(): HistoricServiceAsync.WithRawResponse /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/RecentServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/RecentServiceAsyncImpl.kt index df5842d..7d15ab1 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/RecentServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/RecentServiceAsyncImpl.kt @@ -27,6 +27,12 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class RecentServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : RecentServiceAsync { @@ -45,10 +51,31 @@ class RecentServiceAsyncImpl internal constructor(private val clientOptions: Cli override fun withOptions(modifier: Consumer): RecentServiceAsync = RecentServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun notable(): NotableServiceAsync = notable + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun species(): SpecieServiceAsync = species + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun historic(): HistoricServiceAsync = historic override fun list( @@ -83,10 +110,31 @@ class RecentServiceAsyncImpl internal constructor(private val clientOptions: Cli clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun notable(): NotableServiceAsync.WithRawResponse = notable + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun species(): SpecieServiceAsync.WithRawResponse = species + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun historic(): HistoricServiceAsync.WithRawResponse = historic private val listHandler: Handler> = diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/RecentServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/RecentServiceAsync.kt index 0c1b6bb..33f3dd8 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/RecentServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/RecentServiceAsync.kt @@ -12,6 +12,12 @@ import com.phoebe.api.services.async.data.observations.geo.recent.SpecieServiceA import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface RecentServiceAsync { /** @@ -26,8 +32,22 @@ interface RecentServiceAsync { */ fun withOptions(modifier: Consumer): RecentServiceAsync + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun species(): SpecieServiceAsync + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun notable(): NotableServiceAsync /** @@ -58,8 +78,22 @@ interface RecentServiceAsync { modifier: Consumer ): RecentServiceAsync.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun species(): SpecieServiceAsync.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun notable(): NotableServiceAsync.WithRawResponse /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/RecentServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/RecentServiceAsyncImpl.kt index c6f6595..bf60ac8 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/RecentServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/RecentServiceAsyncImpl.kt @@ -23,6 +23,12 @@ import com.phoebe.api.services.async.data.observations.geo.recent.SpecieServiceA import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class RecentServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : RecentServiceAsync { @@ -39,8 +45,22 @@ class RecentServiceAsyncImpl internal constructor(private val clientOptions: Cli override fun withOptions(modifier: Consumer): RecentServiceAsync = RecentServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun species(): SpecieServiceAsync = species + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun notable(): NotableServiceAsync = notable override fun list( @@ -71,8 +91,22 @@ class RecentServiceAsyncImpl internal constructor(private val clientOptions: Cli clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun species(): SpecieServiceAsync.WithRawResponse = species + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun notable(): NotableServiceAsync.WithRawResponse = notable private val listHandler: Handler> = diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/NotableServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/NotableServiceAsync.kt index 0e948e3..c502f50 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/NotableServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/NotableServiceAsync.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.data.observations.geo.recent.notable.NotableListPar import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface NotableServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/NotableServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/NotableServiceAsyncImpl.kt index dd8f63d..9fba1f8 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/NotableServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/NotableServiceAsyncImpl.kt @@ -19,6 +19,12 @@ import com.phoebe.api.models.data.observations.geo.recent.notable.NotableListPar import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class NotableServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : NotableServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/SpecieServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/SpecieServiceAsync.kt index 955b3b5..8b9e063 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/SpecieServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/SpecieServiceAsync.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.data.observations.geo.recent.species.SpecieListPara import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface SpecieServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/SpecieServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/SpecieServiceAsyncImpl.kt index 5cbb0de..97d405a 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/SpecieServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/geo/recent/SpecieServiceAsyncImpl.kt @@ -21,6 +21,12 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class SpecieServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : SpecieServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/nearest/GeoSpecieServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/nearest/GeoSpecieServiceAsync.kt index 0c1b62a..b74e8db 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/nearest/GeoSpecieServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/nearest/GeoSpecieServiceAsync.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.data.observations.nearest.geospecies.GeoSpecieListP import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface GeoSpecieServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/nearest/GeoSpecieServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/nearest/GeoSpecieServiceAsyncImpl.kt index 41870c7..6125a1c 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/nearest/GeoSpecieServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/nearest/GeoSpecieServiceAsyncImpl.kt @@ -21,6 +21,12 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class GeoSpecieServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : GeoSpecieServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/HistoricServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/HistoricServiceAsync.kt index 629cd8a..22190b2 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/HistoricServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/HistoricServiceAsync.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.data.observations.recent.historic.HistoricListParam import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface HistoricServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/HistoricServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/HistoricServiceAsyncImpl.kt index e39f932..b5745df 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/HistoricServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/HistoricServiceAsyncImpl.kt @@ -21,6 +21,12 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class HistoricServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : HistoricServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/NotableServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/NotableServiceAsync.kt index c114452..d9674a5 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/NotableServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/NotableServiceAsync.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.data.observations.recent.notable.NotableListParams import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface NotableServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/NotableServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/NotableServiceAsyncImpl.kt index 7f26597..f173eb7 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/NotableServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/NotableServiceAsyncImpl.kt @@ -21,6 +21,12 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class NotableServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : NotableServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/SpecieServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/SpecieServiceAsync.kt index 2281fb2..f34f4fd 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/SpecieServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/SpecieServiceAsync.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.data.observations.recent.species.SpecieRetrievePara import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface SpecieServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/SpecieServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/SpecieServiceAsyncImpl.kt index 3bf7a4d..fbe2e45 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/SpecieServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/data/observations/recent/SpecieServiceAsyncImpl.kt @@ -21,6 +21,12 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class SpecieServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : SpecieServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ChecklistServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ChecklistServiceAsync.kt index b197028..983897f 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ChecklistServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ChecklistServiceAsync.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.product.checklist.ChecklistViewResponse import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ interface ChecklistServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ChecklistServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ChecklistServiceAsyncImpl.kt index 124f598..20a240c 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ChecklistServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ChecklistServiceAsyncImpl.kt @@ -21,6 +21,12 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ class ChecklistServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : ChecklistServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ListServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ListServiceAsync.kt index e97f814..c756752 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ListServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ListServiceAsync.kt @@ -11,6 +11,12 @@ import com.phoebe.api.services.async.product.lists.HistoricalServiceAsync import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface ListServiceAsync { /** @@ -25,6 +31,12 @@ interface ListServiceAsync { */ fun withOptions(modifier: Consumer): ListServiceAsync + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ fun historical(): HistoricalServiceAsync /** Get information on the most recently submitted checklists for a region. */ @@ -73,6 +85,12 @@ interface ListServiceAsync { */ fun withOptions(modifier: Consumer): ListServiceAsync.WithRawResponse + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ fun historical(): HistoricalServiceAsync.WithRawResponse /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ListServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ListServiceAsyncImpl.kt index 283a90c..59f2da7 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ListServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/ListServiceAsyncImpl.kt @@ -23,6 +23,12 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class ListServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : ListServiceAsync { @@ -39,6 +45,12 @@ class ListServiceAsyncImpl internal constructor(private val clientOptions: Clien override fun withOptions(modifier: Consumer): ListServiceAsync = ListServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ override fun historical(): HistoricalServiceAsync = historical override fun retrieve( @@ -65,6 +77,12 @@ class ListServiceAsyncImpl internal constructor(private val clientOptions: Clien clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ override fun historical(): HistoricalServiceAsync.WithRawResponse = historical private val retrieveHandler: Handler> = diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/SpeciesListServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/SpeciesListServiceAsync.kt index 3ec57ac..d26801d 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/SpeciesListServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/SpeciesListServiceAsync.kt @@ -9,6 +9,12 @@ import com.phoebe.api.models.product.specieslist.SpeciesListListParams import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ interface SpeciesListServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/SpeciesListServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/SpeciesListServiceAsyncImpl.kt index dc5158c..bfc93c4 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/SpeciesListServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/SpeciesListServiceAsyncImpl.kt @@ -20,6 +20,12 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ class SpeciesListServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : SpeciesListServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/StatServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/StatServiceAsync.kt index b5fdff5..7fdbd34 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/StatServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/StatServiceAsync.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.product.stats.StatRetrieveResponse import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ interface StatServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/StatServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/StatServiceAsyncImpl.kt index 019bee6..eb418d7 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/StatServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/StatServiceAsyncImpl.kt @@ -21,6 +21,12 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ class StatServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : StatServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/Top100ServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/Top100ServiceAsync.kt index 5959cac..30c8b8b 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/Top100ServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/Top100ServiceAsync.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.product.top100.Top100RetrieveResponse import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ interface Top100ServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/Top100ServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/Top100ServiceAsyncImpl.kt index ca37601..e1629e4 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/Top100ServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/Top100ServiceAsyncImpl.kt @@ -21,6 +21,12 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ class Top100ServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : Top100ServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/lists/HistoricalServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/lists/HistoricalServiceAsync.kt index eb57fdf..f90bd38 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/lists/HistoricalServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/lists/HistoricalServiceAsync.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.product.lists.historical.HistoricalRetrieveResponse import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ interface HistoricalServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/lists/HistoricalServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/lists/HistoricalServiceAsyncImpl.kt index b73ad2e..e30a04c 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/lists/HistoricalServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/product/lists/HistoricalServiceAsyncImpl.kt @@ -21,6 +21,12 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ class HistoricalServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : HistoricalServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/HotspotServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/HotspotServiceAsync.kt index 3bd551d..22a0dcc 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/HotspotServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/HotspotServiceAsync.kt @@ -12,6 +12,10 @@ import com.phoebe.api.services.async.ref.hotspot.InfoServiceAsync import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or nearby + * hotspots + */ interface HotspotServiceAsync { /** @@ -26,8 +30,16 @@ interface HotspotServiceAsync { */ fun withOptions(modifier: Consumer): HotspotServiceAsync + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or + * nearby hotspots + */ fun geo(): GeoServiceAsync + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or + * nearby hotspots + */ fun info(): InfoServiceAsync /** Hotspots in a region */ @@ -80,8 +92,16 @@ interface HotspotServiceAsync { modifier: Consumer ): HotspotServiceAsync.WithRawResponse + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region + * or nearby hotspots + */ fun geo(): GeoServiceAsync.WithRawResponse + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region + * or nearby hotspots + */ fun info(): InfoServiceAsync.WithRawResponse /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/HotspotServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/HotspotServiceAsyncImpl.kt index d97a947..4b45528 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/HotspotServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/HotspotServiceAsyncImpl.kt @@ -25,6 +25,10 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or nearby + * hotspots + */ class HotspotServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : HotspotServiceAsync { @@ -41,8 +45,16 @@ class HotspotServiceAsyncImpl internal constructor(private val clientOptions: Cl override fun withOptions(modifier: Consumer): HotspotServiceAsync = HotspotServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or + * nearby hotspots + */ override fun geo(): GeoServiceAsync = geo + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or + * nearby hotspots + */ override fun info(): InfoServiceAsync = info override fun list( @@ -73,8 +85,16 @@ class HotspotServiceAsyncImpl internal constructor(private val clientOptions: Cl clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region + * or nearby hotspots + */ override fun geo(): GeoServiceAsync.WithRawResponse = geo + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region + * or nearby hotspots + */ override fun info(): InfoServiceAsync.WithRawResponse = info private val listHandler: Handler> = diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/RegionServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/RegionServiceAsync.kt index 0ea8b98..1a34d96 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/RegionServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/RegionServiceAsync.kt @@ -22,10 +22,13 @@ interface RegionServiceAsync { */ fun withOptions(modifier: Consumer): RegionServiceAsync + /** With the ref/geo end-point you can find a country's or region's neighbours. */ fun adjacent(): AdjacentServiceAsync + /** The ref/region end-points return information on regions. */ fun info(): InfoServiceAsync + /** The ref/region end-points return information on regions. */ fun list(): ListServiceAsync /** @@ -42,10 +45,13 @@ interface RegionServiceAsync { modifier: Consumer ): RegionServiceAsync.WithRawResponse + /** With the ref/geo end-point you can find a country's or region's neighbours. */ fun adjacent(): AdjacentServiceAsync.WithRawResponse + /** The ref/region end-points return information on regions. */ fun info(): InfoServiceAsync.WithRawResponse + /** The ref/region end-points return information on regions. */ fun list(): ListServiceAsync.WithRawResponse } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/RegionServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/RegionServiceAsyncImpl.kt index b0c90fa..9375f9f 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/RegionServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/RegionServiceAsyncImpl.kt @@ -29,10 +29,13 @@ class RegionServiceAsyncImpl internal constructor(private val clientOptions: Cli override fun withOptions(modifier: Consumer): RegionServiceAsync = RegionServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** With the ref/geo end-point you can find a country's or region's neighbours. */ override fun adjacent(): AdjacentServiceAsync = adjacent + /** The ref/region end-points return information on regions. */ override fun info(): InfoServiceAsync = info + /** The ref/region end-points return information on regions. */ override fun list(): ListServiceAsync = list class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : @@ -57,10 +60,13 @@ class RegionServiceAsyncImpl internal constructor(private val clientOptions: Cli clientOptions.toBuilder().apply(modifier::accept).build() ) + /** With the ref/geo end-point you can find a country's or region's neighbours. */ override fun adjacent(): AdjacentServiceAsync.WithRawResponse = adjacent + /** The ref/region end-points return information on regions. */ override fun info(): InfoServiceAsync.WithRawResponse = info + /** The ref/region end-points return information on regions. */ override fun list(): ListServiceAsync.WithRawResponse = list } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/GeoServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/GeoServiceAsync.kt index d8857b2..93d91bb 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/GeoServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/GeoServiceAsync.kt @@ -10,6 +10,10 @@ import com.phoebe.api.models.ref.hotspot.geo.GeoRetrieveResponse import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or nearby + * hotspots + */ interface GeoServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/GeoServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/GeoServiceAsyncImpl.kt index 3f35e4f..8ae7341 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/GeoServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/GeoServiceAsyncImpl.kt @@ -19,6 +19,10 @@ import com.phoebe.api.models.ref.hotspot.geo.GeoRetrieveResponse import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or nearby + * hotspots + */ class GeoServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : GeoServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/InfoServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/InfoServiceAsync.kt index 1d4298e..c9b0b4b 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/InfoServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/InfoServiceAsync.kt @@ -10,6 +10,10 @@ import com.phoebe.api.models.ref.hotspot.info.InfoRetrieveResponse import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or nearby + * hotspots + */ interface InfoServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/InfoServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/InfoServiceAsyncImpl.kt index f62d734..987ca9a 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/InfoServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/hotspot/InfoServiceAsyncImpl.kt @@ -21,6 +21,10 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or nearby + * hotspots + */ class InfoServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : InfoServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/AdjacentServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/AdjacentServiceAsync.kt index 4dc99e5..5a5b38d 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/AdjacentServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/AdjacentServiceAsync.kt @@ -10,6 +10,7 @@ import com.phoebe.api.models.ref.region.adjacent.AdjacentListResponse import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** With the ref/geo end-point you can find a country's or region's neighbours. */ interface AdjacentServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/AdjacentServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/AdjacentServiceAsyncImpl.kt index c5684f9..ef6fa5c 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/AdjacentServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/AdjacentServiceAsyncImpl.kt @@ -21,6 +21,7 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** With the ref/geo end-point you can find a country's or region's neighbours. */ class AdjacentServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : AdjacentServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/InfoServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/InfoServiceAsync.kt index a50fb67..c7d961a 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/InfoServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/InfoServiceAsync.kt @@ -10,6 +10,7 @@ import com.phoebe.api.models.ref.region.info.InfoRetrieveResponse import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** The ref/region end-points return information on regions. */ interface InfoServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/InfoServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/InfoServiceAsyncImpl.kt index e4adb67..8167b15 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/InfoServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/InfoServiceAsyncImpl.kt @@ -21,6 +21,7 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** The ref/region end-points return information on regions. */ class InfoServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : InfoServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/ListServiceAsync.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/ListServiceAsync.kt index 75b7a3b..ba87750 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/ListServiceAsync.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/ListServiceAsync.kt @@ -10,6 +10,7 @@ import com.phoebe.api.models.ref.region.list.ListListResponse import java.util.concurrent.CompletableFuture import java.util.function.Consumer +/** The ref/region end-points return information on regions. */ interface ListServiceAsync { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/ListServiceAsyncImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/ListServiceAsyncImpl.kt index 0c4a8fb..6e074a9 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/ListServiceAsyncImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/async/ref/region/ListServiceAsyncImpl.kt @@ -21,6 +21,7 @@ import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** The ref/region end-points return information on regions. */ class ListServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : ListServiceAsync { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ProductService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ProductService.kt index f2d20da..2ac82b7 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ProductService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ProductService.kt @@ -24,14 +24,45 @@ interface ProductService { */ fun withOptions(modifier: Consumer): ProductService + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun lists(): ListService + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ fun top100(): Top100Service + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ fun stats(): StatService + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ fun speciesList(): SpeciesListService + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ fun checklist(): ChecklistService /** A view of [ProductService] that provides access to raw HTTP responses for each method. */ @@ -44,14 +75,45 @@ interface ProductService { */ fun withOptions(modifier: Consumer): ProductService.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun lists(): ListService.WithRawResponse + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ fun top100(): Top100Service.WithRawResponse + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ fun stats(): StatService.WithRawResponse + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ fun speciesList(): SpeciesListService.WithRawResponse + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ fun checklist(): ChecklistService.WithRawResponse } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ProductServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ProductServiceImpl.kt index b58b4fa..c4d3a4f 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ProductServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ProductServiceImpl.kt @@ -37,14 +37,45 @@ class ProductServiceImpl internal constructor(private val clientOptions: ClientO override fun withOptions(modifier: Consumer): ProductService = ProductServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun lists(): ListService = lists + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ override fun top100(): Top100Service = top100 + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ override fun stats(): StatService = stats + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ override fun speciesList(): SpeciesListService = speciesList + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ override fun checklist(): ChecklistService = checklist class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : @@ -77,14 +108,45 @@ class ProductServiceImpl internal constructor(private val clientOptions: ClientO clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun lists(): ListService.WithRawResponse = lists + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ override fun top100(): Top100Service.WithRawResponse = top100 + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ override fun stats(): StatService.WithRawResponse = stats + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ override fun speciesList(): SpeciesListService.WithRawResponse = speciesList + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ override fun checklist(): ChecklistService.WithRawResponse = checklist } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/RefService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/RefService.kt index c75b3dc..fa98695 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/RefService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/RefService.kt @@ -24,6 +24,10 @@ interface RefService { fun region(): RegionService + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or + * nearby hotspots + */ fun hotspot(): HotspotService fun taxonomy(): TaxonomyService @@ -40,6 +44,10 @@ interface RefService { fun region(): RegionService.WithRawResponse + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region + * or nearby hotspots + */ fun hotspot(): HotspotService.WithRawResponse fun taxonomy(): TaxonomyService.WithRawResponse diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/RefServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/RefServiceImpl.kt index d354442..9b7f9dc 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/RefServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/RefServiceImpl.kt @@ -30,6 +30,10 @@ class RefServiceImpl internal constructor(private val clientOptions: ClientOptio override fun region(): RegionService = region + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or + * nearby hotspots + */ override fun hotspot(): HotspotService = hotspot override fun taxonomy(): TaxonomyService = taxonomy @@ -58,6 +62,10 @@ class RefServiceImpl internal constructor(private val clientOptions: ClientOptio override fun region(): RegionService.WithRawResponse = region + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region + * or nearby hotspots + */ override fun hotspot(): HotspotService.WithRawResponse = hotspot override fun taxonomy(): TaxonomyService.WithRawResponse = taxonomy diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/ObservationService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/ObservationService.kt index 1278cce..cae2b55 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/ObservationService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/ObservationService.kt @@ -22,6 +22,13 @@ interface ObservationService { */ fun withOptions(modifier: Consumer): ObservationService + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun recent(): RecentService fun geo(): GeoService @@ -42,6 +49,13 @@ interface ObservationService { modifier: Consumer ): ObservationService.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun recent(): RecentService.WithRawResponse fun geo(): GeoService.WithRawResponse diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/ObservationServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/ObservationServiceImpl.kt index f35d196..bb235c5 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/ObservationServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/ObservationServiceImpl.kt @@ -29,6 +29,13 @@ class ObservationServiceImpl internal constructor(private val clientOptions: Cli override fun withOptions(modifier: Consumer): ObservationService = ObservationServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun recent(): RecentService = recent override fun geo(): GeoService = geo @@ -57,6 +64,13 @@ class ObservationServiceImpl internal constructor(private val clientOptions: Cli clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun recent(): RecentService.WithRawResponse = recent override fun geo(): GeoService.WithRawResponse = geo diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/GeoService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/GeoService.kt index 78ee7d1..d8470e6 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/GeoService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/GeoService.kt @@ -20,6 +20,13 @@ interface GeoService { */ fun withOptions(modifier: Consumer): GeoService + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun recent(): RecentService /** A view of [GeoService] that provides access to raw HTTP responses for each method. */ @@ -32,6 +39,13 @@ interface GeoService { */ fun withOptions(modifier: Consumer): GeoService.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun recent(): RecentService.WithRawResponse } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/GeoServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/GeoServiceImpl.kt index c3aca8e..e018b35 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/GeoServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/GeoServiceImpl.kt @@ -20,6 +20,13 @@ class GeoServiceImpl internal constructor(private val clientOptions: ClientOptio override fun withOptions(modifier: Consumer): GeoService = GeoServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun recent(): RecentService = recent class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : @@ -36,6 +43,13 @@ class GeoServiceImpl internal constructor(private val clientOptions: ClientOptio clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun recent(): RecentService.WithRawResponse = recent } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/NearestService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/NearestService.kt index 2636358..ee7bdcb 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/NearestService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/NearestService.kt @@ -20,6 +20,13 @@ interface NearestService { */ fun withOptions(modifier: Consumer): NearestService + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun geoSpecies(): GeoSpecieService /** A view of [NearestService] that provides access to raw HTTP responses for each method. */ @@ -32,6 +39,13 @@ interface NearestService { */ fun withOptions(modifier: Consumer): NearestService.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun geoSpecies(): GeoSpecieService.WithRawResponse } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/NearestServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/NearestServiceImpl.kt index ce3c568..7a33860 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/NearestServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/NearestServiceImpl.kt @@ -21,6 +21,13 @@ class NearestServiceImpl internal constructor(private val clientOptions: ClientO override fun withOptions(modifier: Consumer): NearestService = NearestServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun geoSpecies(): GeoSpecieService = geoSpecies class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : @@ -37,6 +44,13 @@ class NearestServiceImpl internal constructor(private val clientOptions: ClientO clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun geoSpecies(): GeoSpecieService.WithRawResponse = geoSpecies } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/RecentService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/RecentService.kt index 6730ebe..5c71036 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/RecentService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/RecentService.kt @@ -13,6 +13,12 @@ import com.phoebe.api.services.blocking.data.observations.recent.NotableService import com.phoebe.api.services.blocking.data.observations.recent.SpecieService import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface RecentService { /** @@ -27,10 +33,31 @@ interface RecentService { */ fun withOptions(modifier: Consumer): RecentService + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun notable(): NotableService + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun species(): SpecieService + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun historic(): HistoricService /** @@ -76,10 +103,31 @@ interface RecentService { */ fun withOptions(modifier: Consumer): RecentService.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun notable(): NotableService.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun species(): SpecieService.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun historic(): HistoricService.WithRawResponse /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/RecentServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/RecentServiceImpl.kt index e6dd3bc..27417b2 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/RecentServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/RecentServiceImpl.kt @@ -26,6 +26,12 @@ import com.phoebe.api.services.blocking.data.observations.recent.SpecieServiceIm import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class RecentServiceImpl internal constructor(private val clientOptions: ClientOptions) : RecentService { @@ -44,10 +50,31 @@ class RecentServiceImpl internal constructor(private val clientOptions: ClientOp override fun withOptions(modifier: Consumer): RecentService = RecentServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun notable(): NotableService = notable + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun species(): SpecieService = species + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun historic(): HistoricService = historic override fun list(params: RecentListParams, requestOptions: RequestOptions): List = @@ -79,10 +106,31 @@ class RecentServiceImpl internal constructor(private val clientOptions: ClientOp clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun notable(): NotableService.WithRawResponse = notable + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun species(): SpecieService.WithRawResponse = species + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun historic(): HistoricService.WithRawResponse = historic private val listHandler: Handler> = diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/RecentService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/RecentService.kt index 01fdd91..01499e6 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/RecentService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/RecentService.kt @@ -12,6 +12,12 @@ import com.phoebe.api.services.blocking.data.observations.geo.recent.NotableServ import com.phoebe.api.services.blocking.data.observations.geo.recent.SpecieService import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface RecentService { /** @@ -26,8 +32,22 @@ interface RecentService { */ fun withOptions(modifier: Consumer): RecentService + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun species(): SpecieService + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ fun notable(): NotableService /** @@ -53,8 +73,22 @@ interface RecentService { */ fun withOptions(modifier: Consumer): RecentService.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun species(): SpecieService.WithRawResponse + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ fun notable(): NotableService.WithRawResponse /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/RecentServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/RecentServiceImpl.kt index ff0af93..7206e7b 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/RecentServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/RecentServiceImpl.kt @@ -22,6 +22,12 @@ import com.phoebe.api.services.blocking.data.observations.geo.recent.SpecieServi import com.phoebe.api.services.blocking.data.observations.geo.recent.SpecieServiceImpl import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class RecentServiceImpl internal constructor(private val clientOptions: ClientOptions) : RecentService { @@ -38,8 +44,22 @@ class RecentServiceImpl internal constructor(private val clientOptions: ClientOp override fun withOptions(modifier: Consumer): RecentService = RecentServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun species(): SpecieService = species + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, region + * or location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each + * end-point supports optional query parameters which allow you to filter the list of + * observations returned. + */ override fun notable(): NotableService = notable override fun list(params: RecentListParams, requestOptions: RequestOptions): List = @@ -67,8 +87,22 @@ class RecentServiceImpl internal constructor(private val clientOptions: ClientOp clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun species(): SpecieService.WithRawResponse = species + /** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. + * There are two categories of end-point: 1. Fetch observations for a specific country, + * region or location. 2. Fetch observations for nearby locations - up to a distance of + * 50km. Each end-point supports optional query parameters which allow you to filter the + * list of observations returned. + */ override fun notable(): NotableService.WithRawResponse = notable private val listHandler: Handler> = diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/NotableService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/NotableService.kt index ec04886..8b5a510 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/NotableService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/NotableService.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.data.observations.Observation import com.phoebe.api.models.data.observations.geo.recent.notable.NotableListParams import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface NotableService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/NotableServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/NotableServiceImpl.kt index ccd5d57..9dd0a4d 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/NotableServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/NotableServiceImpl.kt @@ -18,6 +18,12 @@ import com.phoebe.api.models.data.observations.Observation import com.phoebe.api.models.data.observations.geo.recent.notable.NotableListParams import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class NotableServiceImpl internal constructor(private val clientOptions: ClientOptions) : NotableService { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/SpecieService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/SpecieService.kt index f8aa22e..759a2c3 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/SpecieService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/SpecieService.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.data.observations.Observation import com.phoebe.api.models.data.observations.geo.recent.species.SpecieListParams import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface SpecieService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/SpecieServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/SpecieServiceImpl.kt index eb2ba29..1461034 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/SpecieServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/geo/recent/SpecieServiceImpl.kt @@ -20,6 +20,12 @@ import com.phoebe.api.models.data.observations.geo.recent.species.SpecieListPara import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class SpecieServiceImpl internal constructor(private val clientOptions: ClientOptions) : SpecieService { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/nearest/GeoSpecieService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/nearest/GeoSpecieService.kt index a1ace2d..e06a9b0 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/nearest/GeoSpecieService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/nearest/GeoSpecieService.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.data.observations.Observation import com.phoebe.api.models.data.observations.nearest.geospecies.GeoSpecieListParams import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface GeoSpecieService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/nearest/GeoSpecieServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/nearest/GeoSpecieServiceImpl.kt index 6c5df31..c8c1567 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/nearest/GeoSpecieServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/nearest/GeoSpecieServiceImpl.kt @@ -20,6 +20,12 @@ import com.phoebe.api.models.data.observations.nearest.geospecies.GeoSpecieListP import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class GeoSpecieServiceImpl internal constructor(private val clientOptions: ClientOptions) : GeoSpecieService { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/HistoricService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/HistoricService.kt index bea6961..a393c7d 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/HistoricService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/HistoricService.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.data.observations.Observation import com.phoebe.api.models.data.observations.recent.historic.HistoricListParams import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface HistoricService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/HistoricServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/HistoricServiceImpl.kt index 2ab0d8d..1827d8f 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/HistoricServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/HistoricServiceImpl.kt @@ -20,6 +20,12 @@ import com.phoebe.api.models.data.observations.recent.historic.HistoricListParam import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class HistoricServiceImpl internal constructor(private val clientOptions: ClientOptions) : HistoricService { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/NotableService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/NotableService.kt index 8ba3c6b..f8d1255 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/NotableService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/NotableService.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.data.observations.Observation import com.phoebe.api.models.data.observations.recent.notable.NotableListParams import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface NotableService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/NotableServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/NotableServiceImpl.kt index 5635eba..5167394 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/NotableServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/NotableServiceImpl.kt @@ -20,6 +20,12 @@ import com.phoebe.api.models.data.observations.recent.notable.NotableListParams import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class NotableServiceImpl internal constructor(private val clientOptions: ClientOptions) : NotableService { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/SpecieService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/SpecieService.kt index 5c62082..af97b53 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/SpecieService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/SpecieService.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.data.observations.Observation import com.phoebe.api.models.data.observations.recent.species.SpecieRetrieveParams import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface SpecieService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/SpecieServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/SpecieServiceImpl.kt index 34e2152..8341abe 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/SpecieServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/data/observations/recent/SpecieServiceImpl.kt @@ -20,6 +20,12 @@ import com.phoebe.api.models.data.observations.recent.species.SpecieRetrievePara import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class SpecieServiceImpl internal constructor(private val clientOptions: ClientOptions) : SpecieService { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ChecklistService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ChecklistService.kt index a988775..93d51eb 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ChecklistService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ChecklistService.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.product.checklist.ChecklistViewParams import com.phoebe.api.models.product.checklist.ChecklistViewResponse import java.util.function.Consumer +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ interface ChecklistService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ChecklistServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ChecklistServiceImpl.kt index 2172651..1c72ff0 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ChecklistServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ChecklistServiceImpl.kt @@ -20,6 +20,12 @@ import com.phoebe.api.models.product.checklist.ChecklistViewResponse import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ class ChecklistServiceImpl internal constructor(private val clientOptions: ClientOptions) : ChecklistService { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ListService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ListService.kt index b18292f..5953c11 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ListService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ListService.kt @@ -11,6 +11,12 @@ import com.phoebe.api.models.product.lists.ListRetrieveResponse import com.phoebe.api.services.blocking.product.lists.HistoricalService import java.util.function.Consumer +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ interface ListService { /** @@ -25,6 +31,12 @@ interface ListService { */ fun withOptions(modifier: Consumer): ListService + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ fun historical(): HistoricalService /** Get information on the most recently submitted checklists for a region. */ @@ -69,6 +81,12 @@ interface ListService { */ fun withOptions(modifier: Consumer): ListService.WithRawResponse + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ fun historical(): HistoricalService.WithRawResponse /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ListServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ListServiceImpl.kt index 9b3f5b8..34e998b 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ListServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/ListServiceImpl.kt @@ -22,6 +22,12 @@ import com.phoebe.api.services.blocking.product.lists.HistoricalServiceImpl import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The data/obs end-points are used to fetch observations submitted to eBird in checklists. There + * are two categories of end-point: 1. Fetch observations for a specific country, region or + * location. 2. Fetch observations for nearby locations - up to a distance of 50km. Each end-point + * supports optional query parameters which allow you to filter the list of observations returned. + */ class ListServiceImpl internal constructor(private val clientOptions: ClientOptions) : ListService { private val withRawResponse: ListService.WithRawResponse by lazy { @@ -35,6 +41,12 @@ class ListServiceImpl internal constructor(private val clientOptions: ClientOpti override fun withOptions(modifier: Consumer): ListService = ListServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a + * given date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted + * on a given date. 5. The details and all the observations of a checklist. + */ override fun historical(): HistoricalService = historical override fun retrieve( @@ -61,6 +73,12 @@ class ListServiceImpl internal constructor(private val clientOptions: ClientOpti clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * The product end-points make it easy to get the information shown in various pages on the + * eBird web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted + * on a given date. 3. The most recent checklists submitted. 4. A summary of the checklists + * submitted on a given date. 5. The details and all the observations of a checklist. + */ override fun historical(): HistoricalService.WithRawResponse = historical private val retrieveHandler: Handler> = diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/SpeciesListService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/SpeciesListService.kt index 932b280..98c4361 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/SpeciesListService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/SpeciesListService.kt @@ -9,6 +9,12 @@ import com.phoebe.api.core.http.HttpResponseFor import com.phoebe.api.models.product.specieslist.SpeciesListListParams import java.util.function.Consumer +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ interface SpeciesListService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/SpeciesListServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/SpeciesListServiceImpl.kt index 8611084..b4a8ab6 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/SpeciesListServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/SpeciesListServiceImpl.kt @@ -19,6 +19,12 @@ import com.phoebe.api.models.product.specieslist.SpeciesListListParams import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ class SpeciesListServiceImpl internal constructor(private val clientOptions: ClientOptions) : SpeciesListService { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/StatService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/StatService.kt index e2e4a90..0dfc96c 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/StatService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/StatService.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.product.stats.StatRetrieveParams import com.phoebe.api.models.product.stats.StatRetrieveResponse import java.util.function.Consumer +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ interface StatService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/StatServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/StatServiceImpl.kt index 956dcce..6019a01 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/StatServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/StatServiceImpl.kt @@ -20,6 +20,12 @@ import com.phoebe.api.models.product.stats.StatRetrieveResponse import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ class StatServiceImpl internal constructor(private val clientOptions: ClientOptions) : StatService { private val withRawResponse: StatService.WithRawResponse by lazy { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/Top100Service.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/Top100Service.kt index 3bd3264..9bf8037 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/Top100Service.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/Top100Service.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.product.top100.Top100RetrieveParams import com.phoebe.api.models.product.top100.Top100RetrieveResponse import java.util.function.Consumer +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ interface Top100Service { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/Top100ServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/Top100ServiceImpl.kt index 82d0609..61b9f65 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/Top100ServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/Top100ServiceImpl.kt @@ -20,6 +20,12 @@ import com.phoebe.api.models.product.top100.Top100RetrieveResponse import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ class Top100ServiceImpl internal constructor(private val clientOptions: ClientOptions) : Top100Service { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/lists/HistoricalService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/lists/HistoricalService.kt index 7112189..63d7444 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/lists/HistoricalService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/lists/HistoricalService.kt @@ -10,6 +10,12 @@ import com.phoebe.api.models.product.lists.historical.HistoricalRetrieveParams import com.phoebe.api.models.product.lists.historical.HistoricalRetrieveResponse import java.util.function.Consumer +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ interface HistoricalService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/lists/HistoricalServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/lists/HistoricalServiceImpl.kt index 5b26b43..b051069 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/lists/HistoricalServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/product/lists/HistoricalServiceImpl.kt @@ -20,6 +20,12 @@ import com.phoebe.api.models.product.lists.historical.HistoricalRetrieveResponse import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * The product end-points make it easy to get the information shown in various pages on the eBird + * web site: 1. The Top 100 contributors on a given date. 2. The checklists submitted on a given + * date. 3. The most recent checklists submitted. 4. A summary of the checklists submitted on a + * given date. 5. The details and all the observations of a checklist. + */ class HistoricalServiceImpl internal constructor(private val clientOptions: ClientOptions) : HistoricalService { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/HotspotService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/HotspotService.kt index 0a68817..9e02aad 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/HotspotService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/HotspotService.kt @@ -12,6 +12,10 @@ import com.phoebe.api.services.blocking.ref.hotspot.GeoService import com.phoebe.api.services.blocking.ref.hotspot.InfoService import java.util.function.Consumer +/** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or nearby + * hotspots + */ interface HotspotService { /** @@ -26,8 +30,16 @@ interface HotspotService { */ fun withOptions(modifier: Consumer): HotspotService + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or + * nearby hotspots + */ fun geo(): GeoService + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or + * nearby hotspots + */ fun info(): InfoService /** Hotspots in a region */ @@ -72,8 +84,16 @@ interface HotspotService { */ fun withOptions(modifier: Consumer): HotspotService.WithRawResponse + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region + * or nearby hotspots + */ fun geo(): GeoService.WithRawResponse + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region + * or nearby hotspots + */ fun info(): InfoService.WithRawResponse /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/HotspotServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/HotspotServiceImpl.kt index 134bc1b..c751131 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/HotspotServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/HotspotServiceImpl.kt @@ -24,6 +24,10 @@ import com.phoebe.api.services.blocking.ref.hotspot.InfoServiceImpl import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or nearby + * hotspots + */ class HotspotServiceImpl internal constructor(private val clientOptions: ClientOptions) : HotspotService { @@ -40,8 +44,16 @@ class HotspotServiceImpl internal constructor(private val clientOptions: ClientO override fun withOptions(modifier: Consumer): HotspotService = HotspotServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or + * nearby hotspots + */ override fun geo(): GeoService = geo + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or + * nearby hotspots + */ override fun info(): InfoService = info override fun list( @@ -72,8 +84,16 @@ class HotspotServiceImpl internal constructor(private val clientOptions: ClientO clientOptions.toBuilder().apply(modifier::accept).build() ) + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region + * or nearby hotspots + */ override fun geo(): GeoService.WithRawResponse = geo + /** + * With the ref/hotspot end-points you can find the hotspots for a given country or region + * or nearby hotspots + */ override fun info(): InfoService.WithRawResponse = info private val listHandler: Handler> = diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/RegionService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/RegionService.kt index 2bdc649..36bfdfc 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/RegionService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/RegionService.kt @@ -22,10 +22,13 @@ interface RegionService { */ fun withOptions(modifier: Consumer): RegionService + /** With the ref/geo end-point you can find a country's or region's neighbours. */ fun adjacent(): AdjacentService + /** The ref/region end-points return information on regions. */ fun info(): InfoService + /** The ref/region end-points return information on regions. */ fun list(): ListService /** A view of [RegionService] that provides access to raw HTTP responses for each method. */ @@ -38,10 +41,13 @@ interface RegionService { */ fun withOptions(modifier: Consumer): RegionService.WithRawResponse + /** With the ref/geo end-point you can find a country's or region's neighbours. */ fun adjacent(): AdjacentService.WithRawResponse + /** The ref/region end-points return information on regions. */ fun info(): InfoService.WithRawResponse + /** The ref/region end-points return information on regions. */ fun list(): ListService.WithRawResponse } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/RegionServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/RegionServiceImpl.kt index 7ab18f2..7a9b448 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/RegionServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/RegionServiceImpl.kt @@ -29,10 +29,13 @@ class RegionServiceImpl internal constructor(private val clientOptions: ClientOp override fun withOptions(modifier: Consumer): RegionService = RegionServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + /** With the ref/geo end-point you can find a country's or region's neighbours. */ override fun adjacent(): AdjacentService = adjacent + /** The ref/region end-points return information on regions. */ override fun info(): InfoService = info + /** The ref/region end-points return information on regions. */ override fun list(): ListService = list class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : @@ -57,10 +60,13 @@ class RegionServiceImpl internal constructor(private val clientOptions: ClientOp clientOptions.toBuilder().apply(modifier::accept).build() ) + /** With the ref/geo end-point you can find a country's or region's neighbours. */ override fun adjacent(): AdjacentService.WithRawResponse = adjacent + /** The ref/region end-points return information on regions. */ override fun info(): InfoService.WithRawResponse = info + /** The ref/region end-points return information on regions. */ override fun list(): ListService.WithRawResponse = list } } diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/GeoService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/GeoService.kt index 9d8f56d..aa97b60 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/GeoService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/GeoService.kt @@ -10,6 +10,10 @@ import com.phoebe.api.models.ref.hotspot.geo.GeoRetrieveParams import com.phoebe.api.models.ref.hotspot.geo.GeoRetrieveResponse import java.util.function.Consumer +/** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or nearby + * hotspots + */ interface GeoService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/GeoServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/GeoServiceImpl.kt index 8781a46..64bcdae 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/GeoServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/GeoServiceImpl.kt @@ -18,6 +18,10 @@ import com.phoebe.api.models.ref.hotspot.geo.GeoRetrieveParams import com.phoebe.api.models.ref.hotspot.geo.GeoRetrieveResponse import java.util.function.Consumer +/** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or nearby + * hotspots + */ class GeoServiceImpl internal constructor(private val clientOptions: ClientOptions) : GeoService { private val withRawResponse: GeoService.WithRawResponse by lazy { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/InfoService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/InfoService.kt index 806217c..362798b 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/InfoService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/InfoService.kt @@ -10,6 +10,10 @@ import com.phoebe.api.models.ref.hotspot.info.InfoRetrieveParams import com.phoebe.api.models.ref.hotspot.info.InfoRetrieveResponse import java.util.function.Consumer +/** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or nearby + * hotspots + */ interface InfoService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/InfoServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/InfoServiceImpl.kt index ca1439f..c8d8059 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/InfoServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/hotspot/InfoServiceImpl.kt @@ -20,6 +20,10 @@ import com.phoebe.api.models.ref.hotspot.info.InfoRetrieveResponse import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** + * With the ref/hotspot end-points you can find the hotspots for a given country or region or nearby + * hotspots + */ class InfoServiceImpl internal constructor(private val clientOptions: ClientOptions) : InfoService { private val withRawResponse: InfoService.WithRawResponse by lazy { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/AdjacentService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/AdjacentService.kt index 16e76a2..cbb0755 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/AdjacentService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/AdjacentService.kt @@ -10,6 +10,7 @@ import com.phoebe.api.models.ref.region.adjacent.AdjacentListParams import com.phoebe.api.models.ref.region.adjacent.AdjacentListResponse import java.util.function.Consumer +/** With the ref/geo end-point you can find a country's or region's neighbours. */ interface AdjacentService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/AdjacentServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/AdjacentServiceImpl.kt index 1bfcdcd..fa18ea2 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/AdjacentServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/AdjacentServiceImpl.kt @@ -20,6 +20,7 @@ import com.phoebe.api.models.ref.region.adjacent.AdjacentListResponse import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** With the ref/geo end-point you can find a country's or region's neighbours. */ class AdjacentServiceImpl internal constructor(private val clientOptions: ClientOptions) : AdjacentService { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/InfoService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/InfoService.kt index e2b2af2..2073cd2 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/InfoService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/InfoService.kt @@ -10,6 +10,7 @@ import com.phoebe.api.models.ref.region.info.InfoRetrieveParams import com.phoebe.api.models.ref.region.info.InfoRetrieveResponse import java.util.function.Consumer +/** The ref/region end-points return information on regions. */ interface InfoService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/InfoServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/InfoServiceImpl.kt index 13a687d..15c13e7 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/InfoServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/InfoServiceImpl.kt @@ -20,6 +20,7 @@ import com.phoebe.api.models.ref.region.info.InfoRetrieveResponse import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** The ref/region end-points return information on regions. */ class InfoServiceImpl internal constructor(private val clientOptions: ClientOptions) : InfoService { private val withRawResponse: InfoService.WithRawResponse by lazy { diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/ListService.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/ListService.kt index 05f61ec..67dcf61 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/ListService.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/ListService.kt @@ -10,6 +10,7 @@ import com.phoebe.api.models.ref.region.list.ListListParams import com.phoebe.api.models.ref.region.list.ListListResponse import java.util.function.Consumer +/** The ref/region end-points return information on regions. */ interface ListService { /** diff --git a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/ListServiceImpl.kt b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/ListServiceImpl.kt index 01ff6b1..d507791 100644 --- a/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/ListServiceImpl.kt +++ b/phoebe-java-core/src/main/kotlin/com/phoebe/api/services/blocking/ref/region/ListServiceImpl.kt @@ -20,6 +20,7 @@ import com.phoebe.api.models.ref.region.list.ListListResponse import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull +/** The ref/region end-points return information on regions. */ class ListServiceImpl internal constructor(private val clientOptions: ClientOptions) : ListService { private val withRawResponse: ListService.WithRawResponse by lazy { diff --git a/phoebe-java-core/src/test/kotlin/com/phoebe/api/TestServerExtension.kt b/phoebe-java-core/src/test/kotlin/com/phoebe/api/TestServerExtension.kt index d533d2d..9a24ecf 100644 --- a/phoebe-java-core/src/test/kotlin/com/phoebe/api/TestServerExtension.kt +++ b/phoebe-java-core/src/test/kotlin/com/phoebe/api/TestServerExtension.kt @@ -15,25 +15,12 @@ class TestServerExtension : BeforeAllCallback, ExecutionCondition { } catch (e: Exception) { throw RuntimeException( """ - The test suite will not run without a mock Prism server running against your OpenAPI spec. + The test suite will not run without a mock server running against your OpenAPI spec. You can set the environment variable `SKIP_MOCK_TESTS` to `true` to skip running any tests that require the mock server. - To fix: - - 1. Install Prism (requires Node 16+): - - With npm: - $ npm install -g @stoplight/prism-cli - - With yarn: - $ yarn global add @stoplight/prism-cli - - 2. Run the mock server - - To run the server, pass in the path of your OpenAPI spec to the prism command: - $ prism mock path/to/your.openapi.yml + To fix run `./scripts/mock` in a separate terminal. """ .trimIndent(), e, diff --git a/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/ClientOptionsTest.kt b/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/ClientOptionsTest.kt index 9c6b5c4..dfb922a 100644 --- a/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/ClientOptionsTest.kt +++ b/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/ClientOptionsTest.kt @@ -16,6 +16,29 @@ internal class ClientOptionsTest { private val httpClient = mock() + @Test + fun putHeader_canOverwriteDefaultHeader() { + val clientOptions = + ClientOptions.builder() + .httpClient(httpClient) + .putHeader("User-Agent", "My User Agent") + .apiKey("My API Key") + .build() + + assertThat(clientOptions.headers.values("User-Agent")).containsExactly("My User Agent") + } + + @Test + fun toBuilder_apiKeyHeaderCanBeUpdated() { + var clientOptions = + ClientOptions.builder().httpClient(httpClient).apiKey("My API Key").build() + + clientOptions = clientOptions.toBuilder().apiKey("another My API Key").build() + + assertThat(clientOptions.headers.values("X-eBirdApiToken")) + .containsExactly("another My API Key") + } + @Test fun toBuilder_whenOriginalClientOptionsGarbageCollected_doesNotCloseOriginalClient() { var clientOptions = diff --git a/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/ObjectMappersTest.kt b/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/ObjectMappersTest.kt index 35ca1ac..115b997 100644 --- a/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/ObjectMappersTest.kt +++ b/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/ObjectMappersTest.kt @@ -3,12 +3,14 @@ package com.phoebe.api.core import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.exc.MismatchedInputException import com.fasterxml.jackson.module.kotlin.readValue -import java.time.LocalDateTime +import java.time.LocalDate +import java.time.LocalTime +import java.time.OffsetDateTime +import java.time.ZoneOffset import kotlin.reflect.KClass import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.catchThrowable import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.EnumSource import org.junitpioneer.jupiter.cartesian.CartesianTest @@ -46,11 +48,7 @@ internal class ObjectMappersTest { val VALID_CONVERSIONS = listOf( FLOAT to DOUBLE, - FLOAT to INTEGER, - FLOAT to LONG, DOUBLE to FLOAT, - DOUBLE to INTEGER, - DOUBLE to LONG, INTEGER to FLOAT, INTEGER to DOUBLE, INTEGER to LONG, @@ -58,14 +56,6 @@ internal class ObjectMappersTest { LONG to DOUBLE, LONG to INTEGER, CLASS to MAP, - // These aren't actually valid, but coercion configs don't work for String until - // v2.14.0: https://github.com/FasterXML/jackson-databind/issues/3240 - // We currently test on v2.13.4. - BOOLEAN to STRING, - FLOAT to STRING, - DOUBLE to STRING, - INTEGER to STRING, - LONG to STRING, ) } } @@ -84,19 +74,44 @@ internal class ObjectMappersTest { } } - enum class LenientLocalDateTimeTestCase(val string: String) { - DATE("1998-04-21"), - DATE_TIME("1998-04-21T04:00:00"), - ZONED_DATE_TIME_1("1998-04-21T04:00:00+03:00"), - ZONED_DATE_TIME_2("1998-04-21T04:00:00Z"), + enum class LenientOffsetDateTimeTestCase( + val string: String, + val expectedOffsetDateTime: OffsetDateTime, + ) { + DATE( + "1998-04-21", + expectedOffsetDateTime = + OffsetDateTime.of(LocalDate.of(1998, 4, 21), LocalTime.of(0, 0), ZoneOffset.UTC), + ), + DATE_TIME( + "1998-04-21T04:00:00", + expectedOffsetDateTime = + OffsetDateTime.of(LocalDate.of(1998, 4, 21), LocalTime.of(4, 0), ZoneOffset.UTC), + ), + ZONED_DATE_TIME_1( + "1998-04-21T04:00:00+03:00", + expectedOffsetDateTime = + OffsetDateTime.of( + LocalDate.of(1998, 4, 21), + LocalTime.of(4, 0), + ZoneOffset.ofHours(3), + ), + ), + ZONED_DATE_TIME_2( + "1998-04-21T04:00:00Z", + expectedOffsetDateTime = + OffsetDateTime.of(LocalDate.of(1998, 4, 21), LocalTime.of(4, 0), ZoneOffset.UTC), + ), } @ParameterizedTest @EnumSource - fun readLocalDateTime_lenient(testCase: LenientLocalDateTimeTestCase) { + fun readOffsetDateTime_lenient(testCase: LenientOffsetDateTimeTestCase) { val jsonMapper = jsonMapper() val json = jsonMapper.writeValueAsString(testCase.string) - assertDoesNotThrow { jsonMapper().readValue(json) } + val offsetDateTime = jsonMapper().readValue(json) + + assertThat(offsetDateTime).isEqualTo(testCase.expectedOffsetDateTime) } } diff --git a/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/http/HttpRequestBodiesTest.kt b/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/http/HttpRequestBodiesTest.kt new file mode 100644 index 0000000..ca3d73d --- /dev/null +++ b/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/http/HttpRequestBodiesTest.kt @@ -0,0 +1,729 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.phoebe.api.core.http + +import com.phoebe.api.core.MultipartField +import com.phoebe.api.core.jsonMapper +import java.io.ByteArrayOutputStream +import java.io.InputStream +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class HttpRequestBodiesTest { + + @Test + fun multipartFormData_serializesFieldWithFilename() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "file" to + MultipartField.builder() + .value("hello") + .filename("hello.txt") + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(output.size().toLong()).isEqualTo(body.contentLength()) + val boundary = body.contentType()!!.substringAfter("multipart/form-data; boundary=") + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="file"; filename="hello.txt" + |Content-Type: text/plain + | + |hello + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesFieldWithoutFilename() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "field" to + MultipartField.builder() + .value("value") + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(output.size().toLong()).isEqualTo(body.contentLength()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="field" + |Content-Type: text/plain + | + |value + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesInputStream() { + // Use `.buffered()` to get a non-ByteArrayInputStream, which hits the non-repeatable code + // path. + val inputStream = "stream content".byteInputStream().buffered() + val body = + multipartFormData( + jsonMapper(), + mapOf( + "data" to + MultipartField.builder() + .value(inputStream) + .contentType("application/octet-stream") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isFalse() + assertThat(body.contentLength()).isEqualTo(-1L) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="data" + |Content-Type: application/octet-stream + | + |stream content + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesByteArray() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "binary" to + MultipartField.builder() + .value("abc".toByteArray()) + .contentType("application/octet-stream") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="binary" + |Content-Type: application/octet-stream + | + |abc + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesBooleanValue() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "flag" to + MultipartField.builder() + .value(true) + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="flag" + |Content-Type: text/plain + | + |true + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesNumberValue() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "count" to + MultipartField.builder().value(42).contentType("text/plain").build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="count" + |Content-Type: text/plain + | + |42 + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesNullValueAsNoParts() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "present" to + MultipartField.builder() + .value("yes") + .contentType("text/plain") + .build(), + "absent" to + MultipartField.builder() + .value(null as String?) + .contentType("text/plain") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="present" + |Content-Type: text/plain + | + |yes + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesArray() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "items" to + MultipartField.builder>() + .value(listOf("alpha", "beta", "gamma")) + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="items" + |Content-Type: text/plain + | + |alpha,beta,gamma + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesObjectAsNestedParts() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "meta" to + MultipartField.builder>() + .value(mapOf("key1" to "val1", "key2" to "val2")) + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="meta[key1]" + |Content-Type: text/plain + | + |val1 + |--$boundary + |Content-Disposition: form-data; name="meta[key2]" + |Content-Type: text/plain + | + |val2 + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesMultipleFields() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "name" to + MultipartField.builder() + .value("Alice") + .contentType("text/plain") + .build(), + "age" to + MultipartField.builder().value(30).contentType("text/plain").build(), + "file" to + MultipartField.builder() + .value("file contents") + .filename("doc.txt") + .contentType("text/plain") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="name" + |Content-Type: text/plain + | + |Alice + |--$boundary + |Content-Disposition: form-data; name="age" + |Content-Type: text/plain + | + |30 + |--$boundary + |Content-Disposition: form-data; name="file"; filename="doc.txt" + |Content-Type: text/plain + | + |file contents + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_quotesSpecialCharactersInNameAndFilename() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "field\nname" to + MultipartField.builder() + .value("value") + .filename("file\r\"name.txt") + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="field%0Aname"; filename="file%0D%22name.txt" + |Content-Type: text/plain + | + |value + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_writeIsRepeatable() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "field" to + MultipartField.builder() + .value("repeatable") + .contentType("text/plain") + .build() + ), + ) + + val output1 = ByteArrayOutputStream() + body.writeTo(output1) + val output2 = ByteArrayOutputStream() + body.writeTo(output2) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output1.size().toLong()) + val boundary = boundary(body) + val expected = + """ + |--$boundary + |Content-Disposition: form-data; name="field" + |Content-Type: text/plain + | + |repeatable + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + assertThat(output1.toString("UTF-8")).isEqualTo(expected) + assertThat(output2.toString("UTF-8")).isEqualTo(expected) + } + + @Test + fun multipartFormData_serializesByteArrayInputStream() { + // ByteArrayInputStream is specifically handled as repeatable with known content length. + val inputStream = "byte array stream".byteInputStream() + val body = + multipartFormData( + jsonMapper(), + mapOf( + "data" to + MultipartField.builder() + .value(inputStream) + .contentType("application/octet-stream") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="data" + |Content-Type: application/octet-stream + | + |byte array stream + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesInputStreamWithFilename() { + // Use `.buffered()` to get a non-ByteArrayInputStream, which hits the non-repeatable code + // path. + val inputStream = "file data".byteInputStream().buffered() + val body = + multipartFormData( + jsonMapper(), + mapOf( + "upload" to + MultipartField.builder() + .value(inputStream) + .filename("upload.bin") + .contentType("application/octet-stream") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isFalse() + assertThat(body.contentLength()).isEqualTo(-1L) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="upload"; filename="upload.bin" + |Content-Type: application/octet-stream + | + |file data + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesNestedArrayInObject() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "data" to + MultipartField.builder>>() + .value(mapOf("tags" to listOf("a", "b"))) + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="data[tags]" + |Content-Type: text/plain + | + |a,b + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_contentLengthIsUnknownWhenInputStreamPresent() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "text" to + MultipartField.builder() + .value("hello") + .contentType("text/plain") + .build(), + "stream" to + MultipartField.builder() + // Use `.buffered()` to get a non-ByteArrayInputStream, which hits the + // non-repeatable code path. + .value("data".byteInputStream().buffered()) + .contentType("application/octet-stream") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isFalse() + assertThat(body.contentLength()).isEqualTo(-1L) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="text" + |Content-Type: text/plain + | + |hello + |--$boundary + |Content-Disposition: form-data; name="stream" + |Content-Type: application/octet-stream + | + |data + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesEmptyArray() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "required" to + MultipartField.builder() + .value("present") + .contentType("text/plain") + .build(), + "items" to + MultipartField.builder>() + .value(emptyList()) + .contentType("text/plain") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="required" + |Content-Type: text/plain + | + |present + |--$boundary + |Content-Disposition: form-data; name="items" + |Content-Type: text/plain + | + | + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesEmptyObject() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "required" to + MultipartField.builder() + .value("present") + .contentType("text/plain") + .build(), + "meta" to + MultipartField.builder>() + .value(emptyMap()) + .contentType("text/plain") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="required" + |Content-Type: text/plain + | + |present + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + private fun boundary(body: HttpRequestBody): String = + body.contentType()!!.substringAfter("multipart/form-data; boundary=") +} diff --git a/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/http/HttpRequestTest.kt b/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/http/HttpRequestTest.kt new file mode 100644 index 0000000..063a971 --- /dev/null +++ b/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/http/HttpRequestTest.kt @@ -0,0 +1,110 @@ +package com.phoebe.api.core.http + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class HttpRequestTest { + + enum class UrlTestCase(val request: HttpRequest, val expectedUrl: String) { + BASE_URL_ONLY( + HttpRequest.builder().method(HttpMethod.GET).baseUrl("https://api.example.com").build(), + expectedUrl = "https://api.example.com", + ), + BASE_URL_WITH_TRAILING_SLASH( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com/") + .build(), + expectedUrl = "https://api.example.com/", + ), + SINGLE_PATH_SEGMENT( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("users") + .build(), + expectedUrl = "https://api.example.com/users", + ), + MULTIPLE_PATH_SEGMENTS( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegments("users", "123", "profile") + .build(), + expectedUrl = "https://api.example.com/users/123/profile", + ), + PATH_SEGMENT_WITH_SPECIAL_CHARS( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("user name") + .build(), + expectedUrl = "https://api.example.com/user+name", + ), + SINGLE_QUERY_PARAM( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("users") + .putQueryParam("limit", "10") + .build(), + expectedUrl = "https://api.example.com/users?limit=10", + ), + MULTIPLE_QUERY_PARAMS( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("users") + .putQueryParam("limit", "10") + .putQueryParam("offset", "20") + .build(), + expectedUrl = "https://api.example.com/users?limit=10&offset=20", + ), + QUERY_PARAM_WITH_SPECIAL_CHARS( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("search") + .putQueryParam("q", "hello world") + .build(), + expectedUrl = "https://api.example.com/search?q=hello+world", + ), + MULTIPLE_VALUES_SAME_PARAM( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("users") + .putQueryParams("tags", listOf("admin", "user")) + .build(), + expectedUrl = "https://api.example.com/users?tags=admin&tags=user", + ), + BASE_URL_WITH_TRAILING_SLASH_AND_PATH( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com/") + .addPathSegment("users") + .build(), + expectedUrl = "https://api.example.com/users", + ), + COMPLEX_URL( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl("https://api.example.com") + .addPathSegments("v1", "users", "123") + .putQueryParams("include", listOf("profile", "settings")) + .putQueryParam("format", "json") + .build(), + expectedUrl = + "https://api.example.com/v1/users/123?include=profile&include=settings&format=json", + ), + } + + @ParameterizedTest + @EnumSource + fun url(testCase: UrlTestCase) { + val actualUrl = testCase.request.url() + + assertThat(actualUrl).isEqualTo(testCase.expectedUrl) + } +} diff --git a/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/http/RetryingHttpClientTest.kt b/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/http/RetryingHttpClientTest.kt index 7825059..06ad6d8 100644 --- a/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/http/RetryingHttpClientTest.kt +++ b/phoebe-java-core/src/test/kotlin/com/phoebe/api/core/http/RetryingHttpClientTest.kt @@ -1,6 +1,17 @@ +// File generated from our OpenAPI spec by Stainless. + package com.phoebe.api.core.http -import com.github.tomakehurst.wiremock.client.WireMock.* +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.ok +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.resetAllScenarios +import com.github.tomakehurst.wiremock.client.WireMock.serviceUnavailable +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.github.tomakehurst.wiremock.client.WireMock.verify import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo import com.github.tomakehurst.wiremock.junit5.WireMockTest import com.github.tomakehurst.wiremock.stubbing.Scenario @@ -9,7 +20,11 @@ import com.phoebe.api.core.RequestOptions import com.phoebe.api.core.Sleeper import com.phoebe.api.errors.PhoebeRetryableException import java.io.InputStream +import java.time.Clock import java.time.Duration +import java.time.OffsetDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter import java.util.concurrent.CompletableFuture import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach @@ -25,6 +40,21 @@ internal class RetryingHttpClientTest { private lateinit var baseUrl: String private lateinit var httpClient: HttpClient + private class RecordingSleeper : Sleeper { + val durations = mutableListOf() + + override fun sleep(duration: Duration) { + durations.add(duration) + } + + override fun sleepAsync(duration: Duration): CompletableFuture { + durations.add(duration) + return CompletableFuture.completedFuture(null) + } + + override fun close() {} + } + @BeforeEach fun beforeEach(wmRuntimeInfo: WireMockRuntimeInfo) { baseUrl = wmRuntimeInfo.httpBaseUrl @@ -75,7 +105,8 @@ internal class RetryingHttpClientTest { @ValueSource(booleans = [false, true]) fun execute(async: Boolean) { stubFor(post(urlPathEqualTo("/something")).willReturn(ok())) - val retryingClient = retryingHttpClientBuilder().build() + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper).build() val response = retryingClient.execute( @@ -89,6 +120,7 @@ internal class RetryingHttpClientTest { assertThat(response.statusCode()).isEqualTo(200) verify(1, postRequestedFor(urlPathEqualTo("/something"))) + assertThat(sleeper.durations).isEmpty() assertNoResponseLeaks() } @@ -100,8 +132,12 @@ internal class RetryingHttpClientTest { .withHeader("X-Some-Header", matching("stainless-java-retry-.+")) .willReturn(ok()) ) + val sleeper = RecordingSleeper() val retryingClient = - retryingHttpClientBuilder().maxRetries(2).idempotencyHeader("X-Some-Header").build() + retryingHttpClientBuilder(sleeper) + .maxRetries(2) + .idempotencyHeader("X-Some-Header") + .build() val response = retryingClient.execute( @@ -115,20 +151,20 @@ internal class RetryingHttpClientTest { assertThat(response.statusCode()).isEqualTo(200) verify(1, postRequestedFor(urlPathEqualTo("/something"))) + assertThat(sleeper.durations).isEmpty() assertNoResponseLeaks() } @ParameterizedTest @ValueSource(booleans = [false, true]) fun execute_withRetryAfterHeader(async: Boolean) { + val retryAfterDate = "Wed, 21 Oct 2015 07:28:00 GMT" stubFor( post(urlPathEqualTo("/something")) // First we fail with a retry after header given as a date .inScenario("foo") .whenScenarioStateIs(Scenario.STARTED) - .willReturn( - serviceUnavailable().withHeader("Retry-After", "Wed, 21 Oct 2015 07:28:00 GMT") - ) + .willReturn(serviceUnavailable().withHeader("Retry-After", retryAfterDate)) .willSetStateTo("RETRY_AFTER_DATE") ) stubFor( @@ -147,7 +183,13 @@ internal class RetryingHttpClientTest { .willReturn(ok()) .willSetStateTo("COMPLETED") ) - val retryingClient = retryingHttpClientBuilder().maxRetries(2).build() + // Fix the clock to 5 seconds before the Retry-After date so the date-based backoff is + // deterministic. + val retryAfterDateTime = + OffsetDateTime.parse(retryAfterDate, DateTimeFormatter.RFC_1123_DATE_TIME) + val clock = Clock.fixed(retryAfterDateTime.minusSeconds(5).toInstant(), ZoneOffset.UTC) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper, clock).maxRetries(2).build() val response = retryingClient.execute( @@ -175,19 +217,20 @@ internal class RetryingHttpClientTest { postRequestedFor(urlPathEqualTo("/something")) .withHeader("x-stainless-retry-count", equalTo("2")), ) + assertThat(sleeper.durations) + .containsExactly(Duration.ofSeconds(5), Duration.ofMillis(1234)) assertNoResponseLeaks() } @ParameterizedTest @ValueSource(booleans = [false, true]) fun execute_withOverwrittenRetryCountHeader(async: Boolean) { + val retryAfterDate = "Wed, 21 Oct 2015 07:28:00 GMT" stubFor( post(urlPathEqualTo("/something")) .inScenario("foo") // first we fail with a retry after header given as a date .whenScenarioStateIs(Scenario.STARTED) - .willReturn( - serviceUnavailable().withHeader("Retry-After", "Wed, 21 Oct 2015 07:28:00 GMT") - ) + .willReturn(serviceUnavailable().withHeader("Retry-After", retryAfterDate)) .willSetStateTo("RETRY_AFTER_DATE") ) stubFor( @@ -197,7 +240,11 @@ internal class RetryingHttpClientTest { .willReturn(ok()) .willSetStateTo("COMPLETED") ) - val retryingClient = retryingHttpClientBuilder().maxRetries(2).build() + val retryAfterDateTime = + OffsetDateTime.parse(retryAfterDate, DateTimeFormatter.RFC_1123_DATE_TIME) + val clock = Clock.fixed(retryAfterDateTime.minusSeconds(5).toInstant(), ZoneOffset.UTC) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper, clock).maxRetries(2).build() val response = retryingClient.execute( @@ -216,6 +263,7 @@ internal class RetryingHttpClientTest { postRequestedFor(urlPathEqualTo("/something")) .withHeader("x-stainless-retry-count", equalTo("42")), ) + assertThat(sleeper.durations).containsExactly(Duration.ofSeconds(5)) assertNoResponseLeaks() } @@ -236,7 +284,8 @@ internal class RetryingHttpClientTest { .willReturn(ok()) .willSetStateTo("COMPLETED") ) - val retryingClient = retryingHttpClientBuilder().maxRetries(1).build() + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper).maxRetries(1).build() val response = retryingClient.execute( @@ -250,6 +299,7 @@ internal class RetryingHttpClientTest { assertThat(response.statusCode()).isEqualTo(200) verify(2, postRequestedFor(urlPathEqualTo("/something"))) + assertThat(sleeper.durations).containsExactly(Duration.ofMillis(10)) assertNoResponseLeaks() } @@ -290,21 +340,12 @@ internal class RetryingHttpClientTest { override fun close() = httpClient.close() } + val sleeper = RecordingSleeper() val retryingClient = RetryingHttpClient.builder() .httpClient(failingHttpClient) .maxRetries(2) - .sleeper( - object : Sleeper { - - override fun sleep(duration: Duration) {} - - override fun sleepAsync(duration: Duration): CompletableFuture = - CompletableFuture.completedFuture(null) - - override fun close() {} - } - ) + .sleeper(sleeper) .build() val response = @@ -328,25 +369,153 @@ internal class RetryingHttpClientTest { postRequestedFor(urlPathEqualTo("/something")) .withHeader("x-stainless-retry-count", equalTo("0")), ) + // Exponential backoff with jitter: 0.5s * jitter where jitter is in [0.75, 1.0]. + assertThat(sleeper.durations).hasSize(1) + assertThat(sleeper.durations[0]).isBetween(Duration.ofMillis(375), Duration.ofMillis(500)) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withExponentialBackoff(async: Boolean) { + stubFor(post(urlPathEqualTo("/something")).willReturn(serviceUnavailable())) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper).maxRetries(3).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + // All retries exhausted; the last 503 response is returned. + assertThat(response.statusCode()).isEqualTo(503) + verify(4, postRequestedFor(urlPathEqualTo("/something"))) + // Exponential backoff with jitter: backoff = min(0.5 * 2^(retries-1), 8) * jitter where + // jitter is in [0.75, 1.0]. + assertThat(sleeper.durations).hasSize(3) + // retries=1: 0.5s * [0.75, 1.0] + assertThat(sleeper.durations[0]).isBetween(Duration.ofMillis(375), Duration.ofMillis(500)) + // retries=2: 1s * [0.75, 1.0] + assertThat(sleeper.durations[1]).isBetween(Duration.ofMillis(750), Duration.ofMillis(1000)) + // retries=3: 2s * [0.75, 1.0] + assertThat(sleeper.durations[2]).isBetween(Duration.ofMillis(1500), Duration.ofMillis(2000)) assertNoResponseLeaks() } - private fun retryingHttpClientBuilder() = - RetryingHttpClient.builder() - .httpClient(httpClient) - // Use a no-op `Sleeper` to make the test fast. - .sleeper( - object : Sleeper { + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withExponentialBackoffCap(async: Boolean) { + stubFor(post(urlPathEqualTo("/something")).willReturn(serviceUnavailable())) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper).maxRetries(6).build() - override fun sleep(duration: Duration) {} + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) - override fun sleepAsync(duration: Duration): CompletableFuture = - CompletableFuture.completedFuture(null) + assertThat(response.statusCode()).isEqualTo(503) + verify(7, postRequestedFor(urlPathEqualTo("/something"))) + assertThat(sleeper.durations).hasSize(6) + // retries=5: backoff hits the 8s cap * [0.75, 1.0] + assertThat(sleeper.durations[4]).isBetween(Duration.ofMillis(6000), Duration.ofMillis(8000)) + // retries=6: still capped at 8s * [0.75, 1.0] + assertThat(sleeper.durations[5]).isBetween(Duration.ofMillis(6000), Duration.ofMillis(8000)) + assertNoResponseLeaks() + } - override fun close() {} - } + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withRetryAfterMsPriorityOverRetryAfter(async: Boolean) { + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") + .whenScenarioStateIs(Scenario.STARTED) + .willReturn( + serviceUnavailable() + .withHeader("Retry-After-Ms", "50") + .withHeader("Retry-After", "2") + ) + .willSetStateTo("RETRY") + ) + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") + .whenScenarioStateIs("RETRY") + .willReturn(ok()) + .willSetStateTo("COMPLETED") + ) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper).maxRetries(1).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + // Retry-After-Ms (50ms) takes priority over Retry-After (2s). + assertThat(sleeper.durations).containsExactly(Duration.ofMillis(50)) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withRetryAfterUnparseable(async: Boolean) { + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") + .whenScenarioStateIs(Scenario.STARTED) + .willReturn(serviceUnavailable().withHeader("Retry-After", "not-a-date-or-number")) + .willSetStateTo("RETRY") + ) + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") + .whenScenarioStateIs("RETRY") + .willReturn(ok()) + .willSetStateTo("COMPLETED") + ) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper).maxRetries(1).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, ) + assertThat(response.statusCode()).isEqualTo(200) + // Unparseable Retry-After falls through to exponential backoff. + assertThat(sleeper.durations).hasSize(1) + assertThat(sleeper.durations[0]).isBetween(Duration.ofMillis(375), Duration.ofMillis(500)) + assertNoResponseLeaks() + } + + private fun retryingHttpClientBuilder( + sleeper: RecordingSleeper, + clock: Clock = Clock.systemUTC(), + ) = RetryingHttpClient.builder().httpClient(httpClient).sleeper(sleeper).clock(clock) + private fun HttpClient.execute(request: HttpRequest, async: Boolean): HttpResponse = if (async) executeAsync(request).get() else execute(request) diff --git a/phoebe-java-proguard-test/build.gradle.kts b/phoebe-java-proguard-test/build.gradle.kts index 932d96a..248bccd 100644 --- a/phoebe-java-proguard-test/build.gradle.kts +++ b/phoebe-java-proguard-test/build.gradle.kts @@ -18,8 +18,8 @@ dependencies { testImplementation(project(":phoebe-java")) testImplementation(kotlin("test")) testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3") - testImplementation("org.assertj:assertj-core:3.25.3") - testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.4") + testImplementation("org.assertj:assertj-core:3.27.7") + testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.0") } tasks.shadowJar { diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..8f98719 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,67 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "simple", + "extra-files": [ + "README.md", + "build.gradle.kts" + ] +} \ No newline at end of file diff --git a/scripts/build b/scripts/build index f406348..16a2b00 100755 --- a/scripts/build +++ b/scripts/build @@ -5,4 +5,4 @@ set -e cd "$(dirname "$0")/.." echo "==> Building classes" -./gradlew build testClasses -x test +./gradlew build testClasses "$@" -x test diff --git a/scripts/fast-format b/scripts/fast-format index 1b3bc47..35a1dee 100755 --- a/scripts/fast-format +++ b/scripts/fast-format @@ -24,8 +24,8 @@ if [ ! -f "$FILE_LIST" ]; then exit 1 fi -if ! command -v ktfmt-fast-format &> /dev/null; then - echo "Error: ktfmt-fast-format not found" +if ! command -v ktfmt &> /dev/null; then + echo "Error: ktfmt not found" exit 1 fi @@ -36,7 +36,7 @@ echo "==> Done looking for Kotlin files" if [[ -n "$kt_files" ]]; then echo "==> will format Kotlin files" - echo "$kt_files" | tr '\n' '\0' | xargs -0 ktfmt-fast-format --kotlinlang-style "$@" + echo "$kt_files" | tr '\n' '\0' | xargs -0 ktfmt --kotlinlang-style "$@" else echo "No Kotlin files to format -- expected outcome during incremental formatting" fi diff --git a/scripts/mock b/scripts/mock index 0b28f6e..5cd7c15 100755 --- a/scripts/mock +++ b/scripts/mock @@ -19,23 +19,34 @@ fi echo "==> Starting mock server with URL ${URL}" -# Run prism mock on the given spec +# Run steady mock on the given spec if [ "$1" == "--daemon" ]; then - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + # Pre-install the package so the download doesn't eat into the startup timeout + npm exec --package=@stdy/cli@0.20.2 -- steady --version - # Wait for server to come online + npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + + # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" - while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + attempts=0 + while ! curl --silent --fail "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1; do + if ! kill -0 $! 2>/dev/null; then + echo + cat .stdy.log + exit 1 + fi + attempts=$((attempts + 1)) + if [ "$attempts" -ge 300 ]; then + echo + echo "Timed out waiting for Steady server to start" + cat .stdy.log + exit 1 + fi echo -n "." sleep 0.1 done - if grep -q "✖ fatal" ".prism.log"; then - cat .prism.log - exit 1 - fi - echo else - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" + npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 047bc1d..61c1163 100755 --- a/scripts/test +++ b/scripts/test @@ -9,8 +9,8 @@ GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color -function prism_is_running() { - curl --silent "http://localhost:4010" >/dev/null 2>&1 +function steady_is_running() { + curl --silent "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1 } kill_server_on_port() { @@ -25,7 +25,7 @@ function is_overriding_api_base_url() { [ -n "$TEST_API_BASE_URL" ] } -if ! is_overriding_api_base_url && ! prism_is_running ; then +if ! is_overriding_api_base_url && ! steady_is_running ; then # When we exit this script, make sure to kill the background mock server process trap 'kill_server_on_port 4010' EXIT @@ -36,19 +36,19 @@ fi if is_overriding_api_base_url ; then echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" echo -elif ! prism_is_running ; then - echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" +elif ! steady_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Steady server" echo -e "running against your OpenAPI spec." echo echo -e "To run the server, pass in the path or url of your OpenAPI" - echo -e "spec to the prism command:" + echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 else - echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo -e "${GREEN}✔ Mock steady server is running with your OpenAPI spec${NC}" echo fi diff --git a/scripts/upload-artifacts b/scripts/upload-artifacts new file mode 100755 index 0000000..10f3c70 --- /dev/null +++ b/scripts/upload-artifacts @@ -0,0 +1,193 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# ANSI Color Codes +GREEN='\033[32m' +RED='\033[31m' +NC='\033[0m' # No Color + +MAVEN_REPO_PATH="./build/local-maven-repo" + +log_error() { + local msg="$1" + local headers="$2" + local body="$3" + echo -e "${RED}${msg}${NC}" + [[ -f "$headers" ]] && echo -e "${RED}Headers:$(cat "$headers")${NC}" + echo -e "${RED}Body: ${body}${NC}" + exit 1 +} + +upload_file() { + local file_name="$1" + local tmp_headers + tmp_headers=$(mktemp) + + if [ -f "$file_name" ]; then + echo -e "${GREEN}Processing file: $file_name${NC}" + pkg_file_name="mvn${file_name#"${MAVEN_REPO_PATH}"}" + + # Get signed URL for uploading artifact file + signed_url_response=$(curl -X POST -G "$URL" \ + -sS --retry 5 \ + -D "$tmp_headers" \ + --data-urlencode "filename=$pkg_file_name" \ + -H "Authorization: Bearer $AUTH" \ + -H "Content-Type: application/json") + + # Validate JSON and extract URL + if ! signed_url=$(echo "$signed_url_response" | jq -e -r '.url' 2>/dev/null) || [[ "$signed_url" == "null" ]]; then + log_error "Failed to get valid signed URL" "$tmp_headers" "$signed_url_response" + fi + + # Set content-type based on file extension + local extension="${file_name##*.}" + local content_type + case "$extension" in + jar) content_type="application/java-archive" ;; + md5|sha1|sha256|sha512) content_type="text/plain" ;; + module) content_type="application/json" ;; + pom|xml) content_type="application/xml" ;; + html) content_type="text/html" ;; + *) content_type="application/octet-stream" ;; + esac + + # Upload file + upload_response=$(curl -v -X PUT \ + --retry 5 \ + --retry-all-errors \ + -D "$tmp_headers" \ + -H "Content-Type: $content_type" \ + --data-binary "@${file_name}" "$signed_url" 2>&1) + + if ! echo "$upload_response" | grep -q "HTTP/[0-9.]* 200"; then + log_error "Failed to upload artifact file" "$tmp_headers" "$upload_response" + fi + + # Insert small throttle to reduce rate limiting risk + sleep 0.1 + fi +} + +walk_tree() { + local current_dir="$1" + + for entry in "$current_dir"/*; do + # Check that entry is valid + [ -e "$entry" ] || [ -h "$entry" ] || continue + + if [ -d "$entry" ]; then + walk_tree "$entry" + else + upload_file "$entry" + fi + done +} + +generate_instructions() { + cat << EOF > "$MAVEN_REPO_PATH/index.html" + + + + Maven Repo + + +

Stainless SDK Maven Repository

+

This is the Maven repository for your Stainless Java SDK build.

+ +

Project configuration

+ +

The details depend on whether you're using Maven or Gradle as your build tool.

+ +

Maven

+ +

Add the following to your project's pom.xml:

+
<repositories>
+    <repository>
+        <id>stainless-sdk-repo</id>
+        <url>https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn</url>
+    </repository>
+</repositories>
+ +

Gradle

+

Add the following to your build.gradle file:

+
repositories {
+    maven {
+        url "https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn"
+    }
+}
+ +
+

Configuring authentication (if required)

+ +

Some accounts may require authentication to access the repository. If so, use the + following instructions, replacing YOUR_STAINLESS_API_TOKEN with your actual token.

+ +

Maven with authentication

+ +

First, ensure you have the following in your Maven settings.xml for repo authentication:

+
<servers>
+    <server>
+        <id>stainless-sdk-repo</id>
+        <configuration>
+            <httpHeaders>
+                <property>
+                    <name>Authorization</name>
+                    <value>Bearer YOUR_STAINLESS_API_TOKEN</value>
+                </property>
+            </httpHeaders>
+        </configuration>
+    </server>
+</servers>
+ +

Then, add the following to your project's pom.xml:

+
<repositories>
+    <repository>
+        <id>stainless-sdk-repo</id>
+        <url>https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn</url>
+    </repository>
+</repositories>
+ +

Gradle with authentication

+

Add the following to your build.gradle file:

+
repositories {
+    maven {
+        url "https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn"
+        credentials(HttpHeaderCredentials) {
+            name = "Authorization"
+            value = "Bearer YOUR_STAINLESS_API_TOKEN"
+        }
+        authentication {
+            header(HttpHeaderAuthentication)
+        }
+    }
+}
+
+ +

Using the repository

+

Once you've configured the repository, you can include dependencies from it as usual. See your + project README + for more details.

+ + +EOF + upload_file "${MAVEN_REPO_PATH}/index.html" + + echo "Configure maven or gradle to use the repo located at 'https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn'" + echo "For more details, see the directions in https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn/index.html" +} + +cd "$(dirname "$0")/.." + +echo "::group::Creating local Maven content" +./gradlew publishMavenPublicationToLocalFileSystemRepository -PpublishLocal +echo "::endgroup::" + +echo "::group::Uploading to pkg.stainless.com" +walk_tree "$MAVEN_REPO_PATH" +echo "::endgroup::" + +echo "::group::Generating instructions" +generate_instructions +echo "::endgroup::"