diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f93c384..6d7583d 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/open-transit-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@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Java
- uses: actions/setup-java@v5
+ uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: temurin
java-version: |
@@ -32,7 +34,7 @@ jobs:
cache: gradle
- name: Set up Gradle
- uses: gradle/actions/setup-gradle@v4
+ uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
- name: Run lints
run: ./scripts/lint
@@ -44,13 +46,13 @@ jobs:
contents: read
id-token: write
runs-on: ${{ github.repository == 'stainless-sdks/open-transit-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@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Java
- uses: actions/setup-java@v5
+ uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: temurin
java-version: |
@@ -59,7 +61,7 @@ jobs:
cache: gradle
- name: Set up Gradle
- uses: gradle/actions/setup-gradle@v4
+ uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
- name: Build SDK
run: ./scripts/build
@@ -69,7 +71,7 @@ jobs:
github.repository == 'stainless-sdks/open-transit-java' &&
!startsWith(github.ref, 'refs/heads/stl/')
id: github-oidc
- uses: actions/github-script@v8
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: core.setOutput('github_token', await core.getIDToken());
@@ -89,10 +91,10 @@ jobs:
runs-on: ${{ github.repository == 'stainless-sdks/open-transit-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Java
- uses: actions/setup-java@v5
+ uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: temurin
java-version: |
@@ -101,7 +103,7 @@ jobs:
cache: gradle
- name: Set up Gradle
- uses: gradle/gradle-build-action@v2
+ uses: gradle/gradle-build-action@a8f75513eafdebd8141bd1cd4e30fcd194af8dfa # v2.12.0
- name: Run tests
run: ./scripts/test
diff --git a/.github/workflows/publish-sonatype.yml b/.github/workflows/publish-sonatype.yml
index 7da89b6..9ee4085 100644
--- a/.github/workflows/publish-sonatype.yml
+++ b/.github/workflows/publish-sonatype.yml
@@ -14,10 +14,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Java
- uses: actions/setup-java@v5
+ uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: temurin
java-version: |
@@ -26,7 +26,7 @@ jobs:
cache: gradle
- name: Set up Gradle
- uses: gradle/gradle-build-action@v2
+ uses: gradle/gradle-build-action@a8f75513eafdebd8141bd1cd4e30fcd194af8dfa # v2.12.0
- name: Publish to Sonatype
run: |-
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
index d7ecefc..3b12bc9 100644
--- a/.github/workflows/release-doctor.yml
+++ b/.github/workflows/release-doctor.yml
@@ -12,7 +12,7 @@ jobs:
if: github.repository == 'OneBusAway/java-sdk' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next')
steps:
- - uses: actions/checkout@v6
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Check release environment
run: |
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
index 0815771..f406ff1 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.1.0-alpha.56"
+ ".": "0.1.0-alpha.57"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index ad9cf70..a3a2c77 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 29
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/open-transit%2Fopen-transit-4fcbe9547537b22a2d68329e1d94e0c1a6f81b5af734ca213f7b95eef5da7adb.yml
-openapi_spec_hash: 417ea17b08e186b15b2986372592185e
-config_hash: 3871f5d21bb38ddd334ec04721dea64d
+configured_endpoints: 30
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/open-transit/open-transit-49611a380a238e29ad714c2f6d66a35ada42e3931d2aad2839afd1f13b585de1.yml
+openapi_spec_hash: b03acca245aef78353d3b7a6a4a62eb2
+config_hash: ff7ff57d4c7f9c3f7a4f9bae39aa00e3
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 24c0ddd..bf52abd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,64 @@
# Changelog
+## 0.1.0-alpha.57 (2026-06-08)
+
+Full Changelog: [v0.1.0-alpha.56...v0.1.0-alpha.57](https://github.com/OneBusAway/java-sdk/compare/v0.1.0-alpha.56...v0.1.0-alpha.57)
+
+### Features
+
+* **api:** api update ([97d101f](https://github.com/OneBusAway/java-sdk/commit/97d101f05496b1456a689edc3405c8ec1daf4087))
+* **api:** api update ([437baa4](https://github.com/OneBusAway/java-sdk/commit/437baa4d600d5504c7e269a4b6f41381810f6027))
+* **api:** api update ([6b55911](https://github.com/OneBusAway/java-sdk/commit/6b55911920235024bc07b4bdfd44768a70df1d48))
+* **api:** api update ([fbaadd8](https://github.com/OneBusAway/java-sdk/commit/fbaadd8e2395cc74e3748f486fb17513a211bc1b))
+* **api:** api update ([0d4ee8e](https://github.com/OneBusAway/java-sdk/commit/0d4ee8e11408ed9a067d3a45038d1bfe45ad58a3))
+* **api:** api update ([1e5faa0](https://github.com/OneBusAway/java-sdk/commit/1e5faa0e778189bb3ffc214ac370b0f8c5085824))
+* **api:** api update ([fff5ec2](https://github.com/OneBusAway/java-sdk/commit/fff5ec2ab5207e5e378a1cf59e79922f1f8ade21))
+* **client:** improve logging ([80d4a74](https://github.com/OneBusAway/java-sdk/commit/80d4a74241fa268e03504f2fc565df666da77400))
+* **client:** more robust error parsing ([8c92200](https://github.com/OneBusAway/java-sdk/commit/8c92200d5138d6ff8acad77742994445992d0c0a))
+* **client:** support proxy authentication ([baeb731](https://github.com/OneBusAway/java-sdk/commit/baeb731d323acdc19d58ec4a0134107203f45e18))
+* support setting headers via env ([58e75c3](https://github.com/OneBusAway/java-sdk/commit/58e75c3de82e535d8c8a1a69922edc71f231d149))
+
+
+### Bug Fixes
+
+* **client:** allow updating header/query affecting fields in `toBuilder()` ([3964864](https://github.com/OneBusAway/java-sdk/commit/3964864f1cb0ebea7318a6e0fd68ea12e6705b24))
+* **client:** incorrect `Retry-After` parsing ([ee33acf](https://github.com/OneBusAway/java-sdk/commit/ee33acffec6143677ceae8a1e155c31736c0e4e0))
+
+
+### Performance Improvements
+
+* **client:** create one json mapper ([8157d7e](https://github.com/OneBusAway/java-sdk/commit/8157d7ecfa89ee3ccb179a57c2b8cd0665a1f5d1))
+
+
+### Chores
+
+* **ci:** skip lint on metadata-only changes ([e23b045](https://github.com/OneBusAway/java-sdk/commit/e23b04511c261dd1b68e84e7302361cae57f8a0c))
+* **internal:** bump ktfmt ([ffd9a1b](https://github.com/OneBusAway/java-sdk/commit/ffd9a1b7d321a1a0d89f5d00184ba94341db23aa))
+* **internal:** codegen related update ([1cad394](https://github.com/OneBusAway/java-sdk/commit/1cad394444409bd3e6264f2de9d345bf9ff4ac4d))
+* **internal:** tweak CI branches ([165398b](https://github.com/OneBusAway/java-sdk/commit/165398b0c6f820ec29093b1122837a990b2ae587))
+* **internal:** update gitignore ([2976e04](https://github.com/OneBusAway/java-sdk/commit/2976e04d3381ba91ed619eac4cdb9a001fbf46e8))
+* **internal:** update multipart form array serialization ([7208a3c](https://github.com/OneBusAway/java-sdk/commit/7208a3c4f47fc1070808d19e78f3d86c6daa9307))
+* **internal:** update retry delay tests ([56092cf](https://github.com/OneBusAway/java-sdk/commit/56092cf073f68b0631750dda62089acb0496affc))
+* redact api-key headers in debug logs ([051e173](https://github.com/OneBusAway/java-sdk/commit/051e173193c87cabce6486b1d419e1a5c230c444))
+* remove duplicated dokka setup ([88e7490](https://github.com/OneBusAway/java-sdk/commit/88e7490658f71bbe6ae94b8727746b5f34722244))
+* **tests:** bump steady to v0.19.4 ([633a61a](https://github.com/OneBusAway/java-sdk/commit/633a61ab965ff86afd6466106c9c1325ace48187))
+* **tests:** bump steady to v0.19.5 ([22f82da](https://github.com/OneBusAway/java-sdk/commit/22f82dae778becca20f549b8410a7c1d2c954655))
+* **tests:** bump steady to v0.19.6 ([e334755](https://github.com/OneBusAway/java-sdk/commit/e334755a99bdf8f096d8247ffd40e416471db36b))
+* **tests:** bump steady to v0.19.7 ([ef0587c](https://github.com/OneBusAway/java-sdk/commit/ef0587c47711d53a97a7982fbf352de7c3f17333))
+* **tests:** bump steady to v0.20.1 ([f74e874](https://github.com/OneBusAway/java-sdk/commit/f74e874bd26003286fbf031c340bf1ae25e1de63))
+* **tests:** bump steady to v0.20.2 ([7e86c45](https://github.com/OneBusAway/java-sdk/commit/7e86c4531e42593b1f50806efd88afaa2f46bdd1))
+* **tests:** bump steady to v0.22.1 ([344888d](https://github.com/OneBusAway/java-sdk/commit/344888d82e5616e2f6e6400c270b8e9e89072bbf))
+
+
+### Documentation
+
+* clarify forwards compat behavior ([ce30a3c](https://github.com/OneBusAway/java-sdk/commit/ce30a3c1bdf6f81bcb3af3cb10470a68b9065b9b))
+
+
+### Refactors
+
+* **tests:** switch from prism to steady ([77efbfa](https://github.com/OneBusAway/java-sdk/commit/77efbfa7675a8d7d8335e445ea0caac680017e57))
+
## 0.1.0-alpha.56 (2026-03-07)
Full Changelog: [v0.1.0-alpha.55...v0.1.0-alpha.56](https://github.com/OneBusAway/java-sdk/compare/v0.1.0-alpha.55...v0.1.0-alpha.56)
diff --git a/README.md b/README.md
index ff51c49..64f61c6 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
-[](https://central.sonatype.com/artifact/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.56)
-[](https://javadoc.io/doc/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.56)
+[](https://central.sonatype.com/artifact/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.57)
+[](https://javadoc.io/doc/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.57)
@@ -15,7 +15,7 @@ It is generated with [Stainless](https://www.stainless.com/).
-The REST API documentation can be found on [developer.onebusaway.org](https://developer.onebusaway.org). Javadocs are available on [javadoc.io](https://javadoc.io/doc/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.56).
+The REST API documentation can be found on [developer.onebusaway.org](https://developer.onebusaway.org). Javadocs are available on [javadoc.io](https://javadoc.io/doc/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.57).
@@ -26,7 +26,7 @@ The REST API documentation can be found on [developer.onebusaway.org](https://de
### Gradle
```kotlin
-implementation("org.onebusaway:onebusaway-sdk-java:0.1.0-alpha.56")
+implementation("org.onebusaway:onebusaway-sdk-java:0.1.0-alpha.57")
```
### Maven
@@ -35,7 +35,7 @@ implementation("org.onebusaway:onebusaway-sdk-java:0.1.0-alpha.56")
org.onebusaway
onebusaway-sdk-java
- 0.1.0-alpha.56
+ 0.1.0-alpha.57
```
@@ -229,8 +229,6 @@ The SDK throws custom unchecked exception types:
## Logging
-The SDK uses the standard [OkHttp logging interceptor](https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor).
-
Enable logging by setting the `ONEBUSAWAY_SDK_LOG` environment variable to `info`:
```sh
@@ -243,6 +241,19 @@ Or to `debug` for more verbose logging:
export ONEBUSAWAY_SDK_LOG=debug
```
+Or configure the client manually using the `logLevel` method:
+
+```java
+import org.onebusaway.client.OnebusawaySdkClient;
+import org.onebusaway.client.okhttp.OnebusawaySdkOkHttpClient;
+import org.onebusaway.core.LogLevel;
+
+OnebusawaySdkClient client = OnebusawaySdkOkHttpClient.builder()
+ .fromEnv()
+ .logLevel(LogLevel.INFO)
+ .build();
+```
+
## ProGuard and R8
Although the SDK uses reflection, it is still usable with [ProGuard](https://github.com/Guardsquare/proguard) and [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization) because `onebusaway-sdk-java-core` is published with a [configuration file](onebusaway-sdk-java-core/src/main/resources/META-INF/proguard/onebusaway-sdk-java-core.pro) containing [keep rules](https://www.guardsquare.com/manual/configuration/usage).
@@ -335,6 +346,21 @@ OnebusawaySdkClient client = OnebusawaySdkOkHttpClient.builder()
.build();
```
+If the proxy responds with `407 Proxy Authentication Required`, supply credentials by also configuring `proxyAuthenticator`:
+
+```java
+import org.onebusaway.client.OnebusawaySdkClient;
+import org.onebusaway.client.okhttp.OnebusawaySdkOkHttpClient;
+import org.onebusaway.core.http.ProxyAuthenticator;
+
+OnebusawaySdkClient client = OnebusawaySdkOkHttpClient.builder()
+ .fromEnv()
+ .proxy(...)
+ // Or a custom implementation of `ProxyAuthenticator`.
+ .proxyAuthenticator(ProxyAuthenticator.basic("username", "password"))
+ .build();
+```
+
### Connection pooling
To customize the underlying OkHttp connection pool, configure the client using the `maxIdleConnections` and `keepAliveDuration` methods:
@@ -553,7 +579,9 @@ In rare cases, the API may return a response that doesn't match the expected typ
By default, the SDK will not throw an exception in this case. It will throw [`OnebusawaySdkInvalidDataException`](onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/OnebusawaySdkInvalidDataException.kt) only if you directly access the property.
-If you would prefer to check that the response is completely well-typed upfront, then either call `validate()`:
+Validating the response is _not_ forwards compatible with new types from the API for existing fields.
+
+If you would still prefer to check that the response is completely well-typed upfront, then either call `validate()`:
```java
import org.onebusaway.models.currenttime.CurrentTimeRetrieveResponse;
diff --git a/build.gradle.kts b/build.gradle.kts
index 22b120e..53cee8d 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -8,7 +8,7 @@ repositories {
allprojects {
group = "org.onebusaway"
- version = "0.1.0-alpha.56" // x-release-please-version
+ version = "0.1.0-alpha.57" // x-release-please-version
}
subprojects {
@@ -21,7 +21,6 @@ subprojects {
group = "Verification"
description = "Verifies all source files are formatted."
}
- apply(plugin = "org.jetbrains.dokka")
}
subprojects {
diff --git a/buildSrc/src/main/kotlin/onebusaway-sdk.kotlin.gradle.kts b/buildSrc/src/main/kotlin/onebusaway-sdk.kotlin.gradle.kts
index fbd8fe3..f42bdbc 100644
--- a/buildSrc/src/main/kotlin/onebusaway-sdk.kotlin.gradle.kts
+++ b/buildSrc/src/main/kotlin/onebusaway-sdk.kotlin.gradle.kts
@@ -40,7 +40,7 @@ tasks.withType().configureEach {
val ktfmt by configurations.creating
dependencies {
- ktfmt("com.facebook:ktfmt:0.56")
+ ktfmt("com.facebook:ktfmt:0.61")
}
fun registerKtfmt(
diff --git a/onebusaway-sdk-java-client-okhttp/build.gradle.kts b/onebusaway-sdk-java-client-okhttp/build.gradle.kts
index f77000f..b093235 100644
--- a/onebusaway-sdk-java-client-okhttp/build.gradle.kts
+++ b/onebusaway-sdk-java-client-okhttp/build.gradle.kts
@@ -7,7 +7,6 @@ dependencies {
api(project(":onebusaway-sdk-java-core"))
implementation("com.squareup.okhttp3:okhttp:4.12.0")
- implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
testImplementation(kotlin("test"))
testImplementation("org.assertj:assertj-core:3.27.7")
diff --git a/onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OkHttpClient.kt b/onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OkHttpClient.kt
index 915d5da..7a96a57 100644
--- a/onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OkHttpClient.kt
+++ b/onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OkHttpClient.kt
@@ -2,6 +2,7 @@ package org.onebusaway.client.okhttp
import java.io.IOException
import java.io.InputStream
+import java.io.OutputStream
import java.net.Proxy
import java.time.Duration
import java.util.concurrent.CancellationException
@@ -11,10 +12,12 @@ import java.util.concurrent.TimeUnit
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
+import kotlin.jvm.optionals.getOrNull
import okhttp3.Call
import okhttp3.Callback
import okhttp3.ConnectionPool
import okhttp3.Dispatcher
+import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor
import okhttp3.MediaType
@@ -23,8 +26,9 @@ import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
-import okhttp3.logging.HttpLoggingInterceptor
import okio.BufferedSink
+import okio.buffer
+import okio.sink
import org.onebusaway.core.RequestOptions
import org.onebusaway.core.Timeout
import org.onebusaway.core.http.Headers
@@ -33,6 +37,7 @@ import org.onebusaway.core.http.HttpMethod
import org.onebusaway.core.http.HttpRequest
import org.onebusaway.core.http.HttpRequestBody
import org.onebusaway.core.http.HttpResponse
+import org.onebusaway.core.http.ProxyAuthenticator
import org.onebusaway.errors.OnebusawaySdkIoException
class OkHttpClient
@@ -42,7 +47,7 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
val call = newCall(request, requestOptions)
return try {
- call.execute().toResponse()
+ call.execute().toHttpResponse()
} catch (e: IOException) {
throw OnebusawaySdkIoException("Request failed", e)
} finally {
@@ -60,7 +65,7 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
call.enqueue(
object : Callback {
override fun onResponse(call: Call, response: Response) {
- future.complete(response.toResponse())
+ future.complete(response.toHttpResponse())
}
override fun onFailure(call: Call, e: IOException) {
@@ -90,17 +95,6 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
// Custom logging interceptor for URL logging
clientBuilder.addNetworkInterceptor(LoggingInterceptor())
-
- val logLevel =
- when (System.getenv("ONEBUSAWAY_SDK_LOG")?.lowercase()) {
- "info" -> HttpLoggingInterceptor.Level.BASIC
- "debug" -> HttpLoggingInterceptor.Level.BODY
- else -> null
- }
- if (logLevel != null) {
- clientBuilder.addNetworkInterceptor(HttpLoggingInterceptor().setLevel(logLevel))
- }
-
requestOptions.timeout?.let {
clientBuilder
.connectTimeout(it.connect())
@@ -113,89 +107,6 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
return client.newCall(request.toRequest(client))
}
- private fun HttpRequest.toRequest(client: okhttp3.OkHttpClient): Request {
- var body: RequestBody? = body?.toRequestBody()
- if (body == null && requiresBody(method)) {
- body = "".toRequestBody()
- }
-
- val builder = Request.Builder().url(toUrl()).method(method.name, body)
- headers.names().forEach { name ->
- headers.values(name).forEach { builder.addHeader(name, it) }
- }
-
- if (
- !headers.names().contains("X-Stainless-Read-Timeout") && client.readTimeoutMillis != 0
- ) {
- builder.addHeader(
- "X-Stainless-Read-Timeout",
- Duration.ofMillis(client.readTimeoutMillis.toLong()).seconds.toString(),
- )
- }
- if (!headers.names().contains("X-Stainless-Timeout") && client.callTimeoutMillis != 0) {
- builder.addHeader(
- "X-Stainless-Timeout",
- Duration.ofMillis(client.callTimeoutMillis.toLong()).seconds.toString(),
- )
- }
-
- return builder.build()
- }
-
- /** `OkHttpClient` always requires a request body for some methods. */
- private fun requiresBody(method: HttpMethod): Boolean =
- when (method) {
- HttpMethod.POST,
- HttpMethod.PUT,
- HttpMethod.PATCH -> true
- else -> false
- }
-
- private fun HttpRequest.toUrl(): String {
- val builder = baseUrl.toHttpUrl().newBuilder()
- pathSegments.forEach(builder::addPathSegment)
- queryParams.keys().forEach { key ->
- queryParams.values(key).forEach { builder.addQueryParameter(key, it) }
- }
-
- return builder.toString()
- }
-
- private fun HttpRequestBody.toRequestBody(): RequestBody {
- val mediaType = contentType()?.toMediaType()
- val length = contentLength()
-
- return object : RequestBody() {
- override fun contentType(): MediaType? = mediaType
-
- override fun contentLength(): Long = length
-
- override fun isOneShot(): Boolean = !repeatable()
-
- override fun writeTo(sink: BufferedSink) = writeTo(sink.outputStream())
- }
- }
-
- private fun Response.toResponse(): HttpResponse {
- val headers = headers.toHeaders()
-
- return object : HttpResponse {
- override fun statusCode(): Int = code
-
- override fun headers(): Headers = headers
-
- override fun body(): InputStream = body!!.byteStream()
-
- override fun close() = body!!.close()
- }
- }
-
- private fun okhttp3.Headers.toHeaders(): Headers {
- val headersBuilder = Headers.builder()
- forEach { (name, value) -> headersBuilder.put(name, value) }
- return headersBuilder.build()
- }
-
companion object {
@JvmStatic fun builder() = Builder()
}
@@ -204,6 +115,7 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
private var timeout: Timeout = Timeout.default()
private var proxy: Proxy? = null
+ private var proxyAuthenticator: ProxyAuthenticator? = null
private var maxIdleConnections: Int? = null
private var keepAliveDuration: Duration? = null
private var dispatcherExecutorService: ExecutorService? = null
@@ -217,6 +129,10 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
+ fun proxyAuthenticator(proxyAuthenticator: ProxyAuthenticator?) = apply {
+ this.proxyAuthenticator = proxyAuthenticator
+ }
+
/**
* Sets the maximum number of idle connections kept by the underlying [ConnectionPool].
*
@@ -266,6 +182,19 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
.callTimeout(timeout.request())
.proxy(proxy)
.apply {
+ proxyAuthenticator?.let { auth ->
+ proxyAuthenticator { route, response ->
+ auth
+ .authenticate(
+ route?.proxy ?: Proxy.NO_PROXY,
+ response.request.toHttpRequest(),
+ response.toHttpResponse(),
+ )
+ .getOrNull()
+ ?.toRequest(client = null)
+ }
+ }
+
dispatcherExecutorService?.let { dispatcher(Dispatcher(it)) }
val maxIdleConnections = maxIdleConnections
@@ -306,6 +235,129 @@ internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClie
}
}
+private fun HttpRequest.toRequest(client: okhttp3.OkHttpClient?): Request {
+ var body: RequestBody? = body?.toRequestBody()
+ if (body == null && requiresBody(method)) {
+ body = "".toRequestBody()
+ }
+
+ val builder = Request.Builder().url(toUrl()).method(method.name, body)
+ headers.names().forEach { name -> headers.values(name).forEach { builder.addHeader(name, it) } }
+
+ if (client != null) {
+ if (
+ !headers.names().contains("X-Stainless-Read-Timeout") && client.readTimeoutMillis != 0
+ ) {
+ builder.addHeader(
+ "X-Stainless-Read-Timeout",
+ Duration.ofMillis(client.readTimeoutMillis.toLong()).seconds.toString(),
+ )
+ }
+ if (!headers.names().contains("X-Stainless-Timeout") && client.callTimeoutMillis != 0) {
+ builder.addHeader(
+ "X-Stainless-Timeout",
+ Duration.ofMillis(client.callTimeoutMillis.toLong()).seconds.toString(),
+ )
+ }
+ }
+
+ return builder.build()
+}
+
+/** `OkHttpClient` always requires a request body for some methods. */
+private fun requiresBody(method: HttpMethod): Boolean =
+ when (method) {
+ HttpMethod.POST,
+ HttpMethod.PUT,
+ HttpMethod.PATCH -> true
+ else -> false
+ }
+
+private fun HttpRequest.toUrl(): String {
+ val builder = baseUrl.toHttpUrl().newBuilder()
+ pathSegments.forEach(builder::addPathSegment)
+ queryParams.keys().forEach { key ->
+ queryParams.values(key).forEach { builder.addQueryParameter(key, it) }
+ }
+
+ return builder.toString()
+}
+
+private fun HttpRequestBody.toRequestBody(): RequestBody {
+ val mediaType = contentType()?.toMediaType()
+ val length = contentLength()
+
+ return object : RequestBody() {
+ override fun contentType(): MediaType? = mediaType
+
+ override fun contentLength(): Long = length
+
+ override fun isOneShot(): Boolean = !repeatable()
+
+ override fun writeTo(sink: BufferedSink) = writeTo(sink.outputStream())
+ }
+}
+
+private fun Request.toHttpRequest(): HttpRequest {
+ val builder = HttpRequest.builder().method(HttpMethod.valueOf(method)).baseUrl(url.toBaseUrl())
+ url.pathSegments.forEach(builder::addPathSegment)
+ url.queryParameterNames.forEach { name ->
+ url.queryParameterValues(name).filterNotNull().forEach { builder.putQueryParam(name, it) }
+ }
+ headers.forEach { (name, value) -> builder.putHeader(name, value) }
+ body?.let { builder.body(it.toHttpRequestBody()) }
+ return builder.build()
+}
+
+private fun HttpUrl.toBaseUrl(): String = buildString {
+ append(scheme).append("://").append(host)
+ if (port != HttpUrl.defaultPort(scheme)) {
+ append(":").append(port)
+ }
+}
+
+private fun RequestBody.toHttpRequestBody(): HttpRequestBody {
+ val mediaType = contentType()?.toString()
+ val length = contentLength()
+ val isOneShot = isOneShot()
+ val source = this
+ return object : HttpRequestBody {
+ override fun contentType(): String? = mediaType
+
+ override fun contentLength(): Long = length
+
+ override fun repeatable(): Boolean = !isOneShot
+
+ override fun writeTo(outputStream: OutputStream) {
+ val sink = outputStream.sink().buffer()
+ source.writeTo(sink)
+ sink.flush()
+ }
+
+ override fun close() {}
+ }
+}
+
+private fun Response.toHttpResponse(): HttpResponse {
+ val headers = headers.toHeaders()
+
+ return object : HttpResponse {
+ override fun statusCode(): Int = code
+
+ override fun headers(): Headers = headers
+
+ override fun body(): InputStream = body!!.byteStream()
+
+ override fun close() = body!!.close()
+ }
+}
+
+private fun okhttp3.Headers.toHeaders(): Headers {
+ val headersBuilder = Headers.builder()
+ forEach { (name, value) -> headersBuilder.put(name, value) }
+ return headersBuilder.build()
+}
+
// --- ✅ New class added below ---
class LoggingInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
diff --git a/onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OnebusawaySdkOkHttpClient.kt b/onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OnebusawaySdkOkHttpClient.kt
index 3f28f91..8054ed9 100644
--- a/onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OnebusawaySdkOkHttpClient.kt
+++ b/onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OnebusawaySdkOkHttpClient.kt
@@ -15,10 +15,12 @@ import kotlin.jvm.optionals.getOrNull
import org.onebusaway.client.OnebusawaySdkClient
import org.onebusaway.client.OnebusawaySdkClientImpl
import org.onebusaway.core.ClientOptions
+import org.onebusaway.core.LogLevel
import org.onebusaway.core.Sleeper
import org.onebusaway.core.Timeout
import org.onebusaway.core.http.Headers
import org.onebusaway.core.http.HttpClient
+import org.onebusaway.core.http.ProxyAuthenticator
import org.onebusaway.core.http.QueryParams
import org.onebusaway.core.jsonMapper
@@ -47,6 +49,7 @@ class OnebusawaySdkOkHttpClient private constructor() {
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
private var dispatcherExecutorService: ExecutorService? = null
private var proxy: Proxy? = null
+ private var proxyAuthenticator: ProxyAuthenticator? = null
private var maxIdleConnections: Int? = null
private var keepAliveDuration: Duration? = null
private var sslSocketFactory: SSLSocketFactory? = null
@@ -77,6 +80,20 @@ class OnebusawaySdkOkHttpClient private constructor() {
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
fun proxy(proxy: Optional) = proxy(proxy.getOrNull())
+ /**
+ * Provides credentials when an HTTP proxy responds with `407 Proxy Authentication
+ * Required`.
+ */
+ fun proxyAuthenticator(proxyAuthenticator: ProxyAuthenticator?) = apply {
+ this.proxyAuthenticator = proxyAuthenticator
+ }
+
+ /**
+ * Alias for calling [Builder.proxyAuthenticator] with `proxyAuthenticator.orElse(null)`.
+ */
+ fun proxyAuthenticator(proxyAuthenticator: Optional) =
+ proxyAuthenticator(proxyAuthenticator.getOrNull())
+
/**
* The maximum number of idle connections kept by the underlying OkHttp connection pool.
*
@@ -217,6 +234,9 @@ class OnebusawaySdkOkHttpClient private constructor() {
/**
* Whether to call `validate` on every response before returning it.
*
+ * Setting this to `true` is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
* Defaults to false, which means the shape of the response will not be validated upfront.
* Instead, validation will only occur for the parts of the response that are accessed.
*/
@@ -258,6 +278,15 @@ class OnebusawaySdkOkHttpClient private constructor() {
*/
fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) }
+ /**
+ * The level at which to log request and response information.
+ *
+ * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv].
+ *
+ * Defaults to [LogLevel.fromEnv].
+ */
+ fun logLevel(logLevel: LogLevel) = apply { clientOptions.logLevel(logLevel) }
+
fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) }
fun headers(headers: Headers) = apply { clientOptions.headers(headers) }
@@ -359,6 +388,7 @@ class OnebusawaySdkOkHttpClient private constructor() {
OkHttpClient.builder()
.timeout(clientOptions.timeout())
.proxy(proxy)
+ .proxyAuthenticator(proxyAuthenticator)
.maxIdleConnections(maxIdleConnections)
.keepAliveDuration(keepAliveDuration)
.dispatcherExecutorService(dispatcherExecutorService)
diff --git a/onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OnebusawaySdkOkHttpClientAsync.kt b/onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OnebusawaySdkOkHttpClientAsync.kt
index d4260af..21d8fed 100644
--- a/onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OnebusawaySdkOkHttpClientAsync.kt
+++ b/onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OnebusawaySdkOkHttpClientAsync.kt
@@ -15,10 +15,12 @@ import kotlin.jvm.optionals.getOrNull
import org.onebusaway.client.OnebusawaySdkClientAsync
import org.onebusaway.client.OnebusawaySdkClientAsyncImpl
import org.onebusaway.core.ClientOptions
+import org.onebusaway.core.LogLevel
import org.onebusaway.core.Sleeper
import org.onebusaway.core.Timeout
import org.onebusaway.core.http.Headers
import org.onebusaway.core.http.HttpClient
+import org.onebusaway.core.http.ProxyAuthenticator
import org.onebusaway.core.http.QueryParams
import org.onebusaway.core.jsonMapper
@@ -47,6 +49,7 @@ class OnebusawaySdkOkHttpClientAsync private constructor() {
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
private var dispatcherExecutorService: ExecutorService? = null
private var proxy: Proxy? = null
+ private var proxyAuthenticator: ProxyAuthenticator? = null
private var maxIdleConnections: Int? = null
private var keepAliveDuration: Duration? = null
private var sslSocketFactory: SSLSocketFactory? = null
@@ -77,6 +80,20 @@ class OnebusawaySdkOkHttpClientAsync private constructor() {
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
fun proxy(proxy: Optional) = proxy(proxy.getOrNull())
+ /**
+ * Provides credentials when an HTTP proxy responds with `407 Proxy Authentication
+ * Required`.
+ */
+ fun proxyAuthenticator(proxyAuthenticator: ProxyAuthenticator?) = apply {
+ this.proxyAuthenticator = proxyAuthenticator
+ }
+
+ /**
+ * Alias for calling [Builder.proxyAuthenticator] with `proxyAuthenticator.orElse(null)`.
+ */
+ fun proxyAuthenticator(proxyAuthenticator: Optional) =
+ proxyAuthenticator(proxyAuthenticator.getOrNull())
+
/**
* The maximum number of idle connections kept by the underlying OkHttp connection pool.
*
@@ -217,6 +234,9 @@ class OnebusawaySdkOkHttpClientAsync private constructor() {
/**
* Whether to call `validate` on every response before returning it.
*
+ * Setting this to `true` is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
* Defaults to false, which means the shape of the response will not be validated upfront.
* Instead, validation will only occur for the parts of the response that are accessed.
*/
@@ -258,6 +278,15 @@ class OnebusawaySdkOkHttpClientAsync private constructor() {
*/
fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) }
+ /**
+ * The level at which to log request and response information.
+ *
+ * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv].
+ *
+ * Defaults to [LogLevel.fromEnv].
+ */
+ fun logLevel(logLevel: LogLevel) = apply { clientOptions.logLevel(logLevel) }
+
fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) }
fun headers(headers: Headers) = apply { clientOptions.headers(headers) }
@@ -359,6 +388,7 @@ class OnebusawaySdkOkHttpClientAsync private constructor() {
OkHttpClient.builder()
.timeout(clientOptions.timeout())
.proxy(proxy)
+ .proxyAuthenticator(proxyAuthenticator)
.maxIdleConnections(maxIdleConnections)
.keepAliveDuration(keepAliveDuration)
.dispatcherExecutorService(dispatcherExecutorService)
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClient.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClient.kt
index 5706000..76e894f 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClient.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClient.kt
@@ -7,6 +7,7 @@ import org.onebusaway.core.ClientOptions
import org.onebusaway.services.blocking.AgenciesWithCoverageService
import org.onebusaway.services.blocking.AgencyService
import org.onebusaway.services.blocking.ArrivalAndDepartureService
+import org.onebusaway.services.blocking.ArrivalsAndDeparturesForLocationService
import org.onebusaway.services.blocking.BlockService
import org.onebusaway.services.blocking.ConfigService
import org.onebusaway.services.blocking.CurrentTimeService
@@ -101,6 +102,8 @@ interface OnebusawaySdkClient {
fun scheduleForRoute(): ScheduleForRouteService
+ fun arrivalsAndDeparturesForLocation(): ArrivalsAndDeparturesForLocationService
+
fun arrivalAndDeparture(): ArrivalAndDepartureService
fun trip(): TripService
@@ -184,6 +187,9 @@ interface OnebusawaySdkClient {
fun scheduleForRoute(): ScheduleForRouteService.WithRawResponse
+ fun arrivalsAndDeparturesForLocation():
+ ArrivalsAndDeparturesForLocationService.WithRawResponse
+
fun arrivalAndDeparture(): ArrivalAndDepartureService.WithRawResponse
fun trip(): TripService.WithRawResponse
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClientAsync.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClientAsync.kt
index ddd6cf9..4dbe860 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClientAsync.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClientAsync.kt
@@ -7,6 +7,7 @@ import org.onebusaway.core.ClientOptions
import org.onebusaway.services.async.AgenciesWithCoverageServiceAsync
import org.onebusaway.services.async.AgencyServiceAsync
import org.onebusaway.services.async.ArrivalAndDepartureServiceAsync
+import org.onebusaway.services.async.ArrivalsAndDeparturesForLocationServiceAsync
import org.onebusaway.services.async.BlockServiceAsync
import org.onebusaway.services.async.ConfigServiceAsync
import org.onebusaway.services.async.CurrentTimeServiceAsync
@@ -101,6 +102,8 @@ interface OnebusawaySdkClientAsync {
fun scheduleForRoute(): ScheduleForRouteServiceAsync
+ fun arrivalsAndDeparturesForLocation(): ArrivalsAndDeparturesForLocationServiceAsync
+
fun arrivalAndDeparture(): ArrivalAndDepartureServiceAsync
fun trip(): TripServiceAsync
@@ -185,6 +188,9 @@ interface OnebusawaySdkClientAsync {
fun scheduleForRoute(): ScheduleForRouteServiceAsync.WithRawResponse
+ fun arrivalsAndDeparturesForLocation():
+ ArrivalsAndDeparturesForLocationServiceAsync.WithRawResponse
+
fun arrivalAndDeparture(): ArrivalAndDepartureServiceAsync.WithRawResponse
fun trip(): TripServiceAsync.WithRawResponse
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClientAsyncImpl.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClientAsyncImpl.kt
index 78327e2..b7916bb 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClientAsyncImpl.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClientAsyncImpl.kt
@@ -11,6 +11,8 @@ import org.onebusaway.services.async.AgencyServiceAsync
import org.onebusaway.services.async.AgencyServiceAsyncImpl
import org.onebusaway.services.async.ArrivalAndDepartureServiceAsync
import org.onebusaway.services.async.ArrivalAndDepartureServiceAsyncImpl
+import org.onebusaway.services.async.ArrivalsAndDeparturesForLocationServiceAsync
+import org.onebusaway.services.async.ArrivalsAndDeparturesForLocationServiceAsyncImpl
import org.onebusaway.services.async.BlockServiceAsync
import org.onebusaway.services.async.BlockServiceAsyncImpl
import org.onebusaway.services.async.ConfigServiceAsync
@@ -142,6 +144,11 @@ class OnebusawaySdkClientAsyncImpl(private val clientOptions: ClientOptions) :
ScheduleForRouteServiceAsyncImpl(clientOptionsWithUserAgent)
}
+ private val arrivalsAndDeparturesForLocation:
+ ArrivalsAndDeparturesForLocationServiceAsync by lazy {
+ ArrivalsAndDeparturesForLocationServiceAsyncImpl(clientOptionsWithUserAgent)
+ }
+
private val arrivalAndDeparture: ArrivalAndDepartureServiceAsync by lazy {
ArrivalAndDepartureServiceAsyncImpl(clientOptionsWithUserAgent)
}
@@ -227,6 +234,9 @@ class OnebusawaySdkClientAsyncImpl(private val clientOptions: ClientOptions) :
override fun scheduleForRoute(): ScheduleForRouteServiceAsync = scheduleForRoute
+ override fun arrivalsAndDeparturesForLocation(): ArrivalsAndDeparturesForLocationServiceAsync =
+ arrivalsAndDeparturesForLocation
+
override fun arrivalAndDeparture(): ArrivalAndDepartureServiceAsync = arrivalAndDeparture
override fun trip(): TripServiceAsync = trip
@@ -320,6 +330,11 @@ class OnebusawaySdkClientAsyncImpl(private val clientOptions: ClientOptions) :
ScheduleForRouteServiceAsyncImpl.WithRawResponseImpl(clientOptions)
}
+ private val arrivalsAndDeparturesForLocation:
+ ArrivalsAndDeparturesForLocationServiceAsync.WithRawResponse by lazy {
+ ArrivalsAndDeparturesForLocationServiceAsyncImpl.WithRawResponseImpl(clientOptions)
+ }
+
private val arrivalAndDeparture: ArrivalAndDepartureServiceAsync.WithRawResponse by lazy {
ArrivalAndDepartureServiceAsyncImpl.WithRawResponseImpl(clientOptions)
}
@@ -418,6 +433,10 @@ class OnebusawaySdkClientAsyncImpl(private val clientOptions: ClientOptions) :
override fun scheduleForRoute(): ScheduleForRouteServiceAsync.WithRawResponse =
scheduleForRoute
+ override fun arrivalsAndDeparturesForLocation():
+ ArrivalsAndDeparturesForLocationServiceAsync.WithRawResponse =
+ arrivalsAndDeparturesForLocation
+
override fun arrivalAndDeparture(): ArrivalAndDepartureServiceAsync.WithRawResponse =
arrivalAndDeparture
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClientImpl.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClientImpl.kt
index d4e4146..dca7735 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClientImpl.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/client/OnebusawaySdkClientImpl.kt
@@ -11,6 +11,8 @@ import org.onebusaway.services.blocking.AgencyService
import org.onebusaway.services.blocking.AgencyServiceImpl
import org.onebusaway.services.blocking.ArrivalAndDepartureService
import org.onebusaway.services.blocking.ArrivalAndDepartureServiceImpl
+import org.onebusaway.services.blocking.ArrivalsAndDeparturesForLocationService
+import org.onebusaway.services.blocking.ArrivalsAndDeparturesForLocationServiceImpl
import org.onebusaway.services.blocking.BlockService
import org.onebusaway.services.blocking.BlockServiceImpl
import org.onebusaway.services.blocking.ConfigService
@@ -137,6 +139,10 @@ class OnebusawaySdkClientImpl(private val clientOptions: ClientOptions) : Onebus
ScheduleForRouteServiceImpl(clientOptionsWithUserAgent)
}
+ private val arrivalsAndDeparturesForLocation: ArrivalsAndDeparturesForLocationService by lazy {
+ ArrivalsAndDeparturesForLocationServiceImpl(clientOptionsWithUserAgent)
+ }
+
private val arrivalAndDeparture: ArrivalAndDepartureService by lazy {
ArrivalAndDepartureServiceImpl(clientOptionsWithUserAgent)
}
@@ -218,6 +224,9 @@ class OnebusawaySdkClientImpl(private val clientOptions: ClientOptions) : Onebus
override fun scheduleForRoute(): ScheduleForRouteService = scheduleForRoute
+ override fun arrivalsAndDeparturesForLocation(): ArrivalsAndDeparturesForLocationService =
+ arrivalsAndDeparturesForLocation
+
override fun arrivalAndDeparture(): ArrivalAndDepartureService = arrivalAndDeparture
override fun trip(): TripService = trip
@@ -311,6 +320,11 @@ class OnebusawaySdkClientImpl(private val clientOptions: ClientOptions) : Onebus
ScheduleForRouteServiceImpl.WithRawResponseImpl(clientOptions)
}
+ private val arrivalsAndDeparturesForLocation:
+ ArrivalsAndDeparturesForLocationService.WithRawResponse by lazy {
+ ArrivalsAndDeparturesForLocationServiceImpl.WithRawResponseImpl(clientOptions)
+ }
+
private val arrivalAndDeparture: ArrivalAndDepartureService.WithRawResponse by lazy {
ArrivalAndDepartureServiceImpl.WithRawResponseImpl(clientOptions)
}
@@ -402,6 +416,10 @@ class OnebusawaySdkClientImpl(private val clientOptions: ClientOptions) : Onebus
override fun scheduleForRoute(): ScheduleForRouteService.WithRawResponse = scheduleForRoute
+ override fun arrivalsAndDeparturesForLocation():
+ ArrivalsAndDeparturesForLocationService.WithRawResponse =
+ arrivalsAndDeparturesForLocation
+
override fun arrivalAndDeparture(): ArrivalAndDepartureService.WithRawResponse =
arrivalAndDeparture
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/ClientOptions.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/ClientOptions.kt
index d5383fc..10331bb 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/ClientOptions.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/ClientOptions.kt
@@ -9,6 +9,7 @@ import java.util.Optional
import kotlin.jvm.optionals.getOrNull
import org.onebusaway.core.http.Headers
import org.onebusaway.core.http.HttpClient
+import org.onebusaway.core.http.LoggingHttpClient
import org.onebusaway.core.http.PhantomReachableClosingHttpClient
import org.onebusaway.core.http.QueryParams
import org.onebusaway.core.http.RetryingHttpClient
@@ -66,6 +67,9 @@ private constructor(
/**
* Whether to call `validate` on every response before returning it.
*
+ * Setting this to `true` is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
* Defaults to false, which means the shape of the response will not be validated upfront.
* Instead, validation will only occur for the parts of the response that are accessed.
*/
@@ -93,6 +97,14 @@ private constructor(
* Defaults to 2.
*/
@get:JvmName("maxRetries") val maxRetries: Int,
+ /**
+ * The level at which to log request and response information.
+ *
+ * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv].
+ *
+ * Defaults to [LogLevel.fromEnv].
+ */
+ @get:JvmName("logLevel") val logLevel: LogLevel,
@get:JvmName("apiKey") val apiKey: String,
) {
@@ -148,6 +160,7 @@ private constructor(
private var responseValidation: Boolean = false
private var timeout: Timeout = Timeout.default()
private var maxRetries: Int = 2
+ private var logLevel: LogLevel = LogLevel.fromEnv()
private var apiKey: String? = null
@JvmSynthetic
@@ -163,6 +176,7 @@ private constructor(
responseValidation = clientOptions.responseValidation
timeout = clientOptions.timeout
maxRetries = clientOptions.maxRetries
+ logLevel = clientOptions.logLevel
apiKey = clientOptions.apiKey
}
@@ -229,6 +243,9 @@ private constructor(
/**
* Whether to call `validate` on every response before returning it.
*
+ * Setting this to `true` is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
* Defaults to false, which means the shape of the response will not be validated upfront.
* Instead, validation will only occur for the parts of the response that are accessed.
*/
@@ -270,6 +287,15 @@ private constructor(
*/
fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries }
+ /**
+ * The level at which to log request and response information.
+ *
+ * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv].
+ *
+ * Defaults to [LogLevel.fromEnv].
+ */
+ fun logLevel(logLevel: LogLevel) = apply { this.logLevel = logLevel }
+
fun apiKey(apiKey: String) = apply { this.apiKey = apiKey }
fun headers(headers: Headers) = apply {
@@ -367,12 +393,21 @@ private constructor(
* System properties take precedence over environment variables.
*/
fun fromEnv() = apply {
+ logLevel(LogLevel.fromEnv())
(System.getProperty("onebusawaysdk.baseUrl")
?: System.getenv("ONEBUSAWAY_SDK_BASE_URL"))
?.let { baseUrl(it) }
(System.getProperty("onebusawaysdk.onebusawayApiKey")
?: System.getenv("ONEBUSAWAY_API_KEY"))
?.let { apiKey(it) }
+ System.getenv("ONEBUSAWAY_SDK_CUSTOM_HEADERS")?.let { customHeadersEnv ->
+ for (line in customHeadersEnv.split("\n")) {
+ val colon = line.indexOf(':')
+ if (colon >= 0) {
+ putHeader(line.substring(0, colon).trim(), line.substring(colon + 1).trim())
+ }
+ }
+ }
}
/**
@@ -403,18 +438,25 @@ private constructor(
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()) {
- queryParams.put("key", it)
+ queryParams.replace("key", it)
}
}
- headers.replaceAll(this.headers.build())
- queryParams.replaceAll(this.queryParams.build())
return ClientOptions(
httpClient,
RetryingHttpClient.builder()
- .httpClient(httpClient)
+ .httpClient(
+ LoggingHttpClient.builder()
+ .httpClient(httpClient)
+ .clock(clock)
+ .level(logLevel)
+ .build()
+ )
.sleeper(sleeper)
.clock(clock)
.maxRetries(maxRetries)
@@ -429,6 +471,7 @@ private constructor(
responseValidation,
timeout,
maxRetries,
+ logLevel,
apiKey,
)
}
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/LogLevel.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/LogLevel.kt
new file mode 100644
index 0000000..252dea3
--- /dev/null
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/LogLevel.kt
@@ -0,0 +1,33 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package org.onebusaway.core
+
+/** The level at which to log request and response information. */
+enum class LogLevel {
+ /** No logging. */
+ OFF,
+ /** Minimal request and response summary logs. No headers or bodies are logged. */
+ INFO,
+ /** [INFO] logs plus details about request failures. */
+ ERROR,
+ /**
+ * Full request and response logs. Sensitive headers are redacted, but sensitive data in request
+ * and response bodies may still be visible.
+ */
+ DEBUG;
+
+ /** Returns whether this level is at or higher than the given [level]. */
+ fun shouldLog(level: LogLevel): Boolean = ordinal >= level.ordinal
+
+ companion object {
+
+ /** Returns a [LogLevel] based on the `ONEBUSAWAY_SDK_LOG` environment variable. */
+ fun fromEnv() =
+ when (System.getenv("ONEBUSAWAY_SDK_LOG")?.lowercase()) {
+ "info" -> INFO
+ "error" -> ERROR
+ "debug" -> DEBUG
+ else -> OFF
+ }
+ }
+}
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/ObjectMappers.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/ObjectMappers.kt
index ab3f530..f894da5 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/ObjectMappers.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/ObjectMappers.kt
@@ -29,7 +29,9 @@ import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoField
-fun jsonMapper(): JsonMapper =
+fun jsonMapper(): JsonMapper = JSON_MAPPER
+
+private val JSON_MAPPER: JsonMapper =
JsonMapper.builder()
.addModule(kotlinModule())
.addModule(Jdk8Module())
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/RequestOptions.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/RequestOptions.kt
index 0967032..e70f47d 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/RequestOptions.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/RequestOptions.kt
@@ -33,6 +33,15 @@ class RequestOptions private constructor(val responseValidation: Boolean?, val t
private var responseValidation: Boolean? = null
private var timeout: Timeout? = null
+ /**
+ * Whether to call `validate` on the response before returning it.
+ *
+ * Setting this to `true` is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * Defaults to false, which means the shape of the response will not be validated upfront.
+ * Instead, validation will only occur for the parts of the response that are accessed.
+ */
fun responseValidation(responseValidation: Boolean) = apply {
this.responseValidation = responseValidation
}
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/Utils.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/Utils.kt
index 36f3a92..5bf70b1 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/Utils.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/Utils.kt
@@ -4,6 +4,7 @@ package org.onebusaway.core
import java.util.Collections
import java.util.SortedMap
+import java.util.SortedSet
import java.util.concurrent.CompletableFuture
import java.util.concurrent.locks.Lock
import org.onebusaway.errors.OnebusawaySdkInvalidDataException
@@ -16,6 +17,11 @@ internal fun T?.getOrThrow(name: String): T =
internal fun List.toImmutable(): List =
if (isEmpty()) Collections.emptyList() else Collections.unmodifiableList(toList())
+@JvmSynthetic
+internal fun > SortedSet.toImmutable(): SortedSet =
+ if (isEmpty()) Collections.emptySortedSet()
+ else Collections.unmodifiableSortedSet(toSortedSet(comparator() ?: Comparator.naturalOrder()))
+
@JvmSynthetic
internal fun Map.toImmutable(): Map =
if (isEmpty()) immutableEmptyMap() else Collections.unmodifiableMap(toMap())
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/LoggingHttpClient.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/LoggingHttpClient.kt
new file mode 100644
index 0000000..98fee55
--- /dev/null
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/LoggingHttpClient.kt
@@ -0,0 +1,628 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package org.onebusaway.core.http
+
+import java.io.ByteArrayOutputStream
+import java.io.InputStream
+import java.io.OutputStream
+import java.nio.ByteBuffer
+import java.nio.charset.CharacterCodingException
+import java.nio.charset.Charset
+import java.nio.charset.CharsetDecoder
+import java.nio.charset.CodingErrorAction
+import java.nio.charset.StandardCharsets
+import java.time.Clock
+import java.time.Duration
+import java.time.OffsetDateTime
+import java.util.SortedSet
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.CompletionException
+import kotlin.time.toKotlinDuration
+import org.onebusaway.core.LogLevel
+import org.onebusaway.core.RequestOptions
+import org.onebusaway.core.checkRequired
+import org.onebusaway.core.toImmutable
+
+/** A wrapper [HttpClient] around [httpClient] that logs request and response information. */
+class LoggingHttpClient
+private constructor(
+ /** The underlying [HttpClient] for making requests. */
+ @get:JvmName("httpClient") val httpClient: HttpClient,
+ /**
+ * Sensitive headers to redact from logs.
+ *
+ * Defaults to `Set.of("authorization", "api-key", "x-api-key", "cookie", "set-cookie")`.
+ */
+ @get:JvmName("redactedHeaders") val redactedHeaders: SortedSet,
+ /**
+ * The clock to use for measuring request and response durations.
+ *
+ * This is primarily useful for using a fake clock in tests.
+ *
+ * Defaults to [Clock.systemUTC].
+ */
+ @get:JvmName("clock") val clock: Clock,
+ /**
+ * The log level to use.
+ *
+ * Pass [LogLevel.fromEnv] to read from environment variables.
+ */
+ @get:JvmName("level") val level: LogLevel,
+) : HttpClient {
+
+ override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse {
+ val loggingRequest = logRequest(request)
+
+ val before = OffsetDateTime.now(clock)
+ val response =
+ try {
+ httpClient.execute(loggingRequest, requestOptions)
+ } catch (e: Throwable) {
+ logFailure(e, Duration.between(before, OffsetDateTime.now(clock)))
+ throw e
+ }
+
+ val took = Duration.between(before, OffsetDateTime.now(clock))
+ return logResponse(response, took)
+ }
+
+ override fun executeAsync(
+ request: HttpRequest,
+ requestOptions: RequestOptions,
+ ): CompletableFuture {
+ val loggingRequest = logRequest(request)
+
+ val before = OffsetDateTime.now(clock)
+ val future =
+ try {
+ httpClient.executeAsync(loggingRequest, requestOptions)
+ } catch (e: Throwable) {
+ logFailure(e, Duration.between(before, OffsetDateTime.now(clock)))
+ throw e
+ }
+ return future.handle { response, error ->
+ val took = Duration.between(before, OffsetDateTime.now(clock))
+ if (error != null) {
+ logFailure(unwrapCompletionException(error), took)
+ throw error
+ }
+ logResponse(response, took)
+ }
+ }
+
+ private fun logRequest(request: HttpRequest): HttpRequest {
+ if (!level.shouldLog(LogLevel.INFO)) {
+ return request
+ }
+
+ System.err.println(
+ buildString {
+ append("--> ${request.method} ${request.url()}")
+ request.body?.let {
+ val length = it.contentLength()
+ append(if (length >= 0) " ($length-byte body)" else " (unknown-length body)")
+ }
+ }
+ )
+
+ if (!level.shouldLog(LogLevel.DEBUG)) {
+ return request
+ }
+
+ logHeaders(request.headers)
+
+ if (request.body == null) {
+ System.err.println("--> END ${request.method}")
+ System.err.println()
+ return request
+ }
+
+ return request
+ .toBuilder()
+ .body(LoggingHttpRequestBody(request.method, request.body))
+ .build()
+ }
+
+ private fun logResponse(response: HttpResponse, took: Duration): HttpResponse {
+ if (!level.shouldLog(LogLevel.INFO)) {
+ return response
+ }
+
+ val contentLength = response.headers().values("Content-Length").firstOrNull()?.toIntOrNull()
+ System.err.println(
+ "<-- ${response.statusCode()} (${
+ buildString {
+ append(took.format())
+ contentLength?.let { append(", $contentLength-byte body") }
+ }
+ })"
+ )
+
+ if (!level.shouldLog(LogLevel.DEBUG)) {
+ return response
+ }
+
+ logHeaders(response.headers())
+ return LoggingHttpResponse(response)
+ }
+
+ private fun logFailure(error: Throwable, took: Duration) {
+ if (!level.shouldLog(LogLevel.ERROR)) {
+ return
+ }
+
+ System.err.println(
+ buildString {
+ append("<-- !! ${error.javaClass.simpleName}")
+ error.message?.let { append(": $it") }
+ append(" (${took.format()})")
+ }
+ )
+ }
+
+ private fun unwrapCompletionException(error: Throwable): Throwable =
+ if (error is CompletionException && error.cause != null) error.cause!! else error
+
+ private fun logHeaders(headers: Headers) =
+ headers.names().forEach { name ->
+ headers.values(name).forEach { value ->
+ System.err.println("$name: ${if (redactedHeaders.contains(name)) "██" else value}")
+ }
+ }
+
+ override fun close() = httpClient.close()
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [LoggingHttpClient].
+ *
+ * The following fields are required:
+ * ```java
+ * .httpClient()
+ * .level()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [LoggingHttpClient]. */
+ class Builder internal constructor() {
+
+ private var httpClient: HttpClient? = null
+ private var redactedHeaders: Set =
+ setOf("authorization", "api-key", "x-api-key", "cookie", "set-cookie")
+ private var clock: Clock = Clock.systemUTC()
+ private var level: LogLevel? = null
+
+ @JvmSynthetic
+ internal fun from(loggingHttpClient: LoggingHttpClient) = apply {
+ httpClient = loggingHttpClient.httpClient
+ redactedHeaders = loggingHttpClient.redactedHeaders
+ clock = loggingHttpClient.clock
+ level = loggingHttpClient.level
+ }
+
+ /** The underlying [HttpClient] for making requests. */
+ fun httpClient(httpClient: HttpClient) = apply { this.httpClient = httpClient }
+
+ /**
+ * Sensitive headers to redact from logs.
+ *
+ * Defaults to `Set.of("authorization", "api-key", "x-api-key", "cookie", "set-cookie")`.
+ */
+ fun redactedHeaders(redactedHeaders: Set) = apply {
+ this.redactedHeaders = redactedHeaders
+ }
+
+ /**
+ * The clock to use for measuring request and response durations.
+ *
+ * This is primarily useful for using a fake clock in tests.
+ *
+ * Defaults to [Clock.systemUTC].
+ */
+ fun clock(clock: Clock) = apply { this.clock = clock }
+
+ /**
+ * The log level to use.
+ *
+ * Pass [LogLevel.fromEnv] to read from environment variables.
+ */
+ fun level(level: LogLevel) = apply { this.level = level }
+
+ /**
+ * Returns an immutable instance of [LoggingHttpClient].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .httpClient()
+ * .level()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): LoggingHttpClient =
+ LoggingHttpClient(
+ checkRequired("httpClient", httpClient),
+ redactedHeaders.toSortedSet(String.CASE_INSENSITIVE_ORDER).toImmutable(),
+ clock,
+ checkRequired("level", level),
+ )
+ }
+}
+
+/**
+ * An [HttpRequestBody] wrapper that delegates to [body] while also logging line by line as it's
+ * written.
+ *
+ * The logging occurs in a streaming manner with minimal buffering.
+ */
+private class LoggingHttpRequestBody(
+ private val method: HttpMethod,
+ private val body: HttpRequestBody,
+) : HttpRequestBody {
+
+ private val charset by lazy { parseCharset(body.contentType()) }
+
+ override fun writeTo(outputStream: OutputStream) {
+ val loggingOutputStream = LoggingOutputStream(outputStream, charset)
+ body.writeTo(loggingOutputStream)
+
+ loggingOutputStream.flush()
+ System.err.println("--> END $method (${loggingOutputStream.writeCount()}-byte body)")
+ System.err.println()
+ }
+
+ override fun contentType(): String? = body.contentType()
+
+ override fun contentLength(): Long = body.contentLength()
+
+ override fun repeatable(): Boolean = body.repeatable()
+
+ override fun close() = body.close()
+}
+
+/**
+ * An [OutputStream] wrapper that delegates to [outputStream] while also logging bytes line by line
+ * as it's written to.
+ *
+ * The written content is assumed to be in the given [charset] and the logging occurs in a streaming
+ * manner with minimal buffering.
+ */
+private class LoggingOutputStream(private val outputStream: OutputStream, charset: Charset?) :
+ OutputStream() {
+
+ private val buffer = LoggingBuffer(charset)
+
+ fun writeCount() = buffer.writeCount()
+
+ override fun write(b: Int) {
+ outputStream.write(b)
+ buffer.write(b)
+ }
+
+ override fun write(b: ByteArray, off: Int, len: Int) {
+ outputStream.write(b, off, len)
+ for (i in off until off + len) {
+ buffer.write(b[i].toInt() and 0xFF)
+ }
+ }
+
+ /** Prints any currently buffered content. */
+ override fun flush() {
+ buffer.flush()
+ outputStream.flush()
+ }
+
+ override fun close() = outputStream.close()
+}
+
+/**
+ * An [HttpResponse] wrapper that delegates to [response] while also logging line-by-line as it's
+ * read.
+ *
+ * The logging occurs in a streaming manner with minimal buffering.
+ */
+private class LoggingHttpResponse(private val response: HttpResponse) : HttpResponse {
+
+ private val loggingBody: Lazy = lazy {
+ LoggingInputStream(
+ response.body(),
+ parseCharset(response.headers().values("Content-Type").firstOrNull()),
+ )
+ }
+
+ override fun statusCode(): Int = response.statusCode()
+
+ override fun headers(): Headers = response.headers()
+
+ override fun body(): InputStream = loggingBody.value
+
+ override fun close() {
+ if (loggingBody.isInitialized()) {
+ loggingBody.value.close()
+ }
+ response.close()
+ }
+}
+
+/**
+ * An [InputStream] wrapper that delegates to [inputStream] while also logging bytes line by line as
+ * it's read.
+ *
+ * The contents of [inputStream] are assumed to be in the given [charset] and the logging occurs in
+ * a streaming manner with minimal buffering.
+ */
+private class LoggingInputStream(private val inputStream: InputStream, charset: Charset?) :
+ InputStream() {
+
+ private var isDone = false
+ private val buffer = LoggingBuffer(charset)
+
+ override fun read(): Int {
+ if (isDone) {
+ return -1
+ }
+
+ val b = inputStream.read()
+
+ if (b == -1) {
+ markDone()
+ return b
+ }
+
+ buffer.write(b)
+ return b
+ }
+
+ override fun read(b: ByteArray, off: Int, len: Int): Int {
+ if (isDone) {
+ return -1
+ }
+
+ val bytesRead = inputStream.read(b, off, len)
+
+ if (bytesRead == -1) {
+ markDone()
+ return bytesRead
+ }
+
+ for (i in off until off + bytesRead) {
+ buffer.write(b[i].toInt() and 0xFF)
+ }
+ return bytesRead
+ }
+
+ override fun close() {
+ if (!isDone) {
+ markDone(closedEarly = true)
+ }
+ inputStream.close()
+ }
+
+ private fun markDone(closedEarly: Boolean = false) {
+ isDone = true
+ buffer.flush()
+ val suffix = if (closedEarly) ", closed early" else ""
+ System.err.println("<-- END HTTP (${buffer.writeCount()}-byte body$suffix)")
+ System.err.println()
+ }
+}
+
+/**
+ * A byte buffer that prints line by line, using the given [charset], as bytes are written to it.
+ *
+ * When [charset] is `null`, the buffer performs an upfront check to detect binary content. If
+ * non-whitespace ISO control characters are found in the first [PROBABLY_UTF8_CODE_POINT_LIMIT]
+ * code points, body logging is suppressed entirely.
+ */
+private class LoggingBuffer(charset: Charset?) {
+
+ private val charset = charset ?: StandardCharsets.UTF_8
+
+ private val decoder: CharsetDecoder =
+ this.charset
+ .newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT)
+ private var writeCount = 0
+ private val buffer = ByteArrayOutputStream(128)
+
+ /**
+ * Whether logging has been suppressed because the content doesn't appear to be readable text.
+ *
+ * This is only set when [charset] is `null` and the content fails the [isProbablyUtf8] check.
+ */
+ private var suppressed = false
+
+ /**
+ * Bytes accumulated for the [isProbablyUtf8] check before any lines are printed.
+ *
+ * Once the check passes (or [charset] is non-null), this is set to `null` and bytes flow
+ * directly to [buffer].
+ */
+ private var prefetchBuffer: ByteArrayOutputStream? =
+ if (charset != null) null else ByteArrayOutputStream(128)
+
+ fun writeCount() = writeCount
+
+ fun write(b: Int) {
+ if (writeCount == 0) {
+ // Print a newline before we start printing anything to separate the printed content
+ // from previous content.
+ System.err.println()
+ }
+
+ writeCount++
+
+ if (suppressed) {
+ return
+ }
+
+ val prefetch = prefetchBuffer
+ if (prefetch != null) {
+ prefetch.write(b)
+ // Continue accumulating until we have enough bytes to decide.
+ if (prefetch.size() < PROBABLY_UTF8_BYTE_LIMIT && b != '\n'.code) {
+ return
+ }
+ // We have enough bytes. Check if the content is probably UTF-8.
+ prefetchBuffer = null
+ val bytes = prefetch.toByteArray()
+ if (!isProbablyUtf8(bytes)) {
+ suppressed = true
+ System.err.println("(binary body omitted)")
+ return
+ }
+ // Content looks like UTF-8. Feed the accumulated bytes into the normal buffer.
+ for (byte in bytes) {
+ writeToBuffer(byte.toInt() and 0xFF)
+ }
+ return
+ }
+
+ writeToBuffer(b)
+ }
+
+ private fun writeToBuffer(b: Int) {
+ if (b == '\n'.code) {
+ flush()
+ return
+ }
+
+ buffer.write(b)
+ }
+
+ /** Prints any currently buffered content. */
+ fun flush() {
+ if (suppressed) {
+ return
+ }
+
+ // If we still have a prefetch buffer when flush is called (body was shorter than the
+ // limit), run the check now.
+ val prefetch = prefetchBuffer
+ if (prefetch != null) {
+ prefetchBuffer = null
+ val bytes = prefetch.toByteArray()
+ if (bytes.isEmpty()) {
+ return
+ }
+ if (!isProbablyUtf8(bytes)) {
+ suppressed = true
+ System.err.println("(binary body omitted)")
+ return
+ }
+ for (byte in bytes) {
+ writeToBuffer(byte.toInt() and 0xFF)
+ }
+ }
+
+ if (buffer.size() == 0) {
+ return
+ }
+
+ val line =
+ try {
+ decoder.decode(ByteBuffer.wrap(buffer.toByteArray()))
+ } catch (e: CharacterCodingException) {
+ "(omitted line is not valid $charset)"
+ }
+ buffer.reset()
+ System.err.println(line)
+ }
+}
+
+/** The maximum number of code points to sample when checking if content is probably UTF-8. */
+private const val PROBABLY_UTF8_CODE_POINT_LIMIT = 64
+
+/**
+ * The maximum number of bytes to accumulate before running the [isProbablyUtf8] check. UTF-8 code
+ * points are at most 4 bytes, so this accommodates [PROBABLY_UTF8_CODE_POINT_LIMIT] code points.
+ */
+private const val PROBABLY_UTF8_BYTE_LIMIT = PROBABLY_UTF8_CODE_POINT_LIMIT * 4
+
+/**
+ * Returns `true` if the given [bytes] probably contain human-readable UTF-8 text.
+ *
+ * Decodes up to [PROBABLY_UTF8_CODE_POINT_LIMIT] code points and returns `false` if any
+ * non-whitespace ISO control characters are found, or if the bytes are not valid UTF-8.
+ */
+private fun isProbablyUtf8(bytes: ByteArray): Boolean {
+ try {
+ val decoder =
+ StandardCharsets.UTF_8.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT)
+ val charBuffer = decoder.decode(ByteBuffer.wrap(bytes))
+ var codePointCount = 0
+ var i = 0
+ while (i < charBuffer.length && codePointCount < PROBABLY_UTF8_CODE_POINT_LIMIT) {
+ val codePoint = Character.codePointAt(charBuffer, i)
+ if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
+ return false
+ }
+ i += Character.charCount(codePoint)
+ codePointCount++
+ }
+ return true
+ } catch (e: CharacterCodingException) {
+ return false
+ }
+}
+
+/** Returns the [Charset] in the given [contentType] string, or `null` if unspecified. */
+private fun parseCharset(contentType: String?): Charset? =
+ contentType
+ ?.split(";")
+ ?.drop(1)
+ ?.map { it.trim() }
+ ?.firstOrNull { it.startsWith("charset=", ignoreCase = true) }
+ ?.substringAfter("=")
+ ?.trim()
+ ?.removeSurrounding("\"")
+ ?.let { runCatching { charset(it) }.getOrNull() }
+
+/** Formats the [Duration] into a string like "1m 40s 467ms". */
+private fun Duration.format(): String =
+ toKotlinDuration().toComponents { days, hours, minutes, seconds, nanoseconds ->
+ buildString {
+ val milliseconds = nanoseconds / 1_000_000
+ if (days > 0) {
+ append("${days}d")
+ }
+ if (hours > 0) {
+ if (isNotEmpty()) {
+ append(" ")
+ }
+ append("${hours}h")
+ }
+ if (minutes > 0) {
+ if (isNotEmpty()) {
+ append(" ")
+ }
+ append("${minutes}m")
+ }
+ if (seconds > 0) {
+ if (isNotEmpty()) {
+ append(" ")
+ }
+ append("${seconds}s")
+ }
+ if (milliseconds > 0) {
+ if (isNotEmpty()) {
+ append(" ")
+ }
+ append("${milliseconds}ms")
+ }
+
+ if (isEmpty()) {
+ append("0s")
+ }
+ }
+ }
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/ProxyAuthenticator.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/ProxyAuthenticator.kt
new file mode 100644
index 0000000..a6559e9
--- /dev/null
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/ProxyAuthenticator.kt
@@ -0,0 +1,59 @@
+package org.onebusaway.core.http
+
+import java.net.Proxy
+import java.nio.charset.Charset
+import java.nio.charset.StandardCharsets
+import java.util.Base64
+import java.util.Optional
+
+/**
+ * Provides credentials when an HTTP proxy responds with `407 Proxy Authentication Required`.
+ *
+ * Implementations inspect the 407 [response] (typically its `Proxy-Authenticate` header) and return
+ * the request to retry with a `Proxy-Authorization` header set, or [Optional.empty] to abandon
+ * authentication and surface the 407 to the caller.
+ *
+ * Implementations must be thread-safe; they may be invoked concurrently from multiple HTTP calls.
+ */
+fun interface ProxyAuthenticator {
+
+ /**
+ * @param proxy the proxy that produced the challenge, or [Proxy.NO_PROXY] if the route is not
+ * yet established
+ * @param request the request that produced [response]
+ * @param response the 407 challenge response
+ * @return the retry request to send (typically [request] with a `Proxy-Authorization` header
+ * added), or [Optional.empty] to abandon authentication
+ */
+ fun authenticate(
+ proxy: Proxy,
+ request: HttpRequest,
+ response: HttpResponse,
+ ): Optional
+
+ companion object {
+
+ /**
+ * A [ProxyAuthenticator] that uses RFC 7617 Basic authentication with the ISO-8859-1
+ * charset.
+ */
+ @JvmStatic
+ fun basic(username: String, password: String): ProxyAuthenticator =
+ basic(username, password, StandardCharsets.ISO_8859_1)
+
+ /**
+ * A [ProxyAuthenticator] that uses RFC 7617 Basic authentication with the given [charset].
+ */
+ @JvmStatic
+ fun basic(username: String, password: String, charset: Charset): ProxyAuthenticator {
+ val token =
+ Base64.getEncoder().encodeToString("$username:$password".toByteArray(charset))
+ val headerValue = "Basic $token"
+ return ProxyAuthenticator { _, request, _ ->
+ Optional.of(
+ request.toBuilder().putHeader("Proxy-Authorization", headerValue).build()
+ )
+ }
+ }
+ }
+}
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/RetryingHttpClient.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/RetryingHttpClient.kt
index 1be8cfe..6023d2e 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/RetryingHttpClient.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/RetryingHttpClient.kt
@@ -201,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,
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/BadRequestException.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/BadRequestException.kt
index 2ed561f..c890a0e 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/BadRequestException.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/BadRequestException.kt
@@ -7,10 +7,14 @@ import kotlin.jvm.optionals.getOrNull
import org.onebusaway.core.JsonValue
import org.onebusaway.core.checkRequired
import org.onebusaway.core.http.Headers
+import org.onebusaway.core.jsonMapper
class BadRequestException
private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
- OnebusawaySdkServiceException("400: $body", cause) {
+ OnebusawaySdkServiceException(
+ "400: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = 400
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/InternalServerException.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/InternalServerException.kt
index feb6ec9..bcfc6e7 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/InternalServerException.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/InternalServerException.kt
@@ -7,6 +7,7 @@ import kotlin.jvm.optionals.getOrNull
import org.onebusaway.core.JsonValue
import org.onebusaway.core.checkRequired
import org.onebusaway.core.http.Headers
+import org.onebusaway.core.jsonMapper
class InternalServerException
private constructor(
@@ -14,7 +15,11 @@ private constructor(
private val headers: Headers,
private val body: JsonValue,
cause: Throwable?,
-) : OnebusawaySdkServiceException("$statusCode: $body", cause) {
+) :
+ OnebusawaySdkServiceException(
+ "$statusCode: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = statusCode
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/NotFoundException.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/NotFoundException.kt
index d6aef12..6056958 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/NotFoundException.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/NotFoundException.kt
@@ -7,10 +7,14 @@ import kotlin.jvm.optionals.getOrNull
import org.onebusaway.core.JsonValue
import org.onebusaway.core.checkRequired
import org.onebusaway.core.http.Headers
+import org.onebusaway.core.jsonMapper
class NotFoundException
private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
- OnebusawaySdkServiceException("404: $body", cause) {
+ OnebusawaySdkServiceException(
+ "404: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = 404
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/PermissionDeniedException.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/PermissionDeniedException.kt
index 28a3b5a..4fc127c 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/PermissionDeniedException.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/PermissionDeniedException.kt
@@ -7,10 +7,14 @@ import kotlin.jvm.optionals.getOrNull
import org.onebusaway.core.JsonValue
import org.onebusaway.core.checkRequired
import org.onebusaway.core.http.Headers
+import org.onebusaway.core.jsonMapper
class PermissionDeniedException
private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
- OnebusawaySdkServiceException("403: $body", cause) {
+ OnebusawaySdkServiceException(
+ "403: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = 403
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/RateLimitException.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/RateLimitException.kt
index 8e68c49..c2b481b 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/RateLimitException.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/RateLimitException.kt
@@ -7,10 +7,14 @@ import kotlin.jvm.optionals.getOrNull
import org.onebusaway.core.JsonValue
import org.onebusaway.core.checkRequired
import org.onebusaway.core.http.Headers
+import org.onebusaway.core.jsonMapper
class RateLimitException
private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
- OnebusawaySdkServiceException("429: $body", cause) {
+ OnebusawaySdkServiceException(
+ "429: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = 429
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/UnauthorizedException.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/UnauthorizedException.kt
index 79e2116..d347602 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/UnauthorizedException.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/UnauthorizedException.kt
@@ -7,10 +7,14 @@ import kotlin.jvm.optionals.getOrNull
import org.onebusaway.core.JsonValue
import org.onebusaway.core.checkRequired
import org.onebusaway.core.http.Headers
+import org.onebusaway.core.jsonMapper
class UnauthorizedException
private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
- OnebusawaySdkServiceException("401: $body", cause) {
+ OnebusawaySdkServiceException(
+ "401: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = 401
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/UnexpectedStatusCodeException.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/UnexpectedStatusCodeException.kt
index f4aaa51..8183b81 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/UnexpectedStatusCodeException.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/UnexpectedStatusCodeException.kt
@@ -7,6 +7,7 @@ import kotlin.jvm.optionals.getOrNull
import org.onebusaway.core.JsonValue
import org.onebusaway.core.checkRequired
import org.onebusaway.core.http.Headers
+import org.onebusaway.core.jsonMapper
class UnexpectedStatusCodeException
private constructor(
@@ -14,7 +15,11 @@ private constructor(
private val headers: Headers,
private val body: JsonValue,
cause: Throwable?,
-) : OnebusawaySdkServiceException("$statusCode: $body", cause) {
+) :
+ OnebusawaySdkServiceException(
+ "$statusCode: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = statusCode
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/UnprocessableEntityException.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/UnprocessableEntityException.kt
index be66612..0c75ac2 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/UnprocessableEntityException.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/errors/UnprocessableEntityException.kt
@@ -7,10 +7,14 @@ import kotlin.jvm.optionals.getOrNull
import org.onebusaway.core.JsonValue
import org.onebusaway.core.checkRequired
import org.onebusaway.core.http.Headers
+import org.onebusaway.core.jsonMapper
class UnprocessableEntityException
private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
- OnebusawaySdkServiceException("422: $body", cause) {
+ OnebusawaySdkServiceException(
+ "422: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = 422
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/References.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/References.kt
index 0a75035..dc89050 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/References.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/References.kt
@@ -10,7 +10,6 @@ import java.util.Collections
import java.util.Objects
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
-import org.onebusaway.core.Enum
import org.onebusaway.core.ExcludeMissing
import org.onebusaway.core.JsonField
import org.onebusaway.core.JsonMissing
@@ -378,6 +377,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): References = apply {
if (validated) {
return@apply
@@ -820,6 +827,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Agency = apply {
if (validated) {
return@apply
@@ -1319,6 +1335,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Route = apply {
if (validated) {
return@apply
@@ -1416,7 +1441,7 @@ private constructor(
private val consequences: JsonField>,
private val description: JsonField,
private val publicationWindows: JsonField>,
- private val reason: JsonField,
+ private val reason: JsonField,
private val severity: JsonField,
private val summary: JsonField,
private val url: JsonField,
@@ -1447,7 +1472,7 @@ private constructor(
@JsonProperty("publicationWindows")
@ExcludeMissing
publicationWindows: JsonField> = JsonMissing.of(),
- @JsonProperty("reason") @ExcludeMissing reason: JsonField = JsonMissing.of(),
+ @JsonProperty("reason") @ExcludeMissing reason: JsonField = JsonMissing.of(),
@JsonProperty("severity")
@ExcludeMissing
severity: JsonField = JsonMissing.of(),
@@ -1532,7 +1557,7 @@ private constructor(
* @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type (e.g.
* if the server responded with an unexpected value).
*/
- fun reason(): Optional = reason.getOptional("reason")
+ fun reason(): Optional = reason.getOptional("reason")
/**
* Severity of the situation.
@@ -1634,7 +1659,7 @@ private constructor(
*
* Unlike [reason], this method doesn't throw if the JSON field has an unexpected type.
*/
- @JsonProperty("reason") @ExcludeMissing fun _reason(): JsonField = reason
+ @JsonProperty("reason") @ExcludeMissing fun _reason(): JsonField = reason
/**
* Returns the raw JSON value of [severity].
@@ -1694,7 +1719,7 @@ private constructor(
private var consequences: JsonField>? = null
private var description: JsonField = JsonMissing.of()
private var publicationWindows: JsonField>? = null
- private var reason: JsonField = JsonMissing.of()
+ private var reason: JsonField = JsonMissing.of()
private var severity: JsonField = JsonMissing.of()
private var summary: JsonField = JsonMissing.of()
private var url: JsonField = JsonMissing.of()
@@ -1875,16 +1900,16 @@ private constructor(
}
/** Reason for the service alert, taken from TPEG codes. */
- fun reason(reason: Reason) = reason(JsonField.of(reason))
+ fun reason(reason: String) = reason(JsonField.of(reason))
/**
* Sets [Builder.reason] to an arbitrary JSON value.
*
- * You should usually call [Builder.reason] with a well-typed [Reason] value instead.
+ * You should usually call [Builder.reason] with a well-typed [String] value instead.
* This method is primarily for setting the field to an undocumented or not yet
* supported value.
*/
- fun reason(reason: JsonField) = apply { this.reason = reason }
+ fun reason(reason: JsonField) = apply { this.reason = reason }
/** Severity of the situation. */
fun severity(severity: String) = severity(JsonField.of(severity))
@@ -1972,6 +1997,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Situation = apply {
if (validated) {
return@apply
@@ -1985,7 +2019,7 @@ private constructor(
consequences().ifPresent { it.forEach { it.validate() } }
description().ifPresent { it.validate() }
publicationWindows().ifPresent { it.forEach { it.validate() } }
- reason().ifPresent { it.validate() }
+ reason()
severity()
summary().ifPresent { it.validate() }
url().ifPresent { it.validate() }
@@ -2016,7 +2050,7 @@ private constructor(
(consequences.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
(description.asKnown().getOrNull()?.validity() ?: 0) +
(publicationWindows.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
- (reason.asKnown().getOrNull()?.validity() ?: 0) +
+ (if (reason.asKnown().isPresent) 1 else 0) +
(if (severity.asKnown().isPresent) 1 else 0) +
(summary.asKnown().getOrNull()?.validity() ?: 0) +
(url.asKnown().getOrNull()?.validity() ?: 0)
@@ -2154,6 +2188,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): ActiveWindow = apply {
if (validated) {
return@apply
@@ -2488,6 +2532,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): AllAffect = apply {
if (validated) {
return@apply
@@ -2702,6 +2756,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): Consequence = apply {
if (validated) {
return@apply
@@ -2895,6 +2959,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): ConditionDetails = apply {
if (validated) {
return@apply
@@ -3110,6 +3184,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected
+ * types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): DiversionPath = apply {
if (validated) {
return@apply
@@ -3338,6 +3422,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): Description = apply {
if (validated) {
return@apply
@@ -3540,6 +3634,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): PublicationWindow = apply {
if (validated) {
return@apply
@@ -3587,154 +3691,6 @@ private constructor(
"PublicationWindow{from=$from, to=$to, additionalProperties=$additionalProperties}"
}
- /** Reason for the service alert, taken from TPEG codes. */
- class Reason @JsonCreator private constructor(private val value: JsonField) : Enum {
-
- /**
- * Returns this class instance's raw value.
- *
- * This is usually only useful if this instance was deserialized from data that doesn't
- * match any known member, and you want to know that value. For example, if the SDK is
- * on an older version than the API, then the API may respond with new members that the
- * SDK is unaware of.
- */
- @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
-
- companion object {
-
- @JvmField val EQUIPMENT_REASON = of("equipmentReason")
-
- @JvmField val ENVIRONMENT_REASON = of("environmentReason")
-
- @JvmField val PERSONNEL_REASON = of("personnelReason")
-
- @JvmField val MISCELLANEOUS_REASON = of("miscellaneousReason")
-
- @JvmField val SECURITY_ALERT = of("securityAlert")
-
- @JvmStatic fun of(value: String) = Reason(JsonField.of(value))
- }
-
- /** An enum containing [Reason]'s known values. */
- enum class Known {
- EQUIPMENT_REASON,
- ENVIRONMENT_REASON,
- PERSONNEL_REASON,
- MISCELLANEOUS_REASON,
- SECURITY_ALERT,
- }
-
- /**
- * An enum containing [Reason]'s known values, as well as an [_UNKNOWN] member.
- *
- * An instance of [Reason] can contain an unknown value in a couple of cases:
- * - It was deserialized from data that doesn't match any known member. For example, if
- * the SDK is on an older version than the API, then the API may respond with new
- * members that the SDK is unaware of.
- * - It was constructed with an arbitrary value using the [of] method.
- */
- enum class Value {
- EQUIPMENT_REASON,
- ENVIRONMENT_REASON,
- PERSONNEL_REASON,
- MISCELLANEOUS_REASON,
- SECURITY_ALERT,
- /**
- * An enum member indicating that [Reason] was instantiated with an unknown value.
- */
- _UNKNOWN,
- }
-
- /**
- * Returns an enum member corresponding to this class instance's value, or
- * [Value._UNKNOWN] if the class was instantiated with an unknown value.
- *
- * Use the [known] method instead if you're certain the value is always known or if you
- * want to throw for the unknown case.
- */
- fun value(): Value =
- when (this) {
- EQUIPMENT_REASON -> Value.EQUIPMENT_REASON
- ENVIRONMENT_REASON -> Value.ENVIRONMENT_REASON
- PERSONNEL_REASON -> Value.PERSONNEL_REASON
- MISCELLANEOUS_REASON -> Value.MISCELLANEOUS_REASON
- SECURITY_ALERT -> Value.SECURITY_ALERT
- else -> Value._UNKNOWN
- }
-
- /**
- * Returns an enum member corresponding to this class instance's value.
- *
- * Use the [value] method instead if you're uncertain the value is always known and
- * don't want to throw for the unknown case.
- *
- * @throws OnebusawaySdkInvalidDataException if this class instance's value is a not a
- * known member.
- */
- fun known(): Known =
- when (this) {
- EQUIPMENT_REASON -> Known.EQUIPMENT_REASON
- ENVIRONMENT_REASON -> Known.ENVIRONMENT_REASON
- PERSONNEL_REASON -> Known.PERSONNEL_REASON
- MISCELLANEOUS_REASON -> Known.MISCELLANEOUS_REASON
- SECURITY_ALERT -> Known.SECURITY_ALERT
- else -> throw OnebusawaySdkInvalidDataException("Unknown Reason: $value")
- }
-
- /**
- * Returns this class instance's primitive wire representation.
- *
- * This differs from the [toString] method because that method is primarily for
- * debugging and generally doesn't throw.
- *
- * @throws OnebusawaySdkInvalidDataException if this class instance's value does not
- * have the expected primitive type.
- */
- fun asString(): String =
- _value().asString().orElseThrow {
- OnebusawaySdkInvalidDataException("Value is not a String")
- }
-
- private var validated: Boolean = false
-
- fun validate(): Reason = apply {
- if (validated) {
- return@apply
- }
-
- known()
- validated = true
- }
-
- fun isValid(): Boolean =
- try {
- validate()
- true
- } catch (e: OnebusawaySdkInvalidDataException) {
- false
- }
-
- /**
- * Returns a score indicating how many valid values are contained in this object
- * recursively.
- *
- * Used for best match union deserialization.
- */
- @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
-
- override fun equals(other: Any?): Boolean {
- if (this === other) {
- return true
- }
-
- return other is Reason && value == other.value
- }
-
- override fun hashCode() = value.hashCode()
-
- override fun toString() = value.toString()
- }
-
class Summary
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
@@ -3867,6 +3823,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): Summary = apply {
if (validated) {
return@apply
@@ -4046,6 +4012,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): Url = apply {
if (validated) {
return@apply
@@ -4624,6 +4600,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Stop = apply {
if (validated) {
return@apply
@@ -5002,6 +4987,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): StopTime = apply {
if (validated) {
return@apply
@@ -5528,6 +5522,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Trip = apply {
if (validated) {
return@apply
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/ResponseWrapper.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/ResponseWrapper.kt
index a646686..5269fef 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/ResponseWrapper.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/ResponseWrapper.kt
@@ -220,6 +220,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): ResponseWrapper = apply {
if (validated) {
return@apply
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/agencieswithcoverage/AgenciesWithCoverageListResponse.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/agencieswithcoverage/AgenciesWithCoverageListResponse.kt
index b9e7051..6f7081f 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/agencieswithcoverage/AgenciesWithCoverageListResponse.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/agencieswithcoverage/AgenciesWithCoverageListResponse.kt
@@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import java.util.Collections
import java.util.Objects
+import java.util.Optional
import kotlin.jvm.optionals.getOrNull
import org.onebusaway.core.ExcludeMissing
import org.onebusaway.core.JsonField
@@ -266,6 +267,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): AgenciesWithCoverageListResponse = apply {
if (validated) {
return@apply
@@ -303,30 +312,24 @@ private constructor(
class Data
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
- private val limitExceeded: JsonField,
private val list: JsonField>,
private val references: JsonField,
+ private val limitExceeded: JsonField,
private val additionalProperties: MutableMap,
) {
@JsonCreator
private constructor(
- @JsonProperty("limitExceeded")
- @ExcludeMissing
- limitExceeded: JsonField = JsonMissing.of(),
@JsonProperty("list")
@ExcludeMissing
list: JsonField> = JsonMissing.of(),
@JsonProperty("references")
@ExcludeMissing
references: JsonField = JsonMissing.of(),
- ) : this(limitExceeded, list, references, mutableMapOf())
-
- /**
- * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun limitExceeded(): Boolean = limitExceeded.getRequired("limitExceeded")
+ @JsonProperty("limitExceeded")
+ @ExcludeMissing
+ limitExceeded: JsonField = JsonMissing.of(),
+ ) : this(list, references, limitExceeded, mutableMapOf())
/**
* @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or is
@@ -341,14 +344,10 @@ private constructor(
fun references(): References = references.getRequired("references")
/**
- * Returns the raw JSON value of [limitExceeded].
- *
- * Unlike [limitExceeded], this method doesn't throw if the JSON field has an unexpected
- * type.
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type (e.g.
+ * if the server responded with an unexpected value).
*/
- @JsonProperty("limitExceeded")
- @ExcludeMissing
- fun _limitExceeded(): JsonField = limitExceeded
+ fun limitExceeded(): Optional = limitExceeded.getOptional("limitExceeded")
/**
* Returns the raw JSON value of [list].
@@ -368,6 +367,16 @@ private constructor(
@ExcludeMissing
fun _references(): JsonField = references
+ /**
+ * Returns the raw JSON value of [limitExceeded].
+ *
+ * Unlike [limitExceeded], this method doesn't throw if the JSON field has an unexpected
+ * type.
+ */
+ @JsonProperty("limitExceeded")
+ @ExcludeMissing
+ fun _limitExceeded(): JsonField = limitExceeded
+
@JsonAnySetter
private fun putAdditionalProperty(key: String, value: JsonValue) {
additionalProperties.put(key, value)
@@ -387,7 +396,6 @@ private constructor(
*
* The following fields are required:
* ```java
- * .limitExceeded()
* .list()
* .references()
* ```
@@ -398,32 +406,19 @@ private constructor(
/** A builder for [Data]. */
class Builder internal constructor() {
- private var limitExceeded: JsonField? = null
private var list: JsonField>? = null
private var references: JsonField? = null
+ private var limitExceeded: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
internal fun from(data: Data) = apply {
- limitExceeded = data.limitExceeded
list = data.list.map { it.toMutableList() }
references = data.references
+ limitExceeded = data.limitExceeded
additionalProperties = data.additionalProperties.toMutableMap()
}
- fun limitExceeded(limitExceeded: Boolean) = limitExceeded(JsonField.of(limitExceeded))
-
- /**
- * Sets [Builder.limitExceeded] to an arbitrary JSON value.
- *
- * You should usually call [Builder.limitExceeded] with a well-typed [Boolean] value
- * instead. This method is primarily for setting the field to an undocumented or not yet
- * supported value.
- */
- fun limitExceeded(limitExceeded: JsonField) = apply {
- this.limitExceeded = limitExceeded
- }
-
fun list(list: kotlin.collections.List) = list(JsonField.of(list))
/**
@@ -462,6 +457,19 @@ private constructor(
this.references = references
}
+ fun limitExceeded(limitExceeded: Boolean) = limitExceeded(JsonField.of(limitExceeded))
+
+ /**
+ * Sets [Builder.limitExceeded] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.limitExceeded] with a well-typed [Boolean] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun limitExceeded(limitExceeded: JsonField) = apply {
+ this.limitExceeded = limitExceeded
+ }
+
fun additionalProperties(additionalProperties: Map) = apply {
this.additionalProperties.clear()
putAllAdditionalProperties(additionalProperties)
@@ -488,7 +496,6 @@ private constructor(
*
* The following fields are required:
* ```java
- * .limitExceeded()
* .list()
* .references()
* ```
@@ -497,23 +504,32 @@ private constructor(
*/
fun build(): Data =
Data(
- checkRequired("limitExceeded", limitExceeded),
checkRequired("list", list).map { it.toImmutable() },
checkRequired("references", references),
+ limitExceeded,
additionalProperties.toMutableMap(),
)
}
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Data = apply {
if (validated) {
return@apply
}
- limitExceeded()
list().forEach { it.validate() }
references().validate()
+ limitExceeded()
validated = true
}
@@ -533,9 +549,9 @@ private constructor(
*/
@JvmSynthetic
internal fun validity(): Int =
- (if (limitExceeded.asKnown().isPresent) 1 else 0) +
- (list.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
- (references.asKnown().getOrNull()?.validity() ?: 0)
+ (list.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
+ (references.asKnown().getOrNull()?.validity() ?: 0) +
+ (if (limitExceeded.asKnown().isPresent) 1 else 0)
class List
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
@@ -789,6 +805,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): List = apply {
if (validated) {
return@apply
@@ -854,20 +880,20 @@ private constructor(
}
return other is Data &&
- limitExceeded == other.limitExceeded &&
list == other.list &&
references == other.references &&
+ limitExceeded == other.limitExceeded &&
additionalProperties == other.additionalProperties
}
private val hashCode: Int by lazy {
- Objects.hash(limitExceeded, list, references, additionalProperties)
+ Objects.hash(list, references, limitExceeded, additionalProperties)
}
override fun hashCode(): Int = hashCode
override fun toString() =
- "Data{limitExceeded=$limitExceeded, list=$list, references=$references, additionalProperties=$additionalProperties}"
+ "Data{list=$list, references=$references, limitExceeded=$limitExceeded, additionalProperties=$additionalProperties}"
}
override fun equals(other: Any?): Boolean {
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/agency/AgencyRetrieveResponse.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/agency/AgencyRetrieveResponse.kt
index 05eaaf3..b668b70 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/agency/AgencyRetrieveResponse.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/agency/AgencyRetrieveResponse.kt
@@ -262,6 +262,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): AgencyRetrieveResponse = apply {
if (validated) {
return@apply
@@ -300,7 +308,6 @@ private constructor(
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
private val entry: JsonField,
- private val limitExceeded: JsonField,
private val references: JsonField,
private val additionalProperties: MutableMap,
) {
@@ -308,13 +315,10 @@ private constructor(
@JsonCreator
private constructor(
@JsonProperty("entry") @ExcludeMissing entry: JsonField = JsonMissing.of(),
- @JsonProperty("limitExceeded")
- @ExcludeMissing
- limitExceeded: JsonField = JsonMissing.of(),
@JsonProperty("references")
@ExcludeMissing
references: JsonField = JsonMissing.of(),
- ) : this(entry, limitExceeded, references, mutableMapOf())
+ ) : this(entry, references, mutableMapOf())
/**
* @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or is
@@ -322,12 +326,6 @@ private constructor(
*/
fun entry(): Entry = entry.getRequired("entry")
- /**
- * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun limitExceeded(): Boolean = limitExceeded.getRequired("limitExceeded")
-
/**
* @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or is
* unexpectedly missing or null (e.g. if the server responded with an unexpected value).
@@ -341,16 +339,6 @@ private constructor(
*/
@JsonProperty("entry") @ExcludeMissing fun _entry(): JsonField = entry
- /**
- * Returns the raw JSON value of [limitExceeded].
- *
- * Unlike [limitExceeded], this method doesn't throw if the JSON field has an unexpected
- * type.
- */
- @JsonProperty("limitExceeded")
- @ExcludeMissing
- fun _limitExceeded(): JsonField = limitExceeded
-
/**
* Returns the raw JSON value of [references].
*
@@ -380,7 +368,6 @@ private constructor(
* The following fields are required:
* ```java
* .entry()
- * .limitExceeded()
* .references()
* ```
*/
@@ -391,14 +378,12 @@ private constructor(
class Builder internal constructor() {
private var entry: JsonField? = null
- private var limitExceeded: JsonField? = null
private var references: JsonField? = null
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
internal fun from(data: Data) = apply {
entry = data.entry
- limitExceeded = data.limitExceeded
references = data.references
additionalProperties = data.additionalProperties.toMutableMap()
}
@@ -414,19 +399,6 @@ private constructor(
*/
fun entry(entry: JsonField) = apply { this.entry = entry }
- fun limitExceeded(limitExceeded: Boolean) = limitExceeded(JsonField.of(limitExceeded))
-
- /**
- * Sets [Builder.limitExceeded] to an arbitrary JSON value.
- *
- * You should usually call [Builder.limitExceeded] with a well-typed [Boolean] value
- * instead. This method is primarily for setting the field to an undocumented or not yet
- * supported value.
- */
- fun limitExceeded(limitExceeded: JsonField) = apply {
- this.limitExceeded = limitExceeded
- }
-
fun references(references: References) = references(JsonField.of(references))
/**
@@ -467,7 +439,6 @@ private constructor(
* The following fields are required:
* ```java
* .entry()
- * .limitExceeded()
* .references()
* ```
*
@@ -476,7 +447,6 @@ private constructor(
fun build(): Data =
Data(
checkRequired("entry", entry),
- checkRequired("limitExceeded", limitExceeded),
checkRequired("references", references),
additionalProperties.toMutableMap(),
)
@@ -484,13 +454,21 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Data = apply {
if (validated) {
return@apply
}
entry().validate()
- limitExceeded()
references().validate()
validated = true
}
@@ -512,7 +490,6 @@ private constructor(
@JvmSynthetic
internal fun validity(): Int =
(entry.asKnown().getOrNull()?.validity() ?: 0) +
- (if (limitExceeded.asKnown().isPresent) 1 else 0) +
(references.asKnown().getOrNull()?.validity() ?: 0)
class Entry
@@ -934,6 +911,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): Entry = apply {
if (validated) {
return@apply
@@ -1027,19 +1014,16 @@ private constructor(
return other is Data &&
entry == other.entry &&
- limitExceeded == other.limitExceeded &&
references == other.references &&
additionalProperties == other.additionalProperties
}
- private val hashCode: Int by lazy {
- Objects.hash(entry, limitExceeded, references, additionalProperties)
- }
+ private val hashCode: Int by lazy { Objects.hash(entry, references, additionalProperties) }
override fun hashCode(): Int = hashCode
override fun toString() =
- "Data{entry=$entry, limitExceeded=$limitExceeded, references=$references, additionalProperties=$additionalProperties}"
+ "Data{entry=$entry, references=$references, additionalProperties=$additionalProperties}"
}
override fun equals(other: Any?): Boolean {
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalanddeparture/ArrivalAndDepartureListResponse.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalanddeparture/ArrivalAndDepartureListResponse.kt
index 0102ccf..3b88287 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalanddeparture/ArrivalAndDepartureListResponse.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalanddeparture/ArrivalAndDepartureListResponse.kt
@@ -267,6 +267,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): ArrivalAndDepartureListResponse = apply {
if (validated) {
return@apply
@@ -451,6 +459,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Data = apply {
if (validated) {
return@apply
@@ -624,6 +641,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): Entry = apply {
if (validated) {
return@apply
@@ -1874,7 +1901,10 @@ private constructor(
}
/** Information about frequency-based scheduling, if applicable to the trip. */
- fun frequency(frequency: String) = frequency(JsonField.of(frequency))
+ fun frequency(frequency: String?) = frequency(JsonField.ofNullable(frequency))
+
+ /** Alias for calling [Builder.frequency] with `frequency.orElse(null)`. */
+ fun frequency(frequency: Optional) = frequency(frequency.getOrNull())
/**
* Sets [Builder.frequency] to an arbitrary JSON value.
@@ -1947,8 +1977,15 @@ private constructor(
}
/** Interval for predicted arrival time, if available. */
- fun predictedArrivalInterval(predictedArrivalInterval: String) =
- predictedArrivalInterval(JsonField.of(predictedArrivalInterval))
+ fun predictedArrivalInterval(predictedArrivalInterval: String?) =
+ predictedArrivalInterval(JsonField.ofNullable(predictedArrivalInterval))
+
+ /**
+ * Alias for calling [Builder.predictedArrivalInterval] with
+ * `predictedArrivalInterval.orElse(null)`.
+ */
+ fun predictedArrivalInterval(predictedArrivalInterval: Optional) =
+ predictedArrivalInterval(predictedArrivalInterval.getOrNull())
/**
* Sets [Builder.predictedArrivalInterval] to an arbitrary JSON value.
@@ -1963,8 +2000,15 @@ private constructor(
}
/** Interval for predicted departure time, if available. */
- fun predictedDepartureInterval(predictedDepartureInterval: String) =
- predictedDepartureInterval(JsonField.of(predictedDepartureInterval))
+ fun predictedDepartureInterval(predictedDepartureInterval: String?) =
+ predictedDepartureInterval(JsonField.ofNullable(predictedDepartureInterval))
+
+ /**
+ * Alias for calling [Builder.predictedDepartureInterval] with
+ * `predictedDepartureInterval.orElse(null)`.
+ */
+ fun predictedDepartureInterval(predictedDepartureInterval: Optional) =
+ predictedDepartureInterval(predictedDepartureInterval.getOrNull())
/**
* Sets [Builder.predictedDepartureInterval] to an arbitrary JSON value.
@@ -2030,8 +2074,15 @@ private constructor(
}
/** Interval for scheduled arrival time. */
- fun scheduledArrivalInterval(scheduledArrivalInterval: String) =
- scheduledArrivalInterval(JsonField.of(scheduledArrivalInterval))
+ fun scheduledArrivalInterval(scheduledArrivalInterval: String?) =
+ scheduledArrivalInterval(JsonField.ofNullable(scheduledArrivalInterval))
+
+ /**
+ * Alias for calling [Builder.scheduledArrivalInterval] with
+ * `scheduledArrivalInterval.orElse(null)`.
+ */
+ fun scheduledArrivalInterval(scheduledArrivalInterval: Optional) =
+ scheduledArrivalInterval(scheduledArrivalInterval.getOrNull())
/**
* Sets [Builder.scheduledArrivalInterval] to an arbitrary JSON value.
@@ -2046,8 +2097,15 @@ private constructor(
}
/** Interval for scheduled departure time. */
- fun scheduledDepartureInterval(scheduledDepartureInterval: String) =
- scheduledDepartureInterval(JsonField.of(scheduledDepartureInterval))
+ fun scheduledDepartureInterval(scheduledDepartureInterval: String?) =
+ scheduledDepartureInterval(JsonField.ofNullable(scheduledDepartureInterval))
+
+ /**
+ * Alias for calling [Builder.scheduledDepartureInterval] with
+ * `scheduledDepartureInterval.orElse(null)`.
+ */
+ fun scheduledDepartureInterval(scheduledDepartureInterval: Optional) =
+ scheduledDepartureInterval(scheduledDepartureInterval.getOrNull())
/**
* Sets [Builder.scheduledDepartureInterval] to an arbitrary JSON value.
@@ -2220,6 +2278,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): ArrivalsAndDeparture = apply {
if (validated) {
return@apply
@@ -2636,7 +2704,7 @@ private constructor(
fun frequency(): Optional = frequency.getOptional("frequency")
/**
- * Last known location of the transit vehicle.
+ * Last known location of the transit vehicle (optional).
*
* @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected
* type (e.g. if the server responded with an unexpected value).
@@ -3366,7 +3434,12 @@ private constructor(
/**
* Information about frequency-based scheduling, if applicable to the trip.
*/
- fun frequency(frequency: String) = frequency(JsonField.of(frequency))
+ fun frequency(frequency: String?) =
+ frequency(JsonField.ofNullable(frequency))
+
+ /** Alias for calling [Builder.frequency] with `frequency.orElse(null)`. */
+ fun frequency(frequency: Optional) =
+ frequency(frequency.getOrNull())
/**
* Sets [Builder.frequency] to an arbitrary JSON value.
@@ -3379,9 +3452,16 @@ private constructor(
this.frequency = frequency
}
- /** Last known location of the transit vehicle. */
- fun lastKnownLocation(lastKnownLocation: LastKnownLocation) =
- lastKnownLocation(JsonField.of(lastKnownLocation))
+ /** Last known location of the transit vehicle (optional). */
+ fun lastKnownLocation(lastKnownLocation: LastKnownLocation?) =
+ lastKnownLocation(JsonField.ofNullable(lastKnownLocation))
+
+ /**
+ * Alias for calling [Builder.lastKnownLocation] with
+ * `lastKnownLocation.orElse(null)`.
+ */
+ fun lastKnownLocation(lastKnownLocation: Optional) =
+ lastKnownLocation(lastKnownLocation.getOrNull())
/**
* Sets [Builder.lastKnownLocation] to an arbitrary JSON value.
@@ -3623,6 +3703,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected
+ * types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): TripStatus = apply {
if (validated) {
return@apply
@@ -3702,7 +3792,7 @@ private constructor(
(situationIds.asKnown().getOrNull()?.size ?: 0) +
(if (vehicleId.asKnown().isPresent) 1 else 0)
- /** Last known location of the transit vehicle. */
+ /** Last known location of the transit vehicle (optional). */
class LastKnownLocation
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
@@ -3850,6 +3940,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this
+ * object doesn't match its expected type.
+ */
fun validate(): LastKnownLocation = apply {
if (validated) {
return@apply
@@ -4046,6 +4146,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this
+ * object doesn't match its expected type.
+ */
fun validate(): Position = apply {
if (validated) {
return@apply
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalanddeparture/ArrivalAndDepartureRetrieveResponse.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalanddeparture/ArrivalAndDepartureRetrieveResponse.kt
index d06b629..f5a5984 100644
--- a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalanddeparture/ArrivalAndDepartureRetrieveResponse.kt
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalanddeparture/ArrivalAndDepartureRetrieveResponse.kt
@@ -268,6 +268,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): ArrivalAndDepartureRetrieveResponse = apply {
if (validated) {
return@apply
@@ -452,6 +460,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Data = apply {
if (validated) {
return@apply
@@ -1688,7 +1705,10 @@ private constructor(
}
/** Information about frequency-based scheduling, if applicable to the trip. */
- fun frequency(frequency: String) = frequency(JsonField.of(frequency))
+ fun frequency(frequency: String?) = frequency(JsonField.ofNullable(frequency))
+
+ /** Alias for calling [Builder.frequency] with `frequency.orElse(null)`. */
+ fun frequency(frequency: Optional) = frequency(frequency.getOrNull())
/**
* Sets [Builder.frequency] to an arbitrary JSON value.
@@ -1757,8 +1777,15 @@ private constructor(
fun predicted(predicted: JsonField) = apply { this.predicted = predicted }
/** Interval for predicted arrival time, if available. */
- fun predictedArrivalInterval(predictedArrivalInterval: String) =
- predictedArrivalInterval(JsonField.of(predictedArrivalInterval))
+ fun predictedArrivalInterval(predictedArrivalInterval: String?) =
+ predictedArrivalInterval(JsonField.ofNullable(predictedArrivalInterval))
+
+ /**
+ * Alias for calling [Builder.predictedArrivalInterval] with
+ * `predictedArrivalInterval.orElse(null)`.
+ */
+ fun predictedArrivalInterval(predictedArrivalInterval: Optional) =
+ predictedArrivalInterval(predictedArrivalInterval.getOrNull())
/**
* Sets [Builder.predictedArrivalInterval] to an arbitrary JSON value.
@@ -1772,8 +1799,15 @@ private constructor(
}
/** Interval for predicted departure time, if available. */
- fun predictedDepartureInterval(predictedDepartureInterval: String) =
- predictedDepartureInterval(JsonField.of(predictedDepartureInterval))
+ fun predictedDepartureInterval(predictedDepartureInterval: String?) =
+ predictedDepartureInterval(JsonField.ofNullable(predictedDepartureInterval))
+
+ /**
+ * Alias for calling [Builder.predictedDepartureInterval] with
+ * `predictedDepartureInterval.orElse(null)`.
+ */
+ fun predictedDepartureInterval(predictedDepartureInterval: Optional) =
+ predictedDepartureInterval(predictedDepartureInterval.getOrNull())
/**
* Sets [Builder.predictedDepartureInterval] to an arbitrary JSON value.
@@ -1839,8 +1873,15 @@ private constructor(
}
/** Interval for scheduled arrival time. */
- fun scheduledArrivalInterval(scheduledArrivalInterval: String) =
- scheduledArrivalInterval(JsonField.of(scheduledArrivalInterval))
+ fun scheduledArrivalInterval(scheduledArrivalInterval: String?) =
+ scheduledArrivalInterval(JsonField.ofNullable(scheduledArrivalInterval))
+
+ /**
+ * Alias for calling [Builder.scheduledArrivalInterval] with
+ * `scheduledArrivalInterval.orElse(null)`.
+ */
+ fun scheduledArrivalInterval(scheduledArrivalInterval: Optional) =
+ scheduledArrivalInterval(scheduledArrivalInterval.getOrNull())
/**
* Sets [Builder.scheduledArrivalInterval] to an arbitrary JSON value.
@@ -1854,8 +1895,15 @@ private constructor(
}
/** Interval for scheduled departure time. */
- fun scheduledDepartureInterval(scheduledDepartureInterval: String) =
- scheduledDepartureInterval(JsonField.of(scheduledDepartureInterval))
+ fun scheduledDepartureInterval(scheduledDepartureInterval: String?) =
+ scheduledDepartureInterval(JsonField.ofNullable(scheduledDepartureInterval))
+
+ /**
+ * Alias for calling [Builder.scheduledDepartureInterval] with
+ * `scheduledDepartureInterval.orElse(null)`.
+ */
+ fun scheduledDepartureInterval(scheduledDepartureInterval: Optional) =
+ scheduledDepartureInterval(scheduledDepartureInterval.getOrNull())
/**
* Sets [Builder.scheduledDepartureInterval] to an arbitrary JSON value.
@@ -2028,6 +2076,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): Entry = apply {
if (validated) {
return@apply
@@ -2437,7 +2495,7 @@ private constructor(
fun frequency(): Optional = frequency.getOptional("frequency")
/**
- * Last known location of the transit vehicle.
+ * Last known location of the transit vehicle (optional).
*
* @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected
* type (e.g. if the server responded with an unexpected value).
@@ -3151,7 +3209,10 @@ private constructor(
}
/** Information about frequency-based scheduling, if applicable to the trip. */
- fun frequency(frequency: String) = frequency(JsonField.of(frequency))
+ fun frequency(frequency: String?) = frequency(JsonField.ofNullable(frequency))
+
+ /** Alias for calling [Builder.frequency] with `frequency.orElse(null)`. */
+ fun frequency(frequency: Optional) = frequency(frequency.getOrNull())
/**
* Sets [Builder.frequency] to an arbitrary JSON value.
@@ -3164,9 +3225,16 @@ private constructor(
this.frequency = frequency
}
- /** Last known location of the transit vehicle. */
- fun lastKnownLocation(lastKnownLocation: LastKnownLocation) =
- lastKnownLocation(JsonField.of(lastKnownLocation))
+ /** Last known location of the transit vehicle (optional). */
+ fun lastKnownLocation(lastKnownLocation: LastKnownLocation?) =
+ lastKnownLocation(JsonField.ofNullable(lastKnownLocation))
+
+ /**
+ * Alias for calling [Builder.lastKnownLocation] with
+ * `lastKnownLocation.orElse(null)`.
+ */
+ fun lastKnownLocation(lastKnownLocation: Optional) =
+ lastKnownLocation(lastKnownLocation.getOrNull())
/**
* Sets [Builder.lastKnownLocation] to an arbitrary JSON value.
@@ -3396,6 +3464,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): TripStatus = apply {
if (validated) {
return@apply
@@ -3475,7 +3553,7 @@ private constructor(
(situationIds.asKnown().getOrNull()?.size ?: 0) +
(if (vehicleId.asKnown().isPresent) 1 else 0)
- /** Last known location of the transit vehicle. */
+ /** Last known location of the transit vehicle (optional). */
class LastKnownLocation
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
@@ -3620,6 +3698,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected
+ * types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): LastKnownLocation = apply {
if (validated) {
return@apply
@@ -3811,6 +3899,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected
+ * types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Position = apply {
if (validated) {
return@apply
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalsanddeparturesforlocation/ArrivalsAndDeparturesForLocationListParams.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalsanddeparturesforlocation/ArrivalsAndDeparturesForLocationListParams.kt
new file mode 100644
index 0000000..1d76ebc
--- /dev/null
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalsanddeparturesforlocation/ArrivalsAndDeparturesForLocationListParams.kt
@@ -0,0 +1,451 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package org.onebusaway.models.arrivalsanddeparturesforlocation
+
+import java.util.Objects
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
+import org.onebusaway.core.Params
+import org.onebusaway.core.checkRequired
+import org.onebusaway.core.http.Headers
+import org.onebusaway.core.http.QueryParams
+
+/**
+ * Returns real-time arrival and departure data for stops within a bounding box or radius centered
+ * on a specific location.
+ */
+class ArrivalsAndDeparturesForLocationListParams
+private constructor(
+ private val lat: Double,
+ private val lon: Double,
+ private val emptyReturnsNotFound: Boolean?,
+ private val latSpan: Double?,
+ private val lonSpan: Double?,
+ private val maxCount: Long?,
+ private val minutesAfter: Long?,
+ private val minutesBefore: Long?,
+ private val radius: Double?,
+ private val routeType: String?,
+ private val time: Long?,
+ private val additionalHeaders: Headers,
+ private val additionalQueryParams: QueryParams,
+) : Params {
+
+ /** The latitude coordinate of the search center. */
+ fun lat(): Double = lat
+
+ /** The longitude coordinate of the search center. */
+ fun lon(): Double = lon
+
+ /** If true, returns a 404 Not Found error instead of an empty result. */
+ fun emptyReturnsNotFound(): Optional = Optional.ofNullable(emptyReturnsNotFound)
+
+ /** Sets the latitude limits of the search bounding box. */
+ fun latSpan(): Optional = Optional.ofNullable(latSpan)
+
+ /** Sets the longitude limits of the search bounding box. */
+ fun lonSpan(): Optional = Optional.ofNullable(lonSpan)
+
+ /**
+ * The max size of the list of nearby stops and arrivals to return. Defaults to 250, max 1000.
+ */
+ fun maxCount(): Optional = Optional.ofNullable(maxCount)
+
+ /** Include arrivals and departures this many minutes after the query time. */
+ fun minutesAfter(): Optional = Optional.ofNullable(minutesAfter)
+
+ /** Include arrivals and departures this many minutes before the query time. */
+ fun minutesBefore(): Optional = Optional.ofNullable(minutesBefore)
+
+ /** The search radius in meters. */
+ fun radius(): Optional = Optional.ofNullable(radius)
+
+ /** Optional list of GTFS routeTypes to filter by (comma delimited) e.g. "1,2,3". */
+ fun routeType(): Optional = Optional.ofNullable(routeType)
+
+ /**
+ * By default, returns the status right now. Can be queried at a specific time (milliseconds
+ * since epoch) for testing.
+ */
+ fun time(): Optional = Optional.ofNullable(time)
+
+ /** Additional headers to send with the request. */
+ fun _additionalHeaders(): Headers = additionalHeaders
+
+ /** Additional query param to send with the request. */
+ fun _additionalQueryParams(): QueryParams = additionalQueryParams
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of
+ * [ArrivalsAndDeparturesForLocationListParams].
+ *
+ * The following fields are required:
+ * ```java
+ * .lat()
+ * .lon()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [ArrivalsAndDeparturesForLocationListParams]. */
+ class Builder internal constructor() {
+
+ private var lat: Double? = null
+ private var lon: Double? = null
+ private var emptyReturnsNotFound: Boolean? = null
+ private var latSpan: Double? = null
+ private var lonSpan: Double? = null
+ private var maxCount: Long? = null
+ private var minutesAfter: Long? = null
+ private var minutesBefore: Long? = null
+ private var radius: Double? = null
+ private var routeType: String? = null
+ private var time: Long? = null
+ private var additionalHeaders: Headers.Builder = Headers.builder()
+ private var additionalQueryParams: QueryParams.Builder = QueryParams.builder()
+
+ @JvmSynthetic
+ internal fun from(
+ arrivalsAndDeparturesForLocationListParams: ArrivalsAndDeparturesForLocationListParams
+ ) = apply {
+ lat = arrivalsAndDeparturesForLocationListParams.lat
+ lon = arrivalsAndDeparturesForLocationListParams.lon
+ emptyReturnsNotFound = arrivalsAndDeparturesForLocationListParams.emptyReturnsNotFound
+ latSpan = arrivalsAndDeparturesForLocationListParams.latSpan
+ lonSpan = arrivalsAndDeparturesForLocationListParams.lonSpan
+ maxCount = arrivalsAndDeparturesForLocationListParams.maxCount
+ minutesAfter = arrivalsAndDeparturesForLocationListParams.minutesAfter
+ minutesBefore = arrivalsAndDeparturesForLocationListParams.minutesBefore
+ radius = arrivalsAndDeparturesForLocationListParams.radius
+ routeType = arrivalsAndDeparturesForLocationListParams.routeType
+ time = arrivalsAndDeparturesForLocationListParams.time
+ additionalHeaders =
+ arrivalsAndDeparturesForLocationListParams.additionalHeaders.toBuilder()
+ additionalQueryParams =
+ arrivalsAndDeparturesForLocationListParams.additionalQueryParams.toBuilder()
+ }
+
+ /** The latitude coordinate of the search center. */
+ fun lat(lat: Double) = apply { this.lat = lat }
+
+ /** The longitude coordinate of the search center. */
+ fun lon(lon: Double) = apply { this.lon = lon }
+
+ /** If true, returns a 404 Not Found error instead of an empty result. */
+ fun emptyReturnsNotFound(emptyReturnsNotFound: Boolean?) = apply {
+ this.emptyReturnsNotFound = emptyReturnsNotFound
+ }
+
+ /**
+ * Alias for [Builder.emptyReturnsNotFound].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun emptyReturnsNotFound(emptyReturnsNotFound: Boolean) =
+ emptyReturnsNotFound(emptyReturnsNotFound as Boolean?)
+
+ /**
+ * Alias for calling [Builder.emptyReturnsNotFound] with
+ * `emptyReturnsNotFound.orElse(null)`.
+ */
+ fun emptyReturnsNotFound(emptyReturnsNotFound: Optional) =
+ emptyReturnsNotFound(emptyReturnsNotFound.getOrNull())
+
+ /** Sets the latitude limits of the search bounding box. */
+ fun latSpan(latSpan: Double?) = apply { this.latSpan = latSpan }
+
+ /**
+ * Alias for [Builder.latSpan].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun latSpan(latSpan: Double) = latSpan(latSpan as Double?)
+
+ /** Alias for calling [Builder.latSpan] with `latSpan.orElse(null)`. */
+ fun latSpan(latSpan: Optional) = latSpan(latSpan.getOrNull())
+
+ /** Sets the longitude limits of the search bounding box. */
+ fun lonSpan(lonSpan: Double?) = apply { this.lonSpan = lonSpan }
+
+ /**
+ * Alias for [Builder.lonSpan].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun lonSpan(lonSpan: Double) = lonSpan(lonSpan as Double?)
+
+ /** Alias for calling [Builder.lonSpan] with `lonSpan.orElse(null)`. */
+ fun lonSpan(lonSpan: Optional) = lonSpan(lonSpan.getOrNull())
+
+ /**
+ * The max size of the list of nearby stops and arrivals to return. Defaults to 250,
+ * max 1000.
+ */
+ fun maxCount(maxCount: Long?) = apply { this.maxCount = maxCount }
+
+ /**
+ * Alias for [Builder.maxCount].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun maxCount(maxCount: Long) = maxCount(maxCount as Long?)
+
+ /** Alias for calling [Builder.maxCount] with `maxCount.orElse(null)`. */
+ fun maxCount(maxCount: Optional) = maxCount(maxCount.getOrNull())
+
+ /** Include arrivals and departures this many minutes after the query time. */
+ fun minutesAfter(minutesAfter: Long?) = apply { this.minutesAfter = minutesAfter }
+
+ /**
+ * Alias for [Builder.minutesAfter].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun minutesAfter(minutesAfter: Long) = minutesAfter(minutesAfter as Long?)
+
+ /** Alias for calling [Builder.minutesAfter] with `minutesAfter.orElse(null)`. */
+ fun minutesAfter(minutesAfter: Optional) = minutesAfter(minutesAfter.getOrNull())
+
+ /** Include arrivals and departures this many minutes before the query time. */
+ fun minutesBefore(minutesBefore: Long?) = apply { this.minutesBefore = minutesBefore }
+
+ /**
+ * Alias for [Builder.minutesBefore].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun minutesBefore(minutesBefore: Long) = minutesBefore(minutesBefore as Long?)
+
+ /** Alias for calling [Builder.minutesBefore] with `minutesBefore.orElse(null)`. */
+ fun minutesBefore(minutesBefore: Optional) = minutesBefore(minutesBefore.getOrNull())
+
+ /** The search radius in meters. */
+ fun radius(radius: Double?) = apply { this.radius = radius }
+
+ /**
+ * Alias for [Builder.radius].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun radius(radius: Double) = radius(radius as Double?)
+
+ /** Alias for calling [Builder.radius] with `radius.orElse(null)`. */
+ fun radius(radius: Optional) = radius(radius.getOrNull())
+
+ /** Optional list of GTFS routeTypes to filter by (comma delimited) e.g. "1,2,3". */
+ fun routeType(routeType: String?) = apply { this.routeType = routeType }
+
+ /** Alias for calling [Builder.routeType] with `routeType.orElse(null)`. */
+ fun routeType(routeType: Optional) = routeType(routeType.getOrNull())
+
+ /**
+ * By default, returns the status right now. Can be queried at a specific time (milliseconds
+ * since epoch) for testing.
+ */
+ fun time(time: Long?) = apply { this.time = time }
+
+ /**
+ * Alias for [Builder.time].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun time(time: Long) = time(time as Long?)
+
+ /** Alias for calling [Builder.time] with `time.orElse(null)`. */
+ fun time(time: Optional) = time(time.getOrNull())
+
+ fun additionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun additionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun putAdditionalHeader(name: String, value: String) = apply {
+ additionalHeaders.put(name, value)
+ }
+
+ fun putAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.put(name, values)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun replaceAdditionalHeaders(name: String, value: String) = apply {
+ additionalHeaders.replace(name, value)
+ }
+
+ fun replaceAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.replace(name, values)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) }
+
+ fun removeAllAdditionalHeaders(names: Set) = apply {
+ additionalHeaders.removeAll(names)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: Map>) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun putAdditionalQueryParam(key: String, value: String) = apply {
+ additionalQueryParams.put(key, value)
+ }
+
+ fun putAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.put(key, values)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, value: String) = apply {
+ additionalQueryParams.replace(key, value)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.replace(key, values)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) }
+
+ fun removeAllAdditionalQueryParams(keys: Set) = apply {
+ additionalQueryParams.removeAll(keys)
+ }
+
+ /**
+ * Returns an immutable instance of [ArrivalsAndDeparturesForLocationListParams].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .lat()
+ * .lon()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): ArrivalsAndDeparturesForLocationListParams =
+ ArrivalsAndDeparturesForLocationListParams(
+ checkRequired("lat", lat),
+ checkRequired("lon", lon),
+ emptyReturnsNotFound,
+ latSpan,
+ lonSpan,
+ maxCount,
+ minutesAfter,
+ minutesBefore,
+ radius,
+ routeType,
+ time,
+ additionalHeaders.build(),
+ additionalQueryParams.build(),
+ )
+ }
+
+ override fun _headers(): Headers = additionalHeaders
+
+ override fun _queryParams(): QueryParams =
+ QueryParams.builder()
+ .apply {
+ put("lat", lat.toString())
+ put("lon", lon.toString())
+ emptyReturnsNotFound?.let { put("emptyReturnsNotFound", it.toString()) }
+ latSpan?.let { put("latSpan", it.toString()) }
+ lonSpan?.let { put("lonSpan", it.toString()) }
+ maxCount?.let { put("maxCount", it.toString()) }
+ minutesAfter?.let { put("minutesAfter", it.toString()) }
+ minutesBefore?.let { put("minutesBefore", it.toString()) }
+ radius?.let { put("radius", it.toString()) }
+ routeType?.let { put("routeType", it) }
+ time?.let { put("time", it.toString()) }
+ putAll(additionalQueryParams)
+ }
+ .build()
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is ArrivalsAndDeparturesForLocationListParams &&
+ lat == other.lat &&
+ lon == other.lon &&
+ emptyReturnsNotFound == other.emptyReturnsNotFound &&
+ latSpan == other.latSpan &&
+ lonSpan == other.lonSpan &&
+ maxCount == other.maxCount &&
+ minutesAfter == other.minutesAfter &&
+ minutesBefore == other.minutesBefore &&
+ radius == other.radius &&
+ routeType == other.routeType &&
+ time == other.time &&
+ additionalHeaders == other.additionalHeaders &&
+ additionalQueryParams == other.additionalQueryParams
+ }
+
+ override fun hashCode(): Int =
+ Objects.hash(
+ lat,
+ lon,
+ emptyReturnsNotFound,
+ latSpan,
+ lonSpan,
+ maxCount,
+ minutesAfter,
+ minutesBefore,
+ radius,
+ routeType,
+ time,
+ additionalHeaders,
+ additionalQueryParams,
+ )
+
+ override fun toString() =
+ "ArrivalsAndDeparturesForLocationListParams{lat=$lat, lon=$lon, emptyReturnsNotFound=$emptyReturnsNotFound, latSpan=$latSpan, lonSpan=$lonSpan, maxCount=$maxCount, minutesAfter=$minutesAfter, minutesBefore=$minutesBefore, radius=$radius, routeType=$routeType, time=$time, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}"
+}
diff --git a/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalsanddeparturesforlocation/ArrivalsAndDeparturesForLocationListResponse.kt b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalsanddeparturesforlocation/ArrivalsAndDeparturesForLocationListResponse.kt
new file mode 100644
index 0000000..4d60364
--- /dev/null
+++ b/onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/models/arrivalsanddeparturesforlocation/ArrivalsAndDeparturesForLocationListResponse.kt
@@ -0,0 +1,4853 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package org.onebusaway.models.arrivalsanddeparturesforlocation
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter
+import com.fasterxml.jackson.annotation.JsonAnySetter
+import com.fasterxml.jackson.annotation.JsonCreator
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.util.Collections
+import java.util.Objects
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
+import org.onebusaway.core.ExcludeMissing
+import org.onebusaway.core.JsonField
+import org.onebusaway.core.JsonMissing
+import org.onebusaway.core.JsonValue
+import org.onebusaway.core.checkKnown
+import org.onebusaway.core.checkRequired
+import org.onebusaway.core.toImmutable
+import org.onebusaway.errors.OnebusawaySdkInvalidDataException
+import org.onebusaway.models.References
+import org.onebusaway.models.ResponseWrapper
+
+class ArrivalsAndDeparturesForLocationListResponse
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val code: JsonField,
+ private val currentTime: JsonField,
+ private val text: JsonField,
+ private val version: JsonField,
+ private val data: JsonField,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("code") @ExcludeMissing code: JsonField = JsonMissing.of(),
+ @JsonProperty("currentTime")
+ @ExcludeMissing
+ currentTime: JsonField = JsonMissing.of(),
+ @JsonProperty("text") @ExcludeMissing text: JsonField = JsonMissing.of(),
+ @JsonProperty("version") @ExcludeMissing version: JsonField = JsonMissing.of(),
+ @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(),
+ ) : this(code, currentTime, text, version, data, mutableMapOf())
+
+ fun toResponseWrapper(): ResponseWrapper =
+ ResponseWrapper.builder()
+ .code(code)
+ .currentTime(currentTime)
+ .text(text)
+ .version(version)
+ .build()
+
+ /**
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun code(): Long = code.getRequired("code")
+
+ /**
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun currentTime(): Long = currentTime.getRequired("currentTime")
+
+ /**
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun text(): String = text.getRequired("text")
+
+ /**
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun version(): Long = version.getRequired("version")
+
+ /**
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun data(): Data = data.getRequired("data")
+
+ /**
+ * Returns the raw JSON value of [code].
+ *
+ * Unlike [code], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("code") @ExcludeMissing fun _code(): JsonField = code
+
+ /**
+ * Returns the raw JSON value of [currentTime].
+ *
+ * Unlike [currentTime], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("currentTime") @ExcludeMissing fun _currentTime(): JsonField = currentTime
+
+ /**
+ * Returns the raw JSON value of [text].
+ *
+ * Unlike [text], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("text") @ExcludeMissing fun _text(): JsonField = text
+
+ /**
+ * Returns the raw JSON value of [version].
+ *
+ * Unlike [version], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("version") @ExcludeMissing fun _version(): JsonField = version
+
+ /**
+ * Returns the raw JSON value of [data].
+ *
+ * Unlike [data], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of
+ * [ArrivalsAndDeparturesForLocationListResponse].
+ *
+ * The following fields are required:
+ * ```java
+ * .code()
+ * .currentTime()
+ * .text()
+ * .version()
+ * .data()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [ArrivalsAndDeparturesForLocationListResponse]. */
+ class Builder internal constructor() {
+
+ private var code: JsonField? = null
+ private var currentTime: JsonField? = null
+ private var text: JsonField? = null
+ private var version: JsonField? = null
+ private var data: JsonField? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(
+ arrivalsAndDeparturesForLocationListResponse:
+ ArrivalsAndDeparturesForLocationListResponse
+ ) = apply {
+ code = arrivalsAndDeparturesForLocationListResponse.code
+ currentTime = arrivalsAndDeparturesForLocationListResponse.currentTime
+ text = arrivalsAndDeparturesForLocationListResponse.text
+ version = arrivalsAndDeparturesForLocationListResponse.version
+ data = arrivalsAndDeparturesForLocationListResponse.data
+ additionalProperties =
+ arrivalsAndDeparturesForLocationListResponse.additionalProperties.toMutableMap()
+ }
+
+ fun code(code: Long) = code(JsonField.of(code))
+
+ /**
+ * Sets [Builder.code] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.code] with a well-typed [Long] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun code(code: JsonField) = apply { this.code = code }
+
+ fun currentTime(currentTime: Long) = currentTime(JsonField.of(currentTime))
+
+ /**
+ * Sets [Builder.currentTime] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.currentTime] with a well-typed [Long] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun currentTime(currentTime: JsonField) = apply { this.currentTime = currentTime }
+
+ fun text(text: String) = text(JsonField.of(text))
+
+ /**
+ * Sets [Builder.text] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.text] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun text(text: JsonField) = apply { this.text = text }
+
+ fun version(version: Long) = version(JsonField.of(version))
+
+ /**
+ * Sets [Builder.version] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.version] with a well-typed [Long] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun version(version: JsonField) = apply { this.version = version }
+
+ fun data(data: Data) = data(JsonField.of(data))
+
+ /**
+ * Sets [Builder.data] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.data] with a well-typed [Data] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun data(data: JsonField) = apply { this.data = data }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [ArrivalsAndDeparturesForLocationListResponse].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .code()
+ * .currentTime()
+ * .text()
+ * .version()
+ * .data()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): ArrivalsAndDeparturesForLocationListResponse =
+ ArrivalsAndDeparturesForLocationListResponse(
+ checkRequired("code", code),
+ checkRequired("currentTime", currentTime),
+ checkRequired("text", text),
+ checkRequired("version", version),
+ checkRequired("data", data),
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
+ fun validate(): ArrivalsAndDeparturesForLocationListResponse = apply {
+ if (validated) {
+ return@apply
+ }
+
+ code()
+ currentTime()
+ text()
+ version()
+ data().validate()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: OnebusawaySdkInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (if (code.asKnown().isPresent) 1 else 0) +
+ (if (currentTime.asKnown().isPresent) 1 else 0) +
+ (if (text.asKnown().isPresent) 1 else 0) +
+ (if (version.asKnown().isPresent) 1 else 0) +
+ (data.asKnown().getOrNull()?.validity() ?: 0)
+
+ class Data
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
+ private constructor(
+ private val entry: JsonField,
+ private val references: JsonField,
+ private val additionalProperties: MutableMap,
+ ) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("entry") @ExcludeMissing entry: JsonField = JsonMissing.of(),
+ @JsonProperty("references")
+ @ExcludeMissing
+ references: JsonField = JsonMissing.of(),
+ ) : this(entry, references, mutableMapOf())
+
+ /**
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun entry(): Entry = entry.getRequired("entry")
+
+ /**
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun references(): References = references.getRequired("references")
+
+ /**
+ * Returns the raw JSON value of [entry].
+ *
+ * Unlike [entry], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("entry") @ExcludeMissing fun _entry(): JsonField = entry
+
+ /**
+ * Returns the raw JSON value of [references].
+ *
+ * Unlike [references], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("references")
+ @ExcludeMissing
+ fun _references(): JsonField = references
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [Data].
+ *
+ * The following fields are required:
+ * ```java
+ * .entry()
+ * .references()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [Data]. */
+ class Builder internal constructor() {
+
+ private var entry: JsonField? = null
+ private var references: JsonField? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(data: Data) = apply {
+ entry = data.entry
+ references = data.references
+ additionalProperties = data.additionalProperties.toMutableMap()
+ }
+
+ fun entry(entry: Entry) = entry(JsonField.of(entry))
+
+ /**
+ * Sets [Builder.entry] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.entry] with a well-typed [Entry] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun entry(entry: JsonField) = apply { this.entry = entry }
+
+ fun references(references: References) = references(JsonField.of(references))
+
+ /**
+ * Sets [Builder.references] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.references] with a well-typed [References] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun references(references: JsonField) = apply {
+ this.references = references
+ }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [Data].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .entry()
+ * .references()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): Data =
+ Data(
+ checkRequired("entry", entry),
+ checkRequired("references", references),
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
+ fun validate(): Data = apply {
+ if (validated) {
+ return@apply
+ }
+
+ entry().validate()
+ references().validate()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: OnebusawaySdkInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (entry.asKnown().getOrNull()?.validity() ?: 0) +
+ (references.asKnown().getOrNull()?.validity() ?: 0)
+
+ class Entry
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
+ private constructor(
+ private val arrivalsAndDepartures: JsonField>,
+ private val limitExceeded: JsonField,
+ private val nearbyStopIds: JsonField>,
+ private val stopIds: JsonField>,
+ private val situationIds: JsonField>,
+ private val additionalProperties: MutableMap,
+ ) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("arrivalsAndDepartures")
+ @ExcludeMissing
+ arrivalsAndDepartures: JsonField> = JsonMissing.of(),
+ @JsonProperty("limitExceeded")
+ @ExcludeMissing
+ limitExceeded: JsonField = JsonMissing.of(),
+ @JsonProperty("nearbyStopIds")
+ @ExcludeMissing
+ nearbyStopIds: JsonField> = JsonMissing.of(),
+ @JsonProperty("stopIds")
+ @ExcludeMissing
+ stopIds: JsonField> = JsonMissing.of(),
+ @JsonProperty("situationIds")
+ @ExcludeMissing
+ situationIds: JsonField> = JsonMissing.of(),
+ ) : this(
+ arrivalsAndDepartures,
+ limitExceeded,
+ nearbyStopIds,
+ stopIds,
+ situationIds,
+ mutableMapOf(),
+ )
+
+ /**
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or
+ * is unexpectedly missing or null (e.g. if the server responded with an unexpected
+ * value).
+ */
+ fun arrivalsAndDepartures(): List =
+ arrivalsAndDepartures.getRequired("arrivalsAndDepartures")
+
+ /**
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or
+ * is unexpectedly missing or null (e.g. if the server responded with an unexpected
+ * value).
+ */
+ fun limitExceeded(): Boolean = limitExceeded.getRequired("limitExceeded")
+
+ /**
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or
+ * is unexpectedly missing or null (e.g. if the server responded with an unexpected
+ * value).
+ */
+ fun nearbyStopIds(): List = nearbyStopIds.getRequired("nearbyStopIds")
+
+ /**
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type or
+ * is unexpectedly missing or null (e.g. if the server responded with an unexpected
+ * value).
+ */
+ fun stopIds(): List = stopIds.getRequired("stopIds")
+
+ /**
+ * @throws OnebusawaySdkInvalidDataException if the JSON field has an unexpected type
+ * (e.g. if the server responded with an unexpected value).
+ */
+ fun situationIds(): Optional> = situationIds.getOptional("situationIds")
+
+ /**
+ * Returns the raw JSON value of [arrivalsAndDepartures].
+ *
+ * Unlike [arrivalsAndDepartures], this method doesn't throw if the JSON field has an
+ * unexpected type.
+ */
+ @JsonProperty("arrivalsAndDepartures")
+ @ExcludeMissing
+ fun _arrivalsAndDepartures(): JsonField> =
+ arrivalsAndDepartures
+
+ /**
+ * Returns the raw JSON value of [limitExceeded].
+ *
+ * Unlike [limitExceeded], this method doesn't throw if the JSON field has an unexpected
+ * type.
+ */
+ @JsonProperty("limitExceeded")
+ @ExcludeMissing
+ fun _limitExceeded(): JsonField = limitExceeded
+
+ /**
+ * Returns the raw JSON value of [nearbyStopIds].
+ *
+ * Unlike [nearbyStopIds], this method doesn't throw if the JSON field has an unexpected
+ * type.
+ */
+ @JsonProperty("nearbyStopIds")
+ @ExcludeMissing
+ fun _nearbyStopIds(): JsonField> = nearbyStopIds
+
+ /**
+ * Returns the raw JSON value of [stopIds].
+ *
+ * Unlike [stopIds], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("stopIds")
+ @ExcludeMissing
+ fun _stopIds(): JsonField> = stopIds
+
+ /**
+ * Returns the raw JSON value of [situationIds].
+ *
+ * Unlike [situationIds], this method doesn't throw if the JSON field has an unexpected
+ * type.
+ */
+ @JsonProperty("situationIds")
+ @ExcludeMissing
+ fun _situationIds(): JsonField> = situationIds
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [Entry].
+ *
+ * The following fields are required:
+ * ```java
+ * .arrivalsAndDepartures()
+ * .limitExceeded()
+ * .nearbyStopIds()
+ * .stopIds()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [Entry]. */
+ class Builder internal constructor() {
+
+ private var arrivalsAndDepartures: JsonField>? =
+ null
+ private var limitExceeded: JsonField? = null
+ private var nearbyStopIds: JsonField>? = null
+ private var stopIds: JsonField>? = null
+ private var situationIds: JsonField>? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(entry: Entry) = apply {
+ arrivalsAndDepartures = entry.arrivalsAndDepartures.map { it.toMutableList() }
+ limitExceeded = entry.limitExceeded
+ nearbyStopIds = entry.nearbyStopIds.map { it.toMutableList() }
+ stopIds = entry.stopIds.map { it.toMutableList() }
+ situationIds = entry.situationIds.map { it.toMutableList() }
+ additionalProperties = entry.additionalProperties.toMutableMap()
+ }
+
+ fun arrivalsAndDepartures(arrivalsAndDepartures: List) =
+ arrivalsAndDepartures(JsonField.of(arrivalsAndDepartures))
+
+ /**
+ * Sets [Builder.arrivalsAndDepartures] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.arrivalsAndDepartures] with a well-typed
+ * `List` value instead. This method is primarily for setting
+ * the field to an undocumented or not yet supported value.
+ */
+ fun arrivalsAndDepartures(
+ arrivalsAndDepartures: JsonField>
+ ) = apply {
+ this.arrivalsAndDepartures = arrivalsAndDepartures.map { it.toMutableList() }
+ }
+
+ /**
+ * Adds a single [ArrivalsAndDeparture] to [arrivalsAndDepartures].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addArrivalsAndDeparture(arrivalsAndDeparture: ArrivalsAndDeparture) = apply {
+ arrivalsAndDepartures =
+ (arrivalsAndDepartures ?: JsonField.of(mutableListOf())).also {
+ checkKnown("arrivalsAndDepartures", it).add(arrivalsAndDeparture)
+ }
+ }
+
+ fun limitExceeded(limitExceeded: Boolean) =
+ limitExceeded(JsonField.of(limitExceeded))
+
+ /**
+ * Sets [Builder.limitExceeded] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.limitExceeded] with a well-typed [Boolean] value
+ * instead. This method is primarily for setting the field to an undocumented or not
+ * yet supported value.
+ */
+ fun limitExceeded(limitExceeded: JsonField) = apply {
+ this.limitExceeded = limitExceeded
+ }
+
+ fun nearbyStopIds(nearbyStopIds: List) =
+ nearbyStopIds(JsonField.of(nearbyStopIds))
+
+ /**
+ * Sets [Builder.nearbyStopIds] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.nearbyStopIds] with a well-typed
+ * `List` value instead. This method is primarily for setting the
+ * field to an undocumented or not yet supported value.
+ */
+ fun nearbyStopIds(nearbyStopIds: JsonField>) = apply {
+ this.nearbyStopIds = nearbyStopIds.map { it.toMutableList() }
+ }
+
+ /**
+ * Adds a single [NearbyStopId] to [nearbyStopIds].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addNearbyStopId(nearbyStopId: NearbyStopId) = apply {
+ nearbyStopIds =
+ (nearbyStopIds ?: JsonField.of(mutableListOf())).also {
+ checkKnown("nearbyStopIds", it).add(nearbyStopId)
+ }
+ }
+
+ fun stopIds(stopIds: List) = stopIds(JsonField.of(stopIds))
+
+ /**
+ * Sets [Builder.stopIds] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.stopIds] with a well-typed `List` value
+ * instead. This method is primarily for setting the field to an undocumented or not
+ * yet supported value.
+ */
+ fun stopIds(stopIds: JsonField>) = apply {
+ this.stopIds = stopIds.map { it.toMutableList() }
+ }
+
+ /**
+ * Adds a single [String] to [stopIds].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addStopId(stopId: String) = apply {
+ stopIds =
+ (stopIds ?: JsonField.of(mutableListOf())).also {
+ checkKnown("stopIds", it).add(stopId)
+ }
+ }
+
+ fun situationIds(situationIds: List) =
+ situationIds(JsonField.of(situationIds))
+
+ /**
+ * Sets [Builder.situationIds] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.situationIds] with a well-typed `List`
+ * value instead. This method is primarily for setting the field to an undocumented
+ * or not yet supported value.
+ */
+ fun situationIds(situationIds: JsonField>) = apply {
+ this.situationIds = situationIds.map { it.toMutableList() }
+ }
+
+ /**
+ * Adds a single [String] to [situationIds].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addSituationId(situationId: String) = apply {
+ situationIds =
+ (situationIds ?: JsonField.of(mutableListOf())).also {
+ checkKnown("situationIds", it).add(situationId)
+ }
+ }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) =
+ apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply {
+ additionalProperties.remove(key)
+ }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [Entry].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .arrivalsAndDepartures()
+ * .limitExceeded()
+ * .nearbyStopIds()
+ * .stopIds()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): Entry =
+ Entry(
+ checkRequired("arrivalsAndDepartures", arrivalsAndDepartures).map {
+ it.toImmutable()
+ },
+ checkRequired("limitExceeded", limitExceeded),
+ checkRequired("nearbyStopIds", nearbyStopIds).map { it.toImmutable() },
+ checkRequired("stopIds", stopIds).map { it.toImmutable() },
+ (situationIds ?: JsonMissing.of()).map { it.toImmutable() },
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws OnebusawaySdkInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
+ fun validate(): Entry = apply {
+ if (validated) {
+ return@apply
+ }
+
+ arrivalsAndDepartures().forEach { it.validate() }
+ limitExceeded()
+ nearbyStopIds().forEach { it.validate() }
+ stopIds()
+ situationIds()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: OnebusawaySdkInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (arrivalsAndDepartures.asKnown().getOrNull()?.sumOf { it.validity().toInt() }
+ ?: 0) +
+ (if (limitExceeded.asKnown().isPresent) 1 else 0) +
+ (nearbyStopIds.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
+ (stopIds.asKnown().getOrNull()?.size ?: 0) +
+ (situationIds.asKnown().getOrNull()?.size ?: 0)
+
+ class ArrivalsAndDeparture
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
+ private constructor(
+ private val arrivalEnabled: JsonField,
+ private val blockTripSequence: JsonField,
+ private val departureEnabled: JsonField,
+ private val numberOfStopsAway: JsonField,
+ private val predictedArrivalTime: JsonField,
+ private val predictedDepartureTime: JsonField,
+ private val routeId: JsonField,
+ private val scheduledArrivalTime: JsonField,
+ private val scheduledDepartureTime: JsonField,
+ private val serviceDate: JsonField,
+ private val stopId: JsonField,
+ private val stopSequence: JsonField,
+ private val totalStopsInTrip: JsonField,
+ private val tripHeadsign: JsonField,
+ private val tripId: JsonField,
+ private val vehicleId: JsonField,
+ private val actualTrack: JsonField,
+ private val distanceFromStop: JsonField,
+ private val frequency: JsonField,
+ private val historicalOccupancy: JsonField,
+ private val lastUpdateTime: JsonField,
+ private val occupancyStatus: JsonField,
+ private val predicted: JsonField,
+ private val predictedArrivalInterval: JsonField,
+ private val predictedDepartureInterval: JsonField,
+ private val predictedOccupancy: JsonField,
+ private val routeLongName: JsonField,
+ private val routeShortName: JsonField,
+ private val scheduledArrivalInterval: JsonField,
+ private val scheduledDepartureInterval: JsonField,
+ private val scheduledTrack: JsonField,
+ private val situationIds: JsonField