committed by
GitHub
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