Skip to content

Commit 1e00ca7

Browse files
committed
Boilerplate part 1
1 parent df044ec commit 1e00ca7

8 files changed

Lines changed: 382 additions & 146 deletions

File tree

demos/MAUITodo/Data/Config.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using dotenv.net;
2+
3+
namespace MAUITodo.Data;
4+
5+
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
6+
public class EnvConfig
7+
{
8+
public string SupabaseUrl { get; set; }
9+
public string SupabaseKey { get; set; }
10+
public string SupabaseStorageUrl { get; set; }
11+
public string PowerSyncUrl { get; set; }
12+
public string BackendUrl { get; set; }
13+
public string SupabaseUsername { get; set; }
14+
public string SupabasePassword { get; set; }
15+
public bool UseSupabase { get; set; }
16+
17+
public EnvConfig()
18+
{
19+
DotEnv.Load();
20+
Console.WriteLine($"Current directory: {Directory.GetCurrentDirectory()}");
21+
22+
// Parse boolean value first
23+
string useSupabaseStr = Environment.GetEnvironmentVariable("USE_SUPABASE") ?? "false";
24+
if (!bool.TryParse(useSupabaseStr, out bool useSupabase))
25+
{
26+
throw new InvalidOperationException("USE_SUPABASE environment variable is not a valid boolean.");
27+
}
28+
UseSupabase = useSupabase;
29+
30+
if (UseSupabase)
31+
Console.WriteLine("Using Supabase");
32+
else
33+
Console.WriteLine("Using Node");
34+
35+
PowerSyncUrl = GetRequiredEnv("POWERSYNC_URL");
36+
37+
if (UseSupabase)
38+
{
39+
SupabaseUrl = GetRequiredEnv("SUPABASE_URL");
40+
SupabaseKey = GetRequiredEnv("SUPABASE_KEY");
41+
SupabaseUsername = GetRequiredEnv("SUPABASE_USERNAME");
42+
SupabasePassword = GetRequiredEnv("SUPABASE_PASSWORD");
43+
}
44+
else
45+
{
46+
BackendUrl = GetRequiredEnv("BACKEND_URL");
47+
}
48+
}
49+
50+
private static string GetRequiredEnv(string key)
51+
{
52+
return Environment.GetEnvironmentVariable(key)
53+
?? throw new InvalidOperationException($"{key} environment variable is not set.");
54+
}
55+
}

demos/MAUITodo/Data/NodeConnector.cs

Lines changed: 0 additions & 127 deletions
This file was deleted.
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
namespace MAUITodo.Data;
2+
3+
using MAUITodo.Models;
4+
using MAUITodo.Config;
5+
using MAUITodo.Helpers;
6+
7+
using Newtonsoft.Json;
8+
9+
using PowerSync.Common.Client;
10+
using PowerSync.Common.Client.Connection;
11+
using PowerSync.Common.DB.Crud;
12+
13+
using Supabase;
14+
using Supabase.Gotrue;
15+
using Supabase.Postgrest.Exceptions;
16+
using Supabase.Postgrest.Interfaces;
17+
18+
public class SupabaseConnector : IPowerSyncBackendConnector
19+
{
20+
private readonly Supabase.Client _supabase;
21+
private readonly EnvConfig _envConfig;
22+
private Session? _currentSession;
23+
24+
public Session? CurrentSession
25+
{
26+
get => _currentSession;
27+
set
28+
{
29+
_currentSession = value;
30+
31+
if (_currentSession?.User?.Id != null)
32+
{
33+
UserID = _currentSession.User.Id;
34+
}
35+
}
36+
}
37+
38+
public string UserID { get; private set; } = "";
39+
40+
public bool Ready { get; private set; }
41+
42+
public SupabaseConnector(EnvConfig envConfig)
43+
{
44+
_envConfig = envConfig;
45+
_supabase = new Supabase.Client(envConfig.SupabaseUrl, envConfig.SupabaseKey, new SupabaseOptions
46+
{
47+
AutoConnectRealtime = true
48+
});
49+
50+
_ = _supabase.InitializeAsync();
51+
}
52+
53+
public async Task Login(string email, string password)
54+
{
55+
var response = await _supabase.Auth.SignInWithPassword(email, password);
56+
if (response?.User == null || response.AccessToken == null)
57+
{
58+
throw new Exception("Login failed.");
59+
}
60+
61+
CurrentSession = response;
62+
}
63+
64+
public Task<PowerSyncCredentials?> FetchCredentials()
65+
{
66+
PowerSyncCredentials? credentials = null;
67+
68+
var sessionResponse = _supabase.Auth.CurrentSession;
69+
if (sessionResponse?.AccessToken != null)
70+
{
71+
credentials = new PowerSyncCredentials(_envConfig.PowerSyncUrl, sessionResponse.AccessToken);
72+
}
73+
74+
return Task.FromResult(credentials);
75+
}
76+
77+
public async Task UploadData(IPowerSyncDatabase database)
78+
{
79+
var transaction = await database.GetNextCrudTransaction();
80+
if (transaction == null) return;
81+
82+
try
83+
{
84+
foreach (var op in transaction.Crud)
85+
{
86+
switch (op.Op)
87+
{
88+
case UpdateType.PUT:
89+
if (op.Table.ToLower().Trim() == "lists")
90+
{
91+
var model = JsonConvert.DeserializeObject<TodoList>(JsonConvert.SerializeObject(op.OpData)) ?? throw new InvalidOperationException("Model is null.");
92+
model.ID = op.Id;
93+
94+
await _supabase.From<TodoList>().Upsert(model);
95+
}
96+
else if (op.Table.ToLower().Trim() == "todos")
97+
{
98+
var model = JsonConvert.DeserializeObject<TodoItem>(JsonConvert.SerializeObject(op.OpData)) ?? throw new InvalidOperationException("Model is null.");
99+
model.ID = op.Id;
100+
101+
await _supabase.From<TodoItem>().Upsert(model);
102+
}
103+
break;
104+
105+
case UpdateType.PATCH:
106+
if (op.OpData is null || op.OpData.Count == 0)
107+
{
108+
Console.WriteLine("PATCH skipped: No data to update.");
109+
break;
110+
}
111+
112+
if (op.Table.ToLower().Trim() == "lists")
113+
{
114+
// Create an update query for the 'TodoItem' table where the 'ID' matches 'op.Id'
115+
IPostgrestTable<TodoList> updateQuery = _supabase
116+
.From<TodoList>()
117+
.Where(x => x.ID == op.Id);
118+
119+
// Loop through each key-value pair in the operation data (op.OpData) to apply updates dynamically
120+
foreach (var kvp in op.OpData)
121+
{
122+
// Apply the "SET" operation for each key-value pair.
123+
// The key represents the JSON property name and the value is the new value to be set
124+
updateQuery = SupabasePatchHelper.ApplySet(updateQuery, kvp.Key, kvp.Value);
125+
}
126+
127+
_ = await updateQuery.Update();
128+
}
129+
else if (op.Table.ToLower().Trim() == "todos")
130+
{
131+
// Create an update query for the 'TodoItem' table where the 'ID' matches 'op.Id'
132+
IPostgrestTable<TodoItem> updateQuery = _supabase
133+
.From<TodoItem>()
134+
.Where(x => x.ID == op.Id);
135+
136+
// Loop through each key-value pair in the operation data (op.OpData) to apply updates dynamically
137+
foreach (var kvp in op.OpData)
138+
{
139+
// Apply the "SET" operation for each key-value pair.
140+
// The key represents the JSON property name and the value is the new value to be set
141+
updateQuery = SupabasePatchHelper.ApplySet(updateQuery, kvp.Key, kvp.Value);
142+
}
143+
144+
_ = await updateQuery.Update();
145+
}
146+
break;
147+
148+
case UpdateType.DELETE:
149+
if (op.Table.ToLower().Trim() == "lists")
150+
{
151+
await _supabase
152+
.From<TodoList>()
153+
.Where(x => x.ID == op.Id)
154+
.Delete();
155+
}
156+
else if (op.Table.ToLower().Trim() == "todos")
157+
{
158+
await _supabase
159+
.From<TodoItem>()
160+
.Where(x => x.ID == op.Id)
161+
.Delete();
162+
}
163+
break;
164+
165+
default:
166+
throw new InvalidOperationException("Unknown operation type.");
167+
}
168+
}
169+
170+
await transaction.Complete();
171+
}
172+
catch (PostgrestException ex)
173+
{
174+
Console.WriteLine($"Error during upload: {ex.Message}");
175+
throw;
176+
}
177+
}
178+
}

0 commit comments

Comments
 (0)