Skip to content

Commit 3befec0

Browse files
authored
feat: implement cancelation token on some of upload method (#30)
* feat: implement cancelation token on some of upload method * fix: change the upload cancelation time
1 parent c6b84e9 commit 3befec0

File tree

8 files changed

+368
-17
lines changed

8 files changed

+368
-17
lines changed

Storage/Extensions/HttpClientProgress.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,31 +130,34 @@ public static Task<HttpResponseMessage> UploadFileAsync(
130130
Uri uri,
131131
string filePath,
132132
Dictionary<string, string>? headers = null,
133-
Progress<float>? progress = null
133+
Progress<float>? progress = null,
134+
CancellationToken cancellationToken = default
134135
)
135136
{
136137
var fileStream = new FileStream(filePath, mode: FileMode.Open, FileAccess.Read);
137-
return UploadAsync(client, uri, fileStream, headers, progress);
138+
return UploadAsync(client, uri, fileStream, headers, progress, cancellationToken);
138139
}
139140

140141
public static Task<HttpResponseMessage> UploadBytesAsync(
141142
this HttpClient client,
142143
Uri uri,
143144
byte[] data,
144145
Dictionary<string, string>? headers = null,
145-
Progress<float>? progress = null
146+
Progress<float>? progress = null,
147+
CancellationToken cancellationToken = default
146148
)
147149
{
148150
var stream = new MemoryStream(data);
149-
return UploadAsync(client, uri, stream, headers, progress);
151+
return UploadAsync(client, uri, stream, headers, progress, cancellationToken);
150152
}
151153

152154
public static async Task<HttpResponseMessage> UploadAsync(
153155
this HttpClient client,
154156
Uri uri,
155157
Stream stream,
156158
Dictionary<string, string>? headers = null,
157-
Progress<float>? progress = null
159+
Progress<float>? progress = null,
160+
CancellationToken cancellationToken = default
158161
)
159162
{
160163
var content = new ProgressableStreamContent(stream, 4096, progress);
@@ -172,7 +175,7 @@ public static async Task<HttpResponseMessage> UploadAsync(
172175
}
173176
}
174177

175-
var response = await client.PostAsync(uri, content);
178+
var response = await client.PostAsync(uri, content, cancellationToken);
176179

177180
if (!response.IsSuccessStatusCode)
178181
{

Storage/Interfaces/IStorageFileApi.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,16 @@ Task<string> Upload(
7676
string supabasePath,
7777
FileOptions? options = null,
7878
EventHandler<float>? onProgress = null,
79-
bool inferContentType = true
79+
bool inferContentType = true,
80+
CancellationToken cancellationToken = default
8081
);
8182
Task<string> Upload(
8283
string localFilePath,
8384
string supabasePath,
8485
FileOptions? options = null,
8586
EventHandler<float>? onProgress = null,
86-
bool inferContentType = true
87+
bool inferContentType = true,
88+
CancellationToken cancellationToken = default
8789
);
8890
Task UploadOrResume(
8991
string localPath,

Storage/StorageFileApi.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -227,15 +227,16 @@ public async Task<string> Upload(
227227
string supabasePath,
228228
FileOptions? options = null,
229229
EventHandler<float>? onProgress = null,
230-
bool inferContentType = true
230+
bool inferContentType = true,
231+
CancellationToken cancellationToken = default
231232
)
232233
{
233234
options ??= new FileOptions();
234235

235236
if (inferContentType)
236237
options.ContentType = MimeMapping.MimeUtility.GetMimeMapping(localFilePath);
237238

238-
var result = await UploadOrUpdate(localFilePath, supabasePath, options, onProgress);
239+
var result = await UploadOrUpdate(localFilePath, supabasePath, options, onProgress, cancellationToken);
239240
return result;
240241
}
241242

@@ -253,15 +254,16 @@ public async Task<string> Upload(
253254
string supabasePath,
254255
FileOptions? options = null,
255256
EventHandler<float>? onProgress = null,
256-
bool inferContentType = true
257+
bool inferContentType = true,
258+
CancellationToken cancellationToken = default
257259
)
258260
{
259261
options ??= new FileOptions();
260262

261263
if (inferContentType)
262264
options.ContentType = MimeMapping.MimeUtility.GetMimeMapping(supabasePath);
263265

264-
var result = await UploadOrUpdate(data, supabasePath, options, onProgress);
266+
var result = await UploadOrUpdate(data, supabasePath, options, onProgress, cancellationToken);
265267
return result;
266268
}
267269

@@ -668,7 +670,8 @@ private async Task<string> UploadOrUpdate(
668670
string localPath,
669671
string supabasePath,
670672
FileOptions options,
671-
EventHandler<float>? onProgress = null
673+
EventHandler<float>? onProgress = null,
674+
CancellationToken cancellationToken = default
672675
)
673676
{
674677
Uri uri = new Uri($"{Url}/object/{GetFinalPath(supabasePath)}");
@@ -695,7 +698,7 @@ private async Task<string> UploadOrUpdate(
695698
if (onProgress != null)
696699
progress.ProgressChanged += onProgress;
697700

698-
await Helpers.HttpUploadClient!.UploadFileAsync(uri, localPath, headers, progress);
701+
await Helpers.HttpUploadClient!.UploadFileAsync(uri, localPath, headers, progress, cancellationToken);
699702

700703
return GetFinalPath(supabasePath);
701704
}
@@ -808,7 +811,8 @@ private async Task<string> UploadOrUpdate(
808811
byte[] data,
809812
string supabasePath,
810813
FileOptions options,
811-
EventHandler<float>? onProgress = null
814+
EventHandler<float>? onProgress = null,
815+
CancellationToken cancellationToken = default
812816
)
813817
{
814818
Uri uri = new Uri($"{Url}/object/{GetFinalPath(supabasePath)}");
@@ -835,7 +839,7 @@ private async Task<string> UploadOrUpdate(
835839
if (onProgress != null)
836840
progress.ProgressChanged += onProgress;
837841

838-
await Helpers.HttpUploadClient!.UploadBytesAsync(uri, data, headers, progress);
842+
await Helpers.HttpUploadClient!.UploadBytesAsync(uri, data, headers, progress, cancellationToken);
839843

840844
return GetFinalPath(supabasePath);
841845
}

StorageTests/StorageFileTests.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ public async Task UploadOrResumeByteWithInterruptionAndResume()
270270

271271
var options = new FileOptions { Duplex = "duplex", Metadata = metadata };
272272

273-
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(1));
273+
using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(300));
274274

275275
try
276276
{
@@ -406,6 +406,33 @@ await _bucket.Upload(
406406

407407
await _bucket.Remove(new List<string> { name });
408408
}
409+
410+
[TestMethod("File: Cancel Upload Arbitrary Byte Array")]
411+
public async Task UploadArbitraryByteArrayCanceled()
412+
{
413+
var tsc = new TaskCompletionSource<bool>();
414+
using var ctk = new CancellationTokenSource(TimeSpan.FromMilliseconds(1));
415+
416+
var data = new byte[20 * 1024 * 1024];
417+
var rng = new Random();
418+
rng.NextBytes(data);
419+
var name = $"{Guid.NewGuid()}.bin";
420+
421+
var action = async () =>
422+
{
423+
await _bucket.Upload(data, name, null, (_, _) => tsc.TrySetResult(true), true, ctk.Token);
424+
};
425+
426+
await Assert.ThrowsExceptionAsync<TaskCanceledException>(action);
427+
428+
var list = await _bucket.List();
429+
Assert.IsNotNull(list);
430+
431+
var existing = list.Find(item => item.Name == name);
432+
Assert.IsNull(existing);
433+
434+
await _bucket.Remove([name]);
435+
}
409436

410437
[TestMethod("File: Download")]
411438
public async Task DownloadFile()

supabase/migrations/00-schema.sql

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
-- Set up reatime
2+
create publication supabase_realtime for all tables;
3+
4+
-- Supabase super admin
5+
create user supabase_admin;
6+
alter user supabase_admin with superuser createdb createrole replication bypassrls;
7+
8+
9+
-- Extension namespacing
10+
create schema extensions;
11+
create extension if not exists "uuid-ossp" with schema extensions;
12+
create extension if not exists pgcrypto with schema extensions;
13+
-- create extension if not exists pgjwt with schema extensions;
14+
15+
-- Set up auth roles for the developer
16+
create role anon nologin noinherit;
17+
create role authenticated nologin noinherit; -- "logged in" user: web_user, app_user, etc
18+
create role service_role nologin noinherit bypassrls; -- allow developers to create JWT's that bypass their policies
19+
20+
create user authenticator noinherit;
21+
grant anon to authenticator;
22+
grant authenticated to authenticator;
23+
grant service_role to authenticator;
24+
grant supabase_admin to authenticator;
25+
26+
grant usage on schema public to postgres, anon, authenticated, service_role;
27+
alter default privileges in schema public grant all on tables to postgres, anon, authenticated, service_role;
28+
alter default privileges in schema public grant all on functions to postgres, anon, authenticated, service_role;
29+
alter default privileges in schema public grant all on sequences to postgres, anon, authenticated, service_role;
30+
31+
-- Set up namespacing
32+
alter user supabase_admin SET search_path TO public, extensions; -- don't include the "auth" schema
33+
34+
-- These are required so that the users receive grants whenever "supabase_admin" creates tables/function
35+
alter default privileges for user supabase_admin in schema public grant all
36+
on sequences to postgres, anon, authenticated, service_role;
37+
alter default privileges for user supabase_admin in schema public grant all
38+
on tables to postgres, anon, authenticated, service_role;
39+
alter default privileges for user supabase_admin in schema public grant all
40+
on functions to postgres, anon, authenticated, service_role;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
2+
CREATE SCHEMA IF NOT EXISTS auth AUTHORIZATION supabase_admin;
3+
4+
-- auth.users definition
5+
6+
CREATE TABLE auth.users (
7+
instance_id uuid NULL,
8+
id uuid NOT NULL UNIQUE,
9+
aud varchar(255) NULL,
10+
"role" varchar(255) NULL,
11+
email varchar(255) NULL UNIQUE,
12+
encrypted_password varchar(255) NULL,
13+
confirmed_at timestamptz NULL,
14+
invited_at timestamptz NULL,
15+
confirmation_token varchar(255) NULL,
16+
confirmation_sent_at timestamptz NULL,
17+
recovery_token varchar(255) NULL,
18+
recovery_sent_at timestamptz NULL,
19+
email_change_token varchar(255) NULL,
20+
email_change varchar(255) NULL,
21+
email_change_sent_at timestamptz NULL,
22+
last_sign_in_at timestamptz NULL,
23+
raw_app_meta_data jsonb NULL,
24+
raw_user_meta_data jsonb NULL,
25+
is_super_admin bool NULL,
26+
created_at timestamptz NULL,
27+
updated_at timestamptz NULL,
28+
CONSTRAINT users_pkey PRIMARY KEY (id)
29+
);
30+
CREATE INDEX users_instance_id_email_idx ON auth.users USING btree (instance_id, email);
31+
CREATE INDEX users_instance_id_idx ON auth.users USING btree (instance_id);
32+
33+
-- auth.refresh_tokens definition
34+
35+
CREATE TABLE auth.refresh_tokens (
36+
instance_id uuid NULL,
37+
id bigserial NOT NULL,
38+
"token" varchar(255) NULL,
39+
user_id varchar(255) NULL,
40+
revoked bool NULL,
41+
created_at timestamptz NULL,
42+
updated_at timestamptz NULL,
43+
CONSTRAINT refresh_tokens_pkey PRIMARY KEY (id)
44+
);
45+
CREATE INDEX refresh_tokens_instance_id_idx ON auth.refresh_tokens USING btree (instance_id);
46+
CREATE INDEX refresh_tokens_instance_id_user_id_idx ON auth.refresh_tokens USING btree (instance_id, user_id);
47+
CREATE INDEX refresh_tokens_token_idx ON auth.refresh_tokens USING btree (token);
48+
49+
-- auth.instances definition
50+
51+
CREATE TABLE auth.instances (
52+
id uuid NOT NULL,
53+
uuid uuid NULL,
54+
raw_base_config text NULL,
55+
created_at timestamptz NULL,
56+
updated_at timestamptz NULL,
57+
CONSTRAINT instances_pkey PRIMARY KEY (id)
58+
);
59+
60+
-- auth.audit_log_entries definition
61+
62+
CREATE TABLE auth.audit_log_entries (
63+
instance_id uuid NULL,
64+
id uuid NOT NULL,
65+
payload json NULL,
66+
created_at timestamptz NULL,
67+
CONSTRAINT audit_log_entries_pkey PRIMARY KEY (id)
68+
);
69+
CREATE INDEX audit_logs_instance_id_idx ON auth.audit_log_entries USING btree (instance_id);
70+
71+
-- auth.schema_migrations definition
72+
73+
CREATE TABLE auth.schema_migrations (
74+
"version" varchar(255) NOT NULL,
75+
CONSTRAINT schema_migrations_pkey PRIMARY KEY ("version")
76+
);
77+
78+
INSERT INTO auth.schema_migrations (version)
79+
VALUES ('20171026211738'),
80+
('20171026211808'),
81+
('20171026211834'),
82+
('20180103212743'),
83+
('20180108183307'),
84+
('20180119214651'),
85+
('20180125194653');
86+
87+
-- Gets the User ID from the request cookie
88+
create or replace function auth.uid() returns uuid as $$
89+
select nullif(current_setting('request.jwt.claim.sub', true), '')::uuid;
90+
$$ language sql stable;
91+
92+
-- Gets the User ID from the request cookie
93+
create or replace function auth.role() returns text as $$
94+
select nullif(current_setting('request.jwt.claim.role', true), '')::text;
95+
$$ language sql stable;
96+
97+
-- Supabase super admin
98+
CREATE USER supabase_auth_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION;
99+
GRANT ALL PRIVILEGES ON SCHEMA auth TO supabase_auth_admin;
100+
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA auth TO supabase_auth_admin;
101+
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA auth TO supabase_auth_admin;
102+
ALTER USER supabase_auth_admin SET search_path = "auth";

0 commit comments

Comments
 (0)