diff --git a/BunnyCDN.Net.Storage.Tests/BunnyCDN.Net.Storage.Tests.csproj b/BunnyCDN.Net.Storage.Tests/BunnyCDN.Net.Storage.Tests.csproj index 219851c..267115a 100644 --- a/BunnyCDN.Net.Storage.Tests/BunnyCDN.Net.Storage.Tests.csproj +++ b/BunnyCDN.Net.Storage.Tests/BunnyCDN.Net.Storage.Tests.csproj @@ -1,16 +1,16 @@ - netcoreapp3.0 + net7.0 false - - - - + + + + diff --git a/BunnyCDN.Net.Storage.Tests/ChecksumTests.cs b/BunnyCDN.Net.Storage.Tests/ChecksumTests.cs new file mode 100644 index 0000000..b18e15e --- /dev/null +++ b/BunnyCDN.Net.Storage.Tests/ChecksumTests.cs @@ -0,0 +1,32 @@ +using Xunit; +using System.IO; +using System.Text; + +namespace BunnyCDN.Net.Storage.Tests +{ + public class ChecksumTests + { + /* + Hashes generated using: echo -n "<>" | shasum -a 256 | awk '{print toupper($1)}' + Shasum version: 6.02 + */ + [Theory] + [InlineData("Test", + "532EAABD9574880DBF76B9B8CC00832C20A6EC113D682299550D7A6E0F345E25")] + [InlineData("szdhjfkgdsjzhfgjsdfjzshfgjkskdjfzdjkhgj", + "48593515AEF42567E7FF4BF352D984A41F86D030CBF439A07D1A5C4FDD4F4179")] + [InlineData("It'sTh3Way...TheBunnyW4y", + "48CF44BB1031DDF5F2FB2531AB1F10CAA1C49DC3EE6764D8180CD26A50C6DF1E")] + public void Generate_ShouldGenerateCorrectHash(string input, string expectedOutput) + { + // Arrange + var stream = new MemoryStream(Encoding.UTF8.GetBytes(input)); + + // Act + var result = Checksum.Generate(stream); + + // Assert + Assert.Equal(expectedOutput, result); + } + } +} \ No newline at end of file diff --git a/BunnyCDN.Net.Storage.Tests/SerializerTests.cs b/BunnyCDN.Net.Storage.Tests/SerializerTests.cs new file mode 100644 index 0000000..6d20d14 --- /dev/null +++ b/BunnyCDN.Net.Storage.Tests/SerializerTests.cs @@ -0,0 +1,40 @@ +using System; +using Xunit; +using BunnyCDN.Net.Storage.Models; + +namespace BunnyCDN.Net.Storage.Tests +{ + public class SerializerTests + { + [Fact] + public void Deserialize_ShouldRetunCorrectObject() + { + // Arrange + var input = "{\"Guid\":\"338d2080-bbbf-407f-b756-048c2eb0838f\",\"StorageZoneName\":\"testStorageZone\",\"Path\":\"/testStorageZone/\",\"ObjectName\":\"folderName\",\"Length\":0,\"LastChanged\":\"2022-11-14T09:34:37.747\",\"ServerId\":0,\"ArrayNumber\":0,\"IsDirectory\":true,\"UserId\":\"6fc9081f-43ce-475a-8215-ba2ebfd36d91\",\"ContentType\":\"\",\"DateCreated\":\"2022-11-14T09:34:37.747\",\"StorageZoneId\":1000009,\"Checksum\":null,\"ReplicatedZones\":null}"; + var expected = new StorageObject + { + Checksum = null, + ContentType = "", + Guid = Guid.Parse("338d2080-bbbf-407f-b756-048c2eb0838f"), + StorageZoneName = "testStorageZone", + Path = "/testStorageZone/", + ObjectName = "folderName", + Length = 0, + LastChanged = DateTime.Parse("2022-11-14T09:34:37.747"), + ServerId = 0, + ArrayNumber = 0, + IsDirectory = true, + UserId = Guid.Parse("6fc9081f-43ce-475a-8215-ba2ebfd36d91"), + DateCreated = DateTime.Parse("2022-11-14T09:34:37.747"), + StorageZoneId = 1000009, + ReplicatedZones = null + }; + + // Act + var result = Serializer.Deserialize(input); + + // Assert + Assert.Equivalent(expected, result); + } + } +} \ No newline at end of file diff --git a/BunnyCDN.Net.Storage/BunnyCDN.Net.Storage.csproj b/BunnyCDN.Net.Storage/BunnyCDN.Net.Storage.csproj index 24a6ae7..d2e6a6c 100644 --- a/BunnyCDN.Net.Storage/BunnyCDN.Net.Storage.csproj +++ b/BunnyCDN.Net.Storage/BunnyCDN.Net.Storage.csproj @@ -1,8 +1,8 @@  - netstandard2.0;netstandard2.1 - 1.0.5 + netstandard2.0;netstandard2.1;net6.0;net7.0 + 1.1.0 true BunnyWay d.o.o. The official .NET library used for interacting with the BunnyCDN Storage API. @@ -15,6 +15,7 @@ storage Copyright 2020 BunnyWay d.o.o. BunnyCDN.Net.Storage + 10.0 @@ -24,7 +25,7 @@ - + @@ -32,6 +33,12 @@ + + + <_Parameter1>BunnyCDN.Net.Storage.Tests + + + True diff --git a/BunnyCDN.Net.Storage/BunnyCDNStorage.cs b/BunnyCDN.Net.Storage/BunnyCDNStorage.cs index 24de12b..8ff2092 100644 --- a/BunnyCDN.Net.Storage/BunnyCDNStorage.cs +++ b/BunnyCDN.Net.Storage/BunnyCDNStorage.cs @@ -8,22 +8,31 @@ namespace BunnyCDN.Net.Storage { - public class BunnyCDNStorage + public class BunnyCDNStorage : IDisposable { /// /// The API access key used for authentication /// - public string ApiAccessKey { get; private set; } +#if !NETSTANDARD + private string ApiAccessKey { get; init; } +#else + private string ApiAccessKey { get; set; } +#endif /// /// The name of the storage zone we are working on /// - public string StorageZoneName { get; private set; } +#if !NETSTANDARD + private string StorageZoneName { get; init; } +#else + private string StorageZoneName { get; set; } +#endif /// /// The HTTP Client used for the API communication /// private HttpClient _http = null; + private bool _disposed = false; /// /// Initializes a new instance of the BunnyCDNStorage class @@ -42,7 +51,7 @@ public BunnyCDNStorage(string storageZoneName, string apiAccessKey, string mainR _http.BaseAddress = new Uri(this.GetBaseAddress(mainReplicationRegion)); } - #region Delete +#region Delete /// /// Delete an object at the given path. If the object is a directory, the contents will also be deleted. /// @@ -63,7 +72,7 @@ public async Task DeleteObjectAsync(string path) } #endregion - #region List +#region List /// /// Get the list of storage objects on the given path /// @@ -84,9 +93,9 @@ public async Task> GetStorageObjectsAsync(string path) throw this.MapResponseToException(response.StatusCode, normalizedPath); } } - #endregion +#endregion - #region Upload +#region Upload /// /// Upload an object from a stream (missing path will be created) /// @@ -171,9 +180,9 @@ public async Task UploadAsync(string localFilePath, string path, bool validateCh await UploadAsync(fileStream, path, validateChecksum, sha256Checksum, contentTypeOverride); } } - #endregion +#endregion - #region Download +#region Download /// /// Download the object to a local file /// @@ -220,9 +229,27 @@ public async Task DownloadObjectAsStreamAsync(string path) throw this.MapResponseToException((HttpStatusCode)(int)ex.Status, path); } } - #endregion +#endregion + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } - #region Utils + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _http.Dispose(); + } + _disposed = true; + } + } + +#region Utils /// /// Map the API response to the correct BunnyCDNStorageExecption /// @@ -245,7 +272,7 @@ private BunnyCDNStorageException MapResponseToException(HttpStatusCode statusCod /// Normalize a path string /// /// Recognizable, valid string for use against API calls - public string NormalizePath(string path, bool? isDirectory = null) + internal string NormalizePath(string path, bool? isDirectory = null) { // Trim all prepending & tailing whitespace, fix windows-like paths then remove prepending slashes path = path.Trim() @@ -268,7 +295,7 @@ public string NormalizePath(string path, bool? isDirectory = null) return path; } - #endregion + /// /// Get the base HTTP URL address of the storage endpoint @@ -284,5 +311,6 @@ private string GetBaseAddress(string mainReplicationRegion) return $"https://{mainReplicationRegion}.storage.bunnycdn.com/"; } +#endregion } } diff --git a/BunnyCDN.Net.Storage/Checksum.cs b/BunnyCDN.Net.Storage/Checksum.cs index 91ea619..bf30d84 100644 --- a/BunnyCDN.Net.Storage/Checksum.cs +++ b/BunnyCDN.Net.Storage/Checksum.cs @@ -9,7 +9,7 @@ internal class Checksum { internal static string Generate(Stream stream) { - using (var sha = new SHA256Managed()) + using (var sha = SHA256.Create()) { byte[] checksumData = sha.ComputeHash(stream); return BitConverter.ToString(checksumData).Replace("-", String.Empty); diff --git a/BunnyCDN.Net.Storage/Models/StorageObject.cs b/BunnyCDN.Net.Storage/Models/StorageObject.cs index 07df676..fce4f4d 100644 --- a/BunnyCDN.Net.Storage/Models/StorageObject.cs +++ b/BunnyCDN.Net.Storage/Models/StorageObject.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace BunnyCDN.Net.Storage.Models { @@ -9,15 +7,24 @@ public class StorageObject /// /// The unique GUID of the file /// - public string Guid { get; set; } + public Guid Guid { get; set; } /// /// The ID of the BunnyCDN user that holds the file /// - public string UserId { get; set; } + public Guid UserId { get; set; } /// /// The date when the file was created /// public DateTime DateCreated { get; set; } + /// + /// Content type of the stoage object + /// + public string ContentType { get; set; } + public int ArrayNumber { get; set; } + /// + /// File checksum for integrity validation + /// + public string Checksum { get; set; } /// /// The date when the file was last modified @@ -28,6 +35,10 @@ public class StorageObject /// public string StorageZoneName { get; set; } /// + /// Replicated zone names + /// + public string ReplicatedZones { get; set; } + /// /// The path to the object /// public string Path { get; set; } @@ -38,7 +49,7 @@ public class StorageObject /// /// The total of the object in bytes /// - public long Length { get; set; } + public ulong Length { get; set; } /// /// True if the object is a directory /// @@ -46,11 +57,11 @@ public class StorageObject /// /// The ID of the storage server that the file resides on /// - public int ServerId { get; set; } + public uint ServerId { get; set; } /// /// The ID of the storage zone that the object is linked to /// - public long StorageZoneId { get; set; } + public ulong StorageZoneId { get; set; } /// /// Gets the full path to the file diff --git a/BunnyCDN.Net.Storage/Serializer.cs b/BunnyCDN.Net.Storage/Serializer.cs index acf50c1..09ea884 100644 --- a/BunnyCDN.Net.Storage/Serializer.cs +++ b/BunnyCDN.Net.Storage/Serializer.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -#if NETSTANDARD2_0 +#if NETSTANDARD2_0 using Newtonsoft.Json; #else using System.Text.Json;