From 741776ac32001282d2931bc8a325dbcb36a8932d Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 4 Sep 2024 17:41:16 +0800 Subject: [PATCH 1/3] Add BlobStoring Google Provider --- Directory.Packages.props | 1 + framework/Volo.Abp.sln | 14 ++ .../Volo.Abp.BlobStoring.Google.csproj | 24 ++++ .../Google/AbpBlobStoringGoogleModule.cs | 9 ++ .../Google/DefaultGoogleBlobNameCalculator.cs | 21 +++ ...gleBlobContainerConfigurationExtensions.cs | 24 ++++ .../Google/GoogleBlobNamingNormalizer.cs | 120 ++++++++++++++++ .../BlobStoring/Google/GoogleBlobProvider.cs | 129 ++++++++++++++++++ .../Google/GoogleBlobProviderConfiguration.cs | 70 ++++++++++ .../GoogleBlobProviderConfigurationNames.cs | 11 ++ .../Google/IGoogleBlobNameCalculator.cs | 6 + .../Volo.Abp.BlobStoring.Google.Tests.csproj | 19 +++ .../Google/AbpBlobStoringGoogleTestBase.cs | 19 +++ .../Google/AbpBlobStoringGoogleTestModule.cs | 60 ++++++++ ...oogleBlobNamingNormalizerProvider_Tests.cs | 94 +++++++++++++ 15 files changed, 621 insertions(+) create mode 100644 framework/src/Volo.Abp.BlobStoring.Google/Volo.Abp.BlobStoring.Google.csproj create mode 100644 framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleModule.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/DefaultGoogleBlobNameCalculator.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobContainerConfigurationExtensions.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobNamingNormalizer.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProviderConfiguration.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProviderConfigurationNames.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/IGoogleBlobNameCalculator.cs create mode 100644 framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo.Abp.BlobStoring.Google.Tests.csproj create mode 100644 framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestBase.cs create mode 100644 framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs create mode 100644 framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/DefaultGoogleBlobNamingNormalizerProvider_Tests.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index b43886f7cf..652b9a836e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,6 +38,7 @@ + diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 3373a239de..19fbf05df8 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -467,6 +467,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.RemoteServices.Tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Abstractions", "src\Volo.Abp.AspNetCore.Abstractions\Volo.Abp.AspNetCore.Abstractions.csproj", "{E1051CD0-9262-4869-832D-B951723F4DDE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Google", "src\Volo.Abp.BlobStoring.Google\Volo.Abp.BlobStoring.Google.csproj", "{DEEB5200-BBF9-464D-9B7E-8FC035A27E94}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Google.Tests", "test\Volo.Abp.BlobStoring.Google.Tests\Volo.Abp.BlobStoring.Google.Tests.csproj", "{40FB8907-9CF7-44D0-8B5F-538AC6DAF8B9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1393,6 +1397,14 @@ Global {E1051CD0-9262-4869-832D-B951723F4DDE}.Debug|Any CPU.Build.0 = Debug|Any CPU {E1051CD0-9262-4869-832D-B951723F4DDE}.Release|Any CPU.ActiveCfg = Release|Any CPU {E1051CD0-9262-4869-832D-B951723F4DDE}.Release|Any CPU.Build.0 = Release|Any CPU + {DEEB5200-BBF9-464D-9B7E-8FC035A27E94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DEEB5200-BBF9-464D-9B7E-8FC035A27E94}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DEEB5200-BBF9-464D-9B7E-8FC035A27E94}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DEEB5200-BBF9-464D-9B7E-8FC035A27E94}.Release|Any CPU.Build.0 = Release|Any CPU + {40FB8907-9CF7-44D0-8B5F-538AC6DAF8B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40FB8907-9CF7-44D0-8B5F-538AC6DAF8B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40FB8907-9CF7-44D0-8B5F-538AC6DAF8B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40FB8907-9CF7-44D0-8B5F-538AC6DAF8B9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1628,6 +1640,8 @@ Global {DFAF8763-D1D6-4EB4-B459-20E31007FE2F} = {447C8A77-E5F0-4538-8687-7383196D04EA} {DACD4485-61BE-4DE5-ACAE-4FFABC122500} = {447C8A77-E5F0-4538-8687-7383196D04EA} {E1051CD0-9262-4869-832D-B951723F4DDE} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {DEEB5200-BBF9-464D-9B7E-8FC035A27E94} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {40FB8907-9CF7-44D0-8B5F-538AC6DAF8B9} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo.Abp.BlobStoring.Google.csproj b/framework/src/Volo.Abp.BlobStoring.Google/Volo.Abp.BlobStoring.Google.csproj new file mode 100644 index 0000000000..073c8c1891 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo.Abp.BlobStoring.Google.csproj @@ -0,0 +1,24 @@ + + + + + + + netstandard2.0;netstandard2.1;net8.0 + enable + Nullable + Volo.Abp.BlobStoring.Google + Volo.Abp.BlobStoring.Google + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleModule.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleModule.cs new file mode 100644 index 0000000000..f5243fda15 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleModule.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Google; + +[DependsOn(typeof(AbpBlobStoringModule))] +public class AbpBlobStoringGoogleModule : AbpModule +{ + +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/DefaultGoogleBlobNameCalculator.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/DefaultGoogleBlobNameCalculator.cs new file mode 100644 index 0000000000..90a7e7e7c4 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/DefaultGoogleBlobNameCalculator.cs @@ -0,0 +1,21 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.BlobStoring.Google; + +public class DefaultGoogleBlobNameCalculator: IGoogleBlobNameCalculator, ITransientDependency +{ + protected ICurrentTenant CurrentTenant { get; } + + public DefaultGoogleBlobNameCalculator(ICurrentTenant currentTenant) + { + CurrentTenant = currentTenant; + } + + public virtual string Calculate(BlobProviderArgs args) + { + return CurrentTenant.Id == null + ? $"host/{args.BlobName}" + : $"tenants/{CurrentTenant.Id.Value.ToString("D")}/{args.BlobName}"; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobContainerConfigurationExtensions.cs new file mode 100644 index 0000000000..b99fd3af6a --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobContainerConfigurationExtensions.cs @@ -0,0 +1,24 @@ +using System; + +namespace Volo.Abp.BlobStoring.Google; + +public static class GoogleBlobContainerConfigurationExtensions +{ + public static GoogleBlobProviderConfiguration GetGoogleConfiguration( + this BlobContainerConfiguration containerConfiguration) + { + return new GoogleBlobProviderConfiguration(containerConfiguration); + } + + public static BlobContainerConfiguration UseGoogle( + this BlobContainerConfiguration containerConfiguration, + Action azureConfigureAction) + { + containerConfiguration.ProviderType = typeof(GoogleBlobProvider); + containerConfiguration.NamingNormalizers.TryAdd(); + + azureConfigureAction(new GoogleBlobProviderConfiguration(containerConfiguration)); + + return containerConfiguration; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobNamingNormalizer.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobNamingNormalizer.cs new file mode 100644 index 0000000000..68f4318acb --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobNamingNormalizer.cs @@ -0,0 +1,120 @@ +using System.Globalization; +using System.Text.RegularExpressions; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Localization; + +namespace Volo.Abp.BlobStoring.Google; + +public class GoogleBlobNamingNormalizer: IBlobNamingNormalizer, ITransientDependency +{ + /// + /// https://cloud.google.com/storage/docs/buckets#naming + /// + public string NormalizeContainerName(string containerName) + { + using (CultureHelper.Use(CultureInfo.InvariantCulture)) + { + // All letters in a Bucket name must be lowercase. + containerName = containerName.ToLower(); + + // Bucket names must contain 3-63 characters. Names containing dots can contain up to 222 characters, but each dot-separated component can be no longer than 63 characters. + if(containerName.Contains(".")) + { + if (containerName.Length > 222) + { + containerName = containerName.Substring(0, 222); + } + + var parts = containerName.Split('.'); + for (var i = 0; i < parts.Length; i++) + { + if (parts[i].Length > 63) + { + parts[i] = parts[i].Substring(0, 63); + } + } + + containerName = string.Join(".", parts); + } + else if (containerName.Length > 63) + { + containerName = containerName.Substring(0, 63); + } + + //Bucket names can only contain lowercase letters, numeric characters, dashes (-), underscores (_), and dots (.). Spaces are not allowed. Names containing dots require verification. + containerName = Regex.Replace(containerName, "[^a-z0-9-_.]", string.Empty); + + //Be a syntactically valid DNS name (for example, bucket..example.com is not valid because it contains two dots in a row). + containerName = Regex.Replace(containerName, "[.]{2,}", "."); + + //Bucket names cannot be represented as an IP address in dotted-decimal notation (for example, 192.168.5.4). + containerName = Regex.Replace(containerName, "^(?:(?:^|\\.)(?:2(?:5[0-5]|[0-4]\\d)|1?\\d?\\d)){4}$", string.Empty); + + //Bucket names cannot begin with the "goog" prefix. + containerName = Regex.Replace(containerName, "^goog", string.Empty); + + //Bucket names cannot contain "google" or close misspellings, such as "g00gle". + containerName = Regex.Replace(containerName, "google", string.Empty); + + //Bucket names must start and end with a number or letter. + containerName = RemoveInvalidStartEndCharacters(containerName); + + // Bucket names must be from 3 through 63 characters long. Names containing dots can contain up to 222 characters. + if (containerName.Length < 3) + { + var length = containerName.Length; + for (var i = 0; i < 3 - length; i++) + { + containerName += "0"; + } + } + + return containerName; + } + } + + protected virtual string RemoveInvalidStartEndCharacters(string containerName) + { + if (string.IsNullOrWhiteSpace(containerName)) + { + return containerName; + } + + if (!char.IsLetterOrDigit(containerName[0])) + { + containerName = containerName.Substring(1); + return RemoveInvalidStartEndCharacters( containerName); + } + + if (!char.IsLetterOrDigit(containerName[containerName.Length - 1])) + { + containerName = containerName.Substring(0, containerName.Length - 1); + return RemoveInvalidStartEndCharacters(containerName); + } + + return containerName; + } + + /// + /// https://cloud.google.com/storage/docs/objects#naming + /// + public string NormalizeBlobName(string blobName) + { + //Object names can contain any sequence of valid Unicode characters, of length 1-1024 bytes when UTF-8 encoded + if (blobName.Length > 1024) + { + blobName = blobName.Substring(0, 1024); + } + + //Object names cannot contain Carriage Return or Line Feed characters. + blobName = Regex.Replace(blobName, "[\r\n]", string.Empty); + + //Object names cannot start with .well-known/acme-challenge/. + blobName = Regex.Replace(blobName, "^\\.well-known/acme-challenge/", string.Empty); + + //Objects cannot be named . or ... + blobName = Regex.Replace(blobName, "^\\.\\.?$", string.Empty); + + return blobName; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs new file mode 100644 index 0000000000..006923996f --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs @@ -0,0 +1,129 @@ +using System; +using System.IO; +using System.Net; +using System.Threading.Tasks; +using Google; +using Google.Apis.Auth.OAuth2; +using Google.Cloud.Storage.V1; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BlobStoring.Google; + +public class GoogleBlobProvider : BlobProviderBase, ITransientDependency +{ + protected IGoogleBlobNameCalculator GoogleBlobNameCalculator { get; } + protected IBlobNormalizeNamingService BlobNormalizeNamingService { get; } + + public GoogleBlobProvider(IGoogleBlobNameCalculator googleBlobNameCalculator, IBlobNormalizeNamingService blobNormalizeNamingService) + { + GoogleBlobNameCalculator = googleBlobNameCalculator; + BlobNormalizeNamingService = blobNormalizeNamingService; + } + + public async override Task SaveAsync(BlobProviderSaveArgs args) + { + var configuration = args.Configuration.GetGoogleConfiguration(); + var storageClient = await GetStorageClientClientAsync(args); + var blobName = GoogleBlobNameCalculator.Calculate(args); + var containerName = GetContainerName(args); + + if(await BlobExistsAsync(args, blobName) && !args.OverrideExisting) + { + throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the container '{GetContainerName(args)}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); + } + + if (configuration.CreateContainerIfNotExists) + { + await CreateContainerIfNotExists(args); + } + + await storageClient.UploadObjectAsync(containerName, blobName, contentType: "application/octet-stream", args.BlobStream, + new UploadObjectOptions + { + IfGenerationMatch = !args.OverrideExisting ? 0 : 1 + }); + } + + public async override Task DeleteAsync(BlobProviderDeleteArgs args) + { + var storageClient = await GetStorageClientClientAsync(args); + var blobName = GoogleBlobNameCalculator.Calculate(args); + var containerName = GetContainerName(args); + + await storageClient.DeleteObjectAsync(containerName, blobName); + + return true; + } + + public async override Task ExistsAsync(BlobProviderExistsArgs args) + { + var blobName = GoogleBlobNameCalculator.Calculate(args); + return await BlobExistsAsync(args, blobName); + } + + public async override Task GetOrNullAsync(BlobProviderGetArgs args) + { + var storageClient = await GetStorageClientClientAsync(args); + var blobName = GoogleBlobNameCalculator.Calculate(args); + var containerName = GetContainerName(args); + + var @object = await storageClient.GetObjectAsync(containerName, blobName); + if (@object == null) + { + return null; + } + + var stream = new MemoryStream(); + + await storageClient.DownloadObjectAsync(containerName, blobName, stream); + + stream.Seek(0, SeekOrigin.Begin); + + return stream; + } + + protected virtual string GetContainerName(BlobProviderArgs args) + { + var configuration = args.Configuration.GetGoogleConfiguration(); + return configuration.ContainerName.IsNullOrWhiteSpace() + ? args.ContainerName + : BlobNormalizeNamingService.NormalizeContainerName(args.Configuration, configuration.ContainerName!); + } + + protected virtual async Task BlobExistsAsync(BlobProviderArgs args, string blobName) + { + var @object = await (await GetStorageClientClientAsync(args)).GetObjectAsync(args.ContainerName, blobName); + + return @object != null; + } + + protected virtual async Task CreateContainerIfNotExists(BlobProviderArgs args) + { + var storageClient = await GetStorageClientClientAsync(args); + var configuration = args.Configuration.GetGoogleConfiguration(); + try + { + await storageClient.GetBucketAsync(args.ContainerName); + } + catch (GoogleApiException e) when (e.HttpStatusCode == HttpStatusCode.NotFound) + { + await storageClient.CreateBucketAsync(configuration.ProjectId, GetContainerName(args)); + } + } + + protected virtual async Task GetStorageClientClientAsync(BlobProviderArgs args) + { + var configuration = args.Configuration.GetGoogleConfiguration(); + var googleCredential = GoogleCredential.FromServiceAccountCredential( + new ServiceAccountCredential( + new ServiceAccountCredential.Initializer(configuration.ClientEmail) + { + ProjectId = configuration.ProjectId, + Scopes = configuration.Scopes + } + .FromPrivateKey(configuration.PrivateKey) + )); + + return await StorageClient.CreateAsync(googleCredential); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProviderConfiguration.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProviderConfiguration.cs new file mode 100644 index 0000000000..f00987b303 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProviderConfiguration.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; + +namespace Volo.Abp.BlobStoring.Google; + +public class GoogleBlobProviderConfiguration +{ + private readonly BlobContainerConfiguration _containerConfiguration; + + public GoogleBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) + { + _containerConfiguration = containerConfiguration; + } + + /// + /// Unique identifier for your project. + /// For more info see: https://cloud.google.com/resource-manager/docs/creating-managing-projects + /// + public string? ProjectId { + get => _containerConfiguration.GetConfigurationOrDefault(GoogleBlobProviderConfigurationNames.ProjectId); + set => _containerConfiguration.SetConfiguration(GoogleBlobProviderConfigurationNames.ProjectId, value); + } + + /// + /// Email address that generated by the Google Cloud. + /// + public string? ClientEmail { + get => _containerConfiguration.GetConfigurationOrDefault(GoogleBlobProviderConfigurationNames.ClientEmail); + set => _containerConfiguration.SetConfiguration(GoogleBlobProviderConfigurationNames.ClientEmail, value); + } + + /// + /// Private key that generated by Google Cloud. + /// Starts with '-----BEGIN PRIVATE KEY-----' + /// and ends with '-----END PRIVATE KEY-----' + /// + public string? PrivateKey { + get => _containerConfiguration.GetConfigurationOrDefault(GoogleBlobProviderConfigurationNames.PrivateKey); + set => _containerConfiguration.SetConfiguration(GoogleBlobProviderConfigurationNames.PrivateKey, value); + } + + /// + /// Available OAuth 2.0 scopes. + /// + public List? Scopes { + get => _containerConfiguration.GetConfigurationOrDefault(GoogleBlobProviderConfigurationNames.Scopes, new List()); + set => _containerConfiguration.SetConfiguration(GoogleBlobProviderConfigurationNames.Scopes, value); + } + + /// + /// The name can only contain lowercase letters, numeric characters, dashes (-), underscores (_), and dots (.). Spaces are not allowed. Names containing dots require verification. + /// Must start and end with a number or letter. + /// Must contain 3-63 characters. Names containing dots can contain up to 222 characters, but each dot-separated component can be no longer than 63 characters. + /// Cannot be represented as an IP address in dotted-decimal notation (for example, 192.168.5.4). + /// Cannot begin with the "goog" prefix. + /// Cannot contain "google" or close misspellings, such as "g00gle". + /// If this parameter is not specified, the ContainerName of the will be used. + /// + public string? ContainerName { + get => _containerConfiguration.GetConfigurationOrDefault(GoogleBlobProviderConfigurationNames.ContainerName); + set => _containerConfiguration.SetConfiguration(GoogleBlobProviderConfigurationNames.ContainerName, value); + } + + /// + /// Default value: false. + /// + public bool CreateContainerIfNotExists { + get => _containerConfiguration.GetConfigurationOrDefault(GoogleBlobProviderConfigurationNames.CreateContainerIfNotExists, false); + set => _containerConfiguration.SetConfiguration(GoogleBlobProviderConfigurationNames.CreateContainerIfNotExists, value); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProviderConfigurationNames.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProviderConfigurationNames.cs new file mode 100644 index 0000000000..0cfd10d129 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProviderConfigurationNames.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.BlobStoring.Google; + +public static class GoogleBlobProviderConfigurationNames +{ + public const string ProjectId = "Google.ProjectId"; + public const string ClientEmail = "Google.ClientEmail"; + public const string PrivateKey = "Google.PrivateKey"; + public const string Scopes = "Google.Scopes"; + public const string ContainerName = "Google.ContainerName"; + public const string CreateContainerIfNotExists = "Google.CreateContainerIfNotExists"; +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/IGoogleBlobNameCalculator.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/IGoogleBlobNameCalculator.cs new file mode 100644 index 0000000000..8099c6fa8c --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/IGoogleBlobNameCalculator.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.BlobStoring.Google; + +public interface IGoogleBlobNameCalculator +{ + string Calculate(BlobProviderArgs args); +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo.Abp.BlobStoring.Google.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo.Abp.BlobStoring.Google.Tests.csproj new file mode 100644 index 0000000000..a36e26fade --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo.Abp.BlobStoring.Google.Tests.csproj @@ -0,0 +1,19 @@ + + + + + + net8.0 + + 9f0d2c00-80c1-435b-bfab-2c39c8249091 + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestBase.cs b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestBase.cs new file mode 100644 index 0000000000..709590f6ed --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestBase.cs @@ -0,0 +1,19 @@ +using Volo.Abp.Testing; + +namespace Volo.Abp.BlobStoring.Google; + +public class AbpBlobStoringGoogleTestCommonBase : AbpIntegratedTest +{ + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } +} + +public class AbpBlobStoringGoogleTestBase : AbpIntegratedTest +{ + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs new file mode 100644 index 0000000000..35345613ac --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs @@ -0,0 +1,60 @@ +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Google; + + +/// +/// This module will not try to connect to azure. +/// +[DependsOn( + typeof(AbpBlobStoringGoogleModule), + typeof(AbpBlobStoringTestModule) +)] +public class AbpBlobStoringGoogleTestCommonModule : AbpModule +{ + +} + +[DependsOn( + typeof(AbpBlobStoringGoogleTestCommonModule) +)] +public class AbpBlobStoringGoogleTestModule : AbpModule +{ + private const string UserSecretsId = "9f0d2c00-80c1-435b-bfab-2c39c8249091"; + + private string _connectionString; + + private readonly string _randomContainerName = "abp-azure-test-container-" + Guid.NewGuid().ToString("N"); + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.ReplaceConfiguration(ConfigurationHelper.BuildConfiguration(builderAction: builder => + { + builder.AddUserSecrets(UserSecretsId); + })); + + // var configuration = context.Services.GetConfiguration(); + // _connectionString = configuration["Azure:ConnectionString"]; + // + // Configure(options => + // { + // options.Containers.ConfigureAll((containerName, containerConfiguration) => + // { + // containerConfiguration.UseAzure(azure => + // { + // azure.ConnectionString = _connectionString; + // azure.ContainerName = _randomContainerName; + // azure.CreateContainerIfNotExists = true; + // }); + // }); + // }); + } + + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/DefaultGoogleBlobNamingNormalizerProvider_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/DefaultGoogleBlobNamingNormalizerProvider_Tests.cs new file mode 100644 index 0000000000..cfe43f8bcf --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/DefaultGoogleBlobNamingNormalizerProvider_Tests.cs @@ -0,0 +1,94 @@ +using Shouldly; +using Xunit; + +namespace Volo.Abp.BlobStoring.Google; + +public class DefaultGoogleBlobNamingNormalizerProvider_Tests: AbpBlobStoringGoogleTestCommonBase +{ + private readonly IBlobNamingNormalizer _blobNamingNormalizer; + + public DefaultGoogleBlobNamingNormalizerProvider_Tests() + { + _blobNamingNormalizer = GetRequiredService(); + } + + [Fact] + public void NormalizeContainerName_Lowercase() + { + var filename = "ThisIsMyContainerName"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("thisismycontainername"); + } + + [Fact] + public void NormalizeContainerName_Only_Letters_Numbers_Dash_Dots_Underscores() + { + var filename = ",./this-i,/s-my-c,/ont,/ai+*/=!@#$n^&*er.name+/"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("this-is-my-container.name"); + } + + [Fact] + public void NormalizeContainerName_Only_Start_With_Letters_Or_Numbers() + { + var filename = "-this.--is-.-.-my--_container---name-"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("this.--is-.-.-my--_container---name"); + + filename = ".this.--is-.-.-my--container---name0._"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("this.--is-.-.-my--container---name0"); + } + + [Fact] + public void NormalizeContainerName_Min_Length() + { + var filename = "a"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.Length.ShouldBeGreaterThanOrEqualTo(3); + } + + [Fact] + public void NormalizeContainerName_Max_Length() + { + var filename = "abpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabp"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.Length.ShouldBeLessThanOrEqualTo(63); + } + + [Fact] + public void NormalizeContainerName_Must_Not_Be_Ip_Address() + { + var filename = "192.168.5.4"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("000"); + + filename = "a.192.168.5.4"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("a.192.168.5.4"); + } + + [Fact] + public void NormalizeContainerName_Dots() + { + var filename = ".this..is.my.container....name."; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("this.is.my.container.name"); + } + + [Fact] + public void NormalizeContainerName_DNS() + { + var filename = "bucket...example..com"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("bucket.example.com"); + } + + [Fact] + public void NormalizeContainerName_Max_Length_Dash() + { + var filename = "-this-is-my-container-name-abpabpabpabpabpabpabpabp-a-b-p-a--b-p-"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("this-is-my-container-name-abpabpabpabpabpabpabpabp-a-b-p-a--b"); + } +} From fdcba83c60e2306f8dddd600f1ff088042bd7285 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Thu, 5 Sep 2024 13:51:09 +0800 Subject: [PATCH 2/3] add unit tests --- .../BlobStoring/Google/GoogleBlobProvider.cs | 62 ++++++++++++----- .../Google/AbpBlobStoringGoogleTestModule.cs | 66 ++++++++++++++----- .../Google/GoogleBlobContainer_Tests.cs | 13 ++++ .../Google/GoogleBlobNameCalculator_Tests.cs | 56 ++++++++++++++++ 4 files changed, 162 insertions(+), 35 deletions(-) create mode 100644 framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/GoogleBlobContainer_Tests.cs create mode 100644 framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/GoogleBlobNameCalculator_Tests.cs diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs index 006923996f..dc5eefc1ee 100644 --- a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs @@ -31,17 +31,13 @@ public class GoogleBlobProvider : BlobProviderBase, ITransientDependency { throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the container '{GetContainerName(args)}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); } - + if (configuration.CreateContainerIfNotExists) { await CreateContainerIfNotExists(args); } - await storageClient.UploadObjectAsync(containerName, blobName, contentType: "application/octet-stream", args.BlobStream, - new UploadObjectOptions - { - IfGenerationMatch = !args.OverrideExisting ? 0 : 1 - }); + await storageClient.UploadObjectAsync(containerName, blobName, contentType: "application/octet-stream", args.BlobStream); } public async override Task DeleteAsync(BlobProviderDeleteArgs args) @@ -49,8 +45,16 @@ public class GoogleBlobProvider : BlobProviderBase, ITransientDependency var storageClient = await GetStorageClientClientAsync(args); var blobName = GoogleBlobNameCalculator.Calculate(args); var containerName = GetContainerName(args); - - await storageClient.DeleteObjectAsync(containerName, blobName); + + try + { + await storageClient.DeleteObjectAsync(containerName, blobName); + } + catch (GoogleApiException e) when (e.HttpStatusCode == HttpStatusCode.NotFound) + { + return true; + } + return true; } @@ -66,13 +70,12 @@ public class GoogleBlobProvider : BlobProviderBase, ITransientDependency var storageClient = await GetStorageClientClientAsync(args); var blobName = GoogleBlobNameCalculator.Calculate(args); var containerName = GetContainerName(args); - - var @object = await storageClient.GetObjectAsync(containerName, blobName); - if (@object == null) + + if(!await BlobExistsAsync(args, blobName)) { return null; } - + var stream = new MemoryStream(); await storageClient.DownloadObjectAsync(containerName, blobName, stream); @@ -92,23 +95,48 @@ public class GoogleBlobProvider : BlobProviderBase, ITransientDependency protected virtual async Task BlobExistsAsync(BlobProviderArgs args, string blobName) { - var @object = await (await GetStorageClientClientAsync(args)).GetObjectAsync(args.ContainerName, blobName); - - return @object != null; + var storageClient = await GetStorageClientClientAsync(args); + if(!await ContainerExistsAsync(args, storageClient)) + { + return false; + } + + try + { + await storageClient.GetObjectAsync(GetContainerName(args), blobName); + } + catch (GoogleApiException e) when (e.HttpStatusCode == HttpStatusCode.NotFound) + { + return false; + } + + return true; } protected virtual async Task CreateContainerIfNotExists(BlobProviderArgs args) { var storageClient = await GetStorageClientClientAsync(args); var configuration = args.Configuration.GetGoogleConfiguration(); + if(await ContainerExistsAsync(args, storageClient)) + { + return; + } + + await storageClient.CreateBucketAsync(configuration.ProjectId, GetContainerName(args)); + } + + protected virtual async Task ContainerExistsAsync(BlobProviderArgs args, StorageClient client) + { try { - await storageClient.GetBucketAsync(args.ContainerName); + await client.GetBucketAsync(GetContainerName(args)); } catch (GoogleApiException e) when (e.HttpStatusCode == HttpStatusCode.NotFound) { - await storageClient.CreateBucketAsync(configuration.ProjectId, GetContainerName(args)); + return false; } + + return true; } protected virtual async Task GetStorageClientClientAsync(BlobProviderArgs args) diff --git a/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs index 35345613ac..cca775a04f 100644 --- a/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs +++ b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs @@ -1,4 +1,7 @@ using System; +using System.Threading.Tasks; +using Google.Apis.Auth.OAuth2; +using Google.Cloud.Storage.V1; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity; @@ -25,9 +28,11 @@ public class AbpBlobStoringGoogleTestModule : AbpModule { private const string UserSecretsId = "9f0d2c00-80c1-435b-bfab-2c39c8249091"; - private string _connectionString; + private string _clientEmail; + private string _projectId; + private string _privateKey; - private readonly string _randomContainerName = "abp-azure-test-container-" + Guid.NewGuid().ToString("N"); + private readonly string _randomContainerName = "abp-test-container-" + Guid.NewGuid().ToString("N"); public override void ConfigureServices(ServiceConfigurationContext context) { @@ -36,25 +41,50 @@ public class AbpBlobStoringGoogleTestModule : AbpModule builder.AddUserSecrets(UserSecretsId); })); - // var configuration = context.Services.GetConfiguration(); - // _connectionString = configuration["Azure:ConnectionString"]; - // - // Configure(options => - // { - // options.Containers.ConfigureAll((containerName, containerConfiguration) => - // { - // containerConfiguration.UseAzure(azure => - // { - // azure.ConnectionString = _connectionString; - // azure.ContainerName = _randomContainerName; - // azure.CreateContainerIfNotExists = true; - // }); - // }); - // }); + var configuration = context.Services.GetConfiguration(); + _clientEmail = configuration["Google:ClientEmail"]; + _projectId = configuration["Google:ProjectId"]; + _privateKey = configuration["Google:PrivateKey"]; + + Configure(options => + { + options.Containers.ConfigureAll((containerName, containerConfiguration) => + { + containerConfiguration.UseGoogle(google => + { + google.ClientEmail = _clientEmail; + google.ProjectId = _projectId = "wide-origin-296910"; + google.PrivateKey = _privateKey; + google.ContainerName = _randomContainerName; + google.CreateContainerIfNotExists = true; + }); + }); + }); } public override void OnApplicationShutdown(ApplicationShutdownContext context) { - + var googleCredential = GoogleCredential.FromServiceAccountCredential( + new ServiceAccountCredential( + new ServiceAccountCredential.Initializer(_clientEmail) + { + ProjectId = _projectId + } + .FromPrivateKey(_privateKey) + )); + + var client = StorageClient.Create(googleCredential); + + try + { + client.DeleteBucket(_randomContainerName, new DeleteBucketOptions + { + DeleteObjects = true + }); + } + catch (Exception e) + { + // ignored + } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/GoogleBlobContainer_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/GoogleBlobContainer_Tests.cs new file mode 100644 index 0000000000..6a4405f7cd --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/GoogleBlobContainer_Tests.cs @@ -0,0 +1,13 @@ +using Xunit; + +namespace Volo.Abp.BlobStoring.Google; + +/* +//Please set the correct connection string in secrets.json and continue the test. +public class GoogleBlobContainer_Tests : BlobContainer_Tests +{ + public GoogleBlobContainer_Tests() + { + } +} +*/ diff --git a/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/GoogleBlobNameCalculator_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/GoogleBlobNameCalculator_Tests.cs new file mode 100644 index 0000000000..dbdc95c6f4 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/GoogleBlobNameCalculator_Tests.cs @@ -0,0 +1,56 @@ +using System; +using Shouldly; +using Volo.Abp.MultiTenancy; +using Xunit; + +namespace Volo.Abp.BlobStoring.Google; + +public class GoogleBlobNameCalculator_Tests : AbpBlobStoringGoogleTestCommonBase +{ + private readonly IGoogleBlobNameCalculator _calculator; + private readonly ICurrentTenant _currentTenant; + + private const string AzureContainerName = "/"; + private const string AzureSeparator = "/"; + + public GoogleBlobNameCalculator_Tests() + { + _calculator = GetRequiredService(); + _currentTenant = GetRequiredService(); + } + + [Fact] + public void Default_Settings() + { + _calculator.Calculate( + GetArgs("my-container", "my-blob") + ).ShouldBe($"host{AzureSeparator}my-blob"); + } + + [Fact] + public void Default_Settings_With_TenantId() + { + var tenantId = Guid.NewGuid(); + + using (_currentTenant.Change(tenantId)) + { + _calculator.Calculate( + GetArgs("my-container", "my-blob") + ).ShouldBe($"tenants{AzureSeparator}{tenantId:D}{AzureSeparator}my-blob"); + } + } + + private static BlobProviderArgs GetArgs( + string containerName, + string blobName) + { + return new BlobProviderGetArgs( + containerName, + new BlobContainerConfiguration().UseGoogle(x => + { + x.ContainerName = containerName; + }), + blobName + ); + } +} \ No newline at end of file From df1705f592b14d76446724a5c4cc178446509e49 Mon Sep 17 00:00:00 2001 From: EngincanV Date: Thu, 5 Sep 2024 09:58:09 +0300 Subject: [PATCH 3/3] Refactoring --- .../BlobStoring/Google/DefaultGoogleBlobNameCalculator.cs | 2 +- .../Google/GoogleBlobContainerConfigurationExtensions.cs | 4 ++-- .../Abp/BlobStoring/Google/GoogleBlobNamingNormalizer.cs | 4 ++-- .../Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs | 3 +-- .../BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs | 2 +- .../BlobStoring/Google/GoogleBlobNameCalculator_Tests.cs | 8 ++++---- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/DefaultGoogleBlobNameCalculator.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/DefaultGoogleBlobNameCalculator.cs index 90a7e7e7c4..fec52b44d1 100644 --- a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/DefaultGoogleBlobNameCalculator.cs +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/DefaultGoogleBlobNameCalculator.cs @@ -3,7 +3,7 @@ using Volo.Abp.MultiTenancy; namespace Volo.Abp.BlobStoring.Google; -public class DefaultGoogleBlobNameCalculator: IGoogleBlobNameCalculator, ITransientDependency +public class DefaultGoogleBlobNameCalculator : IGoogleBlobNameCalculator, ITransientDependency { protected ICurrentTenant CurrentTenant { get; } diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobContainerConfigurationExtensions.cs index b99fd3af6a..3c0efd4c86 100644 --- a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobContainerConfigurationExtensions.cs +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobContainerConfigurationExtensions.cs @@ -12,12 +12,12 @@ public static class GoogleBlobContainerConfigurationExtensions public static BlobContainerConfiguration UseGoogle( this BlobContainerConfiguration containerConfiguration, - Action azureConfigureAction) + Action googleConfigureAction) { containerConfiguration.ProviderType = typeof(GoogleBlobProvider); containerConfiguration.NamingNormalizers.TryAdd(); - azureConfigureAction(new GoogleBlobProviderConfiguration(containerConfiguration)); + googleConfigureAction(new GoogleBlobProviderConfiguration(containerConfiguration)); return containerConfiguration; } diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobNamingNormalizer.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobNamingNormalizer.cs index 68f4318acb..8dc840d805 100644 --- a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobNamingNormalizer.cs +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobNamingNormalizer.cs @@ -5,7 +5,7 @@ using Volo.Abp.Localization; namespace Volo.Abp.BlobStoring.Google; -public class GoogleBlobNamingNormalizer: IBlobNamingNormalizer, ITransientDependency +public class GoogleBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDependency { /// /// https://cloud.google.com/storage/docs/buckets#naming @@ -83,7 +83,7 @@ public class GoogleBlobNamingNormalizer: IBlobNamingNormalizer, ITransientDepend if (!char.IsLetterOrDigit(containerName[0])) { containerName = containerName.Substring(1); - return RemoveInvalidStartEndCharacters( containerName); + return RemoveInvalidStartEndCharacters(containerName); } if (!char.IsLetterOrDigit(containerName[containerName.Length - 1])) diff --git a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs index dc5eefc1ee..6f9b5c280e 100644 --- a/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs +++ b/framework/src/Volo.Abp.BlobStoring.Google/Volo/Abp/BlobStoring/Google/GoogleBlobProvider.cs @@ -27,7 +27,7 @@ public class GoogleBlobProvider : BlobProviderBase, ITransientDependency var blobName = GoogleBlobNameCalculator.Calculate(args); var containerName = GetContainerName(args); - if(await BlobExistsAsync(args, blobName) && !args.OverrideExisting) + if (await BlobExistsAsync(args, blobName) && !args.OverrideExisting) { throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the container '{GetContainerName(args)}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); } @@ -54,7 +54,6 @@ public class GoogleBlobProvider : BlobProviderBase, ITransientDependency { return true; } - return true; } diff --git a/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs index cca775a04f..be2de91a38 100644 --- a/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs +++ b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/AbpBlobStoringGoogleTestModule.cs @@ -10,7 +10,7 @@ namespace Volo.Abp.BlobStoring.Google; /// -/// This module will not try to connect to azure. +/// This module will not try to connect to Google Cloud Storage. /// [DependsOn( typeof(AbpBlobStoringGoogleModule), diff --git a/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/GoogleBlobNameCalculator_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/GoogleBlobNameCalculator_Tests.cs index dbdc95c6f4..781900ad81 100644 --- a/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/GoogleBlobNameCalculator_Tests.cs +++ b/framework/test/Volo.Abp.BlobStoring.Google.Tests/Volo/Abp/BlobStoring/Google/GoogleBlobNameCalculator_Tests.cs @@ -10,8 +10,8 @@ public class GoogleBlobNameCalculator_Tests : AbpBlobStoringGoogleTestCommonBase private readonly IGoogleBlobNameCalculator _calculator; private readonly ICurrentTenant _currentTenant; - private const string AzureContainerName = "/"; - private const string AzureSeparator = "/"; + private const string GoogleContainerName = "/"; + private const string GoogleSeparator = "/"; public GoogleBlobNameCalculator_Tests() { @@ -24,7 +24,7 @@ public class GoogleBlobNameCalculator_Tests : AbpBlobStoringGoogleTestCommonBase { _calculator.Calculate( GetArgs("my-container", "my-blob") - ).ShouldBe($"host{AzureSeparator}my-blob"); + ).ShouldBe($"host{GoogleSeparator}my-blob"); } [Fact] @@ -36,7 +36,7 @@ public class GoogleBlobNameCalculator_Tests : AbpBlobStoringGoogleTestCommonBase { _calculator.Calculate( GetArgs("my-container", "my-blob") - ).ShouldBe($"tenants{AzureSeparator}{tenantId:D}{AzureSeparator}my-blob"); + ).ShouldBe($"tenants{GoogleSeparator}{tenantId:D}{GoogleSeparator}my-blob"); } }