diff --git a/DynamicRest.sln b/DynamicRest.sln index c597dbb..399e5ca 100644 --- a/DynamicRest.sln +++ b/DynamicRest.sln @@ -1,26 +1,29 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicRest", "DynamicRest\DynamicRest.csproj", "{6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples", "Samples\Samples.csproj", "{6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}.Release|Any CPU.Build.0 = Release|Any CPU - {6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicRest", "DynamicRest\DynamicRest.csproj", "{6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples", "Samples\Samples.csproj", "{6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6761F94D-EFCD-49C7-9E8E-ECBCA014FF63}.Release|Any CPU.Build.0 = Release|Any CPU + {6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6761F94D-EFCD-49C7-9E8E-ECBCA014FF64}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = Samples\Samples.csproj + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/DynamicRest/DynamicRest.csproj b/DynamicRest/DynamicRest.csproj index fc3aec5..1d570b1 100644 --- a/DynamicRest/DynamicRest.csproj +++ b/DynamicRest/DynamicRest.csproj @@ -1,4 +1,4 @@ - + Debug @@ -30,18 +30,13 @@ prompt 4 - - - - 3.5 - 3.5 diff --git a/DynamicRest/RestClient.cs b/DynamicRest/RestClient.cs index 0545c23..0a9f86c 100644 --- a/DynamicRest/RestClient.cs +++ b/DynamicRest/RestClient.cs @@ -38,26 +38,71 @@ public sealed class RestClient : DynamicObject { private string _operationGroup; private Dictionary _parameters; private ICredentials _credentials; + private Tuple _basicAuth; + private List _operationsWhichPost; public RestClient(string uriFormat, RestService service) { _uriFormat = uriFormat; _service = service; } - private RestClient(string uriFormat, RestService service, string operationGroup, Dictionary inheritedParameters) + public RestClient(string uriFormat, RestService service, List postOperations) + : this(uriFormat, service) { + _operationsWhichPost = postOperations; + } + + private RestClient(string uriFormat, RestService service, string operationGroup, + Dictionary inheritedParameters) : this(uriFormat, service) { _operationGroup = operationGroup; _parameters = inheritedParameters; } + private RestClient(string uriFormat, RestService service, string operationGroup, + Dictionary inheritedParameters, List postOperations) + : this(uriFormat, service, operationGroup, inheritedParameters) { + _operationsWhichPost = postOperations; + } + private HttpWebRequest CreateRequest(string operationName, JsonObject parameters) { - Uri requestUri = CreateRequestUri(operationName, parameters); - HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(requestUri); + Uri requestUri; + HttpWebRequest webRequest; + + if(null != _operationsWhichPost && _operationsWhichPost.Contains(operationName)) + { + // POST operations don't take their parameters on the querystring + requestUri = CreateRequestUri(operationName, new JsonObject()); + } else { + requestUri = CreateRequestUri(operationName, parameters); + } + + webRequest = (HttpWebRequest)WebRequest.Create(requestUri); if (_credentials != null) { webRequest.Credentials = _credentials; } + if(_basicAuth != null) { + string authInfo = _basicAuth.Item1 + ":" + _basicAuth.Item2; + authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo)); + webRequest.Headers["Authorization"] = "Basic " + authInfo; + } + + if(null != _operationsWhichPost && _operationsWhichPost.Contains(operationName)) + { + // format as a POST request + byte[] body = Encoding.UTF8.GetBytes(CreatePostBody(parameters)); + webRequest.Method = "POST"; + webRequest.ContentLength = body.Length; + webRequest.ContentType = "application/x-www-form-urlencoded"; + + // send up the body + using (var requestStream = webRequest.GetRequestStream()) + { + requestStream.Write( body, 0, body.Length ); + } + } + return webRequest; } @@ -138,6 +183,43 @@ private Uri CreateRequestUri(string operationName, JsonObject parameters) { return uri; } + private string CreatePostBody(JsonObject parameters) { + StringBuilder bodyBuilder = new StringBuilder(); + + if (parameters != null) { + foreach (KeyValuePair param in (IDictionary)parameters) { + string name = param.Key; + object value = param.Value; + + if (value is Delegate) { + // Ignore callbacks in the async scenario. + continue; + } + + if (value is JsonObject) { + // Nested object... use name.subName=value format. + + foreach (KeyValuePair nestedParam in (IDictionary)value) { + bodyBuilder.AppendFormat("&{0}.{1}={2}", + name, nestedParam.Key, + FormatUriParameter(nestedParam.Value)); + } + + continue; + } + + bodyBuilder.AppendFormat("&{0}={1}", name, FormatUriParameter(value)); + } + } + + // ignore the leading "&" + try { + bodyBuilder.Remove(0, 1); + } catch(ArgumentOutOfRangeException) { } + + return bodyBuilder.ToString(); + } + private string FormatUriParameter(object value) { if (value is IEnumerable) { return String.Join("+", (IEnumerable)value); @@ -267,8 +349,15 @@ public override bool TryGetMember(GetMemberBinder binder, out object result) { operationGroup = _operationGroup + "." + operationGroup; } - RestClient operationGroupClient = - new RestClient(_uriFormat, _service, operationGroup, _parameters); + RestClient operationGroupClient; + if(null != _operationsWhichPost) + { + operationGroupClient = + new RestClient(_uriFormat, _service, operationGroup, _parameters, _operationsWhichPost); + } else { + operationGroupClient = + new RestClient(_uriFormat, _service, operationGroup, _parameters); + } result = operationGroupClient; return true; @@ -313,6 +402,14 @@ public RestClient WithCredentials(ICredentials credentials) { return this; } + // 2011-05-14 Matt Cooper + // Add a WithBasicAuth method to support Untappd + public RestClient WithBasicAuth(string Username, string Password) { + _basicAuth = new Tuple(Username, Password); + return this; + } + // end changes + public RestClient WithUriTransformer(IRestUriTransformer uriTransformer) { if (uriTransformer == null) { throw new ArgumentNullException("uriTransformer"); diff --git a/Samples/Program.cs b/Samples/Program.cs index 1936b7c..c8afbca 100644 --- a/Samples/Program.cs +++ b/Samples/Program.cs @@ -52,6 +52,12 @@ public static void Main(string[] args) { Console.WriteLine("Google Search Sample"); Console.WriteLine(HeaderBar); GoogleSearchSample.Run(); + Console.WriteLine(Separator); + + // Untappd Checkin + Console.WriteLine("Untappd Checkin Sample"); + Console.WriteLine(HeaderBar); + UntappdSample.Run(); Console.WriteLine(Separator); Console.ReadLine(); diff --git a/Samples/Samples.csproj b/Samples/Samples.csproj index 3608fbf..ec9eb57 100644 --- a/Samples/Samples.csproj +++ b/Samples/Samples.csproj @@ -1,4 +1,4 @@ - + Debug @@ -34,11 +34,9 @@ - 3.5 - 3.5 @@ -54,6 +52,7 @@ + diff --git a/Samples/Services.cs b/Samples/Services.cs index aec8672..20b900c 100644 --- a/Samples/Services.cs +++ b/Samples/Services.cs @@ -24,5 +24,10 @@ internal static class Services { public const string BingApiKey = "???"; public const string GoogleSearchUri = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0"; + + public const string UntappdUri = "http://api.untappd.com/v3/{operation}?key={apiKey}"; + public const string UntappdApiKey = "???"; + public const string UntappdCheckinAs = "???"; + public const string UntappdCheckinAsPassword = "???"; } } diff --git a/Samples/UntappdSample.cs b/Samples/UntappdSample.cs new file mode 100644 index 0000000..efcedaa --- /dev/null +++ b/Samples/UntappdSample.cs @@ -0,0 +1,51 @@ +// UntappdSample.cs +// DynamicRest provides REST service access using C# 4.0 dynamic programming. +// The latest information and code for the project can be found at +// https://github.com/NikhilK/dynamicrest +// +// This project is licensed under the BSD license. See the License.txt file for +// more information. +// + +using System; +using DynamicRest; +using System.Collections.Generic; +using System.Text; + +namespace Application { + + internal static class UntappdSample { + + public static void Run() { + List PostOps = new List() + { + "checkin_test", + }; + dynamic untappd = new RestClient(Services.UntappdUri, RestService.Json, PostOps); + untappd.apiKey = Services.UntappdApiKey; + untappd = untappd.WithBasicAuth(Services.UntappdCheckinAs, MD5(Services.UntappdCheckinAsPassword)); + + Console.WriteLine("Checking in"); + + dynamic checkinOptions = new JsonObject(); + checkinOptions.gmt_offset = -8; + checkinOptions.bid = 1; + + dynamic checkin = untappd.checkin_test(checkinOptions); + Console.WriteLine(string.Format("That was checkin ID #{0}",checkin.Result.checkin_details.checkin_id)); + } + + private static string MD5(string Input) + { + // from http://msdn.microsoft.com/en-us/library/system.security.cryptography.md5.aspx + System.Security.Cryptography.MD5 Md5Hasher = System.Security.Cryptography.MD5.Create(); + byte[] data = Md5Hasher.ComputeHash(Encoding.Default.GetBytes(Input)); + StringBuilder sb = new StringBuilder(); + foreach(byte b in data) + { + sb.Append(b.ToString("x2")); + } + return sb.ToString(); + } + } +}