Skip to content

Commit f5eecde

Browse files
Merge pull request #101 from OneBusAway/release-please--branches--main--changes--next
release: 0.1.0-alpha.45
2 parents 0b71cbd + 0460e63 commit f5eecde

10 files changed

Lines changed: 210 additions & 8 deletions

File tree

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "0.1.0-alpha.44"
2+
".": "0.1.0-alpha.45"
33
}

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## 0.1.0-alpha.45 (2026-01-09)
4+
5+
Full Changelog: [v0.1.0-alpha.44...v0.1.0-alpha.45](https://github.com/OneBusAway/java-sdk/compare/v0.1.0-alpha.44...v0.1.0-alpha.45)
6+
7+
### Features
8+
9+
* **client:** add `HttpRequest#url()` method ([d7e318b](https://github.com/OneBusAway/java-sdk/commit/d7e318b548f644a4dc9e55f5ebc6604282fe57a3))
10+
* **client:** allow configuring dispatcher executor service ([4ff1629](https://github.com/OneBusAway/java-sdk/commit/4ff16297c9acc59aba99ff7cb1fe278e73c7d38b))
11+
312
## 0.1.0-alpha.44 (2025-12-03)
413

514
Full Changelog: [v0.1.0-alpha.43...v0.1.0-alpha.44](https://github.com/OneBusAway/java-sdk/compare/v0.1.0-alpha.43...v0.1.0-alpha.44)

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
same "printed page" as the copyright notice for easier
187187
identification within third-party archives.
188188

189-
Copyright 2025 Onebusaway SDK
189+
Copyright 2026 Onebusaway SDK
190190

191191
Licensed under the Apache License, Version 2.0 (the "License");
192192
you may not use this file except in compliance with the License.

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
<!-- x-release-please-start-version -->
44

5-
[![Maven Central](https://img.shields.io/maven-central/v/org.onebusaway/onebusaway-sdk-java)](https://central.sonatype.com/artifact/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.44)
6-
[![javadoc](https://javadoc.io/badge2/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.44/javadoc.svg)](https://javadoc.io/doc/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.44)
5+
[![Maven Central](https://img.shields.io/maven-central/v/org.onebusaway/onebusaway-sdk-java)](https://central.sonatype.com/artifact/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.45)
6+
[![javadoc](https://javadoc.io/badge2/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.45/javadoc.svg)](https://javadoc.io/doc/org.onebusaway/onebusaway-sdk-java/0.1.0-alpha.45)
77

88
<!-- x-release-please-end -->
99

@@ -15,7 +15,7 @@ It is generated with [Stainless](https://www.stainless.com/).
1515

1616
<!-- x-release-please-start-version -->
1717

18-
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.44).
18+
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.45).
1919

2020
<!-- x-release-please-end -->
2121

@@ -26,7 +26,7 @@ The REST API documentation can be found on [developer.onebusaway.org](https://de
2626
### Gradle
2727

2828
```kotlin
29-
implementation("org.onebusaway:onebusaway-sdk-java:0.1.0-alpha.44")
29+
implementation("org.onebusaway:onebusaway-sdk-java:0.1.0-alpha.45")
3030
```
3131

3232
### Maven
@@ -35,7 +35,7 @@ implementation("org.onebusaway:onebusaway-sdk-java:0.1.0-alpha.44")
3535
<dependency>
3636
<groupId>org.onebusaway</groupId>
3737
<artifactId>onebusaway-sdk-java</artifactId>
38-
<version>0.1.0-alpha.44</version>
38+
<version>0.1.0-alpha.45</version>
3939
</dependency>
4040
```
4141

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ repositories {
88

99
allprojects {
1010
group = "org.onebusaway"
11-
version = "0.1.0-alpha.44" // x-release-please-version
11+
version = "0.1.0-alpha.45" // x-release-please-version
1212
}
1313

1414
subprojects {

onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OkHttpClient.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import java.net.Proxy
66
import java.time.Duration
77
import java.util.concurrent.CancellationException
88
import java.util.concurrent.CompletableFuture
9+
import java.util.concurrent.ExecutorService
910
import javax.net.ssl.HostnameVerifier
1011
import javax.net.ssl.SSLSocketFactory
1112
import javax.net.ssl.X509TrustManager
1213
import okhttp3.Call
1314
import okhttp3.Callback
15+
import okhttp3.Dispatcher
1416
import okhttp3.HttpUrl.Companion.toHttpUrl
1517
import okhttp3.Interceptor
1618
import okhttp3.MediaType
@@ -200,6 +202,7 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
200202

201203
private var timeout: Timeout = Timeout.default()
202204
private var proxy: Proxy? = null
205+
private var dispatcherExecutorService: ExecutorService? = null
203206
private var sslSocketFactory: SSLSocketFactory? = null
204207
private var trustManager: X509TrustManager? = null
205208
private var hostnameVerifier: HostnameVerifier? = null
@@ -210,6 +213,10 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
210213

211214
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
212215

216+
fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
217+
this.dispatcherExecutorService = dispatcherExecutorService
218+
}
219+
213220
fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply {
214221
this.sslSocketFactory = sslSocketFactory
215222
}
@@ -231,6 +238,8 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
231238
.callTimeout(timeout.request())
232239
.proxy(proxy)
233240
.apply {
241+
dispatcherExecutorService?.let { dispatcher(Dispatcher(it)) }
242+
234243
val sslSocketFactory = sslSocketFactory
235244
val trustManager = trustManager
236245
if (sslSocketFactory != null && trustManager != null) {

onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OnebusawaySdkOkHttpClient.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import java.net.Proxy
77
import java.time.Clock
88
import java.time.Duration
99
import java.util.Optional
10+
import java.util.concurrent.ExecutorService
1011
import javax.net.ssl.HostnameVerifier
1112
import javax.net.ssl.SSLSocketFactory
1213
import javax.net.ssl.X509TrustManager
@@ -44,11 +45,31 @@ class OnebusawaySdkOkHttpClient private constructor() {
4445
class Builder internal constructor() {
4546

4647
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
48+
private var dispatcherExecutorService: ExecutorService? = null
4749
private var proxy: Proxy? = null
4850
private var sslSocketFactory: SSLSocketFactory? = null
4951
private var trustManager: X509TrustManager? = null
5052
private var hostnameVerifier: HostnameVerifier? = null
5153

54+
/**
55+
* The executor service to use for running HTTP requests.
56+
*
57+
* Defaults to OkHttp's
58+
* [default executor service](https://github.com/square/okhttp/blob/ace792f443b2ffb17974f5c0d1cecdf589309f26/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt#L98-L104).
59+
*
60+
* This class takes ownership of the executor service and shuts it down when closed.
61+
*/
62+
fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
63+
this.dispatcherExecutorService = dispatcherExecutorService
64+
}
65+
66+
/**
67+
* Alias for calling [Builder.dispatcherExecutorService] with
68+
* `dispatcherExecutorService.orElse(null)`.
69+
*/
70+
fun dispatcherExecutorService(dispatcherExecutorService: Optional<ExecutorService>) =
71+
dispatcherExecutorService(dispatcherExecutorService.getOrNull())
72+
5273
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
5374

5475
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
@@ -296,6 +317,7 @@ class OnebusawaySdkOkHttpClient private constructor() {
296317
OkHttpClient.builder()
297318
.timeout(clientOptions.timeout())
298319
.proxy(proxy)
320+
.dispatcherExecutorService(dispatcherExecutorService)
299321
.sslSocketFactory(sslSocketFactory)
300322
.trustManager(trustManager)
301323
.hostnameVerifier(hostnameVerifier)

onebusaway-sdk-java-client-okhttp/src/main/kotlin/org/onebusaway/client/okhttp/OnebusawaySdkOkHttpClientAsync.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import java.net.Proxy
77
import java.time.Clock
88
import java.time.Duration
99
import java.util.Optional
10+
import java.util.concurrent.ExecutorService
1011
import javax.net.ssl.HostnameVerifier
1112
import javax.net.ssl.SSLSocketFactory
1213
import javax.net.ssl.X509TrustManager
@@ -44,11 +45,31 @@ class OnebusawaySdkOkHttpClientAsync private constructor() {
4445
class Builder internal constructor() {
4546

4647
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
48+
private var dispatcherExecutorService: ExecutorService? = null
4749
private var proxy: Proxy? = null
4850
private var sslSocketFactory: SSLSocketFactory? = null
4951
private var trustManager: X509TrustManager? = null
5052
private var hostnameVerifier: HostnameVerifier? = null
5153

54+
/**
55+
* The executor service to use for running HTTP requests.
56+
*
57+
* Defaults to OkHttp's
58+
* [default executor service](https://github.com/square/okhttp/blob/ace792f443b2ffb17974f5c0d1cecdf589309f26/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt#L98-L104).
59+
*
60+
* This class takes ownership of the executor service and shuts it down when closed.
61+
*/
62+
fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
63+
this.dispatcherExecutorService = dispatcherExecutorService
64+
}
65+
66+
/**
67+
* Alias for calling [Builder.dispatcherExecutorService] with
68+
* `dispatcherExecutorService.orElse(null)`.
69+
*/
70+
fun dispatcherExecutorService(dispatcherExecutorService: Optional<ExecutorService>) =
71+
dispatcherExecutorService(dispatcherExecutorService.getOrNull())
72+
5273
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
5374

5475
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
@@ -296,6 +317,7 @@ class OnebusawaySdkOkHttpClientAsync private constructor() {
296317
OkHttpClient.builder()
297318
.timeout(clientOptions.timeout())
298319
.proxy(proxy)
320+
.dispatcherExecutorService(dispatcherExecutorService)
299321
.sslSocketFactory(sslSocketFactory)
300322
.trustManager(trustManager)
301323
.hostnameVerifier(hostnameVerifier)

onebusaway-sdk-java-core/src/main/kotlin/org/onebusaway/core/http/HttpRequest.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.onebusaway.core.http
22

3+
import java.net.URLEncoder
34
import org.onebusaway.core.checkRequired
45
import org.onebusaway.core.toImmutable
56

@@ -13,6 +14,35 @@ private constructor(
1314
@get:JvmName("body") val body: HttpRequestBody?,
1415
) {
1516

17+
fun url(): String = buildString {
18+
append(baseUrl)
19+
20+
pathSegments.forEach { segment ->
21+
if (!endsWith("/")) {
22+
append("/")
23+
}
24+
append(URLEncoder.encode(segment, "UTF-8"))
25+
}
26+
27+
if (queryParams.isEmpty()) {
28+
return@buildString
29+
}
30+
31+
append("?")
32+
var isFirst = true
33+
queryParams.keys().forEach { key ->
34+
queryParams.values(key).forEach { value ->
35+
if (!isFirst) {
36+
append("&")
37+
}
38+
append(URLEncoder.encode(key, "UTF-8"))
39+
append("=")
40+
append(URLEncoder.encode(value, "UTF-8"))
41+
isFirst = false
42+
}
43+
}
44+
}
45+
1646
fun toBuilder(): Builder = Builder().from(this)
1747

1848
override fun toString(): String =
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package org.onebusaway.core.http
2+
3+
import org.assertj.core.api.Assertions.assertThat
4+
import org.junit.jupiter.params.ParameterizedTest
5+
import org.junit.jupiter.params.provider.EnumSource
6+
7+
internal class HttpRequestTest {
8+
9+
enum class UrlTestCase(val request: HttpRequest, val expectedUrl: String) {
10+
BASE_URL_ONLY(
11+
HttpRequest.builder().method(HttpMethod.GET).baseUrl("https://api.example.com").build(),
12+
expectedUrl = "https://api.example.com",
13+
),
14+
BASE_URL_WITH_TRAILING_SLASH(
15+
HttpRequest.builder()
16+
.method(HttpMethod.GET)
17+
.baseUrl("https://api.example.com/")
18+
.build(),
19+
expectedUrl = "https://api.example.com/",
20+
),
21+
SINGLE_PATH_SEGMENT(
22+
HttpRequest.builder()
23+
.method(HttpMethod.GET)
24+
.baseUrl("https://api.example.com")
25+
.addPathSegment("users")
26+
.build(),
27+
expectedUrl = "https://api.example.com/users",
28+
),
29+
MULTIPLE_PATH_SEGMENTS(
30+
HttpRequest.builder()
31+
.method(HttpMethod.GET)
32+
.baseUrl("https://api.example.com")
33+
.addPathSegments("users", "123", "profile")
34+
.build(),
35+
expectedUrl = "https://api.example.com/users/123/profile",
36+
),
37+
PATH_SEGMENT_WITH_SPECIAL_CHARS(
38+
HttpRequest.builder()
39+
.method(HttpMethod.GET)
40+
.baseUrl("https://api.example.com")
41+
.addPathSegment("user name")
42+
.build(),
43+
expectedUrl = "https://api.example.com/user+name",
44+
),
45+
SINGLE_QUERY_PARAM(
46+
HttpRequest.builder()
47+
.method(HttpMethod.GET)
48+
.baseUrl("https://api.example.com")
49+
.addPathSegment("users")
50+
.putQueryParam("limit", "10")
51+
.build(),
52+
expectedUrl = "https://api.example.com/users?limit=10",
53+
),
54+
MULTIPLE_QUERY_PARAMS(
55+
HttpRequest.builder()
56+
.method(HttpMethod.GET)
57+
.baseUrl("https://api.example.com")
58+
.addPathSegment("users")
59+
.putQueryParam("limit", "10")
60+
.putQueryParam("offset", "20")
61+
.build(),
62+
expectedUrl = "https://api.example.com/users?limit=10&offset=20",
63+
),
64+
QUERY_PARAM_WITH_SPECIAL_CHARS(
65+
HttpRequest.builder()
66+
.method(HttpMethod.GET)
67+
.baseUrl("https://api.example.com")
68+
.addPathSegment("search")
69+
.putQueryParam("q", "hello world")
70+
.build(),
71+
expectedUrl = "https://api.example.com/search?q=hello+world",
72+
),
73+
MULTIPLE_VALUES_SAME_PARAM(
74+
HttpRequest.builder()
75+
.method(HttpMethod.GET)
76+
.baseUrl("https://api.example.com")
77+
.addPathSegment("users")
78+
.putQueryParams("tags", listOf("admin", "user"))
79+
.build(),
80+
expectedUrl = "https://api.example.com/users?tags=admin&tags=user",
81+
),
82+
BASE_URL_WITH_TRAILING_SLASH_AND_PATH(
83+
HttpRequest.builder()
84+
.method(HttpMethod.GET)
85+
.baseUrl("https://api.example.com/")
86+
.addPathSegment("users")
87+
.build(),
88+
expectedUrl = "https://api.example.com/users",
89+
),
90+
COMPLEX_URL(
91+
HttpRequest.builder()
92+
.method(HttpMethod.POST)
93+
.baseUrl("https://api.example.com")
94+
.addPathSegments("v1", "users", "123")
95+
.putQueryParams("include", listOf("profile", "settings"))
96+
.putQueryParam("format", "json")
97+
.build(),
98+
expectedUrl =
99+
"https://api.example.com/v1/users/123?include=profile&include=settings&format=json",
100+
),
101+
}
102+
103+
@ParameterizedTest
104+
@EnumSource
105+
fun url(testCase: UrlTestCase) {
106+
val actualUrl = testCase.request.url()
107+
108+
assertThat(actualUrl).isEqualTo(testCase.expectedUrl)
109+
}
110+
}

0 commit comments

Comments
 (0)