Skip to content

Instantly share code, notes, and snippets.

@anaisbetts
Created January 15, 2012 07:04
Show Gist options
  • Select an option

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

Select an option

Save anaisbetts/1614805 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Reactive.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Hammock;
using Hammock.Authentication.Basic;
using NLog;
using ReactiveUI;
namespace GitHub.Api
{
public class GitHubClient
{
static readonly Logger log = LogManager.GetCurrentClassLogger();
public const string DefaultAuthority = "https://api.github.com";
static readonly string userAgent;
readonly string authority;
readonly RestClient restClientOverride;
static GitHubClient()
{
userAgent = string.Format("MyAwesomeApp/{0}", Assembly.GetExecutingAssembly().GetName().Version);
}
public GitHubClient(string authority = null)
{
this.authority = authority ?? DefaultAuthority;
}
public GitHubClient(RestClient restClientOverrideForTestingPurposesOnly)
{
Ensure.ArgumentNotNull(restClientOverrideForTestingPurposesOnly);
restClientOverride = restClientOverrideForTestingPurposesOnly;
}
public int RateLimit { get; private set; }
public int RateLimitRemaining { get; private set; }
RestClient RestClient
{
get
{
if (restClientOverride != null)
{
return restClientOverride;
}
return new RestClient
{
Authority = authority,
Credentials = new BasicAuthCredentials
{
Username = /* TODO: Fetch the username from somewhere */
Password = /* TODO: And the Password */
},
UserAgent = userAgent,
Deserializer = new HammockJsonDotNetSerializer(),
Serializer = new HammockJsonDotNetSerializer()
};
}
}
public IObservable<RestResponse<GitHubRepository>> CreateRepository(GitHubRepository repo, string orgLogin = null)
{
var request = new RestRequest
{
Path = orgLogin == null ? "user/repos" : string.Format("orgs/{0}/repos", orgLogin),
Entity = 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<Dictionary<string, string>>> GetEmojis()
{
return ExecuteRequestAsync<Dictionary<string, string>>("emojis");
}
public IObservable<RestResponse<GitHubOrganization>> GetOrganization(string login)
{
Ensure.ArgumentNotNullOrEmptyString(login);
return ExecuteRequestAsync<GitHubOrganization>(string.Format("orgs/{0}", login));
}
public IObservable<RestResponse<GitHubParticipation>> GetParticipation(string nameWithOwner)
{
Ensure.ArgumentNotNullOrEmptyString(nameWithOwner);
return ExecuteRequestAsync<GitHubParticipation>(String.Format("{0}/graphs/participation", nameWithOwner));
}
IObservable<RestResponse<TResponse>> ExecuteRequestAsync<TResponse>(string path) where TResponse : class
{
var request = new RestRequest { Path = path };
var response = RestClient.RequestAsync<TResponse>(request);
return response.Do(resp =>
{
RateLimit = resp.Headers["X-RateLimit-Limit"].ToInt32();
RateLimitRemaining = resp.Headers["X-RateLimit-Remaining"].ToInt32();
});
}
}
public class GitHubRepository
{
public string CloneUrl { get; set; }
public DateTime CreatedAt { get; set; }
public string Description { get; set; }
public bool Fork { get; set; }
public int Forks { get; set; }
public string GitUrl { get; set; }
public bool HasDownloads { get; set; }
public bool HasIssues { get; set; }
public bool HasWiki { get; set; }
public string Homepage { get; set; }
public string HtmlUrl { get; set; }
public int Id { get; set; }
public string Language { get; set; }
public string MasterBranch { get; set; }
public string Name { get; set; }
public int OpenIssues { get; set; }
public GitHubUser Owner { get; set; }
public bool Private { get; set; }
public DateTime? PushedAt { get; set; }
public int Size { get; set; }
public string SshUrl { get; set; }
public string SvnUrl { get; set; }
public string Url { get; set; }
public int Watchers { get; set; }
}
public static class HammockObservableMixins
{
static readonly Logger log = LogManager.GetCurrentClassLogger();
public static IObservable<RestResponse> RequestAsync(this RestClient client, RestRequest request, int timeoutSeconds = 15)
{
var ret = new AsyncSubject<RestResponse>();
client.BeginRequest(request, (rq, resp, state) =>
{
try
{
log.Info(resp.ResponseUri);
if (log.IsDebugEnabled)
resp.Headers.AllKeys.ForEach(x => log.Debug("{0}: {1}", x, resp.Headers[x]));
ret.OnNext(resp);
ret.OnCompleted();
}
catch (Exception ex)
{
ret.OnError(ex);
}
});
return ret
.Timeout(TimeSpan.FromSeconds(timeoutSeconds), RxApp.TaskpoolScheduler)
.ThrowOnRestResponseFailure();
}
public static IObservable<RestResponse<T>> RequestAsync<T>(this RestClient client, RestRequest request, int timeoutSeconds = 15)
{
var ret = new AsyncSubject<RestResponse<T>>();
client.BeginRequest<T>(request, (rq, resp, state) =>
{
try
{
log.Info(resp.ResponseUri);
if(log.IsDebugEnabled)
resp.Headers.AllKeys.ForEach(x => log.Debug("{0}: {1}", x, resp.Headers[x]));
ret.OnNext(resp);
ret.OnCompleted();
}
catch (Exception ex)
{
ret.OnError(ex);
}
});
return ret
.Timeout(TimeSpan.FromSeconds(timeoutSeconds), RxApp.TaskpoolScheduler)
.ThrowOnRestResponseFailure();
}
public static IObservable<T> ThrowOnRestResponseFailure<T>(this IObservable<T> observable)
where T : RestResponseBase
{
return observable.SelectMany(x =>
{
if (x == null)
{
return Observable.Return(x);
}
if (x.InnerException != null)
{
return Observable.Throw<T>(x.InnerException);
}
if ((int)x.StatusCode >= 400)
{
return Observable.Throw<T>(new WebException(x.Content));
}
return Observable.Return(x);
});
}
}
public class HammockJsonDotNetSerializer : ISerializer, IDeserializer
{
readonly JsonSerializerSettings settings;
public HammockJsonDotNetSerializer()
: this(new JsonSerializerSettings { ContractResolver = new CustomContractResolver() })
{
}
public HammockJsonDotNetSerializer(JsonSerializerSettings settings)
{
this.settings = settings;
}
#region IDeserializer Members
public object Deserialize(RestResponseBase response, Type type)
{
return JsonConvert.DeserializeObject(response.Content, type, settings);
}
public T Deserialize<T>(RestResponseBase response)
{
return JsonConvert.DeserializeObject<T>(response.Content, settings);
}
public dynamic DeserializeDynamic(RestResponseBase response)
{
throw new NotImplementedException();
}
#endregion
#region ISerializer Members
public virtual string Serialize(object instance, Type type)
{
return JsonConvert.SerializeObject(instance, Formatting.None/*, settings*/);
}
public virtual string ContentType
{
get { return "application/json"; }
}
public virtual Encoding ContentEncoding
{
get { return Encoding.UTF8; }
}
#endregion
}
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]));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment