diff --git a/src/main/kotlin/io/supabase/postgrest/PostgrestDefaultClient.kt b/src/main/kotlin/io/supabase/postgrest/PostgrestDefaultClient.kt index 7e3e088..8397c35 100644 --- a/src/main/kotlin/io/supabase/postgrest/PostgrestDefaultClient.kt +++ b/src/main/kotlin/io/supabase/postgrest/PostgrestDefaultClient.kt @@ -27,7 +27,7 @@ class PostgrestDefaultClient( headers = headers, schema = schema, httpClient = PostgrestHttpClientApache( - postgrestJsonConverter = jsonConverter, + jsonConverter = jsonConverter, httpClient = { HttpClients.createDefault() } ), jsonConverter = jsonConverter diff --git a/src/main/kotlin/io/supabase/postgrest/builder/PostgrestQueryBuilder.kt b/src/main/kotlin/io/supabase/postgrest/builder/PostgrestQueryBuilder.kt index 4cd7737..4228773 100644 --- a/src/main/kotlin/io/supabase/postgrest/builder/PostgrestQueryBuilder.kt +++ b/src/main/kotlin/io/supabase/postgrest/builder/PostgrestQueryBuilder.kt @@ -48,7 +48,7 @@ class PostgrestQueryBuilder(url: URI, postgrestHttpClient: PostgrestHtt fun insert(values: List, upsert: Boolean = false, onConflict: String? = null, returning: Returning = Returning.REPRESENTATION, count: Count? = null): PostgrestFilterBuilder { setMethod(Method.POST) - val preferHeaders = mutableListOf("return=${returning.identifier}") + val preferHeaders = mutableListOf("return=${returning.identifier}") if (upsert) preferHeaders.add("resolution=merge-duplicates") if (upsert && onConflict != null) setSearchParam("on_conflict", onConflict) diff --git a/src/main/kotlin/io/supabase/postgrest/http/PostgrestHttpClientApache.kt b/src/main/kotlin/io/supabase/postgrest/http/PostgrestHttpClientApache.kt index 52acc0c..46ff0a1 100644 --- a/src/main/kotlin/io/supabase/postgrest/http/PostgrestHttpClientApache.kt +++ b/src/main/kotlin/io/supabase/postgrest/http/PostgrestHttpClientApache.kt @@ -3,7 +3,6 @@ package io.supabase.postgrest.http import io.supabase.postgrest.json.PostgrestJsonConverter import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase import org.apache.hc.client5.http.impl.classic.CloseableHttpClient -import org.apache.hc.client5.http.impl.classic.HttpClients import org.apache.hc.core5.http.ClassicHttpResponse import org.apache.hc.core5.http.HttpStatus import org.apache.hc.core5.http.Method @@ -19,14 +18,14 @@ import java.net.URI */ class PostgrestHttpClientApache( private val httpClient: () -> CloseableHttpClient, - private val postgrestJsonConverter: PostgrestJsonConverter + private val jsonConverter: PostgrestJsonConverter ) : PostgrestHttpClient { override fun execute(uri: URI, method: Method, headers: Map, body: Any?): PostgrestHttpResponse { return httpClient().use { httpClient -> val httpRequest = HttpUriRequestBase(method.name, uri) body?.apply { - val dataAsString = postgrestJsonConverter.serialize(body) + val dataAsString = jsonConverter.serialize(body) httpRequest.entity = StringEntity(dataAsString) } headers.forEach { (name, value) -> httpRequest.addHeader(name, value) } diff --git a/src/test/kotlin/io/supabase/postgrest/http/PostgrestHttpClientApacheTest.kt b/src/test/kotlin/io/supabase/postgrest/http/PostgrestHttpClientApacheTest.kt new file mode 100644 index 0000000..9e0e442 --- /dev/null +++ b/src/test/kotlin/io/supabase/postgrest/http/PostgrestHttpClientApacheTest.kt @@ -0,0 +1,118 @@ +package io.supabase.postgrest.http + +import assertk.assertAll +import assertk.assertThat +import assertk.assertions.hasSize +import assertk.assertions.isEqualTo +import io.mockk.CapturingSlot +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import io.supabase.postgrest.json.PostgrestJsonConverterJackson +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse +import org.apache.hc.core5.http.ClassicHttpRequest +import org.apache.hc.core5.http.Method +import org.apache.hc.core5.http.io.HttpClientResponseHandler +import org.apache.hc.core5.http.io.entity.StringEntity +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import java.net.URI +import java.util.stream.Stream + +internal class PostgrestHttpClientApacheTest { + + private val uri = "https://test.com" + private val httpClientMock = mockk() + + private val postgrestHttpClient = PostgrestHttpClientApache( + httpClient = { httpClientMock }, + jsonConverter = PostgrestJsonConverterJackson() + ) + + init { + every { httpClientMock.close() }.returns(Unit) + } + + @Test + fun `should set http headers`() { + val httpResponse = mockk() + every { httpResponse.code } returns 200 + every { httpResponse.entity } returns null + + val requestCapture = mockHttpCallWithGetRequest(httpResponse) + + val headers = mapOf("Authorization" to "foobar", "Content-Type" to "application/json") + + postgrestHttpClient.execute( + method = Method.GET, + uri = URI(uri), + headers = headers + ) + + val request = requestCapture.captured + + assertAll { + assertThat(request.headers).hasSize(headers.size) + headers.forEach { (name, value) -> + assertThat(request.getHeader(name).value).isEqualTo(value) + } + } + } + + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class ThrowHttpException { + + @ParameterizedTest + @MethodSource("exceptionTestData") + fun `should throw http exception when status is above 300`(testData: ExceptionTestData) { + val httpResponse = mockk() + val responseCode = testData.status + val httpBody = testData.body + + every { httpResponse.code } returns responseCode + every { httpResponse.entity } returns httpBody?.let { StringEntity(it) } + + mockHttpCallWithGetRequest(httpResponse) + + val exception = assertThrows { + postgrestHttpClient.execute( + method = Method.GET, + uri = URI(uri) + ) + } + + assertThat(exception.status).isEqualTo(responseCode) + assertThat(exception.data).isEqualTo(httpBody) + } + + @Suppress("unused") + private fun exceptionTestData(): Stream { + return Stream.of( + ExceptionTestData(301, "httpbody"), + ExceptionTestData(301, null), + ExceptionTestData(400, "httpbody") + ) + } + } + + data class ExceptionTestData( + val status: Int, + val body: String? + ) + + private fun mockHttpCallWithGetRequest(httpResponse: CloseableHttpResponse): CapturingSlot { + val slot = slot() + every { httpClientMock.execute(capture(slot), any>()) }.answers { + val handler = args[1] as HttpClientResponseHandler<*> + handler.handleResponse(httpResponse) + } + + return slot + } +} \ No newline at end of file