using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Newtonsoft.Json;
using System.Net.Http.Formatting;
using CPE.App.Web.Models;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Reflection;
using CPE.App.Web.Code;
namespace CPE.App.Web.Elucidat
{
public class EludicatClient
{
private readonly string _publicKey;
private readonly string _secretKey;
private readonly bool _simulationMode;
private readonly Uri _baseUrl;
private readonly JsonMediaTypeFormatter[] _jsonFormatters =
{
new JsonMediaTypeFormatter
{
SerializerSettings = new JsonSerializerSettings
{
ContractResolver = new UnderscoreContractResolver(),
}
}
};
///
/// Create and configure API client service
///
///
///
///
///
public EludicatClient(string publicKey, string secretKey, bool simulationMode, string baseUrl)
{
this._publicKey = publicKey;
this._secretKey = secretKey;
this._simulationMode = simulationMode;
this._baseUrl = new Uri(baseUrl);
}
///
/// Call a GET API method returning a structure of type T without URL parameters
///
///
///
///
private T Get(string url)
{
return Get(url, new Dictionary());
}
///
/// Call a GET API method returning a structure of type T with URL parameters
///
///
///
///
///
public T Get(string url, IDictionary fields)
{
//string fieldsFirstKeyValue = String.Empty;
//if (fields.Count > 0)
//{
// fieldsFirstKeyValue = fields.First().Key + " " + fields.First().Value;
//}
//Extensions.LogServiceCall("[EludicatClient][Get]", String.Format("url = {0} fieldsFirstKeyValue = {1} _simulationMode = {2}", url, fieldsFirstKeyValue, _simulationMode));
if (_simulationMode)
fields =
fields.Concat(new Dictionary { { "simulation_mode", "simulation" } })
.ToDictionary(x => x.Key, x => x.Value);
return CallGet(AuthHeaders(GetNonce(url)), fields, url);
}
///
/// Internal get-based API call. Used for both actual API call and obtaining nonces.
///
///
///
///
///
///
private T CallGet(IDictionary headers, IDictionary fields, string url)
{
T result = default(T);
string fieldsFirstKeyValue = String.Empty;
//if (fields.Count > 0)
//{
// fieldsFirstKeyValue = fields.First().Key + " " + fields.First().Value;
//}
//Extensions.LogServiceCall("[EludicatClient][CallGet]", String.Format("fields.Count = {0} fieldsFirstKeyValue = {1}", fields.Count, fieldsFirstKeyValue));
var signedHeaders =
headers.Concat(new Dictionary
{
{"oauth_signature", Sign(headers.Concat(fields), url, "GET")}
});
// Remove insecure protocols (SSL3, TLS 1.0, TLS 1.1)
ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Ssl3;
ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Tls;
ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Tls11;
// Add TLS 1.2
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
var client = new WebClient { BaseAddress = _baseUrl.ToString() };
client.Headers.Clear();
client.Headers["Accept"] = "application/json";
client.Headers.Add("Authorization", BuildBaseString(signedHeaders, ","));
var urlFull = url + "?" + BuildBaseString(fields, "&");
var response = client.DownloadString(urlFull);
try
{
result = (T)JsonConvert.DeserializeObject(response, typeof(T), _jsonFormatters[0].SerializerSettings);
}
catch (Exception exception)
{
if (fields.Count > 0)
{
fieldsFirstKeyValue = fields.First().Key + " " + fields.First().Value;
}
Extensions.LogServiceCall("[EludicatClient][CallGet]", String.Format("urlFull = {0} typeof(T) = {1} fields.Count = {2} fieldsFirstKeyValue = {3} _baseUrl = {4} response = {5}", urlFull, typeof(T).ToString(), fields.Count, fieldsFirstKeyValue, _baseUrl, response));
Extensions.LogServiceError("[EludicatClient][CallGet]", exception);
throw;
}
return result;
}
///
/// Call a POST-based API method with parameters
///
///
///
///
///
public T Post(string url, IDictionary fields)
{
if (_simulationMode)
fields =
fields.Concat(new Dictionary { { "simulation_mode", "simulation" } })
.ToDictionary(x => x.Key, x => x.Value);
return CallPost(AuthHeaders(GetNonce(url)), fields, url);
}
///
/// Internal POST call. Used only for actual API method invocations.
///
///
///
///
///
///
private T CallPost(IDictionary headers, IDictionary fields, string url)
{
var signedHeaders =
headers.Concat(new Dictionary
{
{"oauth_signature", Sign(headers.Concat(fields), url, "POST")}
});
// Remove insecure protocols (SSL3, TLS 1.0, TLS 1.1)
ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Ssl3;
ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Tls;
ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Tls11;
// Add TLS 1.2
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
var client = new WebClient();
client.BaseAddress = _baseUrl.ToString();
client.Headers.Clear();
client.Headers["Accept"] = "application/json";
client.Headers["Content-Type"] = "application/x-www-form-urlencoded";
client.Headers.Add("Authorization", BuildBaseString(signedHeaders, ","));
client.Headers.Add("Expect", "");
var content = BuildBaseString(fields, "&");
try
{
var response = client.UploadString(url, content);
var result = (T)JsonConvert.DeserializeObject(response, typeof(T), _jsonFormatters[0].SerializerSettings);
return result;
}
catch (WebException e)
{
throw;
}
}
///
/// Generate an HMAC signature for an API request
///
///
///
///
///
private string Sign(IEnumerable> parameters, string url, string httpMethod)
{
var baseString = httpMethod + "&" + new Uri(_baseUrl, url) + "&" +
BuildBaseString(parameters.OrderBy(x => x.Key), "&");
var hmac = new HMACSHA1(Encoding.ASCII.GetBytes(_secretKey.RawUrlEncode()));
hmac.Initialize();
return Convert.ToBase64String(hmac.ComputeHash(Encoding.ASCII.GetBytes(baseString)));
}
///
/// Create a query-string like single string from a parameter dictionary.
///
///
///
///
private string BuildBaseString(IEnumerable> parameters, string delimiter)
{
return string.Join(delimiter, parameters.Select(x => x.Key.RawUrlEncode() + "=" + x.Value.RawUrlEncode()));
}
///
/// Create a parameter dictionary from an object
///
///
/////
//private IDictionary BuildParameterDictionary(object o)
//{
// IDictionary dict = o.GetType()
// .FindMembers(MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public, null, null)
// .Where(p => (((PropertyInfo)p).GetValue(o)) != null)
// .ToDictionary(propertyInfo => propertyInfo.Name.TransformPropertyName(), propertyInfo => Convert.ToString(((PropertyInfo)propertyInfo).GetValue(o)));
// return dict;
//}
///
/// Base authentication headers required for every API request.
///
///
///
private IDictionary AuthHeaders(string nonce)
{
var headers = new Dictionary
{
{"oauth_consumer_key", _publicKey},
{"oauth_signature_method", "HMAC-SHA1"},
{
"oauth_timestamp",
Math.Floor(
DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds)
.ToString()
},
{"oauth_version", "1.0"}
};
if (nonce != null)
headers.Add("oauth_nonce", nonce);
return headers;
}
///
/// Retrieve a security nonce from the API to authenticate the next method call.
///
///
///
private string GetNonce(string url)
{
string returnValue = null;
var response = CallGet(AuthHeaders(null), new Dictionary(), url);
if (response != null)
{
returnValue = response.Nonce;
}
return returnValue;
}
///
/// Retrieve the details of a single release
///
///
///
public ReleaseModel GetRelease(string releaseCode)
{
return Get("releases/details", new Dictionary { { "release_code", releaseCode } });
}
///
/// Retrieve a link which can be used to launch the given release.
///
///
///
public LinkModel GetLaunchLink(string releaseCode)
{
return Get("releases/launch", new Dictionary
{
{"release_code", releaseCode}
});
}
///
/// Retrieve a link which can be used to launch the given release.
///
///
///
///
///
///
///
public LinkModel GetLaunchLink(string releaseCode, string firstname, string lastname, string email)
{
return Get("releases/launch", new Dictionary
{
{ "release_code", releaseCode},
{ "name", firstname + " " + lastname},
{ "email_address", email}
});
}
public MessageModel ConfigureProject(ProjectSettingModel settings)
{
Extensions.LogServiceCall("[EludicatClient][ConfigureProject]", String.Format("ProjectCode = {0}", settings.ProjectCode));
return Post("projects/configure", BuildParameterDictionary(settings));
}
public MessageModel CreateRelease(ReleaseSettingModel settings)
{
Extensions.LogServiceCall("[EludicatClient][CreateRelease]", String.Format("ReleaseCode = {0} ProjectCode = {1} Description = {2}", settings.ReleaseCode, settings.ProjectCode, settings.Description));
return Post("releases/create", BuildParameterDictionary(settings));
}
///
/// Create a parameter dictionary from an object
///
///
///
private IDictionary BuildParameterDictionary(object o)
{
IDictionary dict = o.GetType()
.FindMembers(MemberTypes.Property, BindingFlags.Instance | BindingFlags.Public, null, null)
.Where(p => (((PropertyInfo)p).GetValue(o)) != null)
.ToDictionary(propertyInfo => propertyInfo.Name.TransformPropertyName(), propertyInfo => Convert.ToString(((PropertyInfo)propertyInfo).GetValue(o)));
return dict;
}
}
}