diff --git a/Octokit.Reactive/Clients/IObservableRepositoryContentsClient.cs b/Octokit.Reactive/Clients/IObservableRepositoryContentsClient.cs
index e08c7128c6..b55afc633c 100644
--- a/Octokit.Reactive/Clients/IObservableRepositoryContentsClient.cs
+++ b/Octokit.Reactive/Clients/IObservableRepositoryContentsClient.cs
@@ -137,6 +137,17 @@ public interface IObservableRepositoryContentsClient
/// The name of the repository
IObservable GetAllContents(string owner, string name);
+ ///
+ /// Returns the raw content of the file at the given or null if the path is a directory.
+ ///
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// The owner of the repository
+ /// The name of the repository
+ /// The content path
+ IObservable GetRawContent(string owner, string name, string path);
+
///
/// Returns the contents of the root directory in a repository.
///
@@ -156,6 +167,18 @@ public interface IObservableRepositoryContentsClient
/// The content path
IObservable GetAllContentsByRef(string owner, string name, string reference, string path);
+ ///
+ /// Returns the raw content of the file at the given or null if the path is a directory.
+ ///
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// The owner of the repository
+ /// The name of the repository
+ /// The content path
+ /// The name of the commit/branch/tag.
+ IObservable GetRawContentByRef(string owner, string name, string path, string reference);
+
///
/// Returns the contents of a file or directory in a repository.
///
diff --git a/Octokit.Reactive/Clients/ObservableRepositoryContentsClient.cs b/Octokit.Reactive/Clients/ObservableRepositoryContentsClient.cs
index f70804c824..a044e09096 100644
--- a/Octokit.Reactive/Clients/ObservableRepositoryContentsClient.cs
+++ b/Octokit.Reactive/Clients/ObservableRepositoryContentsClient.cs
@@ -1,5 +1,6 @@
using System;
using System.Reactive;
+using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using Octokit.Reactive.Internal;
@@ -238,6 +239,28 @@ public IObservable GetAllContents(string owner, string name)
.GetAndFlattenAllPages(ApiUrls.RepositoryContent(owner, name, string.Empty));
}
+ ///
+ /// Returns the raw content of the file at the given or null if the path is a directory.
+ ///
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// The owner of the repository
+ /// The name of the repository
+ /// The content path
+ public IObservable GetRawContent(string owner, string name, string path)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner));
+ Ensure.ArgumentNotNullOrEmptyString(name, nameof(name));
+ Ensure.ArgumentNotNullOrEmptyString(path, nameof(path));
+
+ return _client
+ .Connection
+ .GetRaw(ApiUrls.RepositoryContent(owner, name, path), null)
+ .ToObservable()
+ .Select(e => e.Body);
+ }
+
///
/// Returns the contents of the root directory in a repository.
///
@@ -270,6 +293,30 @@ public IObservable GetAllContentsByRef(string owner, string n
return _client.Connection.GetAndFlattenAllPages(ApiUrls.RepositoryContent(owner, name, path, reference));
}
+ ///
+ /// Returns the raw content of the file at the given or null if the path is a directory.
+ ///
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// The owner of the repository
+ /// The name of the repository
+ /// The content path
+ /// The name of the commit/branch/tag.
+ public IObservable GetRawContentByRef(string owner, string name, string path, string reference)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner));
+ Ensure.ArgumentNotNullOrEmptyString(name, nameof(name));
+ Ensure.ArgumentNotNullOrEmptyString(reference, nameof(reference));
+ Ensure.ArgumentNotNullOrEmptyString(path, nameof(path));
+
+ return _client
+ .Connection
+ .GetRaw(ApiUrls.RepositoryContent(owner, name, path, reference), null)
+ .ToObservable()
+ .Select(e => e.Body);
+ }
+
///
/// Returns the contents of a file or directory in a repository.
///
diff --git a/Octokit.Tests/Clients/RepositoryContentsClientTests.cs b/Octokit.Tests/Clients/RepositoryContentsClientTests.cs
index 58a04b4ec9..4f4ad4429f 100644
--- a/Octokit.Tests/Clients/RepositoryContentsClientTests.cs
+++ b/Octokit.Tests/Clients/RepositoryContentsClientTests.cs
@@ -217,6 +217,39 @@ public async Task EnsuresNonNullArguments()
}
}
+ public class TheGetRawContentMethod
+ {
+ [Fact]
+ public async Task ReturnsRawContent()
+ {
+ var result = new byte[] { 1, 2, 3 };
+
+ var connection = Substitute.For();
+ connection.GetRaw(Args.Uri, default).Returns(result);
+ var contentsClient = new RepositoryContentsClient(connection);
+
+ var rawContent = await contentsClient.GetRawContent("fake", "repo", "path/to/file.txt");
+
+ connection.Received().GetRaw(Arg.Is(u => u.ToString() == "repos/fake/repo/contents/path/to/file.txt"), null);
+ Assert.Same(result, rawContent);
+ }
+
+ [Fact]
+ public async Task EnsuresNonNullArguments()
+ {
+ var connection = Substitute.For();
+ var client = new RepositoryContentsClient(connection);
+
+ await Assert.ThrowsAsync(() => client.GetRawContent(null, "name", "path"));
+ await Assert.ThrowsAsync(() => client.GetRawContent("owner", null, "path"));
+ await Assert.ThrowsAsync(() => client.GetRawContent("owner", "name", null));
+
+ await Assert.ThrowsAsync(() => client.GetRawContent("", "name", "path"));
+ await Assert.ThrowsAsync(() => client.GetRawContent("owner", "", "path"));
+ await Assert.ThrowsAsync(() => client.GetRawContent("owner", "name", ""));
+ }
+ }
+
public class TheGetContentsByRefMethod
{
[Fact]
@@ -311,6 +344,41 @@ public async Task EnsuresNonNullArguments()
}
}
+ public class TheGetRawContentByRefMethod
+ {
+ [Fact]
+ public async Task ReturnsRawContent()
+ {
+ var result = new byte[] { 1, 2, 3 };
+
+ var connection = Substitute.For();
+ connection.GetRaw(Args.Uri, default).Returns(result);
+ var contentsClient = new RepositoryContentsClient(connection);
+
+ var rawContent = await contentsClient.GetRawContentByRef("fake", "repo", "path/to/file.txt", "reference");
+
+ connection.Received().GetRaw(Arg.Is(u => u.ToString() == "repos/fake/repo/contents/path/to/file.txt?ref=reference"), null);
+ Assert.Same(result, rawContent);
+ }
+
+ [Fact]
+ public async Task EnsuresNonNullArguments()
+ {
+ var connection = Substitute.For();
+ var client = new RepositoryContentsClient(connection);
+
+ await Assert.ThrowsAsync(() => client.GetRawContentByRef(null, "name", "path", "reference"));
+ await Assert.ThrowsAsync(() => client.GetRawContentByRef("owner", null, "path", "reference"));
+ await Assert.ThrowsAsync(() => client.GetRawContentByRef("owner", "name", null, "reference"));
+ await Assert.ThrowsAsync(() => client.GetRawContentByRef("owner", "name", "path", null));
+
+ await Assert.ThrowsAsync(() => client.GetRawContentByRef("", "name", "path", "reference"));
+ await Assert.ThrowsAsync(() => client.GetRawContentByRef("owner", "", "path", "reference"));
+ await Assert.ThrowsAsync(() => client.GetRawContentByRef("owner", "name", "", "reference"));
+ await Assert.ThrowsAsync(() => client.GetRawContentByRef("owner", "name", "path", ""));
+ }
+ }
+
public class TheCreateFileMethod
{
[Fact]
diff --git a/Octokit.Tests/Reactive/ObservableRepositoryContentsClientTests.cs b/Octokit.Tests/Reactive/ObservableRepositoryContentsClientTests.cs
index 2d52c22d43..565034b493 100644
--- a/Octokit.Tests/Reactive/ObservableRepositoryContentsClientTests.cs
+++ b/Octokit.Tests/Reactive/ObservableRepositoryContentsClientTests.cs
@@ -274,6 +274,45 @@ public void EnsuresNonNullArguments()
}
}
+ public class TheGetRawContentMethod
+ {
+ [Fact]
+ public async Task ReturnsRawContent()
+ {
+ var result = new byte[] { 1, 2, 3 };
+
+ var connection = Substitute.For();
+ var gitHubClient = new GitHubClient(connection);
+ var contentsClient = new ObservableRepositoryContentsClient(gitHubClient);
+ IApiResponse response = new ApiResponse
+ (
+ new Response { ApiInfo = new ApiInfo(new Dictionary(), new List(), new List(), "etag", new RateLimit()) },
+ result
+ );
+ connection.GetRaw(Args.Uri, default).Returns(response);
+
+ var rawContent = await contentsClient.GetRawContent("fake", "repo", "path/to/file.txt");
+
+ connection.Received().GetRaw(Arg.Is(u => u.ToString() == "repos/fake/repo/contents/path/to/file.txt"), null);
+ Assert.Same(result, rawContent);
+ }
+
+ [Fact]
+ public void EnsuresNonNullArguments()
+ {
+ var gitHubClient = Substitute.For();
+ var client = new ObservableRepositoryContentsClient(gitHubClient);
+
+ Assert.Throws(() => client.GetRawContent(null, "name", "path"));
+ Assert.Throws(() => client.GetRawContent("owner", null, "path"));
+ Assert.Throws(() => client.GetRawContent("owner", "name", null));
+
+ Assert.Throws(() => client.GetRawContent("", "name", "path"));
+ Assert.Throws(() => client.GetRawContent("owner", "", "path"));
+ Assert.Throws(() => client.GetRawContent("owner", "name", ""));
+ }
+ }
+
public class TheGetContentsByRefMethod
{
[Fact]
@@ -396,6 +435,47 @@ public void EnsuresNonNullArguments()
}
}
+ public class TheGetRawContentByRefMethod
+ {
+ [Fact]
+ public async Task ReturnsRawContent()
+ {
+ var result = new byte[] { 1, 2, 3 };
+
+ var connection = Substitute.For();
+ var gitHubClient = new GitHubClient(connection);
+ var contentsClient = new ObservableRepositoryContentsClient(gitHubClient);
+ IApiResponse response = new ApiResponse
+ (
+ new Response { ApiInfo = new ApiInfo(new Dictionary(), new List(), new List(), "etag", new RateLimit()) },
+ result
+ );
+ connection.GetRaw(Args.Uri, default).Returns(response);
+
+ var rawContent = await contentsClient.GetRawContentByRef("fake", "repo", "path/to/file.txt", "reference");
+
+ connection.Received().GetRaw(Arg.Is(u => u.ToString() == "repos/fake/repo/contents/path/to/file.txt?ref=reference"), null);
+ Assert.Same(result, rawContent);
+ }
+
+ [Fact]
+ public void EnsuresNonNullArguments()
+ {
+ var gitHubClient = Substitute.For();
+ var client = new ObservableRepositoryContentsClient(gitHubClient);
+
+ Assert.Throws(() => client.GetRawContentByRef(null, "name", "path", "reference"));
+ Assert.Throws(() => client.GetRawContentByRef("owner", null, "path", "reference"));
+ Assert.Throws(() => client.GetRawContentByRef("owner", "name", null, "reference"));
+ Assert.Throws(() => client.GetRawContentByRef("owner", "name", "path", null));
+
+ Assert.Throws(() => client.GetRawContentByRef("", "name", "path", "reference"));
+ Assert.Throws(() => client.GetRawContentByRef("owner", "", "path", "reference"));
+ Assert.Throws(() => client.GetRawContentByRef("owner", "name", "", "reference"));
+ Assert.Throws(() => client.GetRawContentByRef("owner", "name", "path", ""));
+ }
+ }
+
public class TheCreateFileMethod
{
[Fact]
diff --git a/Octokit/Clients/IRepositoryContentsClient.cs b/Octokit/Clients/IRepositoryContentsClient.cs
index 08f986b1a8..698a44277c 100644
--- a/Octokit/Clients/IRepositoryContentsClient.cs
+++ b/Octokit/Clients/IRepositoryContentsClient.cs
@@ -25,6 +25,17 @@ public interface IRepositoryContentsClient
[ExcludeFromPaginationApiOptionsConventionTest("Pagination not supported by GitHub API (tested 29/08/2017)")]
Task> GetAllContents(string owner, string name, string path);
+ ///
+ /// Returns the raw content of the file at the given or null if the path is a directory.
+ ///
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// The owner of the repository
+ /// The name of the repository
+ /// The content path
+ Task GetRawContent(string owner, string name, string path);
+
///
/// Returns the contents of a file or directory in a repository.
///
@@ -70,6 +81,18 @@ public interface IRepositoryContentsClient
[ExcludeFromPaginationApiOptionsConventionTest("Pagination not supported by GitHub API (tested 29/08/2017)")]
Task> GetAllContentsByRef(string owner, string name, string path, string reference);
+ ///
+ /// Returns the raw content of the file at the given or null if the path is a directory.
+ ///
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// The owner of the repository
+ /// The name of the repository
+ /// The content path
+ /// The name of the commit/branch/tag.
+ Task GetRawContentByRef(string owner, string name, string path, string reference);
+
///
/// Returns the contents of a file or directory in a repository.
///
diff --git a/Octokit/Clients/RepositoryContentsClient.cs b/Octokit/Clients/RepositoryContentsClient.cs
index 8f8f6bf0bc..e5c7a6661f 100644
--- a/Octokit/Clients/RepositoryContentsClient.cs
+++ b/Octokit/Clients/RepositoryContentsClient.cs
@@ -41,6 +41,27 @@ public Task> GetAllContents(string owner, strin
return ApiConnection.GetAll(url);
}
+ ///
+ /// Returns the raw content of the file at the given or null if the path is a directory.
+ ///
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// The owner of the repository
+ /// The name of the repository
+ /// The content path
+ [ManualRoute("GET", "repos/{owner}/{repo}/contents/{path}")]
+ public Task GetRawContent(string owner, string name, string path)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner));
+ Ensure.ArgumentNotNullOrEmptyString(name, nameof(name));
+ Ensure.ArgumentNotNullOrEmptyString(path, nameof(path));
+
+ var url = ApiUrls.RepositoryContent(owner, name, path);
+
+ return ApiConnection.GetRaw(url, null);
+ }
+
///
/// Returns the contents of a file or directory in a repository.
///
@@ -112,13 +133,32 @@ public Task> GetAllContentsByRef(string owner,
Ensure.ArgumentNotNullOrEmptyString(path, nameof(path));
Ensure.ArgumentNotNullOrEmptyString(reference, nameof(reference));
- var url = (path == "/")
- ? ApiUrls.RepositoryContent(owner, name, "", reference)
- : ApiUrls.RepositoryContent(owner, name, path, reference);
-
+ var url = ApiUrls.RepositoryContent(owner, name, path, reference);
return ApiConnection.GetAll(url);
}
+ ///
+ /// Returns the raw content of the file at the given or null if the path is a directory.
+ ///
+ ///
+ /// See the API documentation for more information.
+ ///
+ /// The owner of the repository
+ /// The name of the repository
+ /// The content path
+ /// The name of the commit/branch/tag.
+ [ManualRoute("GET", "repos/{owner}/{repo}/contents/{path}?ref={ref}")]
+ public Task GetRawContentByRef(string owner, string name, string path, string reference)
+ {
+ Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner));
+ Ensure.ArgumentNotNullOrEmptyString(name, nameof(name));
+ Ensure.ArgumentNotNullOrEmptyString(path, nameof(path));
+ Ensure.ArgumentNotNullOrEmptyString(reference, nameof(reference));
+
+ var url = ApiUrls.RepositoryContent(owner, name, path, reference);
+ return ApiConnection.GetRaw(url, null);
+ }
+
///
/// Returns the contents of a file or directory in a repository.
///
diff --git a/Octokit/Helpers/AcceptHeaders.cs b/Octokit/Helpers/AcceptHeaders.cs
index 6f869f6d80..9d0848cee8 100644
--- a/Octokit/Helpers/AcceptHeaders.cs
+++ b/Octokit/Helpers/AcceptHeaders.cs
@@ -13,6 +13,12 @@ public static class AcceptHeaders
public const string CommitReferenceSha1MediaType = "application/vnd.github.v3.sha";
+ ///
+ /// Support for retrieving raw file content with the method.
+ ///
+ /// https://developer.github.com/v3/repos/contents/#custom-media-types
+ public const string RawContentMediaType = "application/vnd.github.v3.raw";
+
public const string StarCreationTimestamps = "application/vnd.github.v3.star+json";
public const string MigrationsApiPreview = "application/vnd.github.wyandotte-preview+json";
diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs
index d10687061e..9b95f9c192 100644
--- a/Octokit/Helpers/ApiUrls.cs
+++ b/Octokit/Helpers/ApiUrls.cs
@@ -2363,7 +2363,7 @@ public static Uri RepositoryArchiveLink(string owner, string name, ArchiveFormat
/// The for getting the contents of the specified repository and path
public static Uri RepositoryContent(string owner, string name, string path, string reference)
{
- return "repos/{0}/{1}/contents/{2}?ref={3}".FormatUri(owner, name, path, reference);
+ return "repos/{0}/{1}/contents/{2}?ref={3}".FormatUri(owner, name, path == "/" ? "" : path, reference);
}
///
diff --git a/Octokit/Http/ApiConnection.cs b/Octokit/Http/ApiConnection.cs
index aa8c514c09..dd3cc04102 100644
--- a/Octokit/Http/ApiConnection.cs
+++ b/Octokit/Http/ApiConnection.cs
@@ -106,6 +106,21 @@ public async Task GetHtml(Uri uri, IDictionary parameter
return response.Body;
}
+ ///
+ /// Gets the raw content of the API resource at the specified URI.
+ ///
+ /// URI of the API resource to get
+ /// Parameters to add to the API request
+ /// The API resource's raw content or null if the points to a directory.
+ /// Thrown when an API error occurs.
+ public async Task GetRaw(Uri uri, IDictionary parameters)
+ {
+ Ensure.ArgumentNotNull(uri, nameof(uri));
+
+ var response = await Connection.GetRaw(uri, parameters).ConfigureAwait(false);
+ return response.Body;
+ }
+
///
/// Gets all API resources in the list at the specified URI.
///
diff --git a/Octokit/Http/Connection.cs b/Octokit/Http/Connection.cs
index 106cac7066..6c2c7560cd 100644
--- a/Octokit/Http/Connection.cs
+++ b/Octokit/Http/Connection.cs
@@ -211,6 +211,25 @@ public Task> GetHtml(Uri uri, IDictionary p
});
}
+ ///
+ /// Performs an asynchronous HTTP GET request that expects a containing raw data.
+ ///
+ /// URI endpoint to send request to
+ /// Querystring parameters for the request
+ /// representing the received HTTP response
+ /// The property will be null if the points to a directory instead of a file
+ public Task> GetRaw(Uri uri, IDictionary parameters)
+ {
+ Ensure.ArgumentNotNull(uri, nameof(uri));
+
+ return GetRaw(new Request
+ {
+ Method = HttpMethod.Get,
+ BaseAddress = BaseAddress,
+ Endpoint = uri.ApplyParameters(parameters)
+ });
+ }
+
public Task> Patch(Uri uri, object body)
{
Ensure.ArgumentNotNull(uri, nameof(uri));
@@ -620,6 +639,13 @@ async Task> GetHtml(IRequest request)
return new ApiResponse(response, response.Body as string);
}
+ async Task> GetRaw(IRequest request)
+ {
+ request.Headers.Add("Accept", AcceptHeaders.RawContentMediaType);
+ var response = await RunRequest(request, CancellationToken.None).ConfigureAwait(false);
+ return new ApiResponse(response, response.Body as byte[]);
+ }
+
async Task> Run(IRequest request, CancellationToken cancellationToken)
{
_jsonPipeline.SerializeRequest(request);
diff --git a/Octokit/Http/HttpClientAdapter.cs b/Octokit/Http/HttpClientAdapter.cs
index 8b997a50ab..2c780342f5 100644
--- a/Octokit/Http/HttpClientAdapter.cs
+++ b/Octokit/Http/HttpClientAdapter.cs
@@ -100,6 +100,7 @@ protected virtual async Task BuildResponse(HttpResponseMessage respon
// We added support for downloading images,zip-files and application/octet-stream.
// Let's constrain this appropriately.
var binaryContentTypes = new[] {
+ AcceptHeaders.RawContentMediaType,
"application/zip" ,
"application/x-gzip" ,
"application/octet-stream"};
diff --git a/Octokit/Http/IApiConnection.cs b/Octokit/Http/IApiConnection.cs
index 616dbfead0..976d6c9b61 100644
--- a/Octokit/Http/IApiConnection.cs
+++ b/Octokit/Http/IApiConnection.cs
@@ -62,6 +62,15 @@ public interface IApiConnection
/// Thrown when an API error occurs.
Task GetHtml(Uri uri, IDictionary parameters);
+ ///
+ /// Gets the raw content of the API resource at the specified URI.
+ ///
+ /// URI of the API resource to get
+ /// Parameters to add to the API request
+ /// The API resource's raw content or null if the points to a directory.
+ /// Thrown when an API error occurs.
+ Task GetRaw(Uri uri, IDictionary parameters);
+
///
/// Gets all API resources in the list at the specified URI.
///
diff --git a/Octokit/Http/IConnection.cs b/Octokit/Http/IConnection.cs
index 08755cb0ee..920b871d9b 100644
--- a/Octokit/Http/IConnection.cs
+++ b/Octokit/Http/IConnection.cs
@@ -20,6 +20,15 @@ public interface IConnection : IApiInfoProvider
/// representing the received HTTP response
Task> GetHtml(Uri uri, IDictionary parameters);
+ ///
+ /// Performs an asynchronous HTTP GET request that expects a containing raw data.
+ ///
+ /// URI endpoint to send request to
+ /// Querystring parameters for the request
+ /// representing the received HTTP response
+ /// The property will be null if the points to a directory instead of a file
+ Task> GetRaw(Uri uri, IDictionary parameters);
+
///
/// Performs an asynchronous HTTP GET request.
/// Attempts to map the response to an object of type