From b435972616dfd5a2d82158650c027e4fd7e80baa Mon Sep 17 00:00:00 2001 From: Haacked Date: Mon, 28 Jan 2013 09:50:09 -0800 Subject: [PATCH] Implemented GetReadme API endpoint --- .../Octopi.Tests.Integration.csproj | 1 - Octopi.Tests.Integration/Readme.cs | 54 ------------------- .../RepositoriesEndpointTests.cs | 19 +++++++ .../Endpoints/RepositoriesEndpointTests.cs | 44 +++++++++++++++ Octopi/Endpoints/RepositoriesEndpoint.cs | 11 ++++ Octopi/GitHubModels.cs | 48 +++++++++++++++++ Octopi/Http/Connection.cs | 19 +++++++ Octopi/Http/IConnection.cs | 2 + Octopi/IRepositoriesEndpoint.cs | 8 +++ 9 files changed, 151 insertions(+), 55 deletions(-) delete mode 100644 Octopi.Tests.Integration/Readme.cs diff --git a/Octopi.Tests.Integration/Octopi.Tests.Integration.csproj b/Octopi.Tests.Integration/Octopi.Tests.Integration.csproj index c6d45924..b3fdedd6 100644 --- a/Octopi.Tests.Integration/Octopi.Tests.Integration.csproj +++ b/Octopi.Tests.Integration/Octopi.Tests.Integration.csproj @@ -51,7 +51,6 @@ - diff --git a/Octopi.Tests.Integration/Readme.cs b/Octopi.Tests.Integration/Readme.cs deleted file mode 100644 index 43fecaa7..00000000 --- a/Octopi.Tests.Integration/Readme.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Threading.Tasks; -using Octopi.Http; - -namespace Octopi.Tests -{ - public class Readme - { - public Readme() - { - // create an anonymous client - var client = new GitHubClient(); - - // create a client with basic auth - client = new GitHubClient { Credentials = new Credentials("xapitestaccountx", "octocat11") }; - - // create a client with an oauth token - client = new GitHubClient { Credentials = new Credentials("ouathtoken") }; - } - - public async Task UserApi() - { - var github = new GitHubClient { Credentials = new Credentials("xapitestaccountx", "octocat11") }; - - // Get the authenticated user - var user = await github.User.Current(); - - // Get a user by username - user = await github.User.Get("tclem"); - - // Update a user - user = await github.User.Update(new UserUpdate { Name = "octolish" }); - } - - public async Task AuthorizationsApi() - { - var github = new GitHubClient { Credentials = new Credentials("xapitestaccountx", "octocat11") }; - - // create a new auth - var auth = await github.Authorization.CreateAsync(new AuthorizationUpdate { Note = "integration test", NoteUrl = "http://example.com", Scopes = new[] { "public_repo" } }); - - // list all authorizations for the authenticated user - var auths = await github.Authorization.GetAll(); - - // get a specific auth - auth = await github.Authorization.GetAsync(auth.Id); - - // update an auth - auth = await github.Authorization.UpdateAsync(auth.Id, new AuthorizationUpdate { Note = "integration test update" }); - - // delete a specific auth - await github.Authorization.DeleteAsync(auth.Id); - } - } -} diff --git a/Octopi.Tests.Integration/RepositoriesEndpointTests.cs b/Octopi.Tests.Integration/RepositoriesEndpointTests.cs index bb7b2721..533f9140 100644 --- a/Octopi.Tests.Integration/RepositoriesEndpointTests.cs +++ b/Octopi.Tests.Integration/RepositoriesEndpointTests.cs @@ -38,5 +38,24 @@ namespace Octopi.Tests.Integration repositories.Count.Should().BeGreaterThan(80); } } + + public class TheGetReadmeMethod + { + [Fact] + public async Task ReturnsReadmeForOctopi() + { + var github = new GitHubClient + { + Credentials = new Credentials("xapitestaccountx", "octocat11") + }; + + // TODO: Change this to request github/octopi once we make this OSS. + var readme = await github.Repository.GetReadme("haacked", "seegit"); + readme.Name.Should().Be("README.md"); + var readMeHtml = await readme.GetHtmlContent(); + readMeHtml.Should().Contain(@"
WARNING: This is some haacky code."); + } + } } } diff --git a/Octopi.Tests/Endpoints/RepositoriesEndpointTests.cs b/Octopi.Tests/Endpoints/RepositoriesEndpointTests.cs index 4b533f78..e933e859 100644 --- a/Octopi.Tests/Endpoints/RepositoriesEndpointTests.cs +++ b/Octopi.Tests/Endpoints/RepositoriesEndpointTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text; using System.Threading.Tasks; using FluentAssertions; using NSubstitute; @@ -130,5 +131,48 @@ namespace Octopi.Tests .GetAsync>(Arg.Is(u => u.ToString() == "/orgs/orgName/repos")); } } + + public class TheGetReadmeMethod + { + [Fact] + public async Task ReturnsReadme() + { + var links = new Dictionary(); + var scopes = new List(); + string encodedContent = Convert.ToBase64String(Encoding.UTF8.GetBytes("Hello world")); + IResponse apiResponse = new ApiResponse + { + ApiInfo = new ApiInfo(links, scopes, scopes, "", 1, 1), + BodyAsObject = new ReadmeResponse + { + Content = encodedContent, + Encoding = "base64", + Name = "README.md", + Url = "https://github.example.com/readme.md", + HtmlUrl = "https://github.example.com/readme" + } + }; + IResponse htmlResponse = new ApiResponse + { + Body = "" + }; + var connection = Substitute.For(); + connection.GetAsync(Args.Uri).Returns(Task.FromResult(apiResponse)); + connection.GetHtml(Args.Uri).Returns(Task.FromResult(htmlResponse)); + var reposEndpoint = new RepositoriesEndpoint(connection); + + var readme = await reposEndpoint.GetReadme("fake", "repo"); + readme.Name.Should().Be("README.md"); + connection.Received() + .GetAsync(Arg.Is(u => u.ToString() == "/repos/fake/repo/readme")); + connection.DidNotReceive() + .GetHtml(Arg.Is(u => u.ToString() == "https://github.example.com/readme")); + var htmlReadme = await readme.GetHtmlContent(); + htmlReadme.Should().Be(""); + connection.Received() + .GetHtml(Arg.Is(u => u.ToString() == "https://github.example.com/readme")); + + } + } } } diff --git a/Octopi/Endpoints/RepositoriesEndpoint.cs b/Octopi/Endpoints/RepositoriesEndpoint.cs index 1f963769..9b1ed691 100644 --- a/Octopi/Endpoints/RepositoriesEndpoint.cs +++ b/Octopi/Endpoints/RepositoriesEndpoint.cs @@ -48,5 +48,16 @@ namespace Octopi.Endpoints return await GetAll(endpoint); } + + public async Task GetReadme(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + var endpoint = new Uri(string.Format("/repos/{0}/{1}/readme", owner, name), UriKind.Relative); + var response = await Connection.GetAsync(endpoint); + var readmeResponse = response.BodyAsObject; + return new Readme(readmeResponse, Connection); + } } } diff --git a/Octopi/GitHubModels.cs b/Octopi/GitHubModels.cs index 0e085b6f..a9d9a2b9 100644 --- a/Octopi/GitHubModels.cs +++ b/Octopi/GitHubModels.cs @@ -1,5 +1,8 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Threading.Tasks; +using Octopi.Http; namespace Octopi { @@ -287,6 +290,51 @@ namespace Octopi public string Url { get; set; } } + internal class ReadmeResponse + { + public string Content { get; set; } + public string Name { get; set; } + public string HtmlUrl { get; set; } + public string Url { get; set; } + public string Encoding { get; set; } + } + + public class Readme + { + readonly Lazy> htmlContent; + + internal Readme(ReadmeResponse response, IConnection connection) + { + Ensure.ArgumentNotNull(response, "response"); + + Name = response.Name; + Url = new Uri(response.Url); + HtmlUrl = new Uri(response.HtmlUrl); + if (response.Encoding.Equals("base64", StringComparison.OrdinalIgnoreCase)) + { + var contentAsBytes = Convert.FromBase64String(response.Content); + Content = Encoding.UTF8.GetString(contentAsBytes, 0, contentAsBytes.Length); + } + htmlContent = new Lazy>(async () => + { + var resp = await connection.GetHtml(HtmlUrl); + return resp.Body; + }); + } + + public string Content { get; private set; } + public string Name { get; private set; } + public Uri HtmlUrl { get; private set; } + public Uri Url { get; private set; } + + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", + Justification = "Makse a network request")] + public async Task GetHtmlContent() + { + return await htmlContent.Value; + } + } + public class SshKey { /// diff --git a/Octopi/Http/Connection.cs b/Octopi/Http/Connection.cs index 663a41cb..d4a0c85c 100644 --- a/Octopi/Http/Connection.cs +++ b/Octopi/Http/Connection.cs @@ -59,6 +59,16 @@ namespace Octopi.Http }); } + public async Task> GetHtml(Uri endpoint) + { + return await GetHtml(new Request + { + Method = HttpMethod.Get, + BaseAddress = BaseAddress, + Endpoint = endpoint + }); + } + public async Task> PatchAsync(Uri endpoint, object body) { return await Run(new Request @@ -109,6 +119,15 @@ namespace Octopi.Http } } + async Task> GetHtml(IRequest request) + { + authenticator.Apply(request); + request.Headers.Add("Accept", "application/vnd.github.html"); + var response = await httpClient.Send(request); + apiInfoParser.ParseApiHttpHeaders(response); + return response; + } + async Task> Run(IRequest request) { jsonPipeline.SerializeRequest(request); diff --git a/Octopi/Http/IConnection.cs b/Octopi/Http/IConnection.cs index 3da19223..296f80a9 100644 --- a/Octopi/Http/IConnection.cs +++ b/Octopi/Http/IConnection.cs @@ -6,6 +6,8 @@ namespace Octopi.Http { public interface IConnection { + Task> GetHtml(Uri endpoint); + Task> GetAsync(Uri endpoint); Task> PatchAsync(Uri endpoint, object body); Task> PostAsync(Uri endpoint, object body); diff --git a/Octopi/IRepositoriesEndpoint.cs b/Octopi/IRepositoriesEndpoint.cs index e147ec4b..e2744acb 100644 --- a/Octopi/IRepositoriesEndpoint.cs +++ b/Octopi/IRepositoriesEndpoint.cs @@ -48,5 +48,13 @@ namespace Octopi [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Makes a network request")] Task> GetAllForOrg(string organization); + + /// + /// Returns the HTML rendered README. + /// + /// The owner of the repository. + /// The name of the repository. + /// + Task GetReadme(string owner, string name); } }