diff --git a/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj b/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj index f8143caa2f..9ad8e3bff6 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj +++ b/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj @@ -18,6 +18,7 @@ + diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs index 80f42ed3ff..8adf03faa9 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs @@ -1,4 +1,6 @@ -using Volo.Abp.Domain; +using System; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Domain; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.VirtualFileSystem; @@ -35,6 +37,11 @@ namespace Volo.Docs options.Stores[GithubDocumentStore.Type] = typeof(GithubDocumentStore); options.Stores[FileSystemDocumentStore.Type] = typeof(FileSystemDocumentStore); }); + + context.Services.AddHttpClient(GithubRepositoryManager.HttpClientName, client => + { + client.Timeout = TimeSpan.FromMilliseconds(15000); + }); } } } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentStore.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentStore.cs index 5baa073b9c..715306ae5c 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentStore.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentStore.cs @@ -1,16 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Octokit; -using Octokit.Internal; using Volo.Abp.Domain.Services; using Volo.Docs.Documents; using Volo.Docs.GitHub.Projects; using Volo.Docs.Projects; using Newtonsoft.Json.Linq; +using Octokit; using ProductHeaderValue = Octokit.ProductHeaderValue; using Project = Volo.Docs.Projects.Project; @@ -22,6 +20,13 @@ namespace Volo.Docs.GitHub.Documents { public const string Type = "GitHub"; + private readonly IGithubRepositoryManager _githubRepositoryManager; + + public GithubDocumentStore(IGithubRepositoryManager githubRepositoryManager) + { + _githubRepositoryManager = githubRepositoryManager; + } + public virtual async Task GetDocumentAsync(Project project, string documentName, string version) { var token = project.GetGitHubAccessTokenOrNull(); @@ -102,20 +107,7 @@ namespace Volo.Docs.GitHub.Documents var url = project.GetGitHubUrl(); var ownerName = GetOwnerNameFromUrl(url); var repositoryName = GetRepositoryNameFromUrl(url); - var gitHubClient = CreateGitHubClient(project.GetGitHubAccessTokenOrNull()); - - return await gitHubClient - .Repository - .Release - .GetAll(ownerName, repositoryName); - } - - private static GitHubClient CreateGitHubClient(string token = null) - { - //TODO: Why hard-coded "abpframework"? Should be configurable? - return token.IsNullOrWhiteSpace() - ? new GitHubClient(new ProductHeaderValue("abpframework")) - : new GitHubClient(new ProductHeaderValue("abpframework"), new InMemoryCredentialStore(new Credentials(token))); + return await _githubRepositoryManager.GetReleasesAsync(ownerName, repositoryName, project.GetGitHubAccessTokenOrNull()); } protected virtual string GetOwnerNameFromUrl(string url) @@ -151,19 +143,7 @@ namespace Volo.Docs.GitHub.Documents { Logger.LogInformation("Downloading content from Github (DownloadWebContentAsStringAsync): " + rawUrl); - using (var webClient = new GithubWebClient()) - { - if (!token.IsNullOrWhiteSpace()) - { - webClient.Headers.Add("Authorization", "token " + token); - } - - webClient.Headers.Add("User-Agent", userAgent ?? ""); - - //TODO: SET TIMEOUT? - - return await webClient.DownloadStringTaskAsync(new Uri(rawUrl)); - } + return await _githubRepositoryManager.GetFileRawStringContentAsync(rawUrl, token, userAgent); } catch (Exception ex) { @@ -179,16 +159,7 @@ namespace Volo.Docs.GitHub.Documents { Logger.LogInformation("Downloading content from Github (DownloadWebContentAsByteArrayAsync): " + rawUrl); - using (var webClient = new GithubWebClient()) - { - if (!token.IsNullOrWhiteSpace()) - { - webClient.Headers.Add("Authorization", "token " + token); - } - webClient.Headers.Add("User-Agent", userAgent ?? ""); - - return await webClient.DownloadDataTaskAsync(new Uri(rawUrl)); - } + return await _githubRepositoryManager.GetFileRawByteArrayContentAsync(rawUrl, token, userAgent); } catch (Exception ex) { @@ -237,21 +208,5 @@ namespace Volo.Docs.GitHub.Documents .Replace("github.com", "raw.githubusercontent.com") .ReplaceFirst("/tree/", "/"); } - - private class GithubWebClient : WebClient - { - protected override WebRequest GetWebRequest(Uri address) - { - var webRequest = base.GetWebRequest(address); - if (webRequest == null) - { - return null; - } - - webRequest.Timeout = 15000; - - return webRequest; - } - } } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs new file mode 100644 index 0000000000..be71fd4772 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using Octokit; +using Octokit.Internal; +using ProductHeaderValue = Octokit.ProductHeaderValue; + +namespace Volo.Docs.GitHub.Documents +{ + public class GithubRepositoryManager : IGithubRepositoryManager + { + public const string HttpClientName = "GithubRepositoryManagerHttpClientName"; + + private readonly IHttpClientFactory _clientFactory; + + public GithubRepositoryManager(IHttpClientFactory clientFactory) + { + _clientFactory = clientFactory; + } + + public async Task GetFileRawStringContentAsync(string rawUrl, string token, string userAgent) + { + var httpClient = _clientFactory.CreateClient(HttpClientName); + if (!token.IsNullOrWhiteSpace()) + { + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", token); + } + + httpClient.DefaultRequestHeaders.Add("User-Agent", userAgent ?? ""); + + return await httpClient.GetStringAsync(new Uri(rawUrl)); + } + + public async Task GetFileRawByteArrayContentAsync(string rawUrl, string token, string userAgent) + { + var httpClient = _clientFactory.CreateClient(HttpClientName); + if (!token.IsNullOrWhiteSpace()) + { + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", token); + } + + httpClient.DefaultRequestHeaders.Add("User-Agent", userAgent ?? ""); + + return await httpClient.GetByteArrayAsync(new Uri(rawUrl)); + } + + public async Task> GetReleasesAsync(string name, string repositoryName, string token) + { + var client = token.IsNullOrWhiteSpace() + ? new GitHubClient(new ProductHeaderValue(name)) + : new GitHubClient(new ProductHeaderValue(name), new InMemoryCredentialStore(new Credentials(token))); + + return (await client + .Repository + .Release + .GetAll(name, repositoryName)).ToList(); + } + } +} diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/IGithubRepositoryManager.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/IGithubRepositoryManager.cs new file mode 100644 index 0000000000..519b9829d5 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/IGithubRepositoryManager.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Octokit; +using Volo.Abp.DependencyInjection; + +namespace Volo.Docs.GitHub.Documents +{ + public interface IGithubRepositoryManager : ITransientDependency + { + Task GetFileRawStringContentAsync(string rawUrl, string token, string userAgent); + + Task GetFileRawByteArrayContentAsync(string rawUrl, string token, string userAgent); + + Task> GetReleasesAsync(string name, string repositoryName, string token); + + } +} diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestBase.cs b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestBase.cs index 1a34d74ddf..7de6bb3182 100644 --- a/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestBase.cs +++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestBase.cs @@ -1,7 +1,43 @@ -namespace Volo.Docs +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; +using Octokit; +using Volo.Docs.GitHub.Documents; + +namespace Volo.Docs { public abstract class DocsDomainTestBase : DocsTestBase { - + protected override void AfterAddApplication(IServiceCollection services) + { + var repositoryManager = Substitute.For(); + repositoryManager.GetFileRawStringContentAsync(Arg.Any(), Arg.Any(), Arg.Any()) + .Returns("stringContent"); + repositoryManager.GetFileRawByteArrayContentAsync(Arg.Any(), Arg.Any(), Arg.Any()) + .Returns(new byte[] { 0x01, 0x02, 0x03 }); + repositoryManager.GetReleasesAsync(Arg.Any(), Arg.Any(), Arg.Any()) + .Returns(new List + { + new Release("https://api.github.com/repos/abpframework/abp/releases/16293679", + "https://github.com/abpframework/abp/releases/tag/0.15.0", + "https://api.github.com/repos/abpframework/abp/releases/16293679/assets", + "https://uploads.github.com/repos/abpframework/abp/releases/16293679/assets{?name,label}", + 16293679, + "0.15.0", + "master", + "0.15.0", + "0.15.0 already release", + false, + false, + DateTimeOffset.Parse("2019-03-22T18:43:58Z"), + DateTimeOffset.Parse("2019-03-22T19:44:25Z"), + null, + "https://api.github.com/repos/abpframework/abp/tarball/0.15.0", + "https://api.github.com/repos/abpframework/abp/zipball/0.15.0", + null) + }); + services.AddSingleton(repositoryManager); + } } -} \ No newline at end of file +} diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/GithubDocumentStore_Tests.cs b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/GithubDocumentStore_Tests.cs new file mode 100644 index 0000000000..ac04ec0e46 --- /dev/null +++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/GithubDocumentStore_Tests.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using NSubstitute; +using Octokit; +using Shouldly; +using Volo.Docs.Documents; +using Volo.Docs.GitHub.Documents; +using Volo.Docs.Projects; +using Xunit; + +namespace Volo.Docs +{ + public class GithubDocumentStore_Tests : DocsDomainTestBase + { + private readonly IDocumentStoreFactory _documentStoreFactory; + private readonly IProjectRepository _projectRepository; + private readonly DocsTestData _testData; + + public GithubDocumentStore_Tests() + { + _documentStoreFactory = GetRequiredService(); + _projectRepository = GetRequiredService(); + _testData = GetRequiredService(); + } + + [Fact] + public async Task GetDocumentAsync() + { + var store = _documentStoreFactory.Create(GithubDocumentStore.Type); + var project = await _projectRepository.FindAsync(_testData.PorjectId); + project.ShouldNotBeNull(); + var document = await store.GetDocumentAsync(project, "index2", "0.123.0"); + document.ShouldNotBeNull(); + + document.Title.ShouldBe("index2"); + document.FileName.ShouldBe("index2"); + document.Version.ShouldBe("0.123.0"); + document.Content.ShouldBe("stringContent"); + } + + [Fact] + public async Task GetVersionsAsync() + { + var store = _documentStoreFactory.Create(GithubDocumentStore.Type); + var project = await _projectRepository.FindAsync(_testData.PorjectId); + project.ShouldNotBeNull(); + + var document = await store.GetVersionsAsync(project); + document.ShouldNotBeNull(); + + document.Count.ShouldBe(1); + document.ShouldContain(x => x.Name == "0.15.0" && x.DisplayName == "0.15.0"); + } + + [Fact] + public async Task GetResource() + { + var store = _documentStoreFactory.Create(GithubDocumentStore.Type); + var project = await _projectRepository.FindAsync(_testData.PorjectId); + project.ShouldNotBeNull(); + + var documentResource = await store.GetResource(project, "index.md", "0.123.0"); + documentResource.ShouldNotBeNull(); + + documentResource.Content.ShouldBe(new byte[] + { + 0x01, 0x02, 0x03 + }); + } + } +}