Skip to content

Commit 729a979

Browse files
feat(client): implement per-endpoint base URL support
Refactor `HttpRequest` to always take a `baseUrl`, instead of storing this in `OkHttpClient`. This allows better reuse of `OkHttpClient` when changing the `baseUrl`.
1 parent 5d19342 commit 729a979

48 files changed

Lines changed: 263 additions & 44 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

braintrust-java-client-okhttp/src/main/kotlin/com/braintrustdata/api/client/okhttp/BraintrustOkHttpClient.kt

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,7 @@ class BraintrustOkHttpClient private constructor() {
171171
fun build(): BraintrustClient =
172172
BraintrustClientImpl(
173173
clientOptions
174-
.httpClient(
175-
OkHttpClient.builder()
176-
.baseUrl(clientOptions.baseUrl())
177-
.timeout(timeout)
178-
.proxy(proxy)
179-
.build()
180-
)
174+
.httpClient(OkHttpClient.builder().timeout(timeout).proxy(proxy).build())
181175
.build()
182176
)
183177
}

braintrust-java-client-okhttp/src/main/kotlin/com/braintrustdata/api/client/okhttp/BraintrustOkHttpClientAsync.kt

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,7 @@ class BraintrustOkHttpClientAsync private constructor() {
173173
fun build(): BraintrustClientAsync =
174174
BraintrustClientAsyncImpl(
175175
clientOptions
176-
.httpClient(
177-
OkHttpClient.builder()
178-
.baseUrl(clientOptions.baseUrl())
179-
.timeout(timeout)
180-
.proxy(proxy)
181-
.build()
182-
)
176+
.httpClient(OkHttpClient.builder().timeout(timeout).proxy(proxy).build())
183177
.build()
184178
)
185179
}

braintrust-java-client-okhttp/src/main/kotlin/com/braintrustdata/api/client/okhttp/OkHttpClient.kt

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.braintrustdata.api.client.okhttp
22

33
import com.braintrustdata.api.core.RequestOptions
44
import com.braintrustdata.api.core.Timeout
5-
import com.braintrustdata.api.core.checkRequired
65
import com.braintrustdata.api.core.http.Headers
76
import com.braintrustdata.api.core.http.HttpClient
87
import com.braintrustdata.api.core.http.HttpMethod
@@ -17,7 +16,6 @@ import java.time.Duration
1716
import java.util.concurrent.CompletableFuture
1817
import okhttp3.Call
1918
import okhttp3.Callback
20-
import okhttp3.HttpUrl
2119
import okhttp3.HttpUrl.Companion.toHttpUrl
2220
import okhttp3.MediaType
2321
import okhttp3.MediaType.Companion.toMediaType
@@ -28,8 +26,7 @@ import okhttp3.Response
2826
import okhttp3.logging.HttpLoggingInterceptor
2927
import okio.BufferedSink
3028

31-
class OkHttpClient
32-
private constructor(private val okHttpClient: okhttp3.OkHttpClient, private val baseUrl: HttpUrl) :
29+
class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpClient) :
3330
HttpClient {
3431

3532
override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse {
@@ -140,11 +137,7 @@ private constructor(private val okHttpClient: okhttp3.OkHttpClient, private val
140137
}
141138

142139
private fun HttpRequest.toUrl(): String {
143-
url?.let {
144-
return it
145-
}
146-
147-
val builder = baseUrl.newBuilder()
140+
val builder = baseUrl.toHttpUrl().newBuilder()
148141
pathSegments.forEach(builder::addPathSegment)
149142
queryParams.keys().forEach { key ->
150143
queryParams.values(key).forEach { builder.addQueryParameter(key, it) }
@@ -194,12 +187,9 @@ private constructor(private val okHttpClient: okhttp3.OkHttpClient, private val
194187

195188
class Builder internal constructor() {
196189

197-
private var baseUrl: HttpUrl? = null
198190
private var timeout: Timeout = Timeout.default()
199191
private var proxy: Proxy? = null
200192

201-
fun baseUrl(baseUrl: String) = apply { this.baseUrl = baseUrl.toHttpUrl() }
202-
203193
fun timeout(timeout: Timeout) = apply { this.timeout = timeout }
204194

205195
fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build())
@@ -214,8 +204,7 @@ private constructor(private val okHttpClient: okhttp3.OkHttpClient, private val
214204
.writeTimeout(timeout.write())
215205
.callTimeout(timeout.request())
216206
.proxy(proxy)
217-
.build(),
218-
checkRequired("baseUrl", baseUrl),
207+
.build()
219208
)
220209
}
221210
}

braintrust-java-core/src/main/kotlin/com/braintrustdata/api/core/ClientOptions.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ private constructor(
2424
@get:JvmName("jsonMapper") val jsonMapper: JsonMapper,
2525
@get:JvmName("streamHandlerExecutor") val streamHandlerExecutor: Executor,
2626
@get:JvmName("clock") val clock: Clock,
27-
@get:JvmName("baseUrl") val baseUrl: String,
27+
private val baseUrl: String?,
2828
@get:JvmName("headers") val headers: Headers,
2929
@get:JvmName("queryParams") val queryParams: QueryParams,
3030
@get:JvmName("responseValidation") val responseValidation: Boolean,
@@ -39,6 +39,8 @@ private constructor(
3939
}
4040
}
4141

42+
fun baseUrl(): String = baseUrl ?: PRODUCTION_URL
43+
4244
fun apiKey(): Optional<String> = Optional.ofNullable(apiKey)
4345

4446
fun toBuilder() = Builder().from(this)
@@ -68,7 +70,7 @@ private constructor(
6870
private var jsonMapper: JsonMapper = jsonMapper()
6971
private var streamHandlerExecutor: Executor? = null
7072
private var clock: Clock = Clock.systemUTC()
71-
private var baseUrl: String = PRODUCTION_URL
73+
private var baseUrl: String? = null
7274
private var headers: Headers.Builder = Headers.builder()
7375
private var queryParams: QueryParams.Builder = QueryParams.builder()
7476
private var responseValidation: Boolean = false
@@ -106,7 +108,10 @@ private constructor(
106108

107109
fun clock(clock: Clock) = apply { this.clock = clock }
108110

109-
fun baseUrl(baseUrl: String) = apply { this.baseUrl = baseUrl }
111+
fun baseUrl(baseUrl: String?) = apply { this.baseUrl = baseUrl }
112+
113+
/** Alias for calling [Builder.baseUrl] with `baseUrl.orElse(null)`. */
114+
fun baseUrl(baseUrl: Optional<String>) = baseUrl(baseUrl.getOrNull())
110115

111116
fun responseValidation(responseValidation: Boolean) = apply {
112117
this.responseValidation = responseValidation
@@ -201,8 +206,6 @@ private constructor(
201206

202207
fun removeAllQueryParams(keys: Set<String>) = apply { queryParams.removeAll(keys) }
203208

204-
fun baseUrl(): String = baseUrl
205-
206209
fun fromEnv() = apply {
207210
System.getenv("BRAINTRUST_BASE_URL")?.let { baseUrl(it) }
208211
System.getenv("BRAINTRUST_API_KEY")?.let { apiKey(it) }

braintrust-java-core/src/main/kotlin/com/braintrustdata/api/core/http/HttpRequest.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import com.braintrustdata.api.core.toImmutable
66
class HttpRequest
77
private constructor(
88
@get:JvmName("method") val method: HttpMethod,
9-
@get:JvmName("url") val url: String?,
9+
@get:JvmName("baseUrl") val baseUrl: String,
1010
@get:JvmName("pathSegments") val pathSegments: List<String>,
1111
@get:JvmName("headers") val headers: Headers,
1212
@get:JvmName("queryParams") val queryParams: QueryParams,
@@ -16,7 +16,7 @@ private constructor(
1616
fun toBuilder(): Builder = Builder().from(this)
1717

1818
override fun toString(): String =
19-
"HttpRequest{method=$method, url=$url, pathSegments=$pathSegments, headers=$headers, queryParams=$queryParams, body=$body}"
19+
"HttpRequest{method=$method, baseUrl=$baseUrl, pathSegments=$pathSegments, headers=$headers, queryParams=$queryParams, body=$body}"
2020

2121
companion object {
2222
@JvmStatic fun builder() = Builder()
@@ -25,7 +25,7 @@ private constructor(
2525
class Builder internal constructor() {
2626

2727
private var method: HttpMethod? = null
28-
private var url: String? = null
28+
private var baseUrl: String? = null
2929
private var pathSegments: MutableList<String> = mutableListOf()
3030
private var headers: Headers.Builder = Headers.builder()
3131
private var queryParams: QueryParams.Builder = QueryParams.builder()
@@ -34,7 +34,7 @@ private constructor(
3434
@JvmSynthetic
3535
internal fun from(request: HttpRequest) = apply {
3636
method = request.method
37-
url = request.url
37+
baseUrl = request.baseUrl
3838
pathSegments = request.pathSegments.toMutableList()
3939
headers = request.headers.toBuilder()
4040
queryParams = request.queryParams.toBuilder()
@@ -43,7 +43,7 @@ private constructor(
4343

4444
fun method(method: HttpMethod) = apply { this.method = method }
4545

46-
fun url(url: String) = apply { this.url = url }
46+
fun baseUrl(baseUrl: String) = apply { this.baseUrl = baseUrl }
4747

4848
fun addPathSegment(pathSegment: String) = apply { pathSegments.add(pathSegment) }
4949

@@ -136,7 +136,7 @@ private constructor(
136136
fun build(): HttpRequest =
137137
HttpRequest(
138138
checkRequired("method", method),
139-
url,
139+
checkRequired("baseUrl", baseUrl),
140140
pathSegments.toImmutable(),
141141
headers.build(),
142142
queryParams.build(),

braintrust-java-core/src/main/kotlin/com/braintrustdata/api/services/async/AclServiceAsyncImpl.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ class AclServiceAsyncImpl internal constructor(private val clientOptions: Client
9595
val request =
9696
HttpRequest.builder()
9797
.method(HttpMethod.POST)
98+
.baseUrl(clientOptions.baseUrl())
9899
.addPathSegments("v1", "acl")
99100
.body(json(clientOptions.jsonMapper, params._body()))
100101
.build()
@@ -128,6 +129,7 @@ class AclServiceAsyncImpl internal constructor(private val clientOptions: Client
128129
val request =
129130
HttpRequest.builder()
130131
.method(HttpMethod.GET)
132+
.baseUrl(clientOptions.baseUrl())
131133
.addPathSegments("v1", "acl", params._pathParam(0))
132134
.build()
133135
.prepareAsync(clientOptions, params)
@@ -158,6 +160,7 @@ class AclServiceAsyncImpl internal constructor(private val clientOptions: Client
158160
val request =
159161
HttpRequest.builder()
160162
.method(HttpMethod.GET)
163+
.baseUrl(clientOptions.baseUrl())
161164
.addPathSegments("v1", "acl")
162165
.build()
163166
.prepareAsync(clientOptions, params)
@@ -198,6 +201,7 @@ class AclServiceAsyncImpl internal constructor(private val clientOptions: Client
198201
val request =
199202
HttpRequest.builder()
200203
.method(HttpMethod.DELETE)
204+
.baseUrl(clientOptions.baseUrl())
201205
.addPathSegments("v1", "acl", params._pathParam(0))
202206
.apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } }
203207
.build()
@@ -229,6 +233,7 @@ class AclServiceAsyncImpl internal constructor(private val clientOptions: Client
229233
val request =
230234
HttpRequest.builder()
231235
.method(HttpMethod.POST)
236+
.baseUrl(clientOptions.baseUrl())
232237
.addPathSegments("v1", "acl", "batch_update")
233238
.body(json(clientOptions.jsonMapper, params._body()))
234239
.build()
@@ -259,6 +264,7 @@ class AclServiceAsyncImpl internal constructor(private val clientOptions: Client
259264
val request =
260265
HttpRequest.builder()
261266
.method(HttpMethod.DELETE)
267+
.baseUrl(clientOptions.baseUrl())
262268
.addPathSegments("v1", "acl")
263269
.body(json(clientOptions.jsonMapper, params._body()))
264270
.build()

braintrust-java-core/src/main/kotlin/com/braintrustdata/api/services/async/AiSecretServiceAsyncImpl.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ class AiSecretServiceAsyncImpl internal constructor(private val clientOptions: C
102102
val request =
103103
HttpRequest.builder()
104104
.method(HttpMethod.POST)
105+
.baseUrl(clientOptions.baseUrl())
105106
.addPathSegments("v1", "ai_secret")
106107
.body(json(clientOptions.jsonMapper, params._body()))
107108
.build()
@@ -135,6 +136,7 @@ class AiSecretServiceAsyncImpl internal constructor(private val clientOptions: C
135136
val request =
136137
HttpRequest.builder()
137138
.method(HttpMethod.GET)
139+
.baseUrl(clientOptions.baseUrl())
138140
.addPathSegments("v1", "ai_secret", params._pathParam(0))
139141
.build()
140142
.prepareAsync(clientOptions, params)
@@ -167,6 +169,7 @@ class AiSecretServiceAsyncImpl internal constructor(private val clientOptions: C
167169
val request =
168170
HttpRequest.builder()
169171
.method(HttpMethod.PATCH)
172+
.baseUrl(clientOptions.baseUrl())
170173
.addPathSegments("v1", "ai_secret", params._pathParam(0))
171174
.body(json(clientOptions.jsonMapper, params._body()))
172175
.build()
@@ -198,6 +201,7 @@ class AiSecretServiceAsyncImpl internal constructor(private val clientOptions: C
198201
val request =
199202
HttpRequest.builder()
200203
.method(HttpMethod.GET)
204+
.baseUrl(clientOptions.baseUrl())
201205
.addPathSegments("v1", "ai_secret")
202206
.build()
203207
.prepareAsync(clientOptions, params)
@@ -238,6 +242,7 @@ class AiSecretServiceAsyncImpl internal constructor(private val clientOptions: C
238242
val request =
239243
HttpRequest.builder()
240244
.method(HttpMethod.DELETE)
245+
.baseUrl(clientOptions.baseUrl())
241246
.addPathSegments("v1", "ai_secret", params._pathParam(0))
242247
.apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } }
243248
.build()
@@ -268,6 +273,7 @@ class AiSecretServiceAsyncImpl internal constructor(private val clientOptions: C
268273
val request =
269274
HttpRequest.builder()
270275
.method(HttpMethod.DELETE)
276+
.baseUrl(clientOptions.baseUrl())
271277
.addPathSegments("v1", "ai_secret")
272278
.body(json(clientOptions.jsonMapper, params._body()))
273279
.build()
@@ -298,6 +304,7 @@ class AiSecretServiceAsyncImpl internal constructor(private val clientOptions: C
298304
val request =
299305
HttpRequest.builder()
300306
.method(HttpMethod.PUT)
307+
.baseUrl(clientOptions.baseUrl())
301308
.addPathSegments("v1", "ai_secret")
302309
.body(json(clientOptions.jsonMapper, params._body()))
303310
.build()

braintrust-java-core/src/main/kotlin/com/braintrustdata/api/services/async/ApiKeyServiceAsyncImpl.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class ApiKeyServiceAsyncImpl internal constructor(private val clientOptions: Cli
7979
val request =
8080
HttpRequest.builder()
8181
.method(HttpMethod.POST)
82+
.baseUrl(clientOptions.baseUrl())
8283
.addPathSegments("v1", "api_key")
8384
.body(json(clientOptions.jsonMapper, params._body()))
8485
.build()
@@ -112,6 +113,7 @@ class ApiKeyServiceAsyncImpl internal constructor(private val clientOptions: Cli
112113
val request =
113114
HttpRequest.builder()
114115
.method(HttpMethod.GET)
116+
.baseUrl(clientOptions.baseUrl())
115117
.addPathSegments("v1", "api_key", params._pathParam(0))
116118
.build()
117119
.prepareAsync(clientOptions, params)
@@ -142,6 +144,7 @@ class ApiKeyServiceAsyncImpl internal constructor(private val clientOptions: Cli
142144
val request =
143145
HttpRequest.builder()
144146
.method(HttpMethod.GET)
147+
.baseUrl(clientOptions.baseUrl())
145148
.addPathSegments("v1", "api_key")
146149
.build()
147150
.prepareAsync(clientOptions, params)
@@ -182,6 +185,7 @@ class ApiKeyServiceAsyncImpl internal constructor(private val clientOptions: Cli
182185
val request =
183186
HttpRequest.builder()
184187
.method(HttpMethod.DELETE)
188+
.baseUrl(clientOptions.baseUrl())
185189
.addPathSegments("v1", "api_key", params._pathParam(0))
186190
.apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } }
187191
.build()

0 commit comments

Comments
 (0)