From f9268ea6be7d20c3872912af09da3d4db9a847f1 Mon Sep 17 00:00:00 2001 From: ceee Date: Thu, 12 Sep 2013 11:39:48 +0200 Subject: [PATCH] start async + pcl implementation --- PocketSharp/APIException.cs | 9 - PocketSharp/Components/Add.cs | 7 +- PocketSharp/Components/Retrieve.cs | 18 +- PocketSharp/JsonDeserializer.cs | 158 +++++++++--------- PocketSharp/JsonExtensions.cs | 38 +++++ PocketSharp/Models/Parameters/Parameter.cs | 2 +- PocketSharp/PocketClient.cs | 182 ++++++++++----------- PocketSharp/PocketSharp.csproj | 53 ++++-- PocketSharp/Properties/AssemblyInfo.cs | 22 +-- PocketSharp/Utilities.cs | 14 +- PocketSharp/packages.config | 7 +- 11 files changed, 285 insertions(+), 225 deletions(-) create mode 100644 PocketSharp/JsonExtensions.cs diff --git a/PocketSharp/APIException.cs b/PocketSharp/APIException.cs index ddbe4b7..e523f50 100644 --- a/PocketSharp/APIException.cs +++ b/PocketSharp/APIException.cs @@ -47,14 +47,5 @@ namespace PocketSharp /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. public APIException(string message, Exception innerException) : base(message, innerException) { } - - - /// - /// Initializes a new instance of the class. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - protected APIException(SerializationInfo info, StreamingContext context) - : base(info, context) { } } } diff --git a/PocketSharp/Components/Add.cs b/PocketSharp/Components/Add.cs index 815013a..c2a8263 100644 --- a/PocketSharp/Components/Add.cs +++ b/PocketSharp/Components/Add.cs @@ -26,7 +26,10 @@ namespace PocketSharp Title = title, TweetID = tweetID }; - return await Request("add", parameters.Convert(), true).Item; + + Add response = await Request("add", parameters.Convert(), true); + + return response.Item; } @@ -37,7 +40,7 @@ namespace PocketSharp /// public async Task Add(Uri uri) { - return await Add(uri, null, null, null); + return await Add(uri, null); } } } diff --git a/PocketSharp/Components/Retrieve.cs b/PocketSharp/Components/Retrieve.cs index 3595480..cb79ab3 100644 --- a/PocketSharp/Components/Retrieve.cs +++ b/PocketSharp/Components/Retrieve.cs @@ -16,7 +16,9 @@ namespace PocketSharp /// public async Task> Retrieve(RetrieveParameters parameters) { - return await Request("get", parameters.Convert(), true).Items; + Retrieve response = await Request("get", parameters.Convert(), true); + + return response.Items; } @@ -53,7 +55,9 @@ namespace PocketSharp parameters.DetailType = DetailType.complete; - return await Request("get", parameters.Convert(), true).Items; + Retrieve response = await Request("get", parameters.Convert(), true); + + return response.Items; } @@ -69,7 +73,10 @@ namespace PocketSharp Tag = tag, DetailType = DetailType.complete }; - return await Request("get", parameters.Convert(), true).Items; + + Retrieve response = await Request("get", parameters.Convert(), true); + + return response.Items; } @@ -85,7 +92,10 @@ namespace PocketSharp Search = searchString, DetailType = DetailType.complete }; - return await Request("get", parameters.Convert(), true).Items; + + Retrieve response = await Request("get", parameters.Convert(), true); + + return response.Items; } } diff --git a/PocketSharp/JsonDeserializer.cs b/PocketSharp/JsonDeserializer.cs index 898df4d..6a2abba 100644 --- a/PocketSharp/JsonDeserializer.cs +++ b/PocketSharp/JsonDeserializer.cs @@ -1,90 +1,90 @@ -using RestSharp; -using RestSharp.Deserializers; -using ServiceStack.Text; -using System; +//using RestSharp; +//using RestSharp.Deserializers; +//using ServiceStack.Text; +//using System; -namespace PocketSharp -{ - /// - /// Custom JSON Deserializer which implements ServiceStack.Text - /// - internal class JsonDeserializer : IDeserializer - { - public const string JsonContentType = "application/json"; +//namespace PocketSharp +//{ +// /// +// /// Custom JSON Deserializer which implements ServiceStack.Text +// /// +// internal class JsonDeserializer : IDeserializer +// { +// public const string JsonContentType = "application/json"; - /// - /// Deserializes the specified response. - /// - /// - /// The response. - /// - public T Deserialize(IRestResponse response) - { - return JsonSerializer.DeserializeFromString(response.Content); - } +// /// +// /// Deserializes the specified response. +// /// +// /// +// /// The response. +// /// +// public T Deserialize(IRestResponse response) +// { +// return JsonSerializer.DeserializeFromString(response.Content); +// } - /// - /// Adds custom deserialization for specific types. - /// - public static void AddCustomDeserialization() - { - // generate correct Uri format - JsConfig.DeSerializeFn = value => - { - Uri result = null; - try - { - result = new Uri(value); - } - catch(ArgumentNullException e) {} - catch(UriFormatException e) {} +// /// +// /// Adds custom deserialization for specific types. +// /// +// public static void AddCustomDeserialization() +// { +// // generate correct Uri format +// JsConfig.DeSerializeFn = value => +// { +// Uri result = null; +// try +// { +// result = new Uri(value); +// } +// catch(ArgumentNullException e) {} +// catch(UriFormatException e) {} - return result; - }; +// return result; +// }; - // create DateTime from UNIX timestamp input - JsConfig.DeSerializeFn = value => - { - if (value == "0") return null; - return new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(Convert.ToDouble(value)).ToLocalTime(); - }; - } +// // create DateTime from UNIX timestamp input +// JsConfig.DeSerializeFn = value => +// { +// if (value == "0") return null; +// return new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(Convert.ToDouble(value)).ToLocalTime(); +// }; +// } - /// - /// Gets or sets the date format. - /// - /// - /// The date format. - /// - public string DateFormat { get; set; } +// /// +// /// Gets or sets the date format. +// /// +// /// +// /// The date format. +// /// +// public string DateFormat { get; set; } - /// - /// Gets or sets the namespace. - /// - /// - /// The namespace. - /// - public string Namespace { get; set; } +// /// +// /// Gets or sets the namespace. +// /// +// /// +// /// The namespace. +// /// +// public string Namespace { get; set; } - /// - /// Gets or sets the root element. - /// - /// - /// The root element. - /// - public string RootElement { get; set; } +// /// +// /// Gets or sets the root element. +// /// +// /// +// /// The root element. +// /// +// public string RootElement { get; set; } - /// - /// Gets the type of the content. - /// - /// - /// The type of the content. - /// - public string ContentType - { - get { return JsonContentType; } - } - } -} +// /// +// /// Gets the type of the content. +// /// +// /// +// /// The type of the content. +// /// +// public string ContentType +// { +// get { return JsonContentType; } +// } +// } +//} diff --git a/PocketSharp/JsonExtensions.cs b/PocketSharp/JsonExtensions.cs new file mode 100644 index 0000000..6c2a0c9 --- /dev/null +++ b/PocketSharp/JsonExtensions.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace PocketSharp +{ + //public class UnixDateTimeConverter : DateTimeConverterBase + //{ + // /// + // /// Writes the JSON representation of the object. + // /// + // /// The to write to.The value.The calling serializer. + // //public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + // //{ + // // DateTime epoc = new DateTime(1970, 1, 1); + // // var delta = (DateTime)value - epoc; + + // // writer.WriteValue((long)delta.TotalSeconds); + // //} + + + // ///// + // ///// Reads the JSON representation of the object. + // ///// + // ///// The to read from. + // ///// Type of the object. + // ///// The existing value of object being read. + // ///// The calling serializer. + // ///// The object value. + // //public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + // //{ + // // return existingValue == "0" ? null : new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(Convert.ToDouble(existingValue)).ToLocalTime(); + // //} + //} +} diff --git a/PocketSharp/Models/Parameters/Parameter.cs b/PocketSharp/Models/Parameters/Parameter.cs index 603c868..7a57b14 100644 --- a/PocketSharp/Models/Parameters/Parameter.cs +++ b/PocketSharp/Models/Parameters/Parameter.cs @@ -25,4 +25,4 @@ namespace PocketSharp.Models return string.Format("{0}={1}", Name, Value); } } -} +} \ No newline at end of file diff --git a/PocketSharp/PocketClient.cs b/PocketSharp/PocketClient.cs index c8fa848..193f9ad 100644 --- a/PocketSharp/PocketClient.cs +++ b/PocketSharp/PocketClient.cs @@ -1,9 +1,11 @@ using PocketSharp.Models; -using RestSharp; -using RestSharp.Contrib; using System; using System.Collections.Generic; using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace PocketSharp { @@ -15,7 +17,7 @@ namespace PocketSharp /// /// REST client used for the API communication /// - protected readonly RestClient _restClient; + protected readonly HttpClient _restClient; /// /// The base URL for the Pocket API @@ -38,11 +40,6 @@ namespace PocketSharp /// public string ConsumerKey { get; set; } - /// - /// Returns all associated data from the last request - /// - public IRestResponse LastRequestData { get; private set; } - /// /// Code retrieved on authentification /// @@ -74,58 +71,29 @@ namespace PocketSharp // assign callback uri if submitted if (callbackUri != null) { - CallbackUri = HttpUtility.UrlEncode(callbackUri.ToString()); + CallbackUri = Uri.EscapeUriString(callbackUri.ToString()); } // initialize REST client - _restClient = new RestClient(baseUri.ToString()); + _restClient = new HttpClient(new HttpClientHandler() + { + AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip + }); - // add default parameters to each request - _restClient.AddDefaultParameter("consumer_key", ConsumerKey); + // set base uri + _restClient.BaseAddress = baseUri; // Pocket needs this specific Accept header :-S - _restClient.AddDefaultHeader("Accept", "*/*"); + _restClient.DefaultRequestHeaders.Add("Accept", "*/*"); // defines the response format (according to the Pocket docs) - _restClient.AddDefaultHeader("X-Accept", "application/json"); + _restClient.DefaultRequestHeaders.Add("X-Accept", "application/json"); // custom JSON deserializer (ServiceStack.Text) - _restClient.AddHandler("application/json", new JsonDeserializer()); + //_restClient.AddHandler("application/json", new JsonDeserializer()); // add custom deserialization lambdas - JsonDeserializer.AddCustomDeserialization(); - } - - - /// - /// Makes a HTTP REST request to the API - /// - /// The request. - /// - protected string Request(RestRequest request) - { - IRestResponse response = _restClient.Execute(request); - - LastRequestData = response; - ValidateResponse(response); - - return response.Content; - } - - /// - /// Makes a typed HTTP REST request to the API - /// - /// - /// The request. - /// - protected T Request(RestRequest request) where T : new() - { - IRestResponse response = _restClient.Execute(request); - - LastRequestData = response; - ValidateResponse(response); - - return response.Data; + //JsonDeserializer.AddCustomDeserialization(); } @@ -138,33 +106,55 @@ namespace PocketSharp /// if set to true [require auth]. /// /// No access token available. Use authentification first. - protected T Get(string method, List parameters = null, bool requireAuth = false) where T : class, new() + protected async Task Request(string method, List parameters = null, bool requireAuth = false) where T : class, new() { if (requireAuth && AccessCode == null) { throw new APIException("No access token available. Use authentification first."); } + // convert parameters + List> kvParameters = new List>(); + + if (parameters != null) + { + foreach (Parameter item in parameters) + { + if (item.Value != null) + { + kvParameters.Add(new KeyValuePair(item.Name, item.Value.ToString())); + } + } + } + // every single Pocket API endpoint requires HTTP POST data - var request = new RestRequest(method, Method.POST); + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, method); + + // add consumer key to each request + kvParameters.Add(new KeyValuePair("consumer_key", ConsumerKey)); // add access token (necessary for all requests except authentification) if (AccessCode != null) { - request.AddParameter("access_token", AccessCode); + kvParameters.Add(new KeyValuePair("access_token", AccessCode)); } - // enumeration for params - if (parameters != null) - { - parameters.ForEach(delegate(Parameter param) - { - request.AddParameter(param); - }); - } + // content of the request + request.Content = new FormUrlEncodedContent(kvParameters); - // do the request - return Request(request); + // make async request + HttpResponseMessage response = await _restClient.SendAsync(request); + + // throw exception if no valid response + response.EnsureSuccessStatusCode(); + + // read response + string responseString = response.Content.ReadAsStringAsync().Result; + + // deserialize object + return JsonConvert.DeserializeObject(responseString); + + //ValidateResponse(response); } @@ -173,14 +163,16 @@ namespace PocketSharp /// /// The action parameter. /// - protected bool PutSendAction(ActionParameter actionParameter) + protected async Task PutSendAction(ActionParameter actionParameter) { ModifyParameters parameters = new ModifyParameters() { Actions = new List() { actionParameter } }; - return Get("send", parameters.Convert(), true).Status; + Modify response = await Request("send", parameters.Convert(), true); + + return response.Status; } @@ -192,44 +184,44 @@ namespace PocketSharp /// /// Error retrieving response /// - protected void ValidateResponse(IRestResponse response) - { - if (response.StatusCode != HttpStatusCode.OK) - { - // get pocket error headers - Parameter error = response.Headers[1]; - Parameter errorCode = response.Headers[2]; + //protected void ValidateResponse(IRestResponse response) + //{ + // if (response.StatusCode != HttpStatusCode.OK) + // { + // // get pocket error headers + // Parameter error = response.Headers[1]; + // Parameter errorCode = response.Headers[2]; - string exceptionString = response.Content; + // string exceptionString = response.Content; - bool isPocketError = error.Name == "X-Error"; + // bool isPocketError = error.Name == "X-Error"; - // update message to include pocket response data - if (isPocketError) - { - exceptionString = exceptionString + "\nPocketResponse: (" + errorCode.Value + ") " + error.Value; - } + // // update message to include pocket response data + // if (isPocketError) + // { + // exceptionString = exceptionString + "\nPocketResponse: (" + errorCode.Value + ") " + error.Value; + // } - // create exception - APIException exception = new APIException(exceptionString, response.ErrorException); + // // create exception + // APIException exception = new APIException(exceptionString, response.ErrorException); - if (isPocketError) - { - // add custom pocket fields - exception.PocketError = error.Value.ToString(); - exception.PocketErrorCode = Convert.ToInt32(errorCode.Value); + // if (isPocketError) + // { + // // add custom pocket fields + // exception.PocketError = error.Value.ToString(); + // exception.PocketErrorCode = Convert.ToInt32(errorCode.Value); - // add to generic exception data - exception.Data.Add(error.Name, error.Value); - exception.Data.Add(errorCode.Name, errorCode.Value); - } + // // add to generic exception data + // exception.Data.Add(error.Name, error.Value); + // exception.Data.Add(errorCode.Name, errorCode.Value); + // } - throw exception; - } - else if (response.ErrorException != null) - { - throw new APIException("Error retrieving response", response.ErrorException); - } - } + // throw exception; + // } + // else if (response.ErrorException != null) + // { + // throw new APIException("Error retrieving response", response.ErrorException); + // } + //} } } diff --git a/PocketSharp/PocketSharp.csproj b/PocketSharp/PocketSharp.csproj index 7e35d7d..9c6d34b 100644 --- a/PocketSharp/PocketSharp.csproj +++ b/PocketSharp/PocketSharp.csproj @@ -13,6 +13,14 @@ 512 ..\ true + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Profile88 + 10.0 + + + + + 4.0 true @@ -33,20 +41,36 @@ 4 - - ..\packages\RestSharp.104.1\lib\net4\RestSharp.dll + + ..\packages\Microsoft.Bcl.Async.1.0.16\lib\portable-net40+sl4+win8+wp71\Microsoft.Threading.Tasks.dll - - False - ..\packages\ServiceStack.Text.3.9.58\lib\net35\ServiceStack.Text.dll + + ..\packages\Microsoft.Bcl.Async.1.0.16\lib\portable-net40+sl4+win8+wp71\Microsoft.Threading.Tasks.Extensions.dll + + + ..\packages\Newtonsoft.Json.5.0.6\lib\portable-net40+sl4+wp7+win8\Newtonsoft.Json.dll + + ..\packages\Microsoft.Bcl.1.1.3\lib\portable-net40+sl4+win8+wp71\System.IO.dll + + + ..\packages\Microsoft.Net.Http.2.2.13\lib\portable-net40+sl4+win8+wp71\System.Net.Http.dll + + + ..\packages\Microsoft.Net.Http.2.2.13\lib\portable-net40+sl4+win8+wp71\System.Net.Http.Extensions.dll + + + ..\packages\Microsoft.Net.Http.2.2.13\lib\portable-net40+sl4+win8+wp71\System.Net.Http.Primitives.dll + + + ..\packages\Microsoft.Bcl.1.1.3\lib\portable-net40+sl4+win8+wp71\System.Runtime.dll + - - - - + + ..\packages\Microsoft.Bcl.1.1.3\lib\portable-net40+sl4+win8+wp71\System.Threading.Tasks.dll + @@ -57,6 +81,8 @@ + + @@ -82,9 +108,14 @@ - + -