diff --git a/CustomDictionary.xml b/CustomDictionary.xml index 36a6b436..a9c1edd1 100644 --- a/CustomDictionary.xml +++ b/CustomDictionary.xml @@ -19,6 +19,8 @@ Prerelease Commitish Mergeable + Symlink + Submodule diff --git a/Octokit.Tests.Integration/Clients/RepositoryContentsClientTests.cs b/Octokit.Tests.Integration/Clients/RepositoryContentsClientTests.cs new file mode 100644 index 00000000..d96e9c51 --- /dev/null +++ b/Octokit.Tests.Integration/Clients/RepositoryContentsClientTests.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; + +namespace Octokit.Tests.Integration.Clients +{ + public class RepositoryContentsClientTests + { + public class TheGetRootMethod + { + [IntegrationTest] + public async Task GetsRootContents() + { + var github = new GitHubClient(new ProductHeaderValue("OctokitTests")) + { + Credentials = Helper.Credentials + }; + + var contents = await github.Repository.Contents.GetForPath("octokit", "octokit.net", "Octokit.Reactive/ObservableGitHubClient.cs"); + } + } + } +} \ No newline at end of file diff --git a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj index 864c56da..3a77029c 100644 --- a/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj +++ b/Octokit.Tests.Integration/Octokit.Tests.Integration.csproj @@ -9,18 +9,18 @@ {01687D54-1D87-4562-A721-C57F1C94052C} Library Properties - Octokit.Tests.Integration - Octokit.Tests.Integration + Octokit.Tests + Octokit.Tests v4.5 512 - 89e72d09 + f1b9719f true full false - bin\Debug\ - DEBUG;TRACE + bin\Debug\Net45\ + TRACE;DEBUG;NET_45 prompt 4 4014, 1998 @@ -29,14 +29,19 @@ pdbonly true bin\Release\Net45\ - TRACE + TRACE;NET_45 prompt 4 4014, 1998 + + False + ..\packages\NSubstitute.1.8.0.0\lib\net45\NSubstitute.dll + + False @@ -50,9 +55,8 @@ False ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll + - - ..\packages\xunit.abstractions.2.0.0-beta5-build2785\lib\net35\xunit.abstractions.dll @@ -62,81 +66,146 @@ ..\packages\xunit.core.2.0.0-beta5-build2785\lib\portable-net45+aspnetcore50+win+wpa81+wp80+monotouch+monoandroid\xunit.core.dll - - False - ..\packages\xunit.core.2.0.0-beta5-build2785\build\portable-net45+win+wpa81+wp80+monotouch+monoandroid\xunit.execution.dll - - - - - - + + + - - - - - - - - + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + - + + {674b69b8-0780-4d54-ae2b-c15821fa51cb} Octokit.Reactive - - {149448d4-c2f2-4df9-86bd-03e3272f093b} - Octokit.Tests - {08dd4305-7787-4823-a53f-4d0f725a07f3} Octokit + + + + + + + + - - Designer - + + - - - diff --git a/Octokit/Clients/IRepositoryContentsClient.cs b/Octokit/Clients/IRepositoryContentsClient.cs index 98020220..d2e55616 100644 --- a/Octokit/Clients/IRepositoryContentsClient.cs +++ b/Octokit/Clients/IRepositoryContentsClient.cs @@ -1,9 +1,14 @@ +using System.Collections.Generic; using System.Threading.Tasks; namespace Octokit { public interface IRepositoryContentsClient { + Task> GetRoot(string owner, string name); + + Task> GetForPath(string owner, string name, string path); + /// /// Gets the preferred README for the specified repository. /// diff --git a/Octokit/Clients/RepositoryContentsClient.cs b/Octokit/Clients/RepositoryContentsClient.cs index f2666823..8fb4ebfb 100644 --- a/Octokit/Clients/RepositoryContentsClient.cs +++ b/Octokit/Clients/RepositoryContentsClient.cs @@ -1,3 +1,6 @@ +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; namespace Octokit @@ -8,6 +11,47 @@ namespace Octokit { } + public Task> GetRoot(string owner, string name) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + + return ApiConnection.GetAll(ApiUrls.RepositoryContent(owner, name)); + } + + public async Task> GetForPath(string owner, string name, string path) + { + Ensure.ArgumentNotNullOrEmptyString(owner, "owner"); + Ensure.ArgumentNotNullOrEmptyString(name, "name"); + Ensure.ArgumentNotNullOrEmptyString(path, "path"); + + // First, find content in parent directory. + var content = await FindContent(owner, name, path); + + if (content == null) + { + // We've asked for a file/folder that don't exist. + return new List(); + } + + var url = ApiUrls.RepositoryContent(owner, name, path); + + // Check which type the content is before fetching/deserializing it. + switch (content.Type) + { + case ContentType.Dir: + return await ApiConnection.GetAll(url); + case ContentType.File: + return new List { await ApiConnection.Get(url) }; + case ContentType.Symlink: + return new List { await ApiConnection.Get(url) }; + case ContentType.Submodule: + return new List { await ApiConnection.Get(url) }; + default: + throw new ArgumentOutOfRangeException(); + } + } + /// /// Gets the preferred README for the specified repository. /// @@ -46,5 +90,22 @@ namespace Octokit return ApiConnection.GetHtml(ApiUrls.RepositoryReadme(owner, name), null); } + + private async Task FindContent(string owner, string name, string path) + { + var pathParts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + + var fileOrDirectoryName = pathParts.Last(); + + var parentPath = string.Join("/", pathParts.TakeWhile(x => x != fileOrDirectoryName)); + + var parentContentsUri = !string.IsNullOrEmpty(parentPath) + ? ApiUrls.RepositoryContent(owner, name, parentPath) + : ApiUrls.RepositoryContent(owner, name); + + var parentContents = await ApiConnection.GetAll(parentContentsUri); + + return parentContents.FirstOrDefault(x => x.Name == fileOrDirectoryName); + } } } \ No newline at end of file diff --git a/Octokit/Helpers/ApiUrls.cs b/Octokit/Helpers/ApiUrls.cs index b46868c8..68f91d92 100644 --- a/Octokit/Helpers/ApiUrls.cs +++ b/Octokit/Helpers/ApiUrls.cs @@ -1356,5 +1356,28 @@ namespace Octokit { return "repos/{0}/{1}/readme".FormatUri(owner, name); } + + /// + /// Creates the relative for getting the contents of the specified repository's root + /// + /// The owner of the repository + /// The name of the repository + /// The for getting the contents of the specified repository's root + public static Uri RepositoryContent(string owner, string name) + { + return "repos/{0}/{1}/contents".FormatUri(owner, name); + } + + /// + /// Creates the relative for getting the contents of the specified repository and path + /// + /// The owner of the repository + /// The name of the repository + /// The path of the contents to get + /// The for getting the contents of the specified repository and path + public static Uri RepositoryContent(string owner, string name, string path) + { + return "repos/{0}/{1}/contents/{2}".FormatUri(owner, name, path); + } } } diff --git a/Octokit/Models/Response/DirectoryContent.cs b/Octokit/Models/Response/DirectoryContent.cs new file mode 100644 index 00000000..d4d3234d --- /dev/null +++ b/Octokit/Models/Response/DirectoryContent.cs @@ -0,0 +1,51 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Octokit +{ + public class DirectoryContent + { + public string Name { get; set; } + + public string Path { get; set; } + + public string Sha { get; set; } + + public int? Size { get; set; } + + [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "Matches the property name used by the API")] + public ContentType Type { get; set; } + + public Uri Url { get; set; } + + public Uri GitUrl { get; set; } + + public Uri HtmlUrl { get; set; } + } + + public class FileContent : DirectoryContent + { + public string Encoding { get; set; } + + public string Content { get; set; } + } + + public class SymlinkContent : DirectoryContent + { + public string Target { get; set; } + } + + public class SubmoduleContent : DirectoryContent + { + public Uri SubmoduleGitUrl { get; set; } + } + + public enum ContentType + { + File, + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Dir", Justification = "Matches the value returned by the API")] + Dir, + Symlink, + Submodule + } +} \ No newline at end of file diff --git a/Octokit/Octokit-Mono.csproj b/Octokit/Octokit-Mono.csproj index 7d0b18aa..ef65e7c6 100644 --- a/Octokit/Octokit-Mono.csproj +++ b/Octokit/Octokit-Mono.csproj @@ -349,6 +349,7 @@ + diff --git a/Octokit/Octokit-MonoAndroid.csproj b/Octokit/Octokit-MonoAndroid.csproj index 1108ca6f..7ddac418 100644 --- a/Octokit/Octokit-MonoAndroid.csproj +++ b/Octokit/Octokit-MonoAndroid.csproj @@ -359,6 +359,7 @@ + \ No newline at end of file diff --git a/Octokit/Octokit-Monotouch.csproj b/Octokit/Octokit-Monotouch.csproj index 6bc98b0e..198629be 100644 --- a/Octokit/Octokit-Monotouch.csproj +++ b/Octokit/Octokit-Monotouch.csproj @@ -354,6 +354,7 @@ + diff --git a/Octokit/Octokit-Portable.csproj b/Octokit/Octokit-Portable.csproj index 9d0d4007..88b7c614 100644 --- a/Octokit/Octokit-Portable.csproj +++ b/Octokit/Octokit-Portable.csproj @@ -347,6 +347,7 @@ + diff --git a/Octokit/Octokit-netcore45.csproj b/Octokit/Octokit-netcore45.csproj index de4f7b6d..cc773fa0 100644 --- a/Octokit/Octokit-netcore45.csproj +++ b/Octokit/Octokit-netcore45.csproj @@ -351,6 +351,7 @@ + diff --git a/Octokit/Octokit.csproj b/Octokit/Octokit.csproj index bcd4a823..2fd26a4c 100644 --- a/Octokit/Octokit.csproj +++ b/Octokit/Octokit.csproj @@ -95,6 +95,7 @@ +