Created
July 3, 2012 17:58
-
-
Save anaisbetts/3041390 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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)); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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)); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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