Skip to content

Commit 3dfc277

Browse files
author
DominicGBauer
committed
feat: add check for all postgres errors
1 parent 5a8ffb2 commit 3dfc277

File tree

1 file changed

+55
-13
lines changed

1 file changed

+55
-13
lines changed

connectors/supabase/src/commonMain/kotlin/com/powersync/connector/supabase/SupabaseConnector.kt

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,50 @@ import com.powersync.connectors.PowerSyncCredentials
77
import com.powersync.db.crud.CrudEntry
88
import com.powersync.db.crud.UpdateType
99
import io.github.jan.supabase.SupabaseClient
10+
import io.github.jan.supabase.annotations.SupabaseInternal
1011
import io.github.jan.supabase.auth.Auth
1112
import io.github.jan.supabase.auth.auth
1213
import io.github.jan.supabase.auth.providers.builtin.Email
1314
import io.github.jan.supabase.auth.status.SessionStatus
1415
import io.github.jan.supabase.auth.user.UserSession
1516
import io.github.jan.supabase.createSupabaseClient
16-
import io.github.jan.supabase.exceptions.BadRequestRestException
1717
import io.github.jan.supabase.postgrest.Postgrest
1818
import io.github.jan.supabase.postgrest.from
19+
import io.ktor.client.plugins.HttpSend
20+
import io.ktor.client.plugins.plugin
21+
import io.ktor.client.statement.bodyAsText
22+
import io.ktor.utils.io.InternalAPI
1923
import kotlinx.coroutines.flow.StateFlow
24+
import kotlinx.serialization.json.Json
2025

2126
/**
2227
* Get a Supabase token to authenticate against the PowerSync instance.
2328
*/
29+
@OptIn(SupabaseInternal::class, InternalAPI::class)
2430
public class SupabaseConnector(
2531
public val supabaseClient: SupabaseClient,
2632
public val powerSyncEndpoint: String,
2733
) : PowerSyncBackendConnector() {
34+
private var errorCode: String? = null
35+
36+
private object PostgresFatalCodes {
37+
// Using Regex patterns for Postgres error codes
38+
private val FATAL_RESPONSE_CODES =
39+
listOf(
40+
// Class 22 — Data Exception
41+
"^22...".toRegex(),
42+
// Class 23 — Integrity Constraint Violation
43+
"^23...".toRegex(),
44+
// INSUFFICIENT PRIVILEGE
45+
"^42501$".toRegex(),
46+
)
47+
48+
fun isFatalError(code: String): Boolean =
49+
FATAL_RESPONSE_CODES.any { pattern ->
50+
pattern.matches(code)
51+
}
52+
}
53+
2854
public constructor(
2955
supabaseUrl: String,
3056
supabaseKey: String,
@@ -43,6 +69,25 @@ public class SupabaseConnector(
4369
require(
4470
supabaseClient.pluginManager.getPluginOrNull(Postgrest) != null,
4571
) { "The Postgrest plugin must be installed on the Supabase client" }
72+
73+
// This retrieves the error code from the response
74+
// as this is not accessible in the Supabase client RestException
75+
// to handle fatal Postgres errors
76+
supabaseClient.httpClient.httpClient.plugin(HttpSend).intercept { request ->
77+
val resp = execute(request)
78+
val response = resp.response
79+
if (response.status.value == 400) {
80+
val responseText = response.bodyAsText()
81+
82+
try {
83+
val error = Json { coerceInputValues = true }.decodeFromString<Map<String, String?>>(responseText)
84+
errorCode = error["code"]
85+
} catch (e: Exception) {
86+
Logger.e("Failed to parse error response: $e")
87+
}
88+
}
89+
resp
90+
}
4691
}
4792

4893
public suspend fun login(
@@ -111,6 +156,7 @@ public class SupabaseConnector(
111156
lastEntry = entry
112157

113158
val table = supabaseClient.from(entry.table)
159+
114160
when (entry.op) {
115161
UpdateType.PUT -> {
116162
val data = entry.opData?.toMutableMap() ?: mutableMapOf()
@@ -138,19 +184,15 @@ public class SupabaseConnector(
138184

139185
transaction.complete(null)
140186
} catch (e: Exception) {
141-
when (e) {
142-
is BadRequestRestException -> {
143-
if (e.message?.contains("violates not-null constraint") == true) {
144-
Logger.e("Not-null constraint violation: ${e.message}")
145-
transaction.complete(null)
146-
return
147-
}
148-
}
149-
else -> {
150-
Logger.e("Data upload error - retrying last entry: $lastEntry, $e")
151-
throw e
152-
}
187+
if (errorCode != null && PostgresFatalCodes.isFatalError(errorCode.toString())) {
188+
Logger.e("Data upload error: ${e.message}")
189+
Logger.e("Discarding entry: $lastEntry")
190+
transaction.complete(null)
191+
return
153192
}
193+
194+
Logger.e("Data upload error - retrying last entry: $lastEntry, $e")
195+
throw e
154196
}
155197
}
156198
}

0 commit comments

Comments
 (0)