Skip to content

Instantly share code, notes, and snippets.

@anaisbetts
Created July 3, 2012 17:58
Show Gist options
  • Select an option

  • Save anaisbetts/3041390 to your computer and use it in GitHub Desktop.

Select an option

Save anaisbetts/3041390 to your computer and use it in GitHub Desktop.
using System.Globalization;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Serialization;
namespace GitHub.Api.Extensions
{
public class CustomContractResolver : CamelCasePropertyNamesContractResolver
{
protected override string ResolvePropertyName(string propertyName)
{
return ToUnderscores(propertyName);
}
static string ToUnderscores(string str)
{
return Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + "_" + char.ToLower(m.Value[1], CultureInfo.InvariantCulture));
}
}
}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Reactive.Linq;
using System.Text.RegularExpressions;
using Akavache;
using GitHub.Api.Extensions;
using GitHub.Extensions;
using GitHub.Helpers;
using NLog;
using RestSharp;
namespace GitHub.Api
{
public class GitHubClient : IGitHubClient
{
static readonly Logger log = LogManager.GetCurrentClassLogger();
public const string DefaultAuthority = "https://api.github.com";
public GitHubClient(string authority, Func<bool> isOnUiThreadFunc) : this(null, authority, isOnUiThreadFunc)
{
}
public GitHubClient(IGitHubRestClient restClient, string authority, Func<bool> isOnUiThreadFunc)
{
RestClient = restClient ?? new GitHubRestClient(BlobCache.Secure, authority ?? DefaultAuthority, isOnUiThreadFunc);
}
public int RateLimit { get; private set; }
public int RateLimitRemaining { get; private set; }
IGitHubRestClient RestClient { get; set; }
public IObservable<RestResponse<GitHubRepository>> CreateRepository(GitHubRepository repo, string orgLogin)
{
var request = new RestRequest
{
Method = Method.POST,
RequestFormat = DataFormat.Json,
JsonSerializer = new RestSharpJsonDotNetSerializer(),
Resource = orgLogin == null ? "user/repos" : string.Format(CultureInfo.InvariantCulture, "orgs/{0}/repos", orgLogin),
};
request.AddBody(new
{
name = repo.Name,
@public = !repo.Private,
description = repo.Description,
has_issues = repo.HasIssues,
has_downloads = repo.HasDownloads,
has_wiki = repo.HasWiki,
});
return RestClient.RequestAsync<GitHubRepository>(request);
}
public IObservable<RestResponse<List<GitHubSSHKey>>> GetSshKeys()
{
return ExecuteRequestAsync<List<GitHubSSHKey>>("user/keys");
}
public IObservable<RestResponse<GitHubSSHKey>> AddSshKey(GitHubSSHKey newKey)
{
log.Info("About to add SSH Key: {0} - {1}", newKey.Title, newKey.Key);
return ExecuteRequestAsync<GitHubSSHKey>("user/keys", Method.POST, new { title = newKey.Title, key = newKey.Key });
}
public IObservable<RestResponse<GitHubUser>> GetUser(string login)
{
var path = string.IsNullOrEmpty(login)
? "user"
: string.Format(CultureInfo.InvariantCulture, "users/{0}", login);
return ExecuteRequestAsync<GitHubUser>(path);
}
public IObservable<RestResponse<Dictionary<string, string>>> GetEmojis()
{
return ExecuteRequestAsync<Dictionary<string, string>>("emojis");
}
public IObservable<RestResponse<GitHubOrganization>> GetOrganization(string login)
{
Ensure.ArgumentNotNullOrEmptyString(login, "login");
return ExecuteRequestAsync<GitHubOrganization>(string.Format(CultureInfo.InvariantCulture, "orgs/{0}", login));
}
public IObservable<PagedRestResponse<GitHubOrganization>> GetOrganizations(Pagination pagination = null)
{
var request = new RestRequest { Resource = "user/orgs", JsonSerializer = new RestSharpJsonDotNetSerializer() };
return ExecuteRequestAsync<GitHubOrganization>(request, pagination);
}
public IObservable<RestResponse<GitHubParticipation>> GetParticipation(string nameWithOwner)
{
var ret = CoreUtility.Try<RestResponse<GitHubParticipation>>(() => Ensure.ArgumentNotNullOrEmptyString(nameWithOwner, "nameWithOwner"));
if (ret != null) return ret;
return ExecuteRequestAsync<GitHubParticipation>(String.Format(CultureInfo.InvariantCulture, "{0}/graphs/participation", nameWithOwner));
}
public IObservable<string> RenderLocalMarkdownAsHtml(string githubFlavoredMarkdown)
{
var ret = CoreUtility.Try<string>(() => Ensure.ArgumentNotNullOrEmptyString(githubFlavoredMarkdown, "githubFlavoredMarkdown"));
if (ret != null) return ret;
return ExecuteRequestAsync("markdown/raw", Method.POST, beforeExecuteHook: rq =>
rq.AddParameter("text/x-markdown", githubFlavoredMarkdown, ParameterType.RequestBody)
).Select(x => x.Content);
}
public IObservable<RestResponse> GetReadmeAsHtml(string nameWithOwner)
{
var ret = CoreUtility.Try<RestResponse>(() => Ensure.ArgumentNotNullOrEmptyString(nameWithOwner, "nameWithOwner"));
if (ret != null) return ret;
return ExecuteRequestAsync(String.Format(CultureInfo.InvariantCulture, "repos/{0}/readme", nameWithOwner), Method.GET, null, new Dictionary<string, string>() { { "Accept", "application/vnd.github.html" } });
}
public IObservable<PagedRestResponse<GitHubRepository>> GetRepositories(string orgLogin, GitHubRepositoryType type = GitHubRepositoryType.All, Pagination pagination = null)
{
var request = new RestRequest { Resource = String.Format(CultureInfo.InvariantCulture, "orgs/{0}/repos", orgLogin), JsonSerializer = new RestSharpJsonDotNetSerializer() };
request.AddParameter("type", type.ToString());
var res = ExecuteRequestAsync<GitHubRepository>(request, pagination);
var pages = res.Where(x => x.RestResponse.StatusCode == HttpStatusCode.OK && x.PagedList.IsNextPage)
.SelectMany(x => GetRepositories(orgLogin, type, x.PagedList.NextPage));
return res.Merge(pages);
}
public IObservable<PagedRestResponse<GitHubRepository>> GetRepositories(GitHubRepositoryType type, Pagination pagination = null)
{
var request = new RestRequest { Resource = "user/repos", JsonSerializer = new RestSharpJsonDotNetSerializer() };
request.AddParameter("type", type.ToString());
var res = ExecuteRequestAsync<GitHubRepository>(request, pagination);
var pages = res.Where(x => x.RestResponse.StatusCode == HttpStatusCode.OK && x.PagedList.IsNextPage)
.SelectMany(x => GetRepositories(type, x.PagedList.NextPage));
return res.Merge(pages);
}
public IObservable<RestResponse<GitHubRepository>> GetRepository(string nameWithOwner)
{
return ExecuteRequestAsync<GitHubRepository>(String.Format(CultureInfo.InvariantCulture, "repos/{0}", nameWithOwner));
}
public IObservable<PagedRestResponse<GitHubRepository>> GetUserRepositories(IEnumerable<GitHubOrganization> orgs)
{
var o = Observable.Merge(GetRepositories(GitHubRepositoryType.Private),
GetRepositories(GitHubRepositoryType.Public),
GetRepositories(GitHubRepositoryType.Member).Do(r => r.PagedList.Each(repo => repo.IsCollaborator = true)),
orgs.ToObservable()
.Select(p => GetRepositories(p.Login))
.SelectMany(p => p.ToEnumerable()));
return o;
}
IObservable<RestResponse> ExecuteRequestAsync(string path, Method method = Method.GET, object Content = null, Dictionary<string, string> headers = null, Action<RestRequest> beforeExecuteHook = null)
{
var request = new RestRequest { Resource = path, Method = method, };
request.RequestFormat = DataFormat.Json;
if (Content != null)
{
request.AddBody(Content);
}
if (headers != null && headers.Count > 0)
{
headers.ForEach(x => request.AddHeader(x.Key, x.Value));
}
if (beforeExecuteHook != null)
{
beforeExecuteHook(request);
}
var response = RestClient.RequestAsync(request);
return response.Do(resp =>
{
var rateLimit = resp.Headers.FirstOrDefault(x => x.Name == "X-RateLimit-Limit");
if (rateLimit != null) RateLimit = rateLimit.Value.ToString().ToInt32();
var rateLimitRemaining = resp.Headers.FirstOrDefault(x => x.Name == "X-RateLimit-Remaining");
if (rateLimitRemaining != null) RateLimitRemaining = rateLimitRemaining.Value.ToString().ToInt32();
});
}
IObservable<RestResponse<T>> ExecuteRequestAsync<T>(string path, Method method = Method.GET, object Content = null, Dictionary<string, string> headers = null, Action<RestRequest> beforeExecuteHook = null)
where T : new()
{
var request = new RestRequest { Resource = path, Method = method, };
request.RequestFormat = DataFormat.Json;
if (Content != null)
{
request.AddBody(Content);
}
if (headers != null && headers.Count > 0)
{
headers.ForEach(x => request.AddHeader(x.Key, x.Value));
}
if (beforeExecuteHook != null)
{
beforeExecuteHook(request);
}
var response = RestClient.RequestAsync<T>(request);
return response.Do(resp =>
{
var rateLimit = resp.Headers.FirstOrDefault(x => x.Name == "X-RateLimit-Limit");
if (rateLimit != null) RateLimit = rateLimit.Value.ToString().ToInt32();
var rateLimitRemaining = resp.Headers.FirstOrDefault(x => x.Name == "X-RateLimit-Remaining");
if (rateLimitRemaining != null) RateLimitRemaining = rateLimitRemaining.Value.ToString().ToInt32();
});
}
IObservable<PagedRestResponse<TResponse>> ExecuteRequestAsync<TResponse>(RestRequest request, Pagination pagination = null)
where TResponse : class
{
pagination = pagination ?? Pagination.Default;
request.AddParameter("page", pagination.PageIndex.ToString(CultureInfo.InvariantCulture));
request.AddParameter("per_page", pagination.PageSize.ToString(CultureInfo.InvariantCulture));
var response = RestClient.RequestAsync<List<TResponse>>(request);
return response
.Do(resp =>
{
var rateLimit = resp.Headers.FirstOrDefault(x => x.Name == "X-RateLimit-Limit");
if (rateLimit != null) RateLimit = rateLimit.Value.ToString().ToInt32();
var rateLimitRemaining = resp.Headers.FirstOrDefault(x => x.Name == "X-RateLimit-Remaining");
if (rateLimitRemaining != null) RateLimitRemaining = rateLimitRemaining.Value.ToString().ToInt32();
})
.Select(resp =>
{
var prResponse = new PagedRestResponse<TResponse>(resp)
{
PagedList =
{
PageIndex = pagination.PageIndex,
PageSize = pagination.PageSize
}
};
prResponse.PagedList.TotalCount = pagination.PageIndex*pagination.PageSize + prResponse.PagedList.Count;
var last = GetLastLink(resp);
if (last != null)
{
var pageParam = new Regex("[^_]page=[0-9]+").Match(last).Value;
var lastPage = new Regex("[0-9]+").Match(pageParam).Value.ToInt32();
prResponse.PagedList.TotalCount =
lastPage == 0
? resp.Data.Count
: (lastPage + 1)*pagination.PageSize;
prResponse.PagedList.IsNextPage = true;
}
return prResponse;
});
}
static string GetLastLink<TResponse>(RestResponse<List<TResponse>> resp) where TResponse : class
{
if (resp.Data == null) return null;
var linkHeader = resp.Headers.SingleOrDefault(x => x.Name == "Link");
if (linkHeader == null) return null;
var link = linkHeader.Value.ToString();
if (string.IsNullOrWhiteSpace(link)) return null;
return link.Split(',').SingleOrDefault(p => new Regex("rel=\"last\"").IsMatch(p));
}
}
}
using System;
using System.Globalization;
using Newtonsoft.Json;
using NLog;
using RestSharp;
using RestSharp.Deserializers;
using RestSharp.Serializers;
namespace GitHub.Api.Extensions
{
public class RestSharpJsonDotNetSerializer : ISerializer, IDeserializer
{
static readonly Logger log = LogManager.GetCurrentClassLogger();
readonly JsonSerializerSettings settings;
public RestSharpJsonDotNetSerializer()
: this(new JsonSerializerSettings { ContractResolver = new CustomContractResolver() })
{
}
public RestSharpJsonDotNetSerializer(JsonSerializerSettings settings)
{
this.settings = settings;
}
public string Serialize(object obj)
{
return JsonConvert.SerializeObject(obj, Formatting.None /*, settings*/);
}
public T Deserialize<T>(RestResponse response) where T : new()
{
try
{
return JsonConvert.DeserializeObject<T>(response.Content, settings);
}
catch (Exception)
{
log.Warn(CultureInfo.InvariantCulture, "Error while deserializing JSON response into a '{0}'", typeof(T).Name);
log.Info(CultureInfo.InvariantCulture, "\tResponse.ResponseUri: '{0}'", response.ResponseUri);
log.Info(CultureInfo.InvariantCulture, "\tResponse.StatusCode: '{0}'", response.StatusCode);
log.Info(CultureInfo.InvariantCulture, "\tResponse.Content: '{0}'", response.Content);
throw;
}
}
public string RootElement { get; set; }
public string Namespace { get; set; }
public string DateFormat { get; set; }
public string ContentType
{
get { return "application/json"; }
set { }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment