mirror of
https://github.com/zoriya/octokit.net.git
synced 2026-06-03 19:11:30 +00:00
Add documentation and refactor some types
This commit is contained in:
@@ -1,16 +1,18 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Octokit.Models.Request;
|
||||
using Xunit;
|
||||
|
||||
namespace Octokit.Tests.Integration.Clients
|
||||
{
|
||||
public class RepositoryContentsClientTests
|
||||
{
|
||||
public class TheGetRootMethod
|
||||
public class TheGetContentsMethod
|
||||
{
|
||||
[IntegrationTest]
|
||||
public async Task GetsRootContents()
|
||||
public async Task GetsFileContent()
|
||||
{
|
||||
var github = new GitHubClient(new ProductHeaderValue("OctokitTests"))
|
||||
{
|
||||
@@ -20,10 +22,78 @@ namespace Octokit.Tests.Integration.Clients
|
||||
var contents = await github
|
||||
.Repository
|
||||
.Content
|
||||
.GetForPath("octokit", "octokit.net", "Octokit.Reactive/ObservableGitHubClient.cs");
|
||||
.GetContents("octokit", "octokit.net", "Octokit.Reactive/ObservableGitHubClient.cs");
|
||||
|
||||
Assert.Equal(1, contents.Count);
|
||||
Assert.Equal(ContentType.File, contents.First().Type);
|
||||
Assert.Equal(new Uri("https://github.com/octokit/octokit.net/blob/master/Octokit.Reactive/ObservableGitHubClient.cs"), contents.First().HtmlUrl);
|
||||
}
|
||||
|
||||
[IntegrationTest]
|
||||
public async Task GetsDirectoryContent()
|
||||
{
|
||||
var github = new GitHubClient(new ProductHeaderValue("OctokitTests"))
|
||||
{
|
||||
Credentials = Helper.Credentials
|
||||
};
|
||||
|
||||
var contents = await github
|
||||
.Repository
|
||||
.Content
|
||||
.GetContents("octokit", "octokit.net", "Octokit");
|
||||
|
||||
Assert.True(contents.Count > 2);
|
||||
Assert.Equal(ContentType.Dir, contents.First().Type);
|
||||
}
|
||||
}
|
||||
|
||||
[IntegrationTest]
|
||||
public async Task CrudTest()
|
||||
{
|
||||
var client = new GitHubClient(new ProductHeaderValue("OctokitTests"))
|
||||
{
|
||||
Credentials = Helper.Credentials
|
||||
};
|
||||
Repository repository = null;
|
||||
try
|
||||
{
|
||||
var fixture = client.Repository.Content;
|
||||
var repoName = Helper.MakeNameWithTimestamp("source-repo");
|
||||
repository = await client.Repository.Create(new NewRepository { Name = repoName, AutoInit = true });
|
||||
|
||||
var file = await fixture.CreateFile(
|
||||
repository.Owner.Login,
|
||||
repository.Name,
|
||||
"somefile.txt",
|
||||
new CreateFileRequest("Test commit", "Some Content"));
|
||||
Assert.Equal("Some Content", file.Content.Content);
|
||||
|
||||
var contents = await fixture.GetContents(repository.Owner.Login, repository.Name, "somefile.txt");
|
||||
string fileSha = contents.First().Sha;
|
||||
Assert.Equal("Some Content", contents.First().Content);
|
||||
|
||||
var update = await fixture.UpdateFile(
|
||||
repository.Owner.Login,
|
||||
repository.Name,
|
||||
"somefile.txt",
|
||||
new UpdateFileRequest("Updating file", "New Content", fileSha));
|
||||
string updatedFileSha = update.Commit.Sha;
|
||||
Assert.Equal("New Content", update.Content.Content);
|
||||
|
||||
await fixture.DeleteFile(
|
||||
repository.Owner.Login,
|
||||
repository.Name,
|
||||
"somefile.txt",
|
||||
new DeleteFileRequest("Deleted file", updatedFileSha));
|
||||
|
||||
await Assert.ThrowsAsync<FileNotFoundException>(
|
||||
async () => await fixture.GetContents(repository.Owner.Login, repository.Name, "somefile.txt"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Assert.NotNull(repository);
|
||||
client.Repository.Delete(repository.Owner.Login, repository.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,19 @@ namespace Octokit
|
||||
{
|
||||
public interface IRepositoryContentsClient
|
||||
{
|
||||
Task<IReadOnlyList<DirectoryContent>> GetRoot(string owner, string name);
|
||||
|
||||
Task<IReadOnlyList<DirectoryContent>> GetForPath(string owner, string name, string path);
|
||||
/// <summary>
|
||||
/// Returns the contents of a file or directory in a repository.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If given a path to a single file, this method returns a collection containing only that file.
|
||||
/// </remarks>
|
||||
/// <param name="owner">The owner of the repository</param>
|
||||
/// <param name="name">The name of the repository</param>
|
||||
/// <param name="path">The content path</param>
|
||||
/// <returns>
|
||||
/// A collection of <see cref="RepositoryContent"/> representing the content at the specified path
|
||||
/// </returns>
|
||||
Task<IReadOnlyList<RepositoryContent>> GetContents(string owner, string name, string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the preferred README for the specified repository.
|
||||
@@ -42,7 +52,7 @@ namespace Octokit
|
||||
/// <param name="path">The path to the file</param>
|
||||
/// <param name="request">Information about the file to create</param>
|
||||
/// <returns></returns>
|
||||
Task<CreatedContent> CreateFile(string owner, string name, string path, CreateFileRequest request);
|
||||
Task<RepositoryContentChangeSet> CreateFile(string owner, string name, string path, CreateFileRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a commit that updates the contents of a file in a repository.
|
||||
@@ -52,7 +62,7 @@ namespace Octokit
|
||||
/// <param name="path">The path to the file</param>
|
||||
/// <param name="request">Information about the file to update</param>
|
||||
/// <returns>The updated content</returns>
|
||||
Task<CreatedContent> UpdateFile(string owner, string name, string path, UpdateFileRequest request);
|
||||
Task<RepositoryContentChangeSet> UpdateFile(string owner, string name, string path, UpdateFileRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a commit that deletes a file in a repository.
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Octokit.Models.Request;
|
||||
|
||||
@@ -12,45 +10,28 @@ namespace Octokit
|
||||
{
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<DirectoryContent>> GetRoot(string owner, string name)
|
||||
{
|
||||
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
|
||||
Ensure.ArgumentNotNullOrEmptyString(name, "name");
|
||||
|
||||
return ApiConnection.GetAll<DirectoryContent>(ApiUrls.RepositoryContent(owner, name));
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<DirectoryContent>> GetForPath(string owner, string name, string path)
|
||||
/// <summary>
|
||||
/// Returns the contents of a file or directory in a repository.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If given a path to a single file, this method returns a collection containing only that file.
|
||||
/// </remarks>
|
||||
/// <param name="owner">The owner of the repository</param>
|
||||
/// <param name="name">The name of the repository</param>
|
||||
/// <param name="path">The content path</param>
|
||||
/// <returns>
|
||||
/// A collection of <see cref="RepositoryContent"/> representing the content at the specified path
|
||||
/// </returns>
|
||||
public async Task<IReadOnlyList<RepositoryContent>> GetContents(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<DirectoryContent>();
|
||||
}
|
||||
|
||||
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<DirectoryContent>(url);
|
||||
case ContentType.File:
|
||||
return new List<DirectoryContent> { await ApiConnection.Get<FileContent>(url) };
|
||||
case ContentType.Symlink:
|
||||
return new List<DirectoryContent> { await ApiConnection.Get<SymlinkContent>(url) };
|
||||
case ContentType.Submodule:
|
||||
return new List<DirectoryContent> { await ApiConnection.Get<SubmoduleContent>(url) };
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
return await ApiConnection.GetAll<RepositoryContent>(url);
|
||||
// return new List<RepositoryContent> { await ApiConnection.Get<RepositoryContent>(url) }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -100,7 +81,7 @@ namespace Octokit
|
||||
/// <param name="path">The path to the file</param>
|
||||
/// <param name="request">Information about the file to create</param>
|
||||
/// <returns></returns>
|
||||
public Task<CreatedContent> CreateFile(string owner, string name, string path, CreateFileRequest request)
|
||||
public Task<RepositoryContentChangeSet> CreateFile(string owner, string name, string path, CreateFileRequest request)
|
||||
{
|
||||
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
|
||||
Ensure.ArgumentNotNullOrEmptyString(name, "name");
|
||||
@@ -108,7 +89,7 @@ namespace Octokit
|
||||
Ensure.ArgumentNotNull(request, "request");
|
||||
|
||||
var createUrl = ApiUrls.RepositoryContent(owner, name, path);
|
||||
return ApiConnection.Put<CreatedContent>(createUrl, request);
|
||||
return ApiConnection.Put<RepositoryContentChangeSet>(createUrl, request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -119,7 +100,7 @@ namespace Octokit
|
||||
/// <param name="path">The path to the file</param>
|
||||
/// <param name="request">Information about the file to update</param>
|
||||
/// <returns>The updated content</returns>
|
||||
public Task<CreatedContent> UpdateFile(string owner, string name, string path, UpdateFileRequest request)
|
||||
public Task<RepositoryContentChangeSet> UpdateFile(string owner, string name, string path, UpdateFileRequest request)
|
||||
{
|
||||
Ensure.ArgumentNotNullOrEmptyString(owner, "owner");
|
||||
Ensure.ArgumentNotNullOrEmptyString(name, "name");
|
||||
@@ -127,7 +108,7 @@ namespace Octokit
|
||||
Ensure.ArgumentNotNull(request, "request");
|
||||
|
||||
var updateUrl = ApiUrls.RepositoryContent(owner, name, path);
|
||||
return ApiConnection.Put<CreatedContent>(updateUrl, request);
|
||||
return ApiConnection.Put<RepositoryContentChangeSet>(updateUrl, request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -147,22 +128,5 @@ namespace Octokit
|
||||
var deleteUrl = ApiUrls.RepositoryContent(owner, name, path);
|
||||
return ApiConnection.Delete(deleteUrl, request);
|
||||
}
|
||||
|
||||
private async Task<DirectoryContent> 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<DirectoryContent>(parentContentsUri);
|
||||
|
||||
return parentContents.FirstOrDefault(x => x.Name == fileOrDirectoryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,6 @@ namespace Octokit
|
||||
Ensure.ArgumentNotNull(uri, "uri");
|
||||
|
||||
return SendData<T>(uri.ApplyParameters(parameters), HttpMethod.Get, null, accepts, null, CancellationToken.None);
|
||||
|
||||
}
|
||||
|
||||
public Task<IResponse<T>> Get<T>(Uri uri, IDictionary<string, string> parameters, string accepts, CancellationToken cancellationToken)
|
||||
|
||||
@@ -3,43 +3,77 @@ using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Octokit
|
||||
{
|
||||
public class DirectoryContent
|
||||
/// <summary>
|
||||
/// Represents a piece of content in the repository. This could be a submodule, a symlink, a directory, or a file.
|
||||
/// Look at the Type property to figure out which one it is.
|
||||
/// </summary>
|
||||
public class RepositoryContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the content.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to this content.
|
||||
/// </summary>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SHA of the last commit that modified this content.
|
||||
/// </summary>
|
||||
public string Sha { get; set; }
|
||||
|
||||
public int? Size { get; set; }
|
||||
/// <summary>
|
||||
/// Size of the content.
|
||||
/// </summary>
|
||||
public int Size { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The encoding of the content if this is a file. Typically "base64". Otherwise it's null.
|
||||
/// </summary>
|
||||
public string Encoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The encoded content if this is a file. Otherwise it's null.
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of this content. It might be a File, Directory, Submodule, or Symlink
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "Matches the property name used by the API")]
|
||||
public ContentType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to the target file in the repository if this is a symlink. Otherwise it's null.
|
||||
/// </summary>
|
||||
public string Target { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The location of the submodule repository if this is a submodule. Otherwise it's null.
|
||||
/// </summary>
|
||||
public Uri SubmoduleGitUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// URL to this content
|
||||
/// </summary>
|
||||
public Uri Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The GIT URL to this content.
|
||||
/// </summary>
|
||||
public Uri GitUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL to view this content on GitHub.
|
||||
/// </summary>
|
||||
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; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The possible repository content types.
|
||||
/// </summary>
|
||||
public enum ContentType
|
||||
{
|
||||
File,
|
||||
@@ -49,9 +83,21 @@ namespace Octokit
|
||||
Submodule
|
||||
}
|
||||
|
||||
public class CreatedContent
|
||||
/// <summary>
|
||||
/// The response from the Repository Contents API. The API assumes a dynamic client type so we need
|
||||
/// to model that.
|
||||
/// </summary>
|
||||
/// <remarks>https://developer.github.com/v3/repos/contents/</remarks>
|
||||
public class RepositoryContentChangeSet
|
||||
{
|
||||
public DirectoryContent Content { get; set; }
|
||||
/// <summary>
|
||||
/// The directory content of the response.
|
||||
/// </summary>
|
||||
public RepositoryContent Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The commit information for the content change.
|
||||
/// </summary>
|
||||
public Commit Commit { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user