From 02e16658c3d2491128462f9d0ebb28bc11efeb5c Mon Sep 17 00:00:00 2001 From: Jan Tennert Date: Mon, 4 Nov 2024 15:14:41 +0100 Subject: [PATCH 1/4] Initial commit --- .../io/github/jan/supabase/auth/AuthImpl.kt | 6 ++--- .../auth/exception/AuthRestException.kt | 8 +++---- .../exception/AuthSessionMissingException.kt | 8 ++++--- .../exception/AuthWeakPasswordException.kt | 6 +++-- .../jan/supabase/postgrest/PostgrestImpl.kt | 13 ++--------- .../exception/PostgrestRestException.kt | 20 ++++++++++++++++ .../jan/supabase/exceptions/RestException.kt | 23 +++++++++++-------- 7 files changed, 51 insertions(+), 33 deletions(-) create mode 100644 Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/exception/PostgrestRestException.kt diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthImpl.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthImpl.kt index 083103374..481e7ec3b 100644 --- a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthImpl.kt +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/AuthImpl.kt @@ -522,16 +522,16 @@ internal class AuthImpl( private fun checkErrorCodes(error: GoTrueErrorResponse, response: HttpResponse): RestException? { return when (error.error) { - AuthWeakPasswordException.CODE -> AuthWeakPasswordException(error.description, response.status.value, error.weakPassword?.reasons ?: emptyList()) + AuthWeakPasswordException.CODE -> AuthWeakPasswordException(error.description, response, error.weakPassword?.reasons ?: emptyList()) AuthSessionMissingException.CODE -> { authScope.launch { Auth.logger.e { "Received session not found api error. Clearing session..." } clearSession() } - AuthSessionMissingException(response.status.value) + AuthSessionMissingException(response) } else -> { - error.error?.let { AuthRestException(it, error.description, response.status.value) } + error.error?.let { AuthRestException(it, error.description, response) } } } } diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthRestException.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthRestException.kt index f5c123a10..4bfb4e713 100644 --- a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthRestException.kt +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthRestException.kt @@ -1,17 +1,17 @@ package io.github.jan.supabase.auth.exception import io.github.jan.supabase.exceptions.RestException +import io.ktor.client.statement.HttpResponse /** * Base class for rest exceptions thrown by the Auth API. * @property errorCode The error code of the rest exception. This should be a known [AuthErrorCode]. If it is not, use [error] instead. * @param message The message of the rest exception. */ -open class AuthRestException(errorCode: String, message: String, statusCode: Int): RestException( +open class AuthRestException(errorCode: String, description: String, response: HttpResponse): RestException( error = errorCode, - description = "Auth API error: $errorCode", - message = message, - statusCode = statusCode + description = "$description: $errorCode", + response = response ) { /** diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthSessionMissingException.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthSessionMissingException.kt index f5a180f10..4c56f08f4 100644 --- a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthSessionMissingException.kt +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthSessionMissingException.kt @@ -1,12 +1,14 @@ package io.github.jan.supabase.auth.exception +import io.ktor.client.statement.HttpResponse + /** * Exception thrown when a session is not found. */ -class AuthSessionMissingException(statusCode: Int): AuthRestException( +class AuthSessionMissingException(response: HttpResponse): AuthRestException( errorCode = CODE, - statusCode = statusCode, - message = "Session not found. This can happen if the user was logged out or deleted." + response = response, + description = "Session not found. This can happen if the user was logged out or deleted." ) { internal companion object { diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthWeakPasswordException.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthWeakPasswordException.kt index d9d1a6dc3..d1f50d339 100644 --- a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthWeakPasswordException.kt +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthWeakPasswordException.kt @@ -1,5 +1,7 @@ package io.github.jan.supabase.auth.exception +import io.ktor.client.statement.HttpResponse + /** * Exception thrown on sign-up if the password is too weak * @param description The description of the exception. @@ -7,12 +9,12 @@ package io.github.jan.supabase.auth.exception */ class AuthWeakPasswordException( description: String, - statusCode: Int, + response: HttpResponse, val reasons: List ) : AuthRestException( CODE, description, - statusCode + response ) { internal companion object { diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestImpl.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestImpl.kt index f4e806326..0ade59c93 100644 --- a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestImpl.kt +++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/PostgrestImpl.kt @@ -4,18 +4,14 @@ import io.github.jan.supabase.SupabaseClient import io.github.jan.supabase.annotations.SupabaseInternal import io.github.jan.supabase.auth.authenticatedSupabaseApi import io.github.jan.supabase.bodyOrNull -import io.github.jan.supabase.exceptions.BadRequestRestException -import io.github.jan.supabase.exceptions.NotFoundRestException import io.github.jan.supabase.exceptions.RestException -import io.github.jan.supabase.exceptions.UnauthorizedRestException -import io.github.jan.supabase.exceptions.UnknownRestException +import io.github.jan.supabase.postgrest.exception.PostgrestRestException import io.github.jan.supabase.postgrest.executor.RestRequestExecutor import io.github.jan.supabase.postgrest.query.PostgrestQueryBuilder import io.github.jan.supabase.postgrest.query.request.RpcRequestBuilder import io.github.jan.supabase.postgrest.request.RpcRequest import io.github.jan.supabase.postgrest.result.PostgrestResult import io.ktor.client.statement.HttpResponse -import io.ktor.http.HttpStatusCode import kotlinx.serialization.json.JsonObject internal class PostgrestImpl(override val supabaseClient: SupabaseClient, override val config: Postgrest.Config) : Postgrest { @@ -48,12 +44,7 @@ internal class PostgrestImpl(override val supabaseClient: SupabaseClient, overri override suspend fun parseErrorResponse(response: HttpResponse): RestException { val body = response.bodyOrNull() ?: PostgrestErrorResponse("Unknown error") - return when(response.status) { - HttpStatusCode.Unauthorized -> UnauthorizedRestException(body.message, response, body.details ?: body.hint) - HttpStatusCode.NotFound -> NotFoundRestException(body.message, response, body.details ?: body.hint) - HttpStatusCode.BadRequest -> BadRequestRestException(body.message, response, body.details ?: body.hint) - else -> UnknownRestException(body.message, response, body.details ?: body.hint) - } + return PostgrestRestException(body.message, body.hint, body.details, body.code, response) } override suspend fun rpc( diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/exception/PostgrestRestException.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/exception/PostgrestRestException.kt new file mode 100644 index 000000000..3212ee3fb --- /dev/null +++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/exception/PostgrestRestException.kt @@ -0,0 +1,20 @@ +package io.github.jan.supabase.postgrest.exception + +import io.github.jan.supabase.exceptions.RestException +import io.ktor.client.statement.HttpResponse + +/** + * Exception thrown when a Postgrest request fails + * @param message The error message + * @param hint A hint to the error + * @param details Additional details about the error + * @param code The error code + * @param response The response that caused the exception + */ +class PostgrestRestException( + message: String, + val hint: String?, + val details: String?, + val code: String?, + response: HttpResponse +): RestException(message, hint ?: details, response) \ No newline at end of file diff --git a/Supabase/src/commonMain/kotlin/io/github/jan/supabase/exceptions/RestException.kt b/Supabase/src/commonMain/kotlin/io/github/jan/supabase/exceptions/RestException.kt index fd19316ce..d8735bc33 100644 --- a/Supabase/src/commonMain/kotlin/io/github/jan/supabase/exceptions/RestException.kt +++ b/Supabase/src/commonMain/kotlin/io/github/jan/supabase/exceptions/RestException.kt @@ -9,39 +9,42 @@ import io.ktor.client.statement.request * Plugins may extend this class to provide more specific exceptions * @param error The error returned by Supabase * @param description The error description returned by Supabase - * @param statusCode The HTTP status code of the rest exception. + * @param response The response that caused the exception * @see UnauthorizedRestException * @see BadRequestRestException * @see NotFoundRestException * @see UnknownRestException */ -open class RestException(val error: String, val description: String?, val statusCode: Int, message: String): Exception(message) { - - constructor(error: String, response: HttpResponse, message: String? = null): this(error, message, response.status.value, """ - $error${message?.let { " ($it)" } ?: ""} +open class RestException(val error: String, val description: String?, val response: HttpResponse): Exception(""" + $error${description?.let { " ($it)" } ?: ""} URL: ${response.request.url} Headers: ${response.request.headers.entries()} Http Method: ${response.request.method.value} - """.trimIndent()) +""".trimIndent()) { + + /** + * The status code of the response + */ + val statusCode = response.status.value } /** * Thrown when supabase-kt receives a response indicating an authentication error */ -class UnauthorizedRestException(error: String, response: HttpResponse, message: String? = null): RestException(error, response, message) +class UnauthorizedRestException(error: String, response: HttpResponse, message: String? = null): RestException(error, message, response) /** * Thrown when supabase-kt receives a response indicating that the request was invalid due to missing or wrong fields */ -class BadRequestRestException(error: String, response: HttpResponse, message: String? = null): RestException(error, response, message) +class BadRequestRestException(error: String, response: HttpResponse, message: String? = null): RestException(error, message, response) /** * Thrown when supabase-kt receives a response indicating that the wanted resource was not found */ -class NotFoundRestException(error: String, response: HttpResponse, message: String? = null): RestException(error, response, message) +class NotFoundRestException(error: String, response: HttpResponse, message: String? = null): RestException(error, message, response) /** * Thrown for all other response codes */ -class UnknownRestException(error: String, response: HttpResponse, message: String? = null): RestException(error, response, message) +class UnknownRestException(error: String, response: HttpResponse, message: String? = null): RestException(error, message, response) From 614d1e391f0cc770c996445768d34b856d3b0e64 Mon Sep 17 00:00:00 2001 From: Jan Tennert Date: Wed, 13 Nov 2024 20:20:53 +0100 Subject: [PATCH 2/4] Update docs --- .../github/jan/supabase/postgrest/Postgrest.kt | 12 +++++++++--- .../postgrest/query/PostgrestQueryBuilder.kt | 18 +++++++++--------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/Postgrest.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/Postgrest.kt index 9d163e831..67435ea8b 100644 --- a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/Postgrest.kt +++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/Postgrest.kt @@ -2,18 +2,20 @@ package io.github.jan.supabase.postgrest import io.github.jan.supabase.SupabaseClient import io.github.jan.supabase.SupabaseSerializer -import io.github.jan.supabase.exceptions.RestException +import io.github.jan.supabase.exceptions.HttpRequestException import io.github.jan.supabase.logging.SupabaseLogger import io.github.jan.supabase.plugins.CustomSerializationConfig import io.github.jan.supabase.plugins.CustomSerializationPlugin import io.github.jan.supabase.plugins.MainConfig import io.github.jan.supabase.plugins.MainPlugin import io.github.jan.supabase.plugins.SupabasePluginProvider +import io.github.jan.supabase.postgrest.exception.PostgrestRestException import io.github.jan.supabase.postgrest.query.PostgrestQueryBuilder import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder import io.github.jan.supabase.postgrest.query.PostgrestUpdate import io.github.jan.supabase.postgrest.query.request.RpcRequestBuilder import io.github.jan.supabase.postgrest.result.PostgrestResult +import io.ktor.client.plugins.HttpRequestTimeoutException import kotlinx.serialization.json.JsonObject /** @@ -66,7 +68,9 @@ sealed interface Postgrest : MainPlugin, CustomSerializationPl * * @param function The name of the function * @param request Filter the result - * @throws RestException or one of its subclasses if the request failed + * @throws PostgrestRestException if receiving an error response + * @throws HttpRequestTimeoutException if the request timed out + * @throws HttpRequestException on network related issues */ suspend fun rpc( function: String, @@ -79,7 +83,9 @@ sealed interface Postgrest : MainPlugin, CustomSerializationPl * @param function The name of the function * @param parameters The parameters for the function * @param request Filter the result - * @throws RestException or one of its subclasses if the request failed + * @throws PostgrestRestException if receiving an error response + * @throws HttpRequestTimeoutException if the request timed out + * @throws HttpRequestException on network related issues */ suspend fun rpc( function: String, diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestQueryBuilder.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestQueryBuilder.kt index 7b9c832ab..2b0d76624 100644 --- a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestQueryBuilder.kt +++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/PostgrestQueryBuilder.kt @@ -4,8 +4,8 @@ package io.github.jan.supabase.postgrest.query import io.github.jan.supabase.auth.PostgrestFilterDSL import io.github.jan.supabase.encodeToJsonElement import io.github.jan.supabase.exceptions.HttpRequestException -import io.github.jan.supabase.exceptions.RestException import io.github.jan.supabase.postgrest.Postgrest +import io.github.jan.supabase.postgrest.exception.PostgrestRestException import io.github.jan.supabase.postgrest.executor.RestRequestExecutor import io.github.jan.supabase.postgrest.mapToFirstValue import io.github.jan.supabase.postgrest.query.request.InsertRequestBuilder @@ -35,7 +35,7 @@ class PostgrestQueryBuilder( * @param columns The columns to retrieve, defaults to [Columns.ALL]. You can also use [Columns.list], [Columns.type] or [Columns.raw] to specify the columns * @param request Additional configurations for the request including filters * @return PostgrestResult which is either an error, an empty JsonArray or the data you requested as an JsonArray - * @throws RestException or one of its subclasses if receiving an error response + * @throws PostgrestRestException if receiving an error response * @throws HttpRequestTimeoutException if the request timed out * @throws HttpRequestException on network related issues */ @@ -67,7 +67,7 @@ class PostgrestQueryBuilder( * * @param values The values to insert, will automatically get serialized into json. * @param request Additional configurations for the request including filters - * @throws RestException or one of its subclasses if receiving an error response + * @throws PostgrestRestException if receiving an error response * @throws HttpRequestTimeoutException if the request timed out * @throws HttpRequestException on network related issues */ @@ -107,7 +107,7 @@ class PostgrestQueryBuilder( * * @param value The value to insert, will automatically get serialized into json. * @param request Additional filtering to apply to the query - * @throws RestException or one of its subclasses if receiving an error response + * @throws PostgrestRestException if receiving an error response * @throws HttpRequestTimeoutException if the request timed out * @throws HttpRequestException on network related issues */ @@ -121,7 +121,7 @@ class PostgrestQueryBuilder( * * @param values The values to insert, will automatically get serialized into json. * @param request Additional filtering to apply to the query - * @throws RestException or one of its subclasses if receiving an error response + * @throws PostgrestRestException if receiving an error response * @throws HttpRequestTimeoutException if the request timed out * @throws HttpRequestException on network related issues */ @@ -150,7 +150,7 @@ class PostgrestQueryBuilder( * * @param value The value to insert, will automatically get serialized into json. * @param request Additional filtering to apply to the query - * @throws RestException or one of its subclasses if receiving an error response + * @throws PostgrestRestException if receiving an error response * @throws HttpRequestTimeoutException if the request timed out * @throws HttpRequestException on network related issues */ @@ -166,7 +166,7 @@ class PostgrestQueryBuilder( * * @param update Specifies the fields to update via a DSL * @param request Additional filtering to apply to the query - * @throws RestException or one of its subclasses if receiving an error response + * @throws PostgrestRestException if receiving an error response * @throws HttpRequestTimeoutException if the request timed out * @throws HttpRequestException on network related issues */ @@ -193,7 +193,7 @@ class PostgrestQueryBuilder( * * @param value The value to update, will automatically get serialized into json. * @param request Additional filtering to apply to the query - * @throws RestException or one of its subclasses if receiving an error response + * @throws PostgrestRestException if receiving an error response * @throws HttpRequestTimeoutException if the request timed out * @throws HttpRequestException on network related issues */ @@ -219,7 +219,7 @@ class PostgrestQueryBuilder( * By default, deleted rows are not returned. To return it, call `[PostgrestRequestBuilder.select]`. * * @param request Additional filtering to apply to the query - * @throws RestException or one of its subclasses if receiving an error response + * @throws PostgrestRestException if receiving an error response * @throws HttpRequestTimeoutException if the request timed out * @throws HttpRequestException on network related issues */ From a0f8b3d65a4efe9bcec62200bbc2a64c11caed98 Mon Sep 17 00:00:00 2001 From: Jan Tennert Date: Wed, 13 Nov 2024 20:22:56 +0100 Subject: [PATCH 3/4] Fix postgrest rpc docs --- .../jan/supabase/postgrest/query/request/RpcRequestBuilder.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/RpcRequestBuilder.kt b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/RpcRequestBuilder.kt index 8d7b1fe68..5f631cf98 100644 --- a/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/RpcRequestBuilder.kt +++ b/Postgrest/src/commonMain/kotlin/io/github/jan/supabase/postgrest/query/request/RpcRequestBuilder.kt @@ -1,11 +1,12 @@ package io.github.jan.supabase.postgrest.query.request +import io.github.jan.supabase.postgrest.Postgrest import io.github.jan.supabase.postgrest.PropertyConversionMethod import io.github.jan.supabase.postgrest.RpcMethod import io.github.jan.supabase.postgrest.query.PostgrestRequestBuilder /** - * Request builder for [Postgrest.rpcRequest] + * Request builder for [Postgrest.rpc] */ class RpcRequestBuilder(defaultSchema: String, propertyConversionMethod: PropertyConversionMethod): PostgrestRequestBuilder(propertyConversionMethod) { From f9956ca8c99cf192447f502ae12792e689200834 Mon Sep 17 00:00:00 2001 From: Jan Tennert Date: Sat, 16 Nov 2024 14:39:09 +0100 Subject: [PATCH 4/4] Fix tests --- .../github/jan/supabase/auth/exception/AuthRestException.kt | 6 +++--- .../supabase/auth/exception/AuthSessionMissingException.kt | 2 +- Auth/src/commonTest/kotlin/AuthRestExceptionTest.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthRestException.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthRestException.kt index 4bfb4e713..5ccf99fed 100644 --- a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthRestException.kt +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthRestException.kt @@ -6,11 +6,11 @@ import io.ktor.client.statement.HttpResponse /** * Base class for rest exceptions thrown by the Auth API. * @property errorCode The error code of the rest exception. This should be a known [AuthErrorCode]. If it is not, use [error] instead. - * @param message The message of the rest exception. + * @param errorDescription The description of the error. */ -open class AuthRestException(errorCode: String, description: String, response: HttpResponse): RestException( +open class AuthRestException(errorCode: String, val errorDescription: String, response: HttpResponse): RestException( error = errorCode, - description = "$description: $errorCode", + description = "$errorDescription: $errorCode", response = response ) { diff --git a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthSessionMissingException.kt b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthSessionMissingException.kt index 4c56f08f4..f2df64c4e 100644 --- a/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthSessionMissingException.kt +++ b/Auth/src/commonMain/kotlin/io/github/jan/supabase/auth/exception/AuthSessionMissingException.kt @@ -8,7 +8,7 @@ import io.ktor.client.statement.HttpResponse class AuthSessionMissingException(response: HttpResponse): AuthRestException( errorCode = CODE, response = response, - description = "Session not found. This can happen if the user was logged out or deleted." + errorDescription = "Session not found. This can happen if the user was logged out or deleted." ) { internal companion object { diff --git a/Auth/src/commonTest/kotlin/AuthRestExceptionTest.kt b/Auth/src/commonTest/kotlin/AuthRestExceptionTest.kt index 39db4412b..4ebcce3bc 100644 --- a/Auth/src/commonTest/kotlin/AuthRestExceptionTest.kt +++ b/Auth/src/commonTest/kotlin/AuthRestExceptionTest.kt @@ -51,7 +51,7 @@ class AuthRestExceptionTest { } } assertEquals("error_code", exception.error) - assertEquals("error_message", exception.message) + assertEquals("error_message", exception.errorDescription) } } @@ -83,7 +83,7 @@ class AuthRestExceptionTest { } } assertEquals("weak_password", exception.error) - assertEquals("error_message", exception.message) + assertEquals("error_message", exception.errorDescription) assertEquals(listOf("reason1", "reason2"), exception.reasons) } }