mirror of https://github.com/abpframework/abp.git
committed by
GitHub
17 changed files with 747 additions and 0 deletions
@ -0,0 +1,24 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0</TargetFrameworks> |
|||
<Nullable>enable</Nullable> |
|||
<WarningsAsErrors>Nullable</WarningsAsErrors> |
|||
<AssemblyName>Volo.Abp.BlobStoring.Google</AssemblyName> |
|||
<PackageId>Volo.Abp.BlobStoring.Google</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj" /> |
|||
<PackageReference Include="Google.Cloud.Storage.V1" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Google; |
|||
|
|||
[DependsOn(typeof(AbpBlobStoringModule))] |
|||
public class AbpBlobStoringGoogleModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
@ -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}"; |
|||
} |
|||
} |
|||
@ -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<GoogleBlobProviderConfiguration> googleConfigureAction) |
|||
{ |
|||
containerConfiguration.ProviderType = typeof(GoogleBlobProvider); |
|||
containerConfiguration.NamingNormalizers.TryAdd<GoogleBlobNamingNormalizer>(); |
|||
|
|||
googleConfigureAction(new GoogleBlobProviderConfiguration(containerConfiguration)); |
|||
|
|||
return containerConfiguration; |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// https://cloud.google.com/storage/docs/buckets#naming
|
|||
/// </summary>
|
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// https://cloud.google.com/storage/docs/objects#naming
|
|||
/// </summary>
|
|||
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; |
|||
} |
|||
} |
|||
@ -0,0 +1,156 @@ |
|||
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); |
|||
} |
|||
|
|||
public async override Task<bool> DeleteAsync(BlobProviderDeleteArgs args) |
|||
{ |
|||
var storageClient = await GetStorageClientClientAsync(args); |
|||
var blobName = GoogleBlobNameCalculator.Calculate(args); |
|||
var containerName = GetContainerName(args); |
|||
|
|||
try |
|||
{ |
|||
await storageClient.DeleteObjectAsync(containerName, blobName); |
|||
} |
|||
catch (GoogleApiException e) when (e.HttpStatusCode == HttpStatusCode.NotFound) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
public async override Task<bool> ExistsAsync(BlobProviderExistsArgs args) |
|||
{ |
|||
var blobName = GoogleBlobNameCalculator.Calculate(args); |
|||
return await BlobExistsAsync(args, blobName); |
|||
} |
|||
|
|||
public async override Task<Stream?> GetOrNullAsync(BlobProviderGetArgs args) |
|||
{ |
|||
var storageClient = await GetStorageClientClientAsync(args); |
|||
var blobName = GoogleBlobNameCalculator.Calculate(args); |
|||
var containerName = GetContainerName(args); |
|||
|
|||
if(!await BlobExistsAsync(args, blobName)) |
|||
{ |
|||
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<bool> BlobExistsAsync(BlobProviderArgs args, string blobName) |
|||
{ |
|||
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<bool> ContainerExistsAsync(BlobProviderArgs args, StorageClient client) |
|||
{ |
|||
try |
|||
{ |
|||
await client.GetBucketAsync(GetContainerName(args)); |
|||
} |
|||
catch (GoogleApiException e) when (e.HttpStatusCode == HttpStatusCode.NotFound) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
protected virtual async Task<StorageClient> 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); |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Unique identifier for your project.
|
|||
/// For more info see: https://cloud.google.com/resource-manager/docs/creating-managing-projects
|
|||
/// </summary>
|
|||
public string? ProjectId { |
|||
get => _containerConfiguration.GetConfigurationOrDefault<string>(GoogleBlobProviderConfigurationNames.ProjectId); |
|||
set => _containerConfiguration.SetConfiguration(GoogleBlobProviderConfigurationNames.ProjectId, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Email address that generated by the Google Cloud.
|
|||
/// </summary>
|
|||
public string? ClientEmail { |
|||
get => _containerConfiguration.GetConfigurationOrDefault<string>(GoogleBlobProviderConfigurationNames.ClientEmail); |
|||
set => _containerConfiguration.SetConfiguration(GoogleBlobProviderConfigurationNames.ClientEmail, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Private key that generated by Google Cloud.
|
|||
/// Starts with '-----BEGIN PRIVATE KEY-----'
|
|||
/// and ends with '-----END PRIVATE KEY-----'
|
|||
/// </summary>
|
|||
public string? PrivateKey { |
|||
get => _containerConfiguration.GetConfigurationOrDefault<string>(GoogleBlobProviderConfigurationNames.PrivateKey); |
|||
set => _containerConfiguration.SetConfiguration(GoogleBlobProviderConfigurationNames.PrivateKey, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Available OAuth 2.0 scopes.
|
|||
/// </summary>
|
|||
public List<string>? Scopes { |
|||
get => _containerConfiguration.GetConfigurationOrDefault(GoogleBlobProviderConfigurationNames.Scopes, new List<string>()); |
|||
set => _containerConfiguration.SetConfiguration(GoogleBlobProviderConfigurationNames.Scopes, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 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 <see cref="BlobProviderArgs"/> will be used.
|
|||
/// </summary>
|
|||
public string? ContainerName { |
|||
get => _containerConfiguration.GetConfigurationOrDefault<string>(GoogleBlobProviderConfigurationNames.ContainerName); |
|||
set => _containerConfiguration.SetConfiguration(GoogleBlobProviderConfigurationNames.ContainerName, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Default value: false.
|
|||
/// </summary>
|
|||
public bool CreateContainerIfNotExists { |
|||
get => _containerConfiguration.GetConfigurationOrDefault(GoogleBlobProviderConfigurationNames.CreateContainerIfNotExists, false); |
|||
set => _containerConfiguration.SetConfiguration(GoogleBlobProviderConfigurationNames.CreateContainerIfNotExists, value); |
|||
} |
|||
} |
|||
@ -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"; |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Volo.Abp.BlobStoring.Google; |
|||
|
|||
public interface IGoogleBlobNameCalculator |
|||
{ |
|||
string Calculate(BlobProviderArgs args); |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.test.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net8.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<UserSecretsId>9f0d2c00-80c1-435b-bfab-2c39c8249091</UserSecretsId> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.BlobStoring.Google\Volo.Abp.BlobStoring.Google.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
|||
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" /> |
|||
<ProjectReference Include="..\Volo.Abp.BlobStoring.Tests\Volo.Abp.BlobStoring.Tests.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,19 @@ |
|||
using Volo.Abp.Testing; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Google; |
|||
|
|||
public class AbpBlobStoringGoogleTestCommonBase : AbpIntegratedTest<AbpBlobStoringGoogleTestCommonModule> |
|||
{ |
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
} |
|||
|
|||
public class AbpBlobStoringGoogleTestBase : AbpIntegratedTest<AbpBlobStoringGoogleTestModule> |
|||
{ |
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
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; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Google; |
|||
|
|||
|
|||
/// <summary>
|
|||
/// This module will not try to connect to Google Cloud Storage.
|
|||
/// </summary>
|
|||
[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 _clientEmail; |
|||
private string _projectId; |
|||
private string _privateKey; |
|||
|
|||
private readonly string _randomContainerName = "abp-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(); |
|||
_clientEmail = configuration["Google:ClientEmail"]; |
|||
_projectId = configuration["Google:ProjectId"]; |
|||
_privateKey = configuration["Google:PrivateKey"]; |
|||
|
|||
Configure<AbpBlobStoringOptions>(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
|
|||
} |
|||
} |
|||
} |
|||
@ -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<IBlobNamingNormalizer>(); |
|||
} |
|||
|
|||
[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"); |
|||
} |
|||
} |
|||
@ -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<AbpBlobStoringGoogleTestModule> |
|||
{ |
|||
public GoogleBlobContainer_Tests() |
|||
{ |
|||
} |
|||
} |
|||
*/ |
|||
@ -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 GoogleContainerName = "/"; |
|||
private const string GoogleSeparator = "/"; |
|||
|
|||
public GoogleBlobNameCalculator_Tests() |
|||
{ |
|||
_calculator = GetRequiredService<IGoogleBlobNameCalculator>(); |
|||
_currentTenant = GetRequiredService<ICurrentTenant>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Default_Settings() |
|||
{ |
|||
_calculator.Calculate( |
|||
GetArgs("my-container", "my-blob") |
|||
).ShouldBe($"host{GoogleSeparator}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{GoogleSeparator}{tenantId:D}{GoogleSeparator}my-blob"); |
|||
} |
|||
} |
|||
|
|||
private static BlobProviderArgs GetArgs( |
|||
string containerName, |
|||
string blobName) |
|||
{ |
|||
return new BlobProviderGetArgs( |
|||
containerName, |
|||
new BlobContainerConfiguration().UseGoogle(x => |
|||
{ |
|||
x.ContainerName = containerName; |
|||
}), |
|||
blobName |
|||
); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue