diff --git a/Storage/FileObjectV2.cs b/Storage/FileObjectV2.cs new file mode 100644 index 0000000..7cbc181 --- /dev/null +++ b/Storage/FileObjectV2.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Supabase.Storage +{ + public class FileObjectV2 + { + + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("version")] + public string Version { get; set; } + + [JsonProperty("name")] + public string? Name { get; set; } + + [JsonProperty("bucket_id")] + public string? BucketId { get; set; } + + [JsonProperty("updated_at")] + public DateTime? UpdatedAt { get; set; } + + [JsonProperty("created_at")] + public DateTime? CreatedAt { get; set; } + + [JsonProperty("last_accessed_at")] + public DateTime? LastAccessedAt { get; set; } + + [JsonProperty("size")] + public int? Size { get; set; } + + [JsonProperty("cache_control")] + public string? CacheControl { get; set; } + + [JsonProperty("content_type")] + public string? ContentType { get; set; } + + [JsonProperty("etag")] + public string? Etag { get; set; } + + [JsonProperty("last_modified")] + public DateTime? LastModified { get; set; } + + [JsonProperty("metadata")] + public Dictionary? Metadata { get; set; } + } +} diff --git a/Storage/FileOptions.cs b/Storage/FileOptions.cs index 7480392..f633da3 100644 --- a/Storage/FileOptions.cs +++ b/Storage/FileOptions.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; namespace Supabase.Storage { @@ -12,5 +13,14 @@ public class FileOptions [JsonProperty("upsert")] public bool Upsert { get; set; } + + [JsonProperty("duplex")] + public string? Duplex { get; set; } + + [JsonProperty("metadata")] + public Dictionary? Metadata { get; set; } + + [JsonProperty("headers")] + public Dictionary? Headers { get; set; } } } diff --git a/Storage/Interfaces/IStorageFileApi.cs b/Storage/Interfaces/IStorageFileApi.cs index 3aaaecc..da54d8a 100644 --- a/Storage/Interfaces/IStorageFileApi.cs +++ b/Storage/Interfaces/IStorageFileApi.cs @@ -18,6 +18,7 @@ public interface IStorageFileApi Task DownloadPublicFile(string supabasePath, string localPath, TransformOptions? transformOptions = null, EventHandler? onProgress = null); string GetPublicUrl(string path, TransformOptions? transformOptions = null); Task?> List(string path = "", SearchOptions? options = null); + Task Info(string path); Task Move(string fromPath, string toPath, DestinationOptions? options = null); Task Copy(string fromPath, string toPath, DestinationOptions? options = null); Task Remove(string path); diff --git a/Storage/StorageFileApi.cs b/Storage/StorageFileApi.cs index f14320f..0ecd260 100644 --- a/Storage/StorageFileApi.cs +++ b/Storage/StorageFileApi.cs @@ -132,6 +132,19 @@ await Helpers.MakeRequest>(HttpMethod.Post, $"{Url}/object/list return response; } + + /// + /// Retrieves the details of an existing file. + /// + /// + /// + public async Task Info(string path) + { + var response = + await Helpers.MakeRequest(HttpMethod.Get, $"{Url}/object/info/{BucketId}/{path}", null, Headers); + + return response; + } /// /// Uploads a file to an existing bucket. @@ -464,6 +477,14 @@ private async Task UploadOrUpdate(string localPath, string supabasePath, if (options.Upsert) headers.Add("x-upsert", options.Upsert.ToString().ToLower()); + if (options.Metadata != null) + headers.Add("x-metadata", ParseMetadata(options.Metadata)); + + options.Headers?.ToList().ForEach(x => headers.Add(x.Key, x.Value)); + + if (options.Duplex != null) + headers.Add("x-duplex", options.Duplex.ToLower()); + var progress = new Progress(); if (onProgress != null) @@ -474,6 +495,14 @@ private async Task UploadOrUpdate(string localPath, string supabasePath, return GetFinalPath(supabasePath); } + private static string ParseMetadata(Dictionary metadata) + { + var json = JsonConvert.SerializeObject(metadata); + var base64 = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(json)); + + return base64; + } + private async Task UploadOrUpdate(byte[] data, string supabasePath, FileOptions options, EventHandler? onProgress = null) { @@ -488,6 +517,14 @@ private async Task UploadOrUpdate(byte[] data, string supabasePath, File if (options.Upsert) headers.Add("x-upsert", options.Upsert.ToString().ToLower()); + if (options.Metadata != null) + headers.Add("x-metadata", ParseMetadata(options.Metadata)); + + options.Headers?.ToList().ForEach(x => headers.Add(x.Key, x.Value)); + + if (options.Duplex != null) + headers.Add("x-duplex", options.Duplex.ToLower()); + var progress = new Progress(); if (onProgress != null) diff --git a/StorageTests/StorageFileTests.cs b/StorageTests/StorageFileTests.cs index e56e16f..9883d5c 100644 --- a/StorageTests/StorageFileTests.cs +++ b/StorageTests/StorageFileTests.cs @@ -7,6 +7,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Supabase.Storage; using Supabase.Storage.Interfaces; +using FileOptions = Supabase.Storage.FileOptions; namespace StorageTests; @@ -75,6 +76,51 @@ public async Task UploadFile() await _bucket.Remove(new List { name }); } + + [TestMethod("File: Upload File With FileOptions")] + public async Task UploadFileWithFileOptions() + { + var didTriggerProgress = new TaskCompletionSource(); + + var asset = "supabase-csharp.png"; + var name = $"{Guid.NewGuid()}.png"; + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)?.Replace("file:", ""); + + Assert.IsNotNull(basePath); + + var imagePath = Path.Combine(basePath, "Assets", asset); + + var metadata = new Dictionary + { + ["custom"] = "metadata", + ["local_file"] = "local_file" + }; + + var headers = new Dictionary + { + ["x-version"] = "123" + }; + + var options = new FileOptions + { + Duplex = "duplex", + Metadata = metadata, + Headers = headers, + }; + await _bucket.Upload(imagePath, name, options, (_, _) => { didTriggerProgress.TrySetResult(true); }); + + var item = await _bucket.Info(name); + + Assert.IsNotNull(item); + Assert.IsNotNull(item.Metadata); + Assert.AreEqual(metadata["custom"], item.Metadata["custom"]); + Assert.AreEqual(metadata["local_file"], item.Metadata["local_file"]); + + var sentProgressEvent = await didTriggerProgress.Task; + Assert.IsTrue(sentProgressEvent); + + await _bucket.Remove([name]); + } [TestMethod("File: Upload Arbitrary Byte Array")] public async Task UploadArbitraryByteArray() @@ -192,7 +238,7 @@ public async Task CopyToAnotherBucket() foreach (var file in copied) { if (file.Name is not null) - await localBucket.Remove(new List { file.Name }); + await localBucket.Remove([file.Name]); } await Storage.DeleteBucket("copyfile");