From ad2176e5d573ebb1347b6ebbe90afd2add33db8e Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 11 Oct 2023 10:25:42 +0800 Subject: [PATCH] feat(oss): add Sonatype Nexus integration. --- aspnet-core/LINGYUN.MicroService.All.sln | 38 ++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++ .../LINGYUN.Abp.BlobStoring.Nexus.csproj | 20 ++ .../Nexus/AbpBlobStoringNexusModule.cs | 12 + .../Nexus/DefaultBlobRawPathCalculator.cs | 70 ++++ .../Nexus/IBlobRawPathCalculator.cs | 9 + ...xusBlobContainerConfigurationExtensions.cs | 26 ++ .../Nexus/NexusBlobNamingNormalizer.cs | 21 ++ .../BlobStoring/Nexus/NexusBlobProvider.cs | 114 ++++++ .../Nexus/NexusBlobProviderConfiguration.cs | 28 ++ .../NexusBlobProviderConfigurationNames.cs | 16 + .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++ .../LINGYUN.Abp.OssManagement.Nexus.csproj | 16 + .../Nexus/AbpOssManagementNexusModule.cs | 23 ++ .../OssManagement/Nexus/NexusOssContainer.cs | 333 ++++++++++++++++++ .../Nexus/NexusOssContainerFactory.cs | 49 +++ .../System/IO/SystemExtensions.cs | 25 ++ .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++ .../LINGYUN.Abp.Sonatype.Nexus.csproj | 16 + .../Sonatype/Nexus/AbpSonatypeNexusModule.cs | 28 ++ .../Sonatype/Nexus/AbpSonatypeNexusOptions.cs | 13 + .../Nexus/Assets/INexusAssetManager.cs | 26 ++ .../Abp/Sonatype/Nexus/Assets/NexusAsset.cs | 34 ++ .../Nexus/Assets/NexusAssetListResult.cs | 15 + .../Nexus/Assets/NexusAssetManager.cs | 98 ++++++ .../Abp/Sonatype/Nexus/Components/Asset.cs | 11 + .../Components/INexusComponentManager.cs | 25 ++ .../Nexus/Components/NexusComponent.cs | 36 ++ .../Components/NexusComponentListResult.cs | 20 ++ .../Nexus/Components/NexusComponentManager.cs | 107 ++++++ .../Components/NexusComponentUploadArgs.cs | 16 + .../Components/NexusRawBlobUploadArgs.cs | 80 +++++ .../Repositories/INexusRepositoryManager.cs | 45 +++ .../Nexus/Repositories/NexusRepository.cs | 14 + .../Repositories/NexusRepositoryCreateArgs.cs | 14 + .../Repositories/NexusRepositoryListResult.cs | 29 ++ .../Repositories/NexusRepositoryUpdateArgs.cs | 14 + .../Raw/INexusRawRepositoryManager.cs | 9 + .../Repositories/Raw/NexusRawRepository.cs | 9 + .../Nexus/Repositories/Raw/RawGroup.cs | 17 + .../Nexus/Repositories/Raw/RawStorage.cs | 17 + .../Nexus/Search/INexusLookupService.cs | 13 + .../Nexus/Search/NexusLookupService.cs | 97 +++++ .../Sonatype/Nexus/Search/NexusSearchArgs.cs | 29 ++ .../Services/CoreUI/Assets/CoreUIAsset.cs | 16 + .../Services/CoreUI/Assets/CoreUIAssetData.cs | 54 +++ .../Services/CoreUI/Assets/CoreUIAssetRead.cs | 13 + .../CoreUI/Assets/CoreUIAssetResult.cs | 14 + .../Services/CoreUI/Browsers/CoreUIBrowse.cs | 16 + .../CoreUI/Browsers/CoreUIBrowseComponent.cs | 29 ++ .../Browsers/CoreUIBrowseComponentResult.cs | 20 ++ .../CoreUI/Browsers/CoreUIBrowseNode.cs | 19 + .../Browsers/CoreUIBrowseReadComponent.cs | 10 + .../Nexus/Services/CoreUI/CoreUIRequest.cs | 35 ++ .../Nexus/Services/CoreUI/CoreUIResponse.cs | 23 ++ .../Services/CoreUI/CoreUiServiceProxy.cs | 55 +++ .../Services/CoreUI/ICoreUiServiceProxy.cs | 9 + .../Nexus/Services/INexusServiceProxy.cs | 5 + .../Abp/Sonatype/Nexus/SonatypeNexusConsts.cs | 5 + .../GlobalUsings.cs | 2 + ...LINGYUN.Abp.BlobStoring.Nexus.Tests.csproj | 27 ++ .../Nexus/AbpBlobStoringNexusTestBase.cs | 6 + .../Nexus/AbpBlobStoringNexusTestModule.cs | 41 +++ .../Nexus/NexusBlobContainer_Tests.cs | 11 + .../Abp/BlobStoring/BlobContainer_Tests.cs | 160 +++++++++ .../BlobStoring/TestObjects/TestContainer1.cs | 6 + .../BlobStoring/TestObjects/TestContainer2.cs | 7 + .../BlobStoring/TestObjects/TestContainer3.cs | 6 + .../GlobalUsings.cs | 1 + ...NGYUN.Abp.OssManagement.Nexus.Tests.csproj | 27 ++ .../Nexus/AbpOssManagementNexusTestsBase.cs | 6 + .../Nexus/AbpOssManagementNexusTestsModule.cs | 12 + .../Nexus/NexusOssContainerFactoryTests.cs | 44 +++ 76 files changed, 2410 insertions(+) create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/FodyWeavers.xml create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/FodyWeavers.xsd create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN.Abp.BlobStoring.Nexus.csproj create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/AbpBlobStoringNexusModule.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/DefaultBlobRawPathCalculator.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/IBlobRawPathCalculator.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobContainerConfigurationExtensions.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobNamingNormalizer.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobProvider.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobProviderConfiguration.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobProviderConfigurationNames.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/FodyWeavers.xml create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/FodyWeavers.xsd create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN.Abp.OssManagement.Nexus.csproj create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN/Abp/OssManagement/Nexus/AbpOssManagementNexusModule.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN/Abp/OssManagement/Nexus/NexusOssContainer.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN/Abp/OssManagement/Nexus/NexusOssContainerFactory.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/System/IO/SystemExtensions.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/FodyWeavers.xml create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/FodyWeavers.xsd create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN.Abp.Sonatype.Nexus.csproj create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/AbpSonatypeNexusModule.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/AbpSonatypeNexusOptions.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/INexusAssetManager.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/NexusAsset.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/NexusAssetListResult.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/NexusAssetManager.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/Asset.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/INexusComponentManager.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponent.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponentListResult.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponentManager.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponentUploadArgs.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusRawBlobUploadArgs.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/INexusRepositoryManager.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepository.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepositoryCreateArgs.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepositoryListResult.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepositoryUpdateArgs.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/INexusRawRepositoryManager.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/NexusRawRepository.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/RawGroup.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/RawStorage.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Search/INexusLookupService.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Search/NexusLookupService.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Search/NexusSearchArgs.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAsset.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAssetData.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAssetRead.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAssetResult.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowse.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseComponent.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseComponentResult.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseNode.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseReadComponent.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/CoreUIRequest.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/CoreUIResponse.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/CoreUiServiceProxy.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/ICoreUiServiceProxy.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/INexusServiceProxy.cs create mode 100644 aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/SonatypeNexusConsts.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/GlobalUsings.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN.Abp.BlobStoring.Nexus.Tests.csproj create mode 100644 aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN/Abp/BlobStoring/Nexus/AbpBlobStoringNexusTestBase.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN/Abp/BlobStoring/Nexus/AbpBlobStoringNexusTestModule.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobContainer_Tests.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/BlobContainer_Tests.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer1.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer2.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer3.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/GlobalUsings.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN.Abp.OssManagement.Nexus.Tests.csproj create mode 100644 aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN/Abp/OssManagement/Nexus/AbpOssManagementNexusTestsBase.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN/Abp/OssManagement/Nexus/AbpOssManagementNexusTestsModule.cs create mode 100644 aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN/Abp/OssManagement/Nexus/NexusOssContainerFactoryTests.cs diff --git a/aspnet-core/LINGYUN.MicroService.All.sln b/aspnet-core/LINGYUN.MicroService.All.sln index cf72d5b24..410a5913b 100644 --- a/aspnet-core/LINGYUN.MicroService.All.sln +++ b/aspnet-core/LINGYUN.MicroService.All.sln @@ -668,6 +668,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Notifications.W EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.Identity.WeChat.Work", "modules\wechat\LINGYUN.Abp.Identity.WeChat.Work\LINGYUN.Abp.Identity.WeChat.Work.csproj", "{3E32DBDA-1C63-42B4-85D1-E84BBD072D89}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nexus", "nexus", "{87CE2F0B-0469-4C76-B325-00EA7AB94B99}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.BlobStoring.Nexus", "modules\nexus\LINGYUN.Abp.BlobStoring.Nexus\LINGYUN.Abp.BlobStoring.Nexus.csproj", "{34987F45-8234-428C-AB41-783D42295C32}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.BlobStoring.Nexus.Tests", "tests\LINGYUN.Abp.BlobStoring.Nexus.Tests\LINGYUN.Abp.BlobStoring.Nexus.Tests.csproj", "{227DA969-291B-4749-985C-7A83523B7F53}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.OssManagement.Nexus", "modules\nexus\LINGYUN.Abp.OssManagement.Nexus\LINGYUN.Abp.OssManagement.Nexus.csproj", "{A35E90D3-5375-4BFF-B6E2-24A8CDCBF973}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Sonatype.Nexus", "modules\nexus\LINGYUN.Abp.Sonatype.Nexus\LINGYUN.Abp.Sonatype.Nexus.csproj", "{CDE35EAE-4B48-40D2-BF57-68EFC5B5A97C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.OssManagement.Nexus.Tests", "tests\LINGYUN.Abp.OssManagement.Nexus.Tests\LINGYUN.Abp.OssManagement.Nexus.Tests.csproj", "{F197F8BB-87E3-43FC-92E7-DE8E60EB22E9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1718,6 +1730,26 @@ Global {3E32DBDA-1C63-42B4-85D1-E84BBD072D89}.Debug|Any CPU.Build.0 = Debug|Any CPU {3E32DBDA-1C63-42B4-85D1-E84BBD072D89}.Release|Any CPU.ActiveCfg = Release|Any CPU {3E32DBDA-1C63-42B4-85D1-E84BBD072D89}.Release|Any CPU.Build.0 = Release|Any CPU + {34987F45-8234-428C-AB41-783D42295C32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {34987F45-8234-428C-AB41-783D42295C32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34987F45-8234-428C-AB41-783D42295C32}.Release|Any CPU.ActiveCfg = Release|Any CPU + {34987F45-8234-428C-AB41-783D42295C32}.Release|Any CPU.Build.0 = Release|Any CPU + {227DA969-291B-4749-985C-7A83523B7F53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {227DA969-291B-4749-985C-7A83523B7F53}.Debug|Any CPU.Build.0 = Debug|Any CPU + {227DA969-291B-4749-985C-7A83523B7F53}.Release|Any CPU.ActiveCfg = Release|Any CPU + {227DA969-291B-4749-985C-7A83523B7F53}.Release|Any CPU.Build.0 = Release|Any CPU + {A35E90D3-5375-4BFF-B6E2-24A8CDCBF973}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A35E90D3-5375-4BFF-B6E2-24A8CDCBF973}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A35E90D3-5375-4BFF-B6E2-24A8CDCBF973}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A35E90D3-5375-4BFF-B6E2-24A8CDCBF973}.Release|Any CPU.Build.0 = Release|Any CPU + {CDE35EAE-4B48-40D2-BF57-68EFC5B5A97C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CDE35EAE-4B48-40D2-BF57-68EFC5B5A97C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDE35EAE-4B48-40D2-BF57-68EFC5B5A97C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CDE35EAE-4B48-40D2-BF57-68EFC5B5A97C}.Release|Any CPU.Build.0 = Release|Any CPU + {F197F8BB-87E3-43FC-92E7-DE8E60EB22E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F197F8BB-87E3-43FC-92E7-DE8E60EB22E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F197F8BB-87E3-43FC-92E7-DE8E60EB22E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F197F8BB-87E3-43FC-92E7-DE8E60EB22E9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2041,6 +2073,12 @@ Global {2C86306D-D626-41F8-BA3C-5C9B4123CE7D} = {83E698F6-F8CD-4604-AB80-01A203389501} {2DC43D15-F20F-44EC-B3A3-47BD8BBB50CA} = {DD9BE9E7-F6BF-4869-BCD2-82F5072BDA21} {3E32DBDA-1C63-42B4-85D1-E84BBD072D89} = {DD9BE9E7-F6BF-4869-BCD2-82F5072BDA21} + {87CE2F0B-0469-4C76-B325-00EA7AB94B99} = {C5CAD011-DF84-4914-939C-0C029DCEF26F} + {34987F45-8234-428C-AB41-783D42295C32} = {87CE2F0B-0469-4C76-B325-00EA7AB94B99} + {227DA969-291B-4749-985C-7A83523B7F53} = {370D7CD5-1E17-4F3D-BBFA-03429F6D4F2F} + {A35E90D3-5375-4BFF-B6E2-24A8CDCBF973} = {B05CB08F-C088-4D6D-97EE-A94A5D1AE4A6} + {CDE35EAE-4B48-40D2-BF57-68EFC5B5A97C} = {87CE2F0B-0469-4C76-B325-00EA7AB94B99} + {F197F8BB-87E3-43FC-92E7-DE8E60EB22E9} = {370D7CD5-1E17-4F3D-BBFA-03429F6D4F2F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/FodyWeavers.xml b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/FodyWeavers.xsd b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN.Abp.BlobStoring.Nexus.csproj b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN.Abp.BlobStoring.Nexus.csproj new file mode 100644 index 000000000..65dc76e53 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN.Abp.BlobStoring.Nexus.csproj @@ -0,0 +1,20 @@ + + + + + + + netstandard2.0 + + Oss对象存储Nexus集成 + + + + + + + + + + + diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/AbpBlobStoringNexusModule.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/AbpBlobStoringNexusModule.cs new file mode 100644 index 000000000..f1f868445 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/AbpBlobStoringNexusModule.cs @@ -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 +{ +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/DefaultBlobRawPathCalculator.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/DefaultBlobRawPathCalculator.cs new file mode 100644 index 000000000..872931bd1 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/DefaultBlobRawPathCalculator.cs @@ -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(); + 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('/'); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/IBlobRawPathCalculator.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/IBlobRawPathCalculator.cs new file mode 100644 index 000000000..9ce0ba27e --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/IBlobRawPathCalculator.cs @@ -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); +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobContainerConfigurationExtensions.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobContainerConfigurationExtensions.cs new file mode 100644 index 000000000..40670bc0b --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobContainerConfigurationExtensions.cs @@ -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 nexusConfigureAction) + { + containerConfiguration.ProviderType = typeof(NexusBlobProvider); + containerConfiguration.NamingNormalizers.TryAdd(); + + nexusConfigureAction(new NexusBlobProviderConfiguration(containerConfiguration)); + + return containerConfiguration; + } + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobNamingNormalizer.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobNamingNormalizer.cs new file mode 100644 index 000000000..8c2f91131 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobNamingNormalizer.cs @@ -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("//", "/"); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobProvider.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobProvider.cs new file mode 100644 index 000000000..3f40842a0 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobProvider.cs @@ -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 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 ExistsAsync(BlobProviderExistsArgs args) + { + var nexusAsset = await GetNexusAssetOrNull(args); + return nexusAsset != null; + } + + public async override Task 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 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 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; + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobProviderConfiguration.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobProviderConfiguration.cs new file mode 100644 index 000000000..e1f298190 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobProviderConfiguration.cs @@ -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(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(NexusBlobProviderConfigurationNames.Repository); + set => _containerConfiguration.SetConfiguration(NexusBlobProviderConfigurationNames.Repository, Check.NotNullOrWhiteSpace(value, nameof(value))); + } + + private readonly BlobContainerConfiguration _containerConfiguration; + + public NexusBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) + { + _containerConfiguration = containerConfiguration; + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobProviderConfigurationNames.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobProviderConfigurationNames.cs new file mode 100644 index 000000000..641d5580c --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.BlobStoring.Nexus/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobProviderConfigurationNames.cs @@ -0,0 +1,16 @@ +namespace LINGYUN.Abp.BlobStoring.Nexus; +public static class NexusBlobProviderConfigurationNames +{ + /// + /// 基础路径 + /// + public const string BasePath = "Sonatype:Nexus:Raw:BasePath"; + /// + /// 添加容器名称到基础路径 + /// + public const string AppendContainerNameToBasePath = "Sonatype:Nexus:Raw:AppendContainerNameToBasePath"; + /// + /// Nexus raw仓库名称 + /// + public const string Repository = "Sonatype:Nexus:Raw:Repository"; +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/FodyWeavers.xml b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/FodyWeavers.xml new file mode 100644 index 000000000..00e1d9a1c --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/FodyWeavers.xsd b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN.Abp.OssManagement.Nexus.csproj b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN.Abp.OssManagement.Nexus.csproj new file mode 100644 index 000000000..c4aa8db10 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN.Abp.OssManagement.Nexus.csproj @@ -0,0 +1,16 @@ + + + + + + + netstandard2.0 + + + + + + + + + diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN/Abp/OssManagement/Nexus/AbpOssManagementNexusModule.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN/Abp/OssManagement/Nexus/AbpOssManagementNexusModule.cs new file mode 100644 index 000000000..90584bfd2 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN/Abp/OssManagement/Nexus/AbpOssManagementNexusModule.cs @@ -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(); + + context.Services.AddTransient(provider => + provider + .GetRequiredService() + .Create() + .As()); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN/Abp/OssManagement/Nexus/NexusOssContainer.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN/Abp/OssManagement/Nexus/NexusOssContainer.cs new file mode 100644 index 000000000..49b845e7c --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN/Abp/OssManagement/Nexus/NexusOssContainer.cs @@ -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 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 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 ExistsAsync(string name) + { + throw new NotImplementedException(); + } + + public Task ExpireAsync(ExprieOssObjectRequest request) + { + throw new NotImplementedException(); + } + + public async virtual Task 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 GetListAsync(GetOssContainersRequest request) + { + var nexusConfiguration = GetNexusConfiguration(); + var blobPath = request.Prefix.RemovePreFix(".").RemovePreFix("/"); + var readComponent = CoreUIBrowse.Read(nexusConfiguration.Repository, blobPath); + + var coreUIResponse = await CoreUiServiceProxy.SearchAsync(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())) + .ToList()); + } + + public async virtual Task 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(readAsset); + var checksum = coreUIResponse.Result.Data.Attributes.GetOrDefault("checksum"); + var metadata = new Dictionary(); + 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 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(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(), + component.Type == "folder") + { + FullName = component.Id + }) + .ToList()); + + return response; + } + + protected virtual NexusBlobProviderConfiguration GetNexusConfiguration() + { + var configuration = ConfigurationProvider.Get(); + 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]); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN/Abp/OssManagement/Nexus/NexusOssContainerFactory.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN/Abp/OssManagement/Nexus/NexusOssContainerFactory.cs new file mode 100644 index 000000000..de29b88a5 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/LINGYUN/Abp/OssManagement/Nexus/NexusOssContainerFactory.cs @@ -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); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/System/IO/SystemExtensions.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/System/IO/SystemExtensions.cs new file mode 100644 index 000000000..5a28f21d1 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.OssManagement.Nexus/System/IO/SystemExtensions.cs @@ -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(); + } + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/FodyWeavers.xml b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/FodyWeavers.xsd b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN.Abp.Sonatype.Nexus.csproj b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN.Abp.Sonatype.Nexus.csproj new file mode 100644 index 000000000..e0e5d8f12 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN.Abp.Sonatype.Nexus.csproj @@ -0,0 +1,16 @@ + + + + + + + netstandard2.0 + + + + + + + + + diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/AbpSonatypeNexusModule.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/AbpSonatypeNexusModule.cs new file mode 100644 index 000000000..05b1899c4 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/AbpSonatypeNexusModule.cs @@ -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(configuration.GetSection("Sonatype:Nexus")); + + context.Services.AddHttpClient( + SonatypeNexusConsts.ApiClient, + (serviceProvider, client) => + { + var options = serviceProvider.GetRequiredService>().Value; + client.BaseAddress = new Uri(options.BaseUrl); + client.DefaultRequestHeaders.Add("X-Nexus-Ui", "true"); + client.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest"); + }); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/AbpSonatypeNexusOptions.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/AbpSonatypeNexusOptions.cs new file mode 100644 index 000000000..f07f72ec7 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/AbpSonatypeNexusOptions.cs @@ -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"; + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/INexusAssetManager.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/INexusAssetManager.cs new file mode 100644 index 000000000..f3ea41370 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/INexusAssetManager.cs @@ -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 ListAsync( + [NotNull] string repository, + string continuationToken = null, + CancellationToken cancellationToken = default); + + Task GetAsync( + [NotNull] string id, + CancellationToken cancellationToken = default); + + Task DeleteAsync( + [NotNull] string id, + CancellationToken cancellationToken = default); + + Task GetContentOrNullAsync( + [NotNull] NexusAsset asset, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/NexusAsset.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/NexusAsset.cs new file mode 100644 index 000000000..230894162 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/NexusAsset.cs @@ -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 Checksum { get; set; } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/NexusAssetListResult.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/NexusAssetListResult.cs new file mode 100644 index 000000000..0632471b6 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/NexusAssetListResult.cs @@ -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 Items { get; set; } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/NexusAssetManager.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/NexusAssetManager.cs new file mode 100644 index 000000000..dc6fe331c --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Assets/NexusAssetManager.cs @@ -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 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 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(responseContent); + + return nexusAsset; + } + + public async virtual Task 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 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(responseContent); + + return nexusAssetListResult; + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/Asset.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/Asset.cs new file mode 100644 index 000000000..417013b20 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/Asset.cs @@ -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; + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/INexusComponentManager.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/INexusComponentManager.cs new file mode 100644 index 000000000..1802676ad --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/INexusComponentManager.cs @@ -0,0 +1,25 @@ +using JetBrains.Annotations; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Sonatype.Nexus.Components; + +public interface INexusComponentManager +{ + Task ListAsync( + [NotNull] string repository, + string continuationToken = null, + CancellationToken cancellationToken = default); + + Task GetAsync( + [NotNull] string id, + CancellationToken cancellationToken = default); + + Task DeleteAsync( + [NotNull] string id, + CancellationToken cancellationToken = default); + + Task UploadAsync( + [NotNull] NexusComponentUploadArgs args, + CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponent.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponent.cs new file mode 100644 index 000000000..af0c54213 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponent.cs @@ -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 Assets { get; set; } + + public NexusComponent() + { + Assets = new List(); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponentListResult.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponentListResult.cs new file mode 100644 index 000000000..4186d6107 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponentListResult.cs @@ -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 Items { get; set; } + + public NexusComponentListResult() + { + Items = new List(); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponentManager.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponentManager.cs new file mode 100644 index 000000000..3a91593be --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponentManager.cs @@ -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 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 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 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(responseContent); + + return nexusComponent; + } + + public async virtual Task 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(responseContent); + + return nexusComponentListResult; + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponentUploadArgs.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponentUploadArgs.cs new file mode 100644 index 000000000..3d8ebb348 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusComponentUploadArgs.cs @@ -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(); +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusRawBlobUploadArgs.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusRawBlobUploadArgs.cs new file mode 100644 index 000000000..1b781d92c --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Components/NexusRawBlobUploadArgs.cs @@ -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; + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/INexusRepositoryManager.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/INexusRepositoryManager.cs new file mode 100644 index 000000000..7904314f1 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/INexusRepositoryManager.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Sonatype.Nexus.Repositories; + +public interface INexusRepositoryManager + where TRepository : NexusRepository + where TRepositoryCreateArgs : NexusRepositoryCreateArgs + where TRepositoryUpdateArgs: NexusRepositoryUpdateArgs +{ + Task CreateAsync(TRepositoryCreateArgs args, CancellationToken cancellationToken = default); + + Task GetAsync(string name, CancellationToken cancellationToken = default); + + Task UpdateAsync(string name, TRepositoryUpdateArgs args, CancellationToken cancellationToken = default); + + Task DeleteAsync(string name, CancellationToken cancellationToken = default); + + Task> ListAsync(CancellationToken cancellationToken = default); +} + +public interface INexusRepositoryManager + where TRepository : NexusRepository + where TRepositoryCreateArgs : NexusRepositoryCreateArgs +{ + Task CreateAsync(TRepositoryCreateArgs args, CancellationToken cancellationToken = default); + + Task GetAsync(string name, CancellationToken cancellationToken = default); + + Task DeleteAsync(string name, CancellationToken cancellationToken = default); + + Task> ListAsync(CancellationToken cancellationToken = default); +} + + +public interface INexusRepositoryManager + where TRepository : NexusRepository +{ + Task GetAsync(string name, CancellationToken cancellationToken = default); + + Task DeleteAsync(string name, CancellationToken cancellationToken = default); + + Task> ListAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepository.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepository.cs new file mode 100644 index 000000000..f04fd533b --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepository.cs @@ -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; } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepositoryCreateArgs.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepositoryCreateArgs.cs new file mode 100644 index 000000000..31d680af3 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepositoryCreateArgs.cs @@ -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; } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepositoryListResult.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepositoryListResult.cs new file mode 100644 index 000000000..9ca93ebf6 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepositoryListResult.cs @@ -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 Attributes { get; set; } + + public NexusRepositoryListResult() + { + Attributes = new Dictionary(); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepositoryUpdateArgs.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepositoryUpdateArgs.cs new file mode 100644 index 000000000..46b188b7a --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/NexusRepositoryUpdateArgs.cs @@ -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; } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/INexusRawRepositoryManager.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/INexusRawRepositoryManager.cs new file mode 100644 index 000000000..a6fbae003 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/INexusRawRepositoryManager.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace LINGYUN.Abp.Sonatype.Nexus.Repositories.Raw; + +public interface INexusRawRepositoryManager +{ +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/NexusRawRepository.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/NexusRawRepository.cs new file mode 100644 index 000000000..b16666972 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/NexusRawRepository.cs @@ -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; } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/RawGroup.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/RawGroup.cs new file mode 100644 index 000000000..b556153df --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/RawGroup.cs @@ -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 MemberNames { get; set; } + + public RawGroup() + { + MemberNames = new List(); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/RawStorage.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/RawStorage.cs new file mode 100644 index 000000000..5c1aefb6b --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Repositories/Raw/RawStorage.cs @@ -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; } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Search/INexusLookupService.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Search/INexusLookupService.cs new file mode 100644 index 000000000..86d8aa531 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Search/INexusLookupService.cs @@ -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 ListComponentAsync(NexusSearchArgs args, CancellationToken cancellationToken = default); + + Task ListAssetAsync(NexusSearchArgs args, CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Search/NexusLookupService.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Search/NexusLookupService.cs new file mode 100644 index 000000000..efa046f18 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Search/NexusLookupService.cs @@ -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 options) + { + Options = options.Value; + JsonSerializer = jsonSerializer; + HttpClientFactory = httpClientFactory; + } + + public async virtual Task 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(responseContent); + + return nexusAssetListResult; + } + + public async virtual Task 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(responseContent); + + return nexusComponentListResult; + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Search/NexusSearchArgs.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Search/NexusSearchArgs.cs new file mode 100644 index 000000000..7593da112 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Search/NexusSearchArgs.cs @@ -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; + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAsset.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAsset.cs new file mode 100644 index 000000000..8742588ea --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAsset.cs @@ -0,0 +1,16 @@ +namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Assets; + +public static class CoreUIAsset +{ + public static CoreUIRequest Read( + string assetId, + string repository) + { + var asset = new CoreUIAssetRead(assetId, repository); + + return new CoreUIRequest( + "coreui_Component", + "readAsset", + asset); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAssetData.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAssetData.cs new file mode 100644 index 000000000..265c8cce3 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAssetData.cs @@ -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> Attributes { get; set; } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAssetRead.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAssetRead.cs new file mode 100644 index 000000000..5ff7225e6 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAssetRead.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Assets; +public class CoreUIAssetRead : List +{ + public CoreUIAssetRead( + string assetId, + string repository) + { + Add(assetId); + Add(repository); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAssetResult.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAssetResult.cs new file mode 100644 index 000000000..1c9fdaa4b --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Assets/CoreUIAssetResult.cs @@ -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; } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowse.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowse.cs new file mode 100644 index 000000000..487b8f20b --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowse.cs @@ -0,0 +1,16 @@ +namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Browsers; + +public static class CoreUIBrowse +{ + public static CoreUIRequest Read( + string repository, + string node = "/") + { + var readComponent = new CoreUIBrowseReadComponent(repository, node); + + return new CoreUIRequest( + "coreui_Browse", + "read", + readComponent); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseComponent.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseComponent.cs new file mode 100644 index 000000000..51dfce235 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseComponent.cs @@ -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; } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseComponentResult.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseComponentResult.cs new file mode 100644 index 000000000..9da2a1813 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseComponentResult.cs @@ -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 Data { get; set; } + + public CoreUIBrowseComponentResult() + { + Data = new List(); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseNode.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseNode.cs new file mode 100644 index 000000000..180a99c50 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseNode.cs @@ -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; + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseReadComponent.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseReadComponent.cs new file mode 100644 index 000000000..5910d61c6 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/Browsers/CoreUIBrowseReadComponent.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI.Browsers; +public class CoreUIBrowseReadComponent : List +{ + public CoreUIBrowseReadComponent(string repository, string node = "/") + { + Add(new CoreUIBrowseNode(repository, node)); + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/CoreUIRequest.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/CoreUIRequest.cs new file mode 100644 index 000000000..250d51eb8 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/CoreUIRequest.cs @@ -0,0 +1,35 @@ +using System; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI; + +[Serializable] +public class CoreUIRequest +{ + [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; + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/CoreUIResponse.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/CoreUIResponse.cs new file mode 100644 index 000000000..dc7d64e60 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/CoreUIResponse.cs @@ -0,0 +1,23 @@ +using System; +using System.Text.Json.Serialization; + +namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI; + +[Serializable] +public class CoreUIResponse +{ + [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; } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/CoreUiServiceProxy.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/CoreUiServiceProxy.cs new file mode 100644 index 000000000..f2ab4969d --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/CoreUiServiceProxy.cs @@ -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 options) + { + Options = options.Value; + JsonSerializer = jsonSerializer; + HttpClientFactory = httpClientFactory; + } + + public async virtual Task> SearchAsync( + CoreUIRequest 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>(responseContent); + + return coreUiResponse; + } +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/ICoreUiServiceProxy.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/ICoreUiServiceProxy.cs new file mode 100644 index 000000000..49790e478 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/CoreUI/ICoreUiServiceProxy.cs @@ -0,0 +1,9 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Sonatype.Nexus.Services.CoreUI; + +public interface ICoreUiServiceProxy : INexusServiceProxy +{ + Task> SearchAsync(CoreUIRequest request, CancellationToken cancellationToken = default); +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/INexusServiceProxy.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/INexusServiceProxy.cs new file mode 100644 index 000000000..75dba860a --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/Services/INexusServiceProxy.cs @@ -0,0 +1,5 @@ +namespace LINGYUN.Abp.Sonatype.Nexus.Services; +public interface INexusServiceProxy +{ + +} diff --git a/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/SonatypeNexusConsts.cs b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/SonatypeNexusConsts.cs new file mode 100644 index 000000000..887bbddc4 --- /dev/null +++ b/aspnet-core/modules/nexus/LINGYUN.Abp.Sonatype.Nexus/LINGYUN/Abp/Sonatype/Nexus/SonatypeNexusConsts.cs @@ -0,0 +1,5 @@ +namespace LINGYUN.Abp.Sonatype.Nexus; +internal static class SonatypeNexusConsts +{ + public const string ApiClient = "Sonatype.Nexus"; +} diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/GlobalUsings.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/GlobalUsings.cs new file mode 100644 index 000000000..bd8299f6f --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Xunit; +global using Shouldly; \ No newline at end of file diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN.Abp.BlobStoring.Nexus.Tests.csproj b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN.Abp.BlobStoring.Nexus.Tests.csproj new file mode 100644 index 000000000..322977a07 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN.Abp.BlobStoring.Nexus.Tests.csproj @@ -0,0 +1,27 @@ + + + + net7.0 + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN/Abp/BlobStoring/Nexus/AbpBlobStoringNexusTestBase.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN/Abp/BlobStoring/Nexus/AbpBlobStoringNexusTestBase.cs new file mode 100644 index 000000000..94ab0df57 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN/Abp/BlobStoring/Nexus/AbpBlobStoringNexusTestBase.cs @@ -0,0 +1,6 @@ +using LINGYUN.Abp.Tests; + +namespace LINGYUN.Abp.BlobStoring.Nexus; +public abstract class AbpBlobStoringNexusTestBase : AbpTestsBase +{ +} diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN/Abp/BlobStoring/Nexus/AbpBlobStoringNexusTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN/Abp/BlobStoring/Nexus/AbpBlobStoringNexusTestModule.cs new file mode 100644 index 000000000..bd6a9c5b2 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN/Abp/BlobStoring/Nexus/AbpBlobStoringNexusTestModule.cs @@ -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(options => + { + options.Containers.ConfigureAll((containerName, containerConfiguration) => + { + containerConfiguration.UseNexus(nexus => + { + nexus.BasePath = configuration[NexusBlobProviderConfigurationNames.BasePath]; + nexus.Repository = configuration[NexusBlobProviderConfigurationNames.Repository]; + }); + }); + }); + } +} diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobContainer_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobContainer_Tests.cs new file mode 100644 index 000000000..0fb47cd9d --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/LINGYUN/Abp/BlobStoring/Nexus/NexusBlobContainer_Tests.cs @@ -0,0 +1,11 @@ +using Volo.Abp.BlobStoring; + +namespace LINGYUN.Abp.BlobStoring.Nexus; +public class NexusBlobContainer_Tests : BlobContainer_Tests +{ + public NexusBlobContainer_Tests() + { + + } +} + diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/BlobContainer_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/BlobContainer_Tests.cs new file mode 100644 index 000000000..e41421da5 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/BlobContainer_Tests.cs @@ -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 : AbpIntegratedTest + where TStartupModule : IAbpModule +{ + protected IBlobContainer Container { get; } + + protected ICurrentTenant CurrentTenant { get; } + + protected BlobContainer_Tests() + { + Container = GetRequiredService>(); + CurrentTenant = GetRequiredService(); + } + + 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(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(() => + 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(); + } +} diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer1.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer1.cs new file mode 100644 index 000000000..b39fde642 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer1.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.BlobStoring.TestObjects; + +public class TestContainer1 +{ + +} diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer2.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer2.cs new file mode 100644 index 000000000..192387db5 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer2.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.TestObjects; + +[BlobContainerName("Test2")] +public class TestContainer2 +{ + +} diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer3.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer3.cs new file mode 100644 index 000000000..77589bf19 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Nexus.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer3.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.BlobStoring.TestObjects; + +public class TestContainer3 +{ + +} diff --git a/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/GlobalUsings.cs b/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/GlobalUsings.cs new file mode 100644 index 000000000..8c927eb74 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/GlobalUsings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN.Abp.OssManagement.Nexus.Tests.csproj b/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN.Abp.OssManagement.Nexus.Tests.csproj new file mode 100644 index 000000000..40e07a32e --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN.Abp.OssManagement.Nexus.Tests.csproj @@ -0,0 +1,27 @@ + + + + net7.0 + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN/Abp/OssManagement/Nexus/AbpOssManagementNexusTestsBase.cs b/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN/Abp/OssManagement/Nexus/AbpOssManagementNexusTestsBase.cs new file mode 100644 index 000000000..e1d9501e6 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN/Abp/OssManagement/Nexus/AbpOssManagementNexusTestsBase.cs @@ -0,0 +1,6 @@ +using LINGYUN.Abp.Tests; + +namespace LINGYUN.Abp.OssManagement.Nexus; +public abstract class AbpOssManagementNexusTestsBase : AbpTestsBase +{ +} diff --git a/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN/Abp/OssManagement/Nexus/AbpOssManagementNexusTestsModule.cs b/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN/Abp/OssManagement/Nexus/AbpOssManagementNexusTestsModule.cs new file mode 100644 index 000000000..513369192 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN/Abp/OssManagement/Nexus/AbpOssManagementNexusTestsModule.cs @@ -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 +{ + +} diff --git a/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN/Abp/OssManagement/Nexus/NexusOssContainerFactoryTests.cs b/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN/Abp/OssManagement/Nexus/NexusOssContainerFactoryTests.cs new file mode 100644 index 000000000..c990b9258 --- /dev/null +++ b/aspnet-core/tests/LINGYUN.Abp.OssManagement.Nexus.Tests/LINGYUN/Abp/OssManagement/Nexus/NexusOssContainerFactoryTests.cs @@ -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(); + } + + [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(); + } +}