Skip to content

Commit d037a54

Browse files
authored
chore: release 2.1.0
* feat: implement partially method copy * test: increment unit test * chore: implement supabase stack * chore: automate release with release-please * feat: format class Release-As: 2.1.0
1 parent a66944f commit d037a54

14 files changed

+499
-48
lines changed

.github/workflows/build-and-test.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,16 @@ jobs:
2828

2929
- name: Build
3030
run: dotnet build --configuration Release --no-restore
31+
32+
- uses: supabase/setup-cli@v1
33+
with:
34+
version: latest
35+
36+
- name: Start supabase
37+
run: supabase start
3138

32-
- name: Initialize Testing Stack
33-
run: chmod +x ./prepare-infra.sh && ./prepare-infra.sh && sleep 5
39+
# - name: Initialize Testing Stack
40+
# run: chmod +x ./prepare-infra.sh && ./prepare-infra.sh && sleep 5
3441

3542
- name: Test
3643
run: dotnet test --no-restore

.github/workflows/release.yml

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
1-
name: Publish NuGet Package
1+
name: Release - Publish NuGet Package
22

33
on:
44
push:
55
branches:
6-
- release/* # Default release branch
6+
- master
77

88
jobs:
9+
release-please:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: write
13+
pull-requests: write
14+
issues: write
15+
steps:
16+
- uses: googleapis/release-please-action@v4
17+
with:
18+
target-branch: ${{ github.ref_name }}
19+
manifest-file: .release-please-manifest.json
20+
config-file: release-please-config.json
21+
922
publish:
23+
needs: release-please
24+
if: ${{ github.repository_owner == 'supabase-community' && startsWith(github.event.head_commit.message, 'chore(master)') && github.ref == 'refs/heads/master' && github.event_name == 'push' }}
1025
name: build, pack & publish
1126
runs-on: ubuntu-latest
1227
steps:

.release-please-manifest.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
".": "2.0.2"
3+
}

Storage/DestinationOptions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Supabase.Storage;
2+
3+
/// <summary>
4+
/// Represents the options for a destination in the context of Supabase Storage.
5+
/// </summary>
6+
public class DestinationOptions
7+
{
8+
/// <summary>
9+
/// Gets or sets the name of the destination bucket in the context of Supabase Storage.
10+
/// </summary>
11+
public string? DestinationBucket { get; set; }
12+
}

Storage/Exceptions/FailureHint.cs

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,37 @@
44
namespace Supabase.Storage.Exceptions
55
{
66
public static class FailureHint
7-
{
8-
public enum Reason
9-
{
10-
Unknown,
11-
NotAuthorized,
12-
Internal,
13-
NotFound,
14-
AlreadyExists,
15-
InvalidInput
16-
}
17-
18-
public static Reason DetectReason(SupabaseStorageException storageException)
19-
{
20-
if (storageException.Content == null)
21-
return Unknown;
22-
23-
return storageException.StatusCode switch
24-
{
25-
400 when storageException.Content.ToLower().Contains("authorization") => NotAuthorized,
26-
400 when storageException.Content.ToLower().Contains("malformed") => NotAuthorized,
27-
400 when storageException.Content.ToLower().Contains("invalid signature") => NotAuthorized,
28-
400 when storageException.Content.ToLower().Contains("invalid") => InvalidInput,
29-
401 => NotAuthorized,
30-
404 when storageException.Content.ToLower().Contains("not found") => NotFound,
31-
409 when storageException.Content.ToLower().Contains("exists") => AlreadyExists,
32-
500 => Internal,
33-
_ => Unknown
34-
};
35-
}
36-
}
37-
38-
}
7+
{
8+
public enum Reason
9+
{
10+
Unknown,
11+
NotAuthorized,
12+
Internal,
13+
NotFound,
14+
AlreadyExists,
15+
InvalidInput
16+
}
17+
18+
public static Reason DetectReason(SupabaseStorageException storageException)
19+
{
20+
if (storageException.Content == null)
21+
return Unknown;
22+
23+
return storageException.StatusCode switch
24+
{
25+
400 when storageException.Content.ToLower().Contains("authorization") => NotAuthorized,
26+
400 when storageException.Content.ToLower().Contains("malformed") => NotAuthorized,
27+
400 when storageException.Content.ToLower().Contains("invalid signature") => NotAuthorized,
28+
400 when storageException.Content.ToLower().Contains("invalid") => InvalidInput,
29+
401 => NotAuthorized,
30+
403 when storageException.Content.ToLower().Contains("invalid compact jws") => NotAuthorized,
31+
403 when storageException.Content.ToLower().Contains("signature verification failed") => NotAuthorized,
32+
404 when storageException.Content.ToLower().Contains("not found") => NotFound,
33+
409 when storageException.Content.ToLower().Contains("exists") => AlreadyExists,
34+
500 => Internal,
35+
_ => Unknown
36+
};
37+
}
38+
}
39+
40+
}

Storage/Interfaces/IStorageFileApi.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ public interface IStorageFileApi<TFileObject>
1818
Task<string> DownloadPublicFile(string supabasePath, string localPath, TransformOptions? transformOptions = null, EventHandler<float>? onProgress = null);
1919
string GetPublicUrl(string path, TransformOptions? transformOptions = null);
2020
Task<List<TFileObject>?> List(string path = "", SearchOptions? options = null);
21-
Task<bool> Move(string fromPath, string toPath);
21+
Task<bool> Move(string fromPath, string toPath, DestinationOptions? options = null);
22+
Task<bool> Copy(string fromPath, string toPath, DestinationOptions? options = null);
2223
Task<TFileObject?> Remove(string path);
2324
Task<List<TFileObject>?> Remove(List<string> paths);
2425
Task<string> Update(byte[] data, string supabasePath, FileOptions? options = null, EventHandler<float>? onProgress = null);

Storage/Storage.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
<Title>Supabase Storage</Title>
1717
<PackageIconUrl>https://avatars.githubusercontent.com/u/54469796?s=200&amp;v=4</PackageIconUrl>
1818
<PackageTags>supabase, storage</PackageTags>
19+
<!-- x-release-please-start-version -->
1920
<ReleaseVersion>2.0.2</ReleaseVersion>
2021
<PackageVersion>2.0.2</PackageVersion>
22+
<!-- x-release-please-end -->
2123
<RepositoryUrl>https://github.com/supabase-community/storage-csharp</RepositoryUrl>
2224
<RepositoryType>git</RepositoryType>
2325
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@@ -33,7 +35,7 @@
3335
</PropertyGroup>
3436

3537
<PropertyGroup Condition=" '$(Version)' == '' ">
36-
<VersionPrefix Condition=" '$(VersionPrefix)' == '' ">2.0.2</VersionPrefix>
38+
<VersionPrefix Condition=" '$(VersionPrefix)' == '' ">2.0.2</VersionPrefix> <!-- x-release-please-version -->
3739
<Version Condition=" '$(Version)' == '' ">$(VersionPrefix)</Version>
3840
</PropertyGroup>
3941

Storage/StorageFileApi.cs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -281,23 +281,46 @@ public Task<string> Update(byte[] data, string supabasePath, FileOptions? option
281281
}
282282

283283
/// <summary>
284-
/// Moves an existing file, optionally renaming it at the same time.
284+
/// Moves an existing file to a new location, optionally allowing renaming.
285285
/// </summary>
286-
/// <param name="fromPath">The original file path, including the current file name. For example `folder/image.png`.</param>
287-
/// <param name="toPath">The new file path, including the new file name. For example `folder/image-copy.png`.</param>
288-
/// <returns></returns>
289-
public async Task<bool> Move(string fromPath, string toPath)
286+
/// <param name="fromPath">The original file path, including the current file name (e.g., `folder/image.png`).</param>
287+
/// <param name="toPath">The target file path, including the new file name (e.g., `folder/image-copy.png`).</param>
288+
/// <param name="options">Optional parameters for specifying the destination bucket and other settings.</param>
289+
/// <returns>Returns a boolean value indicating whether the operation was successful.</returns>
290+
public async Task<bool> Move(string fromPath, string toPath, DestinationOptions? options = null)
290291
{
291292
var body = new Dictionary<string, string?>
292293
{
293294
{ "bucketId", BucketId },
294295
{ "sourceKey", fromPath },
295-
{ "destinationKey", toPath }
296+
{ "destinationKey", toPath },
297+
{ "destinationBucket", options?.DestinationBucket }
296298
};
297299
await Helpers.MakeRequest<GenericResponse>(HttpMethod.Post, $"{Url}/object/move", body, Headers);
298300
return true;
299301
}
300302

303+
/// <summary>
304+
/// Copies a file/object from one path to another within a bucket or across buckets.
305+
/// </summary>
306+
/// <param name="fromPath">The source path of the file/object to copy.</param>
307+
/// <param name="toPath">The destination path for the copied file/object.</param>
308+
/// <param name="options">Optional parameters such as the destination bucket.</param>
309+
/// <returns>True if the copy operation was successful.</returns>
310+
public async Task<bool> Copy(string fromPath, string toPath, DestinationOptions? options = null)
311+
{
312+
var body = new Dictionary<string, string?>
313+
{
314+
{ "bucketId", BucketId },
315+
{ "sourceKey", fromPath },
316+
{ "destinationKey", toPath },
317+
{ "destinationBucket", options?.DestinationBucket }
318+
};
319+
320+
await Helpers.MakeRequest<GenericResponse>(HttpMethod.Post, $"{Url}/object/copy", body, Headers);
321+
return true;
322+
}
323+
301324
/// <summary>
302325
/// Downloads a file from a private bucket. For public buckets, use <see cref="DownloadPublicFile(string, string, TransformOptions?, EventHandler{float}?)"/>
303326
/// </summary>

StorageTests/Helpers.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ namespace StorageTests
55
{
66
public static class Helpers
77
{
8-
public static string SupabaseUrl => "http://localhost:5000";
9-
public static string ServiceKey => "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaWF0IjoxNjEzNTMxOTg1LCJleHAiOjE5MjkxMDc5ODV9.th84OKK0Iz8QchDyXZRrojmKSEZ-OuitQm_5DvLiSIc";
10-
public static string PublicKey => "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwic3ViIjoiMzE3ZWFkY2UtNjMxYS00NDI5LWEwYmItZjE5YTdhNTE3YjRhIiwiZW1haWwiOiJpbmlhbit0ZXN0MUBzdXBhYmFzZS5pbyIsImV4cCI6MTkzOTEwNzk4NSwiYXBwX21ldGFkYXRhIjp7InByb3ZpZGVyIjoiZW1haWwifSwidXNlcl9tZXRhZGF0YSI6e30sInJvbGUiOiJhdXRoZW50aWNhdGVkIn0.E-x3oYcHIjFCdUO1M3wKDl1Ln32mik0xdHT2PjrvN70";
8+
public static string SupabaseUrl => "http://127.0.0.1:54321/storage/v1";
9+
public static string PublicKey => "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0";
10+
public static string ServiceKey => "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU";
1111

1212
public static string StorageUrl => $"{SupabaseUrl}";
1313

StorageTests/StorageFileAnonTests.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public async Task InitializeTest()
2828

2929
if (_bucket == null && await Storage.GetBucket(_bucketId) == null)
3030
{
31-
await AdminStorage.CreateBucket(_bucketId, new BucketUpsertOptions { Public = true });
31+
await AdminStorage.CreateBucket(_bucketId, new BucketUpsertOptions { Public = false });
3232
}
3333

3434
_adminBucket = AdminStorage.From(_bucketId);
@@ -131,6 +131,18 @@ await Assert.ThrowsExceptionAsync<SupabaseStorageException>(async () =>
131131
});
132132
}
133133

134+
[TestMethod("File: Throws attempting to Copy")]
135+
public async Task Copy()
136+
{
137+
var name = $"{Guid.NewGuid()}.bin";
138+
await _adminBucket.Upload(new Byte[] { 0x0, 0x1 }, name);
139+
140+
await Assert.ThrowsExceptionAsync<SupabaseStorageException>(async () =>
141+
{
142+
await _bucket.Copy(name, "new-file.bin");
143+
});
144+
}
145+
134146
[TestMethod("File: Get Public Link")]
135147
public async Task GetPublicLink()
136148
{

0 commit comments

Comments
 (0)