/
SupabaseConnector.kt
122 lines (103 loc) · 4.14 KB
/
SupabaseConnector.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package com.powersync.connector.supabase
import com.powersync.PowerSyncDatabase
import com.powersync.connectors.PowerSyncBackendConnector
import com.powersync.connectors.PowerSyncCredentials
import com.powersync.db.crud.CrudEntry
import com.powersync.db.crud.UpdateType
import io.github.jan.supabase.SupabaseClient
import io.github.jan.supabase.createSupabaseClient
import io.github.jan.supabase.gotrue.Auth
import io.github.jan.supabase.gotrue.SessionStatus
import io.github.jan.supabase.gotrue.auth
import io.github.jan.supabase.gotrue.providers.builtin.Email
import io.github.jan.supabase.postgrest.Postgrest
import io.github.jan.supabase.postgrest.from
/**
* Get a Supabase token to authenticate against the PowerSync instance.
*/
public class SupabaseConnector(
public val supabaseClient: SupabaseClient,
public val powerSyncEndpoint: String
) : PowerSyncBackendConnector() {
public constructor(
supabaseUrl: String,
supabaseKey: String,
powerSyncEndpoint: String
) : this(
supabaseClient = createSupabaseClient(supabaseUrl, supabaseKey) {
install(Auth)
install(Postgrest)
},
powerSyncEndpoint = powerSyncEndpoint
)
init {
require(supabaseClient.pluginManager.getPluginOrNull(Auth) != null) { "The Auth plugin must be installed on the Supabase client" }
require(supabaseClient.pluginManager.getPluginOrNull(Postgrest) != null) { "The Postgrest plugin must be installed on the Supabase client" }
}
public suspend fun login(email: String, password: String) {
supabaseClient.auth.signInWith(Email) {
this.email = email
this.password = password
}
}
public suspend fun loginAnonymously() {
supabaseClient.auth.signInAnonymously()
}
/**
* Get credentials for PowerSync.
*/
override suspend fun fetchCredentials(): PowerSyncCredentials {
check(supabaseClient.auth.sessionStatus.value is SessionStatus.Authenticated) { "Supabase client is not authenticated" }
// Use Supabase token for PowerSync
val session = supabaseClient.auth.currentSessionOrNull() ?: error("Could not fetch Supabase credentials");
check(session.user != null) { "No user data" }
// userId and expiresAt are for debugging purposes only
return PowerSyncCredentials(
endpoint = powerSyncEndpoint,
token = session.accessToken, // Use the access token to authenticate against PowerSync
expiresAt = session.expiresAt,
userId = session.user!!.id
);
}
/**
* Upload local changes to the app backend (in this case Supabase).
*
* This function is called whenever there is data to upload, whether the device is online or offline.
* If this call throws an error, it is retried periodically.
*/
override suspend fun uploadData(database: PowerSyncDatabase) {
val transaction = database.getNextCrudTransaction() ?: return;
var lastEntry: CrudEntry? = null;
try {
for (entry in transaction.crud) {
lastEntry = entry;
val table = supabaseClient.from(entry.table)
when (entry.op) {
UpdateType.PUT -> {
val data = entry.opData?.toMutableMap() ?: mutableMapOf()
data["id"] = entry.id
table.upsert(data)
}
UpdateType.PATCH -> {
table.update(entry.opData!!) {
filter {
eq("id", entry.id)
}
}
}
UpdateType.DELETE -> {
table.delete {
filter {
eq("id", entry.id)
}
}
}
}
}
transaction.complete(null);
} catch (e: Exception) {
println("Data upload error - retrying last entry: ${lastEntry!!}, $e")
throw e
}
}
}