76 changed files with 2410 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<Description>Oss对象存储Nexus集成</Description> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.BlobStoring" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Sonatype.Nexus\LINGYUN.Abp.Sonatype.Nexus.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,12 @@ |
|||
using LINGYUN.Abp.Sonatype.Nexus; |
|||
using Volo.Abp.BlobStoring; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Nexus; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpBlobStoringModule), |
|||
typeof(AbpSonatypeNexusModule))] |
|||
public class AbpBlobStoringNexusModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
using System; |
|||
using Volo.Abp.BlobStoring; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Nexus; |
|||
public class DefaultBlobRawPathCalculator : IBlobRawPathCalculator, ITransientDependency |
|||
{ |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IBlobContainerConfigurationProvider ConfigurationProvider { get; } |
|||
|
|||
public DefaultBlobRawPathCalculator( |
|||
ICurrentTenant currentTenant, |
|||
IBlobContainerConfigurationProvider configurationProvider) |
|||
{ |
|||
CurrentTenant = currentTenant; |
|||
ConfigurationProvider = configurationProvider; |
|||
} |
|||
|
|||
public string CalculateGroup(string containerName, string blobName) |
|||
{ |
|||
var blobPath = CalculateBasePath(containerName); |
|||
|
|||
var lastFolderIndex = blobName.LastIndexOf("/"); |
|||
if (lastFolderIndex > 0) |
|||
{ |
|||
blobPath = blobPath.EnsureEndsWith('/'); |
|||
blobPath += blobName.Substring(0, lastFolderIndex); |
|||
} |
|||
|
|||
return blobPath.EnsureStartsWith('/').RemovePostFix("/"); |
|||
} |
|||
|
|||
public string CalculateName(string containerName, string blobName, bool replacePath = false) |
|||
{ |
|||
var blobPath = CalculateBasePath(containerName); |
|||
blobPath = blobPath.EnsureEndsWith('/'); |
|||
blobPath += blobName; |
|||
|
|||
if (replacePath) |
|||
{ |
|||
return blobName.Replace(blobPath.RemovePreFix("/"), "").RemovePreFix("/"); |
|||
} |
|||
|
|||
return blobPath.RemovePreFix("/"); |
|||
} |
|||
|
|||
protected virtual string CalculateBasePath(string containerName) |
|||
{ |
|||
var configuration = ConfigurationProvider.Get<DefaultContainer>(); |
|||
var nexusConfiguration = configuration.GetNexusConfiguration(); |
|||
var blobPath = nexusConfiguration.BasePath; |
|||
|
|||
if (CurrentTenant.Id == null) |
|||
{ |
|||
blobPath = $"{blobPath}/host"; |
|||
} |
|||
else |
|||
{ |
|||
blobPath = $"{blobPath}/tenants/{CurrentTenant.Id.Value.ToString("D")}"; |
|||
} |
|||
|
|||
if (nexusConfiguration.AppendContainerNameToBasePath) |
|||
{ |
|||
blobPath = $"{blobPath}/{containerName.RemovePreFix("/")}"; |
|||
} |
|||
|
|||
return blobPath.EnsureStartsWith('/'); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.BlobStoring; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Nexus; |
|||
public interface IBlobRawPathCalculator |
|||
{ |
|||
string CalculateGroup(string containerName, string blobName); |
|||
|
|||
string CalculateName(string containerName, string blobName, bool replacePath = false); |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using Volo.Abp.BlobStoring; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Nexus |
|||
{ |
|||
public static class NexusBlobContainerConfigurationExtensions |
|||
{ |
|||
public static NexusBlobProviderConfiguration GetNexusConfiguration( |
|||
this BlobContainerConfiguration containerConfiguration) |
|||
{ |
|||
return new NexusBlobProviderConfiguration(containerConfiguration); |
|||
} |
|||
|
|||
public static BlobContainerConfiguration UseNexus( |
|||
this BlobContainerConfiguration containerConfiguration, |
|||
Action<NexusBlobProviderConfiguration> nexusConfigureAction) |
|||
{ |
|||
containerConfiguration.ProviderType = typeof(NexusBlobProvider); |
|||
containerConfiguration.NamingNormalizers.TryAdd<NexusBlobNamingNormalizer>(); |
|||
|
|||
nexusConfigureAction(new NexusBlobProviderConfiguration(containerConfiguration)); |
|||
|
|||
return containerConfiguration; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using Volo.Abp.BlobStoring; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Nexus; |
|||
public class NexusBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDependency |
|||
{ |
|||
public virtual string NormalizeContainerName(string containerName) |
|||
{ |
|||
return Normalize(containerName); |
|||
} |
|||
|
|||
public virtual string NormalizeBlobName(string blobName) |
|||
{ |
|||
return Normalize(blobName); |
|||
} |
|||
|
|||
protected virtual string Normalize(string fileName) |
|||
{ |
|||
return fileName.Replace("\\", "/").Replace("//", "/"); |
|||
} |
|||
} |
|||
@ -0,0 +1,114 @@ |
|||
using LINGYUN.Abp.Sonatype.Nexus.Assets; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Components; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Search; |
|||
using System; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.BlobStoring; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Nexus; |
|||
public class NexusBlobProvider : BlobProviderBase, ITransientDependency |
|||
{ |
|||
protected INexusAssetManager NexusAssetManager { get; } |
|||
protected INexusComponentManager NexusComponentManager { get; } |
|||
protected INexusLookupService NexusLookupService { get; } |
|||
protected IBlobRawPathCalculator BlobDirectoryCalculator { get; } |
|||
|
|||
public NexusBlobProvider( |
|||
INexusAssetManager nexusAssetManager, |
|||
INexusComponentManager nexusComponentManager, |
|||
INexusLookupService nexusLookupService, |
|||
IBlobRawPathCalculator blobDirectoryCalculator) |
|||
{ |
|||
NexusAssetManager = nexusAssetManager; |
|||
NexusComponentManager = nexusComponentManager; |
|||
NexusLookupService = nexusLookupService; |
|||
BlobDirectoryCalculator = blobDirectoryCalculator; |
|||
} |
|||
|
|||
public async override Task<bool> DeleteAsync(BlobProviderDeleteArgs args) |
|||
{ |
|||
var nexusComponent = await GetNexusomponentOrNull(args); |
|||
if (nexusComponent == null) |
|||
{ |
|||
return false; |
|||
} |
|||
return await NexusComponentManager.DeleteAsync(nexusComponent.Id, args.CancellationToken); |
|||
} |
|||
|
|||
public async override Task<bool> ExistsAsync(BlobProviderExistsArgs args) |
|||
{ |
|||
var nexusAsset = await GetNexusAssetOrNull(args); |
|||
return nexusAsset != null; |
|||
} |
|||
|
|||
public async override Task<Stream> GetOrNullAsync(BlobProviderGetArgs args) |
|||
{ |
|||
var nexusAsset = await GetNexusAssetOrNull(args); |
|||
if (nexusAsset == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return await NexusAssetManager.GetContentOrNullAsync(nexusAsset); |
|||
} |
|||
|
|||
public async override Task SaveAsync(BlobProviderSaveArgs args) |
|||
{ |
|||
var nexusAsset = await GetNexusAssetOrNull(args); |
|||
if (!args.OverrideExisting && nexusAsset != null) |
|||
{ |
|||
throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the container '{args.ContainerName}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); |
|||
} |
|||
|
|||
var fileBytes = await args.BlobStream.GetAllBytesAsync(); |
|||
var blobPath = BlobDirectoryCalculator.CalculateGroup(args.ContainerName, args.BlobName); |
|||
var blobName = BlobDirectoryCalculator.CalculateName(args.ContainerName, args.BlobName, true); |
|||
// blobName = blobName.Replace(blobPath.RemovePreFix("/"), "").RemovePreFix("/");
|
|||
var asset1 = new Asset(blobName, fileBytes); |
|||
|
|||
var nexusConfiguration = args.Configuration.GetNexusConfiguration(); |
|||
var repository = nexusConfiguration.Repository; |
|||
|
|||
var nexusRawBlobUploadArgs = new NexusRawBlobUploadArgs( |
|||
repository, |
|||
blobPath, |
|||
asset1); |
|||
|
|||
await NexusComponentManager.UploadAsync(nexusRawBlobUploadArgs, args.CancellationToken); |
|||
} |
|||
|
|||
protected async virtual Task<NexusAsset> GetNexusAssetOrNull(BlobProviderArgs args) |
|||
{ |
|||
var nexusConfiguration = args.Configuration.GetNexusConfiguration(); |
|||
var blobPath = BlobDirectoryCalculator.CalculateGroup(args.ContainerName, args.BlobName); |
|||
var blobName = BlobDirectoryCalculator.CalculateName(args.ContainerName, args.BlobName); |
|||
var nexusSearchArgs = new NexusSearchArgs( |
|||
nexusConfiguration.Repository, |
|||
blobPath, |
|||
blobName); |
|||
|
|||
var nexusAssetListResult = await NexusLookupService.ListAssetAsync(nexusSearchArgs, args.CancellationToken); |
|||
var nexusAsset = nexusAssetListResult.Items.FirstOrDefault(); |
|||
|
|||
return nexusAsset; |
|||
} |
|||
|
|||
protected async virtual Task<NexusComponent> GetNexusomponentOrNull(BlobProviderArgs args) |
|||
{ |
|||
var nexusConfiguration = args.Configuration.GetNexusConfiguration(); |
|||
var blobPath = BlobDirectoryCalculator.CalculateGroup(args.ContainerName, args.BlobName); |
|||
var blobName = BlobDirectoryCalculator.CalculateName(args.ContainerName, args.BlobName); |
|||
var nexusSearchArgs = new NexusSearchArgs( |
|||
nexusConfiguration.Repository, |
|||
blobPath, |
|||
blobName); |
|||
|
|||
var nexusComponentResult = await NexusLookupService.ListComponentAsync(nexusSearchArgs, args.CancellationToken); |
|||
var nexusComponent = nexusComponentResult.Items.FirstOrDefault(); |
|||
|
|||
return nexusComponent; |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using Volo.Abp; |
|||
using Volo.Abp.BlobStoring; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Nexus; |
|||
public class NexusBlobProviderConfiguration |
|||
{ |
|||
public string BasePath { |
|||
get => _containerConfiguration.GetConfiguration<string>(NexusBlobProviderConfigurationNames.BasePath); |
|||
set => _containerConfiguration.SetConfiguration(NexusBlobProviderConfigurationNames.BasePath, value); |
|||
} |
|||
|
|||
public bool AppendContainerNameToBasePath { |
|||
get => _containerConfiguration.GetConfigurationOrDefault(NexusBlobProviderConfigurationNames.AppendContainerNameToBasePath, true); |
|||
set => _containerConfiguration.SetConfiguration(NexusBlobProviderConfigurationNames.AppendContainerNameToBasePath, value); |
|||
} |
|||
|
|||
public string Repository { |
|||
get => _containerConfiguration.GetConfiguration<string>(NexusBlobProviderConfigurationNames.Repository); |
|||
set => _containerConfiguration.SetConfiguration(NexusBlobProviderConfigurationNames.Repository, Check.NotNullOrWhiteSpace(value, nameof(value))); |
|||
} |
|||
|
|||
private readonly BlobContainerConfiguration _containerConfiguration; |
|||
|
|||
public NexusBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) |
|||
{ |
|||
_containerConfiguration = containerConfiguration; |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
namespace LINGYUN.Abp.BlobStoring.Nexus; |
|||
public static class NexusBlobProviderConfigurationNames |
|||
{ |
|||
/// <summary>
|
|||
/// 基础路径
|
|||
/// </summary>
|
|||
public const string BasePath = "Sonatype:Nexus:Raw:BasePath"; |
|||
/// <summary>
|
|||
/// 添加容器名称到基础路径
|
|||
/// </summary>
|
|||
public const string AppendContainerNameToBasePath = "Sonatype:Nexus:Raw:AppendContainerNameToBasePath"; |
|||
/// <summary>
|
|||
/// Nexus raw仓库名称
|
|||
/// </summary>
|
|||
public const string Repository = "Sonatype:Nexus:Raw:Repository"; |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.BlobStoring.Nexus\LINGYUN.Abp.BlobStoring.Nexus.csproj" /> |
|||
<ProjectReference Include="..\..\oss-management\LINGYUN.Abp.OssManagement.Domain\LINGYUN.Abp.OssManagement.Domain.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,23 @@ |
|||
using LINGYUN.Abp.BlobStoring.Nexus; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.OssManagement.Nexus; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpBlobStoringNexusModule), |
|||
typeof(AbpOssManagementDomainModule))] |
|||
public class AbpOssManagementNexusModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddTransient<IOssContainerFactory, NexusOssContainerFactory>(); |
|||
|
|||
context.Services.AddTransient<IOssObjectExpireor>(provider => |
|||
provider |
|||
.GetRequiredService<IOssContainerFactory>() |
|||
.Create() |
|||
.As<NexusOssContainer>()); |
|||
} |
|||
} |
|||
@ -0,0 +1,333 @@ |
|||
using LINGYUN.Abp.BlobStoring.Nexus; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Assets; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Components; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Search; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Assets; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Browsers; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.BlobStoring; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.OssManagement.Nexus; |
|||
|
|||
internal class NexusOssContainer : IOssContainer, IOssObjectExpireor |
|||
{ |
|||
protected ICoreUiServiceProxy CoreUiServiceProxy { get; } |
|||
protected INexusAssetManager NexusAssetManager { get; } |
|||
protected INexusComponentManager NexusComponentManager { get; } |
|||
protected INexusLookupService NexusLookupService { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IBlobRawPathCalculator BlobRawPathCalculator { get; } |
|||
protected IBlobContainerConfigurationProvider ConfigurationProvider { get; } |
|||
|
|||
public NexusOssContainer( |
|||
ICoreUiServiceProxy coreUiServiceProxy, |
|||
INexusAssetManager nexusAssetManager, |
|||
INexusComponentManager nexusComponentManager, |
|||
INexusLookupService nexusLookupService, |
|||
ICurrentTenant currentTenant, |
|||
IBlobRawPathCalculator blobRawPathCalculator, |
|||
IBlobContainerConfigurationProvider configurationProvider) |
|||
{ |
|||
CoreUiServiceProxy = coreUiServiceProxy; |
|||
NexusAssetManager = nexusAssetManager; |
|||
NexusComponentManager = nexusComponentManager; |
|||
NexusLookupService = nexusLookupService; |
|||
CurrentTenant = currentTenant; |
|||
BlobRawPathCalculator = blobRawPathCalculator; |
|||
ConfigurationProvider = configurationProvider; |
|||
} |
|||
|
|||
public Task BulkDeleteObjectsAsync(BulkDeleteObjectRequest request) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public async virtual Task<OssContainer> CreateAsync(string name) |
|||
{ |
|||
// 创建容器的逻辑就是创建一个包含一个空白assets的component
|
|||
|
|||
var blobPath = BlobRawPathCalculator.CalculateGroup(name, "readme"); |
|||
|
|||
var nexusConfiguration = GetNexusConfiguration(); |
|||
|
|||
var uploadArgs = new NexusRawBlobUploadArgs( |
|||
nexusConfiguration.Repository, |
|||
blobPath, |
|||
new Asset("readme", Encoding.UTF8.GetBytes("A placeholder for an empty container."))); |
|||
|
|||
await NexusComponentManager.UploadAsync(uploadArgs); |
|||
|
|||
return new OssContainer( |
|||
name, |
|||
DateTime.Now, |
|||
0L, |
|||
DateTime.Now); |
|||
} |
|||
|
|||
public async virtual Task<OssObject> CreateObjectAsync(CreateOssObjectRequest request) |
|||
{ |
|||
var nexusConfiguration = GetNexusConfiguration(); |
|||
var blobPath = GetBasePath(request.Bucket, request.Path, request.Object); |
|||
if (!request.Overwrite) |
|||
{ |
|||
var searchBlobName = GetObjectName(request.Bucket, request.Path, request.Object); |
|||
var nexusSearchArgs = new NexusSearchArgs( |
|||
nexusConfiguration.Repository, |
|||
blobPath, |
|||
searchBlobName); |
|||
|
|||
var nexusAssetListResult = await NexusLookupService.ListAssetAsync(nexusSearchArgs); |
|||
if (nexusAssetListResult.Items.Any()) |
|||
{ |
|||
throw new BusinessException(code: OssManagementErrorCodes.ObjectAlreadyExists); |
|||
} |
|||
} |
|||
|
|||
var blobName = GetObjectName(request.Bucket, request.Path, request.Object, true); |
|||
var blobBytes = await request.Content.GetAllBytesAsync(); |
|||
var uploadArgs = new NexusRawBlobUploadArgs( |
|||
nexusConfiguration.Repository, |
|||
blobPath, |
|||
new Asset(blobName, blobBytes)); |
|||
|
|||
await NexusComponentManager.UploadAsync(uploadArgs); |
|||
|
|||
var getOssObjectRequest = new GetOssObjectRequest( |
|||
request.Bucket, request.Object, request.Path); |
|||
|
|||
return await GetObjectAsync(getOssObjectRequest); |
|||
} |
|||
|
|||
public async virtual Task DeleteAsync(string name) |
|||
{ |
|||
var nexusConfiguration = GetNexusConfiguration(); |
|||
var blobPath = BlobRawPathCalculator.CalculateGroup(name, "/"); |
|||
|
|||
var nexusSearchArgs = new NexusSearchArgs( |
|||
nexusConfiguration.Repository, |
|||
blobPath, |
|||
"/"); |
|||
|
|||
var nexusComponentListResult = await NexusLookupService.ListComponentAsync(nexusSearchArgs); |
|||
var nexusComponent = nexusComponentListResult.Items.FirstOrDefault(); |
|||
if (nexusComponent != null) |
|||
{ |
|||
await NexusComponentManager.DeleteAsync(nexusComponent.Id); |
|||
} |
|||
} |
|||
|
|||
public async virtual Task DeleteObjectAsync(GetOssObjectRequest request) |
|||
{ |
|||
var nexusConfiguration = GetNexusConfiguration(); |
|||
var blobPath = GetBasePath(request.Bucket, request.Path, request.Object); |
|||
var blobName = GetObjectName(request.Bucket, request.Path, request.Object); |
|||
|
|||
var nexusSearchArgs = new NexusSearchArgs( |
|||
nexusConfiguration.Repository, |
|||
blobPath, |
|||
blobName); |
|||
|
|||
var nexusAssetListResult = await NexusLookupService.ListAssetAsync(nexusSearchArgs); |
|||
var nexusAsset = nexusAssetListResult.Items.FirstOrDefault(); |
|||
if (nexusAsset != null) |
|||
{ |
|||
await NexusAssetManager.DeleteAsync(nexusAsset.Id); |
|||
} |
|||
} |
|||
|
|||
public virtual Task<bool> ExistsAsync(string name) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public Task ExpireAsync(ExprieOssObjectRequest request) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public async virtual Task<OssContainer> GetAsync(string name) |
|||
{ |
|||
var nexusConfiguration = GetNexusConfiguration(); |
|||
var blobPath = BlobRawPathCalculator.CalculateGroup(name, "/"); |
|||
|
|||
var nexusSearchArgs = new NexusSearchArgs( |
|||
nexusConfiguration.Repository, |
|||
blobPath, |
|||
"/"); |
|||
|
|||
var nexusComponentListResult = await NexusLookupService.ListComponentAsync(nexusSearchArgs); |
|||
var nexusComponent = nexusComponentListResult.Items.FirstOrDefault(); |
|||
if (nexusComponent == null) |
|||
{ |
|||
throw new BusinessException(code: OssManagementErrorCodes.ContainerNotFound); |
|||
} |
|||
|
|||
var lastModified = nexusComponent.Assets |
|||
.OrderBy(asset => asset.LastModified) |
|||
.Select(asset => asset.LastModified) |
|||
.FirstOrDefault(); |
|||
|
|||
return new OssContainer( |
|||
nexusComponent.Name, |
|||
lastModified ?? new DateTime(), |
|||
0L, |
|||
lastModified); |
|||
} |
|||
|
|||
public async virtual Task<GetOssContainersResponse> GetListAsync(GetOssContainersRequest request) |
|||
{ |
|||
var nexusConfiguration = GetNexusConfiguration(); |
|||
var blobPath = request.Prefix.RemovePreFix(".").RemovePreFix("/"); |
|||
var readComponent = CoreUIBrowse.Read(nexusConfiguration.Repository, blobPath); |
|||
|
|||
var coreUIResponse = await CoreUiServiceProxy.SearchAsync<CoreUIBrowseReadComponent, CoreUIBrowseComponentResult>(readComponent); |
|||
var ifFolderComponents = coreUIResponse.Result.Data.Where(component => component.Type == "folder").ToArray(); |
|||
|
|||
return new GetOssContainersResponse( |
|||
request.Prefix, |
|||
request.Marker, |
|||
"", |
|||
ifFolderComponents.Length, |
|||
ifFolderComponents.Select(component => |
|||
new OssContainer( |
|||
component.Text, |
|||
new DateTime(), |
|||
0L, |
|||
null, |
|||
new Dictionary<string, string>())) |
|||
.ToList()); |
|||
} |
|||
|
|||
public async virtual Task<OssObject> GetObjectAsync(GetOssObjectRequest request) |
|||
{ |
|||
var nexusConfiguration = GetNexusConfiguration(); |
|||
var blobPath = GetBasePath(request.Bucket, request.Path, request.Object); |
|||
var blobFullName = GetObjectName(request.Bucket, request.Path, request.Object); |
|||
var blobName = GetObjectName(request.Bucket, request.Path, request.Object, true); |
|||
|
|||
var nexusSearchArgs = new NexusSearchArgs( |
|||
nexusConfiguration.Repository, |
|||
blobPath, |
|||
blobFullName); |
|||
|
|||
var nexusAssetListResult = await NexusLookupService.ListAssetAsync(nexusSearchArgs); |
|||
var nexusAssetItem = nexusAssetListResult.Items.FirstOrDefault(); |
|||
if (nexusAssetItem == null) |
|||
{ |
|||
throw new BusinessException(code: OssManagementErrorCodes.ObjectNotFound); |
|||
} |
|||
|
|||
var (Repository, ComponentId) = DecodeBase64Id(nexusAssetItem.Id); |
|||
|
|||
var readAsset = CoreUIAsset.Read(ComponentId, Repository); |
|||
|
|||
var coreUIResponse = await CoreUiServiceProxy.SearchAsync<CoreUIAssetRead, CoreUIAssetResult>(readAsset); |
|||
var checksum = coreUIResponse.Result.Data.Attributes.GetOrDefault("checksum"); |
|||
var metadata = new Dictionary<string, string>(); |
|||
if (checksum != null) |
|||
{ |
|||
foreach (var data in checksum) |
|||
{ |
|||
metadata.Add(data.Key, data.Value.ToString()); |
|||
} |
|||
} |
|||
|
|||
return new OssObject( |
|||
blobName, |
|||
blobPath, |
|||
checksum?.GetOrDefault("md5")?.ToString(), |
|||
coreUIResponse.Result.Data.BlobCreated, |
|||
coreUIResponse.Result.Data.Size, |
|||
coreUIResponse.Result.Data.BlobUpdated, |
|||
metadata |
|||
); |
|||
} |
|||
|
|||
public async virtual Task<GetOssObjectsResponse> GetObjectsAsync(GetOssObjectsRequest request) |
|||
{ |
|||
var nexusConfiguration = GetNexusConfiguration(); |
|||
var blobPath = GetBasePath(request.BucketName, request.Prefix, ""); |
|||
var readComponent = CoreUIBrowse.Read(nexusConfiguration.Repository, blobPath.RemovePreFix("/")); |
|||
|
|||
var coreUIResponse = await CoreUiServiceProxy.SearchAsync<CoreUIBrowseReadComponent, CoreUIBrowseComponentResult>(readComponent); |
|||
var filterComponents = coreUIResponse.Result.Data |
|||
.WhereIf(string.Equals(request.Delimiter, "/"), component => component.Type == "folder") |
|||
.OrderBy(component => component.Text) |
|||
.AsQueryable() |
|||
.PageBy(request.Current, request.MaxKeys ?? 10) |
|||
.ToArray(); |
|||
|
|||
var response = new GetOssObjectsResponse( |
|||
request.BucketName, |
|||
request.Prefix, |
|||
request.Marker, |
|||
"", |
|||
"/", // 文件系统目录分隔符
|
|||
coreUIResponse.Result.Data.Count, |
|||
filterComponents.Select(component => new OssObject( |
|||
component.Text, |
|||
request.Prefix, |
|||
"", |
|||
null, |
|||
0L, |
|||
null, |
|||
new Dictionary<string, string>(), |
|||
component.Type == "folder") |
|||
{ |
|||
FullName = component.Id |
|||
}) |
|||
.ToList()); |
|||
|
|||
return response; |
|||
} |
|||
|
|||
protected virtual NexusBlobProviderConfiguration GetNexusConfiguration() |
|||
{ |
|||
var configuration = ConfigurationProvider.Get<AbpOssManagementContainer>(); |
|||
var nexusConfiguration = configuration.GetNexusConfiguration(); |
|||
return nexusConfiguration; |
|||
} |
|||
|
|||
protected virtual string GetBasePath(string bucket, string path, string @object) |
|||
{ |
|||
var objectPath = bucket.EnsureEndsWith('/') + (!path.IsNullOrWhiteSpace() ? path.RemovePreFix("/") : ""); |
|||
objectPath = BlobRawPathCalculator.CalculateGroup(objectPath, @object); |
|||
return objectPath; |
|||
} |
|||
|
|||
protected virtual string GetObjectName(string bucket, string path, string @object, bool replaceObjectPath = false) |
|||
{ |
|||
var objectPath = bucket.EnsureEndsWith('/') + (!path.IsNullOrWhiteSpace() ? path.RemovePreFix("/") : ""); |
|||
//objectPath = BlobRawPathCalculator.CalculateGroup(objectPath, @object);
|
|||
var objectName = BlobRawPathCalculator.CalculateName(objectPath, @object, replaceObjectPath); |
|||
return objectName; |
|||
} |
|||
|
|||
protected virtual (string Repository, string ComponentId) DecodeBase64Id(string base64id) |
|||
{ |
|||
base64id = base64id.Replace("-", "+").Replace("_", "/"); |
|||
var base64 = Encoding.ASCII.GetBytes(base64id); |
|||
var padding = base64.Length * 3 % 4;//(base64.Length*6 % 8)/2
|
|||
if (padding != 0) |
|||
{ |
|||
base64id = base64id.PadRight(base64id.Length + padding, '='); |
|||
} |
|||
|
|||
var buffer = Convert.FromBase64String(base64id); |
|||
var decoded = Encoding.UTF8.GetString(buffer); |
|||
var parts = decoded.Split(":"); |
|||
if (parts.Length != 2) |
|||
{ |
|||
throw new AbpException("Unable to parse component id " + decoded); |
|||
} |
|||
return (parts[0], parts[1]); |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
using LINGYUN.Abp.BlobStoring.Nexus; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Assets; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Components; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Search; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI; |
|||
using Volo.Abp.BlobStoring; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.OssManagement.Nexus; |
|||
internal class NexusOssContainerFactory : IOssContainerFactory |
|||
{ |
|||
protected ICoreUiServiceProxy CoreUiServiceProxy { get; } |
|||
protected INexusAssetManager NexusAssetManager { get; } |
|||
protected INexusComponentManager NexusComponentManager { get; } |
|||
protected INexusLookupService NexusLookupService { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IBlobRawPathCalculator BlobRawPathCalculator { get; } |
|||
protected IBlobContainerConfigurationProvider ConfigurationProvider { get; } |
|||
|
|||
public NexusOssContainerFactory( |
|||
ICoreUiServiceProxy coreUiServiceProxy, |
|||
INexusAssetManager nexusAssetManager, |
|||
INexusComponentManager nexusComponentManager, |
|||
INexusLookupService nexusLookupService, |
|||
ICurrentTenant currentTenant, |
|||
IBlobRawPathCalculator blobRawPathCalculator, |
|||
IBlobContainerConfigurationProvider configurationProvider) |
|||
{ |
|||
CoreUiServiceProxy = coreUiServiceProxy; |
|||
NexusAssetManager = nexusAssetManager; |
|||
NexusComponentManager = nexusComponentManager; |
|||
NexusLookupService = nexusLookupService; |
|||
CurrentTenant = currentTenant; |
|||
BlobRawPathCalculator = blobRawPathCalculator; |
|||
ConfigurationProvider = configurationProvider; |
|||
} |
|||
|
|||
public IOssContainer Create() |
|||
{ |
|||
return new NexusOssContainer( |
|||
CoreUiServiceProxy, |
|||
NexusAssetManager, |
|||
NexusComponentManager, |
|||
NexusLookupService, |
|||
CurrentTenant, |
|||
BlobRawPathCalculator, |
|||
ConfigurationProvider); |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using System.Security.Cryptography; |
|||
using System.Text; |
|||
|
|||
namespace System.IO |
|||
{ |
|||
internal static class SystemExtensions |
|||
{ |
|||
public static string MD5(this Stream stream) |
|||
{ |
|||
if (stream.CanSeek) |
|||
{ |
|||
stream.Seek(0, SeekOrigin.Begin); |
|||
} |
|||
using MD5 md5 = new MD5CryptoServiceProvider(); |
|||
byte[] retVal = md5.ComputeHash(stream); |
|||
StringBuilder sb = new StringBuilder(); |
|||
for (int i = 0; i < retVal.Length; i++) |
|||
{ |
|||
sb.Append(retVal[i].ToString("x2")); |
|||
} |
|||
stream.Seek(0, SeekOrigin.Begin); |
|||
return sb.ToString(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Json" Version="$(VoloAbpPackageVersion)" /> |
|||
<PackageReference Include="Microsoft.Extensions.Http" Version="$(MicrosoftPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,28 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpJsonModule))] |
|||
public class AbpSonatypeNexusModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Services.GetConfiguration(); |
|||
Configure<AbpSonatypeNexusOptions>(configuration.GetSection("Sonatype:Nexus")); |
|||
|
|||
context.Services.AddHttpClient( |
|||
SonatypeNexusConsts.ApiClient, |
|||
(serviceProvider, client) => |
|||
{ |
|||
var options = serviceProvider.GetRequiredService<IOptions<AbpSonatypeNexusOptions>>().Value; |
|||
client.BaseAddress = new Uri(options.BaseUrl); |
|||
client.DefaultRequestHeaders.Add("X-Nexus-Ui", "true"); |
|||
client.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest"); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
namespace LINGYUN.Abp.Sonatype.Nexus; |
|||
public class AbpSonatypeNexusOptions |
|||
{ |
|||
public string BaseUrl { get; set; } |
|||
public string UserName { get; set; } |
|||
public string Password { get; set; } |
|||
public AbpSonatypeNexusOptions() |
|||
{ |
|||
BaseUrl = "http://127.0.0.1:8081"; |
|||
UserName = "sonatype"; |
|||
Password = "sonatype"; |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using JetBrains.Annotations; |
|||
using System.IO; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Assets; |
|||
|
|||
public interface INexusAssetManager |
|||
{ |
|||
Task<NexusAssetListResult> ListAsync( |
|||
[NotNull] string repository, |
|||
string continuationToken = null, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<NexusAsset> GetAsync( |
|||
[NotNull] string id, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task DeleteAsync( |
|||
[NotNull] string id, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<Stream> GetContentOrNullAsync( |
|||
[NotNull] NexusAsset asset, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Assets; |
|||
public class NexusAsset |
|||
{ |
|||
[JsonPropertyName("downloadUrl")] |
|||
public string DownloadUrl { get; set; } |
|||
|
|||
[JsonPropertyName("path")] |
|||
public string Path { get; set; } |
|||
|
|||
[JsonPropertyName("id")] |
|||
public string Id { get; set; } |
|||
|
|||
[JsonPropertyName("repository")] |
|||
public string Repository { get; set; } |
|||
|
|||
[JsonPropertyName("format")] |
|||
public string Format { get; set; } |
|||
|
|||
[JsonPropertyName("contentType")] |
|||
public string ContentType { get; set; } |
|||
|
|||
[JsonPropertyName("lastModified")] |
|||
public DateTime? LastModified { get; set; } |
|||
|
|||
[JsonPropertyName("blobCreated")] |
|||
public DateTime? BlobCreated { get; set; } |
|||
|
|||
[JsonPropertyName("checksum")] |
|||
public Dictionary<string, string> Checksum { get; set; } |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Assets; |
|||
|
|||
[Serializable] |
|||
public class NexusAssetListResult |
|||
{ |
|||
[JsonPropertyName("continuationToken")] |
|||
public string ContinuationToken { get; set; } |
|||
|
|||
[JsonPropertyName("items")] |
|||
public List<NexusAsset> Items { get; set; } |
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.IO; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Assets; |
|||
public class NexusAssetManager : INexusAssetManager, ISingletonDependency |
|||
{ |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected IHttpClientFactory HttpClientFactory { get; } |
|||
protected AbpSonatypeNexusOptions Options { get; } |
|||
|
|||
public NexusAssetManager( |
|||
IJsonSerializer jsonSerializer, |
|||
IHttpClientFactory httpClientFactory, |
|||
IOptions<AbpSonatypeNexusOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
JsonSerializer = jsonSerializer; |
|||
HttpClientFactory = httpClientFactory; |
|||
} |
|||
|
|||
public async virtual Task DeleteAsync([NotNull] string id, CancellationToken cancellationToken = default) |
|||
{ |
|||
var client = HttpClientFactory.CreateClient(SonatypeNexusConsts.ApiClient); |
|||
var username = Options.UserName; |
|||
var password = Options.Password; |
|||
var authBase64Data = Convert.ToBase64String(Encoding.UTF8.GetBytes(username + ":" + password)); |
|||
|
|||
var requestMessage = new HttpRequestMessage(HttpMethod.Delete, $"/service/rest/v1/assets/{id}"); |
|||
requestMessage.Headers.Add("Authorization", $"Basic {authBase64Data}"); |
|||
|
|||
var response = await client.SendAsync(requestMessage, cancellationToken); |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
throw new AbpException(response.ReasonPhrase); |
|||
} |
|||
} |
|||
|
|||
public async virtual Task<NexusAsset> GetAsync([NotNull] string id, CancellationToken cancellationToken = default) |
|||
{ |
|||
var client = HttpClientFactory.CreateClient(SonatypeNexusConsts.ApiClient); |
|||
|
|||
var response = await client.GetAsync($"/service/rest/v1/assets/{id}", cancellationToken); |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
throw new AbpException(response.ReasonPhrase); |
|||
} |
|||
|
|||
var responseContent = await response.Content.ReadAsStringAsync(); |
|||
var nexusAsset = JsonSerializer.Deserialize<NexusAsset>(responseContent); |
|||
|
|||
return nexusAsset; |
|||
} |
|||
|
|||
public async virtual Task<Stream> GetContentOrNullAsync([NotNull] NexusAsset asset, CancellationToken cancellationToken = default) |
|||
{ |
|||
if (asset == null || asset.DownloadUrl.IsNullOrWhiteSpace()) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var client = HttpClientFactory.CreateClient(SonatypeNexusConsts.ApiClient); |
|||
|
|||
return await client.GetStreamAsync(asset.DownloadUrl); |
|||
} |
|||
|
|||
public async virtual Task<NexusAssetListResult> ListAsync([NotNull] string repository, string continuationToken = null, CancellationToken cancellationToken = default) |
|||
{ |
|||
var client = HttpClientFactory.CreateClient(SonatypeNexusConsts.ApiClient); |
|||
|
|||
var urlBuilder = new StringBuilder(); |
|||
urlBuilder.Append("/service/rest/v1/assets"); |
|||
urlBuilder.AppendFormat("?repository={0}", repository); |
|||
if (!continuationToken.IsNullOrWhiteSpace()) |
|||
{ |
|||
urlBuilder.AppendFormat("&continuationToken={0}", continuationToken); |
|||
} |
|||
|
|||
var response = await client.GetAsync(urlBuilder.ToString(), cancellationToken); |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
throw new AbpException(response.ReasonPhrase); |
|||
} |
|||
|
|||
var responseContent = await response.Content.ReadAsStringAsync(); |
|||
var nexusAssetListResult = JsonSerializer.Deserialize<NexusAssetListResult>(responseContent); |
|||
|
|||
return nexusAssetListResult; |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Components; |
|||
public class Asset |
|||
{ |
|||
public string FileName { get; } |
|||
public byte[] FileBytes { get; } |
|||
public Asset(string fileName, byte[] fileBytes) |
|||
{ |
|||
FileName = fileName; |
|||
FileBytes = fileBytes; |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using JetBrains.Annotations; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Components; |
|||
|
|||
public interface INexusComponentManager |
|||
{ |
|||
Task<NexusComponentListResult> ListAsync( |
|||
[NotNull] string repository, |
|||
string continuationToken = null, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<NexusComponent> GetAsync( |
|||
[NotNull] string id, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<bool> DeleteAsync( |
|||
[NotNull] string id, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task UploadAsync( |
|||
[NotNull] NexusComponentUploadArgs args, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using LINGYUN.Abp.Sonatype.Nexus.Assets; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Components; |
|||
|
|||
[Serializable] |
|||
public class NexusComponent |
|||
{ |
|||
[JsonPropertyName("id")] |
|||
public string Id { get; set; } |
|||
|
|||
[JsonPropertyName("repository")] |
|||
public string Repository { get; set; } |
|||
|
|||
[JsonPropertyName("format")] |
|||
public string Format { get; set; } |
|||
|
|||
[JsonPropertyName("group")] |
|||
public string Group { get; set; } |
|||
|
|||
[JsonPropertyName("name")] |
|||
public string Name { get; set; } |
|||
|
|||
[JsonPropertyName("version")] |
|||
public string Version { get; set; } |
|||
|
|||
[JsonPropertyName("assets")] |
|||
public List<NexusAsset> Assets { get; set; } |
|||
|
|||
public NexusComponent() |
|||
{ |
|||
Assets = new List<NexusAsset>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Components; |
|||
|
|||
[Serializable] |
|||
public class NexusComponentListResult |
|||
{ |
|||
[JsonPropertyName("continuationToken")] |
|||
public string ContinuationToken { get; set; } |
|||
|
|||
[JsonPropertyName("items")] |
|||
public List<NexusComponent> Items { get; set; } |
|||
|
|||
public NexusComponentListResult() |
|||
{ |
|||
Items = new List<NexusComponent>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,107 @@ |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Components; |
|||
public class NexusComponentManager : INexusComponentManager, ISingletonDependency |
|||
{ |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected IHttpClientFactory HttpClientFactory { get; } |
|||
protected AbpSonatypeNexusOptions Options { get; } |
|||
|
|||
public NexusComponentManager( |
|||
IJsonSerializer jsonSerializer, |
|||
IHttpClientFactory httpClientFactory, |
|||
IOptions<AbpSonatypeNexusOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
JsonSerializer = jsonSerializer; |
|||
HttpClientFactory = httpClientFactory; |
|||
} |
|||
|
|||
public async virtual Task UploadAsync([NotNull] NexusComponentUploadArgs args, CancellationToken cancellationToken = default) |
|||
{ |
|||
var client = HttpClientFactory.CreateClient(SonatypeNexusConsts.ApiClient); |
|||
|
|||
var username = Options.UserName; |
|||
var password = Options.Password; |
|||
var authBase64Data = Convert.ToBase64String(Encoding.UTF8.GetBytes(username + ":" + password)); |
|||
|
|||
using var formDataContent = args.BuildContent(); |
|||
var requestMessage = new HttpRequestMessage( |
|||
HttpMethod.Post, |
|||
$"/service/rest/v1/components?repository={args.Repository}") |
|||
{ |
|||
Content = formDataContent, |
|||
}; |
|||
requestMessage.Headers.Add("Authorization", $"Basic {authBase64Data}"); |
|||
|
|||
var response = await client.SendAsync(requestMessage, cancellationToken); |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
throw new AbpException(response.ReasonPhrase); |
|||
} |
|||
} |
|||
|
|||
public async virtual Task<bool> DeleteAsync([NotNull] string id, CancellationToken cancellationToken = default) |
|||
{ |
|||
var client = HttpClientFactory.CreateClient(SonatypeNexusConsts.ApiClient); |
|||
|
|||
var username = Options.UserName; |
|||
var password = Options.Password; |
|||
var authBase64Data = Convert.ToBase64String(Encoding.UTF8.GetBytes(username + ":" + password)); |
|||
|
|||
var requestMessage = new HttpRequestMessage(HttpMethod.Delete, $"/service/rest/v1/components/{id}"); |
|||
requestMessage.Headers.Add("Authorization", $"Basic {authBase64Data}"); |
|||
|
|||
var response = await client.SendAsync(requestMessage, cancellationToken); |
|||
return response.IsSuccessStatusCode; |
|||
} |
|||
|
|||
public async virtual Task<NexusComponent> GetAsync([NotNull] string id, CancellationToken cancellationToken = default) |
|||
{ |
|||
var client = HttpClientFactory.CreateClient(SonatypeNexusConsts.ApiClient); |
|||
|
|||
var response = await client.GetAsync($"/service/rest/v1/components/{id}", cancellationToken); |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
throw new AbpException(response.ReasonPhrase); |
|||
} |
|||
|
|||
var responseContent = await response.Content.ReadAsStringAsync(); |
|||
var nexusComponent = JsonSerializer.Deserialize<NexusComponent>(responseContent); |
|||
|
|||
return nexusComponent; |
|||
} |
|||
|
|||
public async virtual Task<NexusComponentListResult> ListAsync([NotNull] string repository, string continuationToken = null, CancellationToken cancellationToken = default) |
|||
{ |
|||
var client = HttpClientFactory.CreateClient(SonatypeNexusConsts.ApiClient); |
|||
|
|||
var urlBuilder = new StringBuilder(); |
|||
urlBuilder.Append("/service/rest/v1/components"); |
|||
urlBuilder.AppendFormat("?repository={0}", repository); |
|||
if (!continuationToken.IsNullOrWhiteSpace()) |
|||
{ |
|||
urlBuilder.AppendFormat("&continuationToken={0}", continuationToken); |
|||
} |
|||
|
|||
var response = await client.GetAsync(urlBuilder.ToString(), cancellationToken); |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
throw new AbpException(response.ReasonPhrase); |
|||
} |
|||
|
|||
var responseContent = await response.Content.ReadAsStringAsync(); |
|||
var nexusComponentListResult = JsonSerializer.Deserialize<NexusComponentListResult>(responseContent); |
|||
|
|||
return nexusComponentListResult; |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System.Net.Http; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Components; |
|||
public abstract class NexusComponentUploadArgs |
|||
{ |
|||
public string Repository { get; } |
|||
public string Directory { get; } |
|||
|
|||
protected NexusComponentUploadArgs(string repository, string directory) |
|||
{ |
|||
Repository = repository; |
|||
Directory = directory; |
|||
} |
|||
|
|||
public abstract HttpContent BuildContent(); |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
using System; |
|||
using System.Net.Http; |
|||
using System.Net.Http.Headers; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Components; |
|||
public class NexusRawBlobUploadArgs : NexusComponentUploadArgs |
|||
{ |
|||
public Asset Asset1 { get; } |
|||
public Asset Asset2 { get; } |
|||
public Asset Asset3 { get; } |
|||
|
|||
public NexusRawBlobUploadArgs( |
|||
string repository, |
|||
string directory, |
|||
Asset asset1, |
|||
Asset asset2 = null, |
|||
Asset asset3 = null) |
|||
: base(repository, directory) |
|||
{ |
|||
Asset1 = asset1; |
|||
Asset2 = asset2; |
|||
Asset3 = asset3; |
|||
} |
|||
|
|||
public override HttpContent BuildContent() |
|||
{ |
|||
var boundary = "--BOUNDARY--" + DateTimeOffset.Now.Ticks.ToString("x"); |
|||
var formDataContent = new MultipartFormDataContent(boundary); |
|||
formDataContent.Headers.ContentType = MediaTypeHeaderValue.Parse($"multipart/form-data; boundary={boundary}"); |
|||
|
|||
var rawDirectory = new StringContent(Directory); |
|||
rawDirectory.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse($"form-data; name=raw.directory"); |
|||
|
|||
if (Asset1 != null) |
|||
{ |
|||
var rawAsset1 = new ByteArrayContent(Asset1.FileBytes); |
|||
rawAsset1.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse($"form-data; name=raw.asset1"); |
|||
rawAsset1.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream"); |
|||
rawAsset1.Headers.ContentLength = Asset1.FileBytes.Length; |
|||
|
|||
var rawAsset1FileName = new StringContent(Asset1.FileName); |
|||
rawAsset1FileName.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse($"form-data; name=raw.asset1.filename"); |
|||
|
|||
formDataContent.Add(rawAsset1); |
|||
formDataContent.Add(rawAsset1FileName); |
|||
} |
|||
|
|||
if (Asset2 != null) |
|||
{ |
|||
var rawAsset2 = new ByteArrayContent(Asset2.FileBytes); |
|||
rawAsset2.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse($"form-data; name=raw.asset2"); |
|||
rawAsset2.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream"); |
|||
rawAsset2.Headers.ContentLength = Asset2.FileBytes.Length; |
|||
|
|||
var rawAsset2FileName = new StringContent(Asset2.FileName); |
|||
rawAsset2FileName.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse($"form-data; name=raw.asset2.filename"); |
|||
|
|||
formDataContent.Add(rawAsset2); |
|||
formDataContent.Add(rawAsset2FileName); |
|||
} |
|||
|
|||
if (Asset3 != null) |
|||
{ |
|||
var rawAsset3 = new ByteArrayContent(Asset2.FileBytes); |
|||
rawAsset3.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse($"form-data; name=raw.asset3"); |
|||
rawAsset3.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream"); |
|||
rawAsset3.Headers.ContentLength = Asset2.FileBytes.Length; |
|||
|
|||
var rawAsset3FileName = new StringContent(Asset2.FileName); |
|||
rawAsset3FileName.Headers.ContentDisposition = ContentDispositionHeaderValue.Parse($"form-data; name=raw.asset3.filename"); |
|||
|
|||
formDataContent.Add(rawAsset3); |
|||
formDataContent.Add(rawAsset3FileName); |
|||
} |
|||
|
|||
formDataContent.Add(rawDirectory); |
|||
|
|||
return formDataContent; |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Repositories; |
|||
|
|||
public interface INexusRepositoryManager<TRepository, TRepositoryCreateArgs, TRepositoryUpdateArgs> |
|||
where TRepository : NexusRepository |
|||
where TRepositoryCreateArgs : NexusRepositoryCreateArgs |
|||
where TRepositoryUpdateArgs: NexusRepositoryUpdateArgs |
|||
{ |
|||
Task CreateAsync(TRepositoryCreateArgs args, CancellationToken cancellationToken = default); |
|||
|
|||
Task<TRepository> GetAsync(string name, CancellationToken cancellationToken = default); |
|||
|
|||
Task UpdateAsync(string name, TRepositoryUpdateArgs args, CancellationToken cancellationToken = default); |
|||
|
|||
Task DeleteAsync(string name, CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<NexusRepositoryListResult>> ListAsync(CancellationToken cancellationToken = default); |
|||
} |
|||
|
|||
public interface INexusRepositoryManager<TRepository, TRepositoryCreateArgs> |
|||
where TRepository : NexusRepository |
|||
where TRepositoryCreateArgs : NexusRepositoryCreateArgs |
|||
{ |
|||
Task CreateAsync(TRepositoryCreateArgs args, CancellationToken cancellationToken = default); |
|||
|
|||
Task<TRepository> GetAsync(string name, CancellationToken cancellationToken = default); |
|||
|
|||
Task DeleteAsync(string name, CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<NexusRepositoryListResult>> ListAsync(CancellationToken cancellationToken = default); |
|||
} |
|||
|
|||
|
|||
public interface INexusRepositoryManager<TRepository> |
|||
where TRepository : NexusRepository |
|||
{ |
|||
Task<TRepository> GetAsync(string name, CancellationToken cancellationToken = default); |
|||
|
|||
Task DeleteAsync(string name, CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<NexusRepositoryListResult>> ListAsync(CancellationToken cancellationToken = default); |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Repositories; |
|||
|
|||
[Serializable] |
|||
public abstract class NexusRepository |
|||
{ |
|||
[JsonPropertyName("name")] |
|||
public string Name { get; set; } |
|||
|
|||
[JsonPropertyName("online")] |
|||
public bool Online { get; set; } |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Repositories; |
|||
|
|||
[Serializable] |
|||
public abstract class NexusRepositoryCreateArgs |
|||
{ |
|||
[JsonPropertyName("name")] |
|||
public string Name { get; set; } |
|||
|
|||
[JsonPropertyName("online")] |
|||
public bool Online { get; set; } |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Repositories; |
|||
|
|||
[Serializable] |
|||
public class NexusRepositoryListResult |
|||
{ |
|||
[JsonPropertyName("name")] |
|||
public string Name { get; set; } |
|||
|
|||
[JsonPropertyName("format")] |
|||
public string Format { get; set; } |
|||
|
|||
[JsonPropertyName("type")] |
|||
public string Type { get; set; } |
|||
|
|||
[JsonPropertyName("url")] |
|||
public string Url { get; set; } |
|||
|
|||
[JsonPropertyName("attributes")] |
|||
public Dictionary<string, object> Attributes { get; set; } |
|||
|
|||
public NexusRepositoryListResult() |
|||
{ |
|||
Attributes = new Dictionary<string, object>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Repositories; |
|||
|
|||
[Serializable] |
|||
public abstract class NexusRepositoryUpdateArgs |
|||
{ |
|||
[JsonPropertyName("name")] |
|||
public string Name { get; set; } |
|||
|
|||
[JsonPropertyName("online")] |
|||
public bool Online { get; set; } |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Repositories.Raw; |
|||
|
|||
public interface INexusRawRepositoryManager |
|||
{ |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Repositories.Raw; |
|||
|
|||
public class NexusRawRepository : NexusRepository |
|||
{ |
|||
[JsonPropertyName("storage")] |
|||
public RawStorage Storage { get; set; } |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Repositories.Raw; |
|||
|
|||
[Serializable] |
|||
public class RawGroup |
|||
{ |
|||
[JsonPropertyName("memberNames")] |
|||
public List<string> MemberNames { get; set; } |
|||
|
|||
public RawGroup() |
|||
{ |
|||
MemberNames = new List<string>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Repositories.Raw; |
|||
|
|||
[Serializable] |
|||
public class RawStorage |
|||
{ |
|||
[JsonPropertyName("blobStoreName")] |
|||
public string BlobStoreName { get; set; } |
|||
|
|||
[JsonPropertyName("strictContentTypeValidation")] |
|||
public bool StrictContentTypeValidation { get; set; } |
|||
|
|||
[JsonPropertyName("RawGroup")] |
|||
public RawGroup Group { get; set; } |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using LINGYUN.Abp.Sonatype.Nexus.Assets; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Components; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Search; |
|||
|
|||
public interface INexusLookupService |
|||
{ |
|||
Task<NexusComponentListResult> ListComponentAsync(NexusSearchArgs args, CancellationToken cancellationToken = default); |
|||
|
|||
Task<NexusAssetListResult> ListAssetAsync(NexusSearchArgs args, CancellationToken cancellationToken = default); |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
using LINGYUN.Abp.Sonatype.Nexus.Assets; |
|||
using LINGYUN.Abp.Sonatype.Nexus.Components; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Search; |
|||
public class NexusLookupService : INexusLookupService, ISingletonDependency |
|||
{ |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected IHttpClientFactory HttpClientFactory { get; } |
|||
protected AbpSonatypeNexusOptions Options { get; } |
|||
|
|||
public NexusLookupService( |
|||
IJsonSerializer jsonSerializer, |
|||
IHttpClientFactory httpClientFactory, |
|||
IOptions<AbpSonatypeNexusOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
JsonSerializer = jsonSerializer; |
|||
HttpClientFactory = httpClientFactory; |
|||
} |
|||
|
|||
public async virtual Task<NexusAssetListResult> ListAssetAsync(NexusSearchArgs args, CancellationToken cancellationToken = default) |
|||
{ |
|||
var client = HttpClientFactory.CreateClient(SonatypeNexusConsts.ApiClient); |
|||
|
|||
var urlBuilder = new StringBuilder(); |
|||
urlBuilder.AppendFormat("/service/rest/v1/search/assets?format={0}", args.Format); |
|||
urlBuilder.AppendFormat("&repository={0}", args.Repository); |
|||
urlBuilder.AppendFormat("&group={0}", args.Group); |
|||
urlBuilder.AppendFormat("&name={0}", args.Name); |
|||
if(!args.Keyword.IsNullOrWhiteSpace()) |
|||
{ |
|||
urlBuilder.AppendFormat("&q={0}", args.Keyword); |
|||
} |
|||
if (!args.Version.IsNullOrWhiteSpace()) |
|||
{ |
|||
urlBuilder.AppendFormat("&version={0}", args.Version); |
|||
} |
|||
if (args.Timeout.HasValue) |
|||
{ |
|||
urlBuilder.AppendFormat("&timeout={0}", args.Timeout.Value); |
|||
} |
|||
|
|||
var response = await client.GetAsync(urlBuilder.ToString(), cancellationToken); |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
throw new AbpException(response.ReasonPhrase); |
|||
} |
|||
|
|||
var responseContent = await response.Content.ReadAsStringAsync(); |
|||
var nexusAssetListResult = JsonSerializer.Deserialize<NexusAssetListResult>(responseContent); |
|||
|
|||
return nexusAssetListResult; |
|||
} |
|||
|
|||
public async virtual Task<NexusComponentListResult> ListComponentAsync(NexusSearchArgs args, CancellationToken cancellationToken = default) |
|||
{ |
|||
var client = HttpClientFactory.CreateClient(SonatypeNexusConsts.ApiClient); |
|||
|
|||
var urlBuilder = new StringBuilder(); |
|||
urlBuilder.AppendFormat("/service/rest/v1/search?format={0}", args.Format); |
|||
urlBuilder.AppendFormat("&repository={0}", args.Repository); |
|||
urlBuilder.AppendFormat("&group={0}", args.Group); |
|||
urlBuilder.AppendFormat("&name={0}", args.Name); |
|||
if (!args.Keyword.IsNullOrWhiteSpace()) |
|||
{ |
|||
urlBuilder.AppendFormat("&q={0}", args.Keyword); |
|||
} |
|||
if (!args.Version.IsNullOrWhiteSpace()) |
|||
{ |
|||
urlBuilder.AppendFormat("&version={0}", args.Version); |
|||
} |
|||
if (args.Timeout.HasValue) |
|||
{ |
|||
urlBuilder.AppendFormat("&timeout={0}", args.Timeout.Value); |
|||
} |
|||
|
|||
var response = await client.GetAsync(urlBuilder.ToString(), cancellationToken); |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
throw new AbpException(response.ReasonPhrase); |
|||
} |
|||
|
|||
var responseContent = await response.Content.ReadAsStringAsync(); |
|||
var nexusComponentListResult = JsonSerializer.Deserialize<NexusComponentListResult>(responseContent); |
|||
|
|||
return nexusComponentListResult; |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Search; |
|||
public class NexusSearchArgs |
|||
{ |
|||
public string Keyword { get; } |
|||
public string Repository { get; } |
|||
public string Group { get; } |
|||
public string Name { get; } |
|||
public string Format { get; set; } = "raw"; |
|||
public int? Timeout { get; set; } |
|||
public string Version { get; } |
|||
|
|||
public NexusSearchArgs( |
|||
string repository, |
|||
string group, |
|||
string name, |
|||
string format = "raw", |
|||
string keyword = null, |
|||
string version = null, |
|||
int? timeout = null) |
|||
{ |
|||
Keyword = keyword; |
|||
Repository = repository; |
|||
Group = group; |
|||
Name = name; |
|||
Format = format; |
|||
Timeout = timeout; |
|||
Version = version; |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Assets; |
|||
|
|||
public static class CoreUIAsset |
|||
{ |
|||
public static CoreUIRequest<CoreUIAssetRead> Read( |
|||
string assetId, |
|||
string repository) |
|||
{ |
|||
var asset = new CoreUIAssetRead(assetId, repository); |
|||
|
|||
return new CoreUIRequest<CoreUIAssetRead>( |
|||
"coreui_Component", |
|||
"readAsset", |
|||
asset); |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Assets; |
|||
|
|||
[Serializable] |
|||
public class CoreUIAssetData |
|||
{ |
|||
[JsonPropertyName("id")] |
|||
public string Id { get; set; } |
|||
|
|||
[JsonPropertyName("name")] |
|||
public string Name { get; set; } |
|||
|
|||
[JsonPropertyName("format")] |
|||
public string Format { get; set; } |
|||
|
|||
[JsonPropertyName("contentType")] |
|||
public string ContentType { get; set; } |
|||
|
|||
[JsonPropertyName("blobUpdated")] |
|||
public DateTime? BlobUpdated { get; set; } |
|||
|
|||
[JsonPropertyName("blobCreated")] |
|||
public DateTime? BlobCreated { get; set; } |
|||
|
|||
[JsonPropertyName("createdBy")] |
|||
public string CreatedBy { get; set; } |
|||
|
|||
[JsonPropertyName("createdByIp")] |
|||
public string CreatedByIp { get; set; } |
|||
|
|||
[JsonPropertyName("blobRef")] |
|||
public string BlobRef { get; set; } |
|||
|
|||
[JsonPropertyName("componentId")] |
|||
public string ComponentId { get; set; } |
|||
|
|||
[JsonPropertyName("lastDownloaded")] |
|||
public string LastDownloaded { get; set; } |
|||
|
|||
[JsonPropertyName("containingRepositoryName")] |
|||
public string ContainingRepositoryName { get; set; } |
|||
|
|||
[JsonPropertyName("repositoryName")] |
|||
public string RepositoryName { get; set; } |
|||
|
|||
[JsonPropertyName("size")] |
|||
public long Size { get; set; } |
|||
|
|||
[JsonPropertyName("attributes")] |
|||
public Dictionary<string, Dictionary<string, object>> Attributes { get; set; } |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Assets; |
|||
public class CoreUIAssetRead : List<string> |
|||
{ |
|||
public CoreUIAssetRead( |
|||
string assetId, |
|||
string repository) |
|||
{ |
|||
Add(assetId); |
|||
Add(repository); |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Assets; |
|||
|
|||
[Serializable] |
|||
public class CoreUIAssetResult |
|||
{ |
|||
[JsonPropertyName("success")] |
|||
public bool Success { get; set; } |
|||
|
|||
[JsonPropertyName("data")] |
|||
public CoreUIAssetData Data { get; set; } |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Browsers; |
|||
|
|||
public static class CoreUIBrowse |
|||
{ |
|||
public static CoreUIRequest<CoreUIBrowseReadComponent> Read( |
|||
string repository, |
|||
string node = "/") |
|||
{ |
|||
var readComponent = new CoreUIBrowseReadComponent(repository, node); |
|||
|
|||
return new CoreUIRequest<CoreUIBrowseReadComponent>( |
|||
"coreui_Browse", |
|||
"read", |
|||
readComponent); |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Browsers; |
|||
|
|||
[Serializable] |
|||
public class CoreUIBrowseComponent |
|||
{ |
|||
[JsonPropertyName("id")] |
|||
public string Id { get; set; } |
|||
|
|||
[JsonPropertyName("assetId")] |
|||
public string AssetId { get; set; } |
|||
|
|||
[JsonPropertyName("componentId")] |
|||
public string ComponentId { get; set; } |
|||
|
|||
[JsonPropertyName("packageUrl")] |
|||
public string PackageUrl { get; set; } |
|||
|
|||
[JsonPropertyName("text")] |
|||
public string Text { get; set; } |
|||
|
|||
[JsonPropertyName("type")] |
|||
public string Type { get; set; } |
|||
|
|||
[JsonPropertyName("leaf")] |
|||
public bool Leaf { get; set; } |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Browsers; |
|||
|
|||
[Serializable] |
|||
public class CoreUIBrowseComponentResult |
|||
{ |
|||
[JsonPropertyName("success")] |
|||
public bool Success { get; set; } |
|||
|
|||
[JsonPropertyName("data")] |
|||
public List<CoreUIBrowseComponent> Data { get; set; } |
|||
|
|||
public CoreUIBrowseComponentResult() |
|||
{ |
|||
Data = new List<CoreUIBrowseComponent>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Browsers; |
|||
|
|||
[Serializable] |
|||
public class CoreUIBrowseNode |
|||
{ |
|||
[JsonPropertyName("node")] |
|||
public string Node { get; set; } |
|||
|
|||
[JsonPropertyName("repositoryName")] |
|||
public string RepositoryName { get; set; } |
|||
public CoreUIBrowseNode(string repositoryName, string node = "/") |
|||
{ |
|||
Node = node; |
|||
RepositoryName = repositoryName; |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Browsers; |
|||
public class CoreUIBrowseReadComponent : List<CoreUIBrowseNode> |
|||
{ |
|||
public CoreUIBrowseReadComponent(string repository, string node = "/") |
|||
{ |
|||
Add(new CoreUIBrowseNode(repository, node)); |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using System; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI; |
|||
|
|||
[Serializable] |
|||
public class CoreUIRequest<TData> |
|||
{ |
|||
[JsonPropertyName("action")] |
|||
public string Action { get; set; } |
|||
|
|||
[JsonPropertyName("method")] |
|||
public string Method { get; set; } |
|||
|
|||
[JsonPropertyName("tid")] |
|||
public long Tid { get; set; } |
|||
|
|||
[JsonPropertyName("type")] |
|||
public string Type { get; set; } |
|||
|
|||
[JsonPropertyName("data")] |
|||
public TData Data { get; set; } |
|||
|
|||
public CoreUIRequest( |
|||
string action, |
|||
string method, |
|||
TData data, |
|||
string type = "rpc") |
|||
{ |
|||
Action = action; |
|||
Method = method; |
|||
Type = type; |
|||
Data = data; |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System; |
|||
using System.Text.Json.Serialization; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI; |
|||
|
|||
[Serializable] |
|||
public class CoreUIResponse<TResult> |
|||
{ |
|||
[JsonPropertyName("action")] |
|||
public string Action { get; set; } |
|||
|
|||
[JsonPropertyName("method")] |
|||
public string Method { get; set; } |
|||
|
|||
[JsonPropertyName("tid")] |
|||
public long Tid { get; set; } |
|||
|
|||
[JsonPropertyName("type")] |
|||
public string Type { get; set; } |
|||
|
|||
[JsonPropertyName("result")] |
|||
public TResult Result { get; set; } |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Net.Http; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI; |
|||
|
|||
public class CoreUiServiceProxy : ICoreUiServiceProxy, ISingletonDependency |
|||
{ |
|||
private static int _idCurrent = 1; |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected IHttpClientFactory HttpClientFactory { get; } |
|||
protected AbpSonatypeNexusOptions Options { get; } |
|||
|
|||
public CoreUiServiceProxy( |
|||
IJsonSerializer jsonSerializer, |
|||
IHttpClientFactory httpClientFactory, |
|||
IOptions<AbpSonatypeNexusOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
JsonSerializer = jsonSerializer; |
|||
HttpClientFactory = httpClientFactory; |
|||
} |
|||
|
|||
public async virtual Task<CoreUIResponse<TResult>> SearchAsync<TData, TResult>( |
|||
CoreUIRequest<TData> request, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
Interlocked.Increment(ref _idCurrent); |
|||
request.Tid = _idCurrent; |
|||
var client = HttpClientFactory.CreateClient(SonatypeNexusConsts.ApiClient); |
|||
|
|||
using var requestContent = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json"); |
|||
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/service/extdirect") |
|||
{ |
|||
Content = requestContent, |
|||
}; |
|||
|
|||
var response = await client.SendAsync(requestMessage, cancellationToken); |
|||
if (!response.IsSuccessStatusCode) |
|||
{ |
|||
throw new AbpException(response.ReasonPhrase); |
|||
} |
|||
|
|||
var responseContent = await response.Content.ReadAsStringAsync(); |
|||
var coreUiResponse = JsonSerializer.Deserialize<CoreUIResponse<TResult>>(responseContent); |
|||
|
|||
return coreUiResponse; |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI; |
|||
|
|||
public interface ICoreUiServiceProxy : INexusServiceProxy |
|||
{ |
|||
Task<CoreUIResponse<TResult>> SearchAsync<TData, TResult>(CoreUIRequest<TData> request, CancellationToken cancellationToken = default); |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
namespace LINGYUN.Abp.Sonatype.Nexus.Services; |
|||
public interface INexusServiceProxy |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
namespace LINGYUN.Abp.Sonatype.Nexus; |
|||
internal static class SonatypeNexusConsts |
|||
{ |
|||
public const string ApiClient = "Sonatype.Nexus"; |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
global using Xunit; |
|||
global using Shouldly; |
|||
@ -0,0 +1,27 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net7.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" /> |
|||
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitPackageVersion)"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
<PackageReference Include="coverlet.collector" Version="$(CoverletCollectorPackageVersion)"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\modules\nexus\LINGYUN.Abp.BlobStoring.Nexus\LINGYUN.Abp.BlobStoring.Nexus.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.TestBase\LINGYUN.Abp.TestsBase.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,6 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Nexus; |
|||
public abstract class AbpBlobStoringNexusTestBase : AbpTestsBase<AbpBlobStoringNexusTestModule> |
|||
{ |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System; |
|||
using Volo.Abp.BlobStoring; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Nexus; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpBlobStoringNexusModule), |
|||
typeof(AbpTestsBaseModule))] |
|||
public class AbpBlobStoringNexusTestModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configurationOptions = new AbpConfigurationBuilderOptions |
|||
{ |
|||
BasePath = @"D:\Projects\Development\Abp\BlobStoring\Nexus", |
|||
EnvironmentName = "Test" |
|||
}; |
|||
|
|||
context.Services.ReplaceConfiguration(ConfigurationHelper.BuildConfiguration(configurationOptions)); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Services.GetConfiguration(); |
|||
Configure<AbpBlobStoringOptions>(options => |
|||
{ |
|||
options.Containers.ConfigureAll((containerName, containerConfiguration) => |
|||
{ |
|||
containerConfiguration.UseNexus(nexus => |
|||
{ |
|||
nexus.BasePath = configuration[NexusBlobProviderConfigurationNames.BasePath]; |
|||
nexus.Repository = configuration[NexusBlobProviderConfigurationNames.Repository]; |
|||
}); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using Volo.Abp.BlobStoring; |
|||
|
|||
namespace LINGYUN.Abp.BlobStoring.Nexus; |
|||
public class NexusBlobContainer_Tests : BlobContainer_Tests<AbpBlobStoringNexusTestModule> |
|||
{ |
|||
public NexusBlobContainer_Tests() |
|||
{ |
|||
|
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,160 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Shouldly; |
|||
using Volo.Abp.BlobStoring.TestObjects; |
|||
using Volo.Abp.Clients; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Testing; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.BlobStoring; |
|||
|
|||
public abstract class BlobContainer_Tests<TStartupModule> : AbpIntegratedTest<TStartupModule> |
|||
where TStartupModule : IAbpModule |
|||
{ |
|||
protected IBlobContainer<TestContainer1> Container { get; } |
|||
|
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
|
|||
protected BlobContainer_Tests() |
|||
{ |
|||
Container = GetRequiredService<IBlobContainer<TestContainer1>>(); |
|||
CurrentTenant = GetRequiredService<ICurrentTenant>(); |
|||
} |
|||
|
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("Should_Save_And_Get_Blobs")] |
|||
[InlineData("Should_Save_And_Get_Blobs.txt")] |
|||
[InlineData("test-folder/Should_Save_And_Get_Blobs")] |
|||
public async Task Should_Save_And_Get_Blobs(string blobName) |
|||
{ |
|||
var testContent = "test content".GetBytes(); |
|||
await Container.SaveAsync(blobName, testContent); |
|||
await Task.Delay(3000); |
|||
var result = await Container.GetAllBytesAsync(blobName); |
|||
result.SequenceEqual(testContent).ShouldBeTrue(); |
|||
await Container.DeleteAsync(blobName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Save_And_Get_Blobs_In_Different_Tenant() |
|||
{ |
|||
var blobName = "Should_Save_And_Get_Blobs_In_Different_Tenant"; |
|||
var testContent = "test content".GetBytes(); |
|||
|
|||
using (CurrentTenant.Change(Guid.NewGuid())) |
|||
{ |
|||
await Container.SaveAsync(blobName, testContent); |
|||
await Task.Delay(2000); |
|||
(await Container.GetAllBytesAsync(blobName)).SequenceEqual(testContent).ShouldBeTrue(); |
|||
await Container.DeleteAsync(blobName); |
|||
await Task.Delay(2000); |
|||
} |
|||
|
|||
using (CurrentTenant.Change(Guid.NewGuid())) |
|||
{ |
|||
await Container.SaveAsync(blobName, testContent); |
|||
await Task.Delay(2000); |
|||
(await Container.GetAllBytesAsync(blobName)).SequenceEqual(testContent).ShouldBeTrue(); |
|||
|
|||
using (CurrentTenant.Change(null)) |
|||
{ |
|||
// Could not found the requested BLOB...
|
|||
await Assert.ThrowsAsync<AbpException>(async () => |
|||
await Container.GetAllBytesAsync(blobName) |
|||
); |
|||
} |
|||
|
|||
await Container.DeleteAsync(blobName); |
|||
await Task.Delay(2000); |
|||
} |
|||
|
|||
using (CurrentTenant.Change(null)) |
|||
{ |
|||
await Container.SaveAsync(blobName, testContent); |
|||
await Task.Delay(2000); |
|||
(await Container.GetAllBytesAsync(blobName)).SequenceEqual(testContent).ShouldBeTrue(); |
|||
await Container.DeleteAsync(blobName); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Overwrite_Pre_Saved_Blob_If_Requested() |
|||
{ |
|||
var blobName = "Should_Overwrite_Pre_Saved_Blob_If_Requested"; |
|||
|
|||
var testContent = "test content".GetBytes(); |
|||
await Container.SaveAsync(blobName, testContent); |
|||
await Task.Delay(2000); |
|||
var testContentOverwritten = "test content overwritten".GetBytes(); |
|||
await Container.SaveAsync(blobName, testContentOverwritten, true); |
|||
await Task.Delay(2000); |
|||
var result = await Container.GetAllBytesAsync(blobName); |
|||
result.SequenceEqual(testContentOverwritten).ShouldBeTrue(); |
|||
await Container.DeleteAsync(blobName); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Not_Allow_To_Overwrite_Pre_Saved_Blob_By_Default() |
|||
{ |
|||
var blobName = "Should_Not_Allow_To_Overwrite_Pre_Saved_Blob_By_Default"; |
|||
|
|||
var testContent = "test content".GetBytes(); |
|||
await Container.SaveAsync(blobName, testContent); |
|||
await Task.Delay(2000); |
|||
var testContentOverwritten = "test content overwritten".GetBytes(); |
|||
await Assert.ThrowsAsync<BlobAlreadyExistsException>(() => |
|||
Container.SaveAsync(blobName, testContentOverwritten) |
|||
); |
|||
|
|||
await Container.DeleteAsync(blobName); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("Should_Delete_Saved_Blobs")] |
|||
[InlineData("Should_Delete_Saved_Blobs.txt")] |
|||
[InlineData("test-folder/Should_Delete_Saved_Blobs")] |
|||
public async Task Should_Delete_Saved_Blobs(string blobName) |
|||
{ |
|||
await Container.SaveAsync(blobName, "test content".GetBytes()); |
|||
await Task.Delay(2000); |
|||
(await Container.GetAllBytesAsync(blobName)).ShouldNotBeNull(); |
|||
|
|||
await Container.DeleteAsync(blobName); |
|||
await Task.Delay(2000); |
|||
(await Container.GetAllBytesOrNullAsync(blobName)).ShouldBeNull(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("Saved_Blobs_Should_Exists")] |
|||
[InlineData("Saved_Blobs_Should_Exists.txt")] |
|||
[InlineData("test-folder/Saved_Blobs_Should_Exists")] |
|||
public async Task Saved_Blobs_Should_Exists(string blobName) |
|||
{ |
|||
await Container.SaveAsync(blobName, "test content".GetBytes()); |
|||
await Task.Delay(2000); |
|||
(await Container.ExistsAsync(blobName)).ShouldBeTrue(); |
|||
|
|||
await Container.DeleteAsync(blobName); |
|||
await Task.Delay(2000); |
|||
(await Container.ExistsAsync(blobName)).ShouldBeFalse(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("Unknown_Blobs_Should_Not_Exists")] |
|||
[InlineData("Unknown_Blobs_Should_Not_Exists.txt")] |
|||
[InlineData("test-folder/Unknown_Blobs_Should_Not_Exists")] |
|||
public async Task Unknown_Blobs_Should_Not_Exists(string blobName) |
|||
{ |
|||
await Container.DeleteAsync(blobName); |
|||
await Task.Delay(2000); |
|||
(await Container.ExistsAsync(blobName)).ShouldBeFalse(); |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Volo.Abp.BlobStoring.TestObjects; |
|||
|
|||
public class TestContainer1 |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.BlobStoring.TestObjects; |
|||
|
|||
[BlobContainerName("Test2")] |
|||
public class TestContainer2 |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Volo.Abp.BlobStoring.TestObjects; |
|||
|
|||
public class TestContainer3 |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1 @@ |
|||
global using Xunit; |
|||
@ -0,0 +1,27 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net7.0</TargetFramework> |
|||
<RootNamespace /> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" /> |
|||
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" /> |
|||
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitPackageVersion)"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
<PackageReference Include="coverlet.collector" Version="$(CoverletCollectorPackageVersion)"> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
</PackageReference> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\modules\nexus\LINGYUN.Abp.OssManagement.Nexus\LINGYUN.Abp.OssManagement.Nexus.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.BlobStoring.Nexus.Tests\LINGYUN.Abp.BlobStoring.Nexus.Tests.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,6 @@ |
|||
using LINGYUN.Abp.Tests; |
|||
|
|||
namespace LINGYUN.Abp.OssManagement.Nexus; |
|||
public abstract class AbpOssManagementNexusTestsBase : AbpTestsBase<AbpOssManagementNexusTestsModule> |
|||
{ |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using LINGYUN.Abp.BlobStoring.Nexus; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.OssManagement.Nexus; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpOssManagementNexusModule), |
|||
typeof(AbpBlobStoringNexusTestModule))] |
|||
public class AbpOssManagementNexusTestsModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using Shouldly; |
|||
using System.IO; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.OssManagement.Nexus; |
|||
public class NexusOssContainerFactoryTests : AbpOssManagementNexusTestsBase |
|||
{ |
|||
protected IOssContainerFactory OssContainerFactory { get; } |
|||
|
|||
public NexusOssContainerFactoryTests() |
|||
{ |
|||
OssContainerFactory = GetRequiredService<IOssContainerFactory>(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("/test-repo")] |
|||
public async virtual Task CreateAsync(string containerName) |
|||
{ |
|||
var ossContainer = OssContainerFactory.Create(); |
|||
|
|||
await ossContainer.CreateAsync(containerName); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData("/test-repo", "CreateObjectAsync", "/aaa/bbb/ccc")] |
|||
public async virtual Task CreateObjectAsync(string containerName, string objectName, string path = null) |
|||
{ |
|||
var textBytes = Encoding.UTF8.GetBytes("CreateObjectAsync"); |
|||
using var stream = new MemoryStream(); |
|||
await stream.WriteAsync(textBytes, 0, textBytes.Length); |
|||
|
|||
var ossContainer = OssContainerFactory.Create(); |
|||
var createObjectRequest = new CreateOssObjectRequest( |
|||
containerName, |
|||
objectName, |
|||
stream, |
|||
path); |
|||
|
|||
var oss = await ossContainer.CreateObjectAsync(createObjectRequest); |
|||
|
|||
oss.ShouldNotBeNull(); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue