Skip to content

Instantly share code, notes, and snippets.

@RickStrahl
Last active February 23, 2019 02:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RickStrahl/e2a8ab089a02d2b79fbfe79cb2ba0ffe to your computer and use it in GitHub Desktop.
Save RickStrahl/e2a8ab089a02d2b79fbfe79cb2ba0ffe to your computer and use it in GitHub Desktop.
Recursively parse a Github Repository with GraphQL
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Octokit.GraphQL;
using Octokit.GraphQL.Model;
namespace GithubGraphQl
{
public class GithubRepositoryTree
{
private readonly string _token;
private readonly string _owner;
private readonly string _repository;
public string ApiName { get; set; } = "GraphQLTest";
public string ApiVersion { get; set; } = "0.1";
public GithubRepositoryTree(string token, string owner, string repository,
string apiName = null, string apiVersion = null)
{
_token = token;
_owner = owner;
_repository = repository;
if (apiName != null)
ApiName = apiName;
if (apiVersion != null)
ApiName = apiName;
}
/// <summary>
/// Retrieves all files in a given Github branch/path. Optionally
/// allows recursively retrieving all content in child folders/trees.
///
/// Note: child folders are lazy loaded so recursive action can
/// result in a lot of separate HTTP requests made to the server
/// </summary>
/// <param name="path">Branch and path to to start parsing on using `branch:path` syntax:
/// Example: `master:` or `master:subpath\subpath2`</param>
/// <param name="recursive">If true recurses any subpaths</param>
/// <returns>List of Items optionally with child items</returns>
public async Task<List<GithubFolderItem>> GetFolder(string path = "master:", bool recursive = true)
{
var productInformation = new ProductHeaderValue(ApiName, ApiVersion);
var connection = new Connection(productInformation, _token);
var query = new Query()
.Repository(name: _repository, owner: _owner)
.Object(expression: path)
.Cast<Tree>()
.Entries.Select(x => new GithubFolderItem
{
Name = x.Name,
Type = x.Type,
})
.Compile();
var entries = await connection.Run(query);
foreach (var entry in entries)
{
entry.FullPath = path + entry.Name;
if (!recursive || entry.Type != "tree")
continue;
entry.Items = await GetFolder(entry.FullPath + "/");
}
return entries.ToList();
}
/// <summary>
/// Retrieve the contents of an file/resource from a
/// from a Github repository. Provide a path to retrieve in
/// `branch:path\file.md` format.
/// </summary>
/// <param name="path">path to a resource: `master:file.txt` or `master:path/file.txt`</param>
/// <returns>file content or throws</returns>
public async Task<GithubItem> GetItemContent(string path)
{
var productInformation = new ProductHeaderValue(ApiName, ApiVersion);
var connection = new Connection(productInformation, _token);
var query = new Query()
.Repository(name: _repository, owner: _owner)
.Object(expression: path)
.Cast<Blob>()
.Select(x => new GithubItem
{
FullPath = path,
IsBinary = x.IsBinary,
ByteSize = x.ByteSize,
Text = x.Text
}).Compile();
var result = await connection.Run(query);
result.Name = Path.GetFileName(path);
return result;
}
/// <summary>
///
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public async Task<GithubItem> GetItemContent(GithubFolderItem item)
{
return await GetItemContent(item.FullPath);
}
}
/// <summary>
/// Holds information about an individual folder item
/// </summary>
public class GithubFolderItem
{
/// <summary>
/// The simple name of this file/resource
/// </summary>
public string Name { get; set; }
/// <summary>
/// Type: blob or tree
/// </summary>
public string Type { get; set; }
/// <summary>
/// Full Github path to this item
/// </summary>
public string FullPath { get; set; }
/// <summary>
/// If this is a tree, contains child FolderItems entries
/// </summary>
public List<GithubFolderItem> Items { get; set; }
public override string ToString()
{
return $"{Name} ({Items?.Count ?? 0})";
}
}
/// <summary>
/// An individual Github file/resource item
/// </summary>
public class GithubItem
{
/// <summary>
/// The simple name of the resource blob
/// </summary>
public string Name { get; set; }
/// <summary>
/// The full path to the resource
/// </summary>
public string FullPath { get; set; }
/// <summary>
/// Size in bytes
/// </summary>
public int ByteSize { get; set; }
/// <summary>
/// If the contents is binary data
/// </summary>
public bool IsBinary { get; set; }
/// <summary>
/// Contents of the file/resource
/// </summary>
public string Text { get; set; }
public override string ToString()
{
return $"{Name} ({ByteSize.ToString("n0")})";
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment