Skip to content

Commit

Permalink
Using HttpClient instead of RestSharp.
Browse files Browse the repository at this point in the history
- Use of .NET 4's HttpClient instead of RestSharp
- Removed depdendency to RestSharp
- No longer compiling project as net-sdk.dll but as MercadoLibreSdk.dll (this will help to publish the SDK to nuget)
- New mercado libre site enum so that the SDK can be used against a given mercardo libre country/site
- API end points no longer static
- Credentials are in their own class
- Ability to listen to changes to the application access tokens
- No longer relying on node JS to unit test the HTTP requests (using mock HTTP library instead)
- MeliApiService implements IMeliApiService to make it easy to moq the service
- Using the moq library to mock calls to interface in unit test
- Added rules to .gitignore file
  • Loading branch information
bounav committed Oct 6, 2015
1 parent 5b59198 commit b706ac0
Show file tree
Hide file tree
Showing 233 changed files with 63,334 additions and 47,773 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@ TestResult.xml
net-sdk.userprefs
.DS_Store
SDK/.DS_Store
*.obj
*.exe
*.pdb
*.user
[Bb]in
[Dd]ebug*/
Binary file removed Lib/.DS_Store
Binary file not shown.
Binary file removed Lib/Newtonsoft.Json.dll
Binary file not shown.
8 changes: 0 additions & 8 deletions Makefile

This file was deleted.

3 changes: 1 addition & 2 deletions SDK/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;

// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
Expand All @@ -17,7 +16,7 @@
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.

[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.0.2.*")]

// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
Expand Down
15 changes: 0 additions & 15 deletions SDK/AuthorizationException.cs

This file was deleted.

89 changes: 89 additions & 0 deletions SDK/Http/HttpClientProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace MercadoLibre.SDK.Http
{
/// <summary>
/// Helper class to easily get new instances of a <see cref="HttpClient"/> with default behaviour and a retry mechanism.
/// </summary>
/// <remarks>
/// Most of the time only one instance of <see cref="HttpClient"/> should be used per request:
/// If you hang on to one instance of HTTP client and change its Timeout setting after a fist request was issued an exception will be thrown.
/// </remarks>
public class HttpClientProvider : IHttpClientProvider
{
/// <summary>
/// Hook to intercept HTTP responses and react to them (e.g. automatically authenticate upon 401 Unauthorized responses).
/// </summary>
/// <value>
/// The custom send asynchronous.
/// </value>
/// <returns>True when the original request should be retried.</returns>
public Func<HttpResponseMessage, Task<bool>> RetryIntercept { get; set; }

/// <summary>
/// Hook to set default settings on the HTTP client that <see cref="Create(System.Net.Http.HttpMessageHandler, bool)"/> returns.
/// </summary>
/// <value>
/// The initialise with.
/// </value>
public Action<HttpClient> InitialiseWith { get; set; }

/// <summary>
/// Gets a new instance of <see cref="HttpClient" />.
/// </summary>
/// <param name="configureRetryIntercept">if set to <c>true</c> [configure the retry intercept].</param>
/// <returns></returns>
public HttpClient Create(bool configureRetryIntercept = true)
{
return Create(null, configureRetryIntercept);
}

/// <summary>
/// Gets a new instance of <see cref="HttpClient" />.
/// </summary>
/// <param name="innerMessageHandler">The inner message handler.</param>
/// <param name="configureRetryIntercept">if set to <c>true</c> [configure the retry intercept].</param>
/// <returns></returns>
public HttpClient Create(HttpMessageHandler innerMessageHandler, bool configureRetryIntercept = true)
{
HttpClient client;

if (innerMessageHandler == null)
{
var innerClientHandler = new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};

client = RetryIntercept == null || configureRetryIntercept == false
? new HttpClient(innerClientHandler)
: new HttpClient(new RetryDelegatingHandler
{
InnerHandler = innerClientHandler,
RetryIntercept = RetryIntercept
});
}
else
{
client = RetryIntercept == null || configureRetryIntercept == false
? new HttpClient(innerMessageHandler)
: new HttpClient(new RetryDelegatingHandler
{
RetryIntercept = RetryIntercept,
InnerHandler = innerMessageHandler
});
}

if (InitialiseWith != null)
{
InitialiseWith(client);
}

return client;
}
}
}
25 changes: 25 additions & 0 deletions SDK/Http/IHttpClientProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Net.Http;

namespace MercadoLibre.SDK.Http
{
/// <summary>
/// Interface to provide a HTTP client
/// </summary>
public interface IHttpClientProvider
{
/// <summary>
/// Gets a new instance of <see cref="HttpClient" />.
/// </summary>
/// <param name="configureRetryIntercept">if set to <c>true</c> [configure the retry intercept].</param>
/// <returns></returns>
HttpClient Create(bool configureRetryIntercept = true);

/// <summary>
/// Gets a new instance of <see cref="HttpClient" />.
/// </summary>
/// <param name="innerMessageHandler">The inner message handler (optional).</param>
/// <param name="configureRetryIntercept">if set to <c>true</c> [configure the retry intercept].</param>
/// <returns></returns>
HttpClient Create(HttpMessageHandler innerMessageHandler, bool configureRetryIntercept = true);
}
}
47 changes: 47 additions & 0 deletions SDK/Http/RetryDelegatingHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace MercadoLibre.SDK.Http
{
/// <summary>
/// HTTP Message handler that inspect status code in responses and automatically tries to refresh the token if a refresh token is set.
/// </summary>
public class RetryDelegatingHandler : DelegatingHandler
{
/// <summary>
/// Gets or sets a hook to determinue (and optionaly take action (e.g. authenticate)) wether the request should be retried or not.
/// </summary>
/// <value>
/// The retry intercept.
/// </value>
/// <returns>True when the original request should be retried.</returns>
public Func<HttpResponseMessage, Task<bool>> RetryIntercept { get; set; }

/// <summary>
/// Sends an HTTP request to the inner handler to send to the server as an asynchronous operation.
/// </summary>
/// <param name="request">The HTTP request message to send to the server.</param>
/// <param name="cancellationToken">A cancellation token to cancel operation.</param>
/// <returns>
/// Returns <see cref="T:System.Threading.Tasks.Task`1" />. The task object representing the asynchronous operation.
/// </returns>
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);

if (RetryIntercept != null)
{
// Hook to determine wether the request should be retried or not
if (await RetryIntercept(response))
{
// Retry
response = await base.SendAsync(request, cancellationToken);
}
}

return response;
}
}
}
114 changes: 114 additions & 0 deletions SDK/IMeliApiService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System.Net.Http;
using System.Threading.Tasks;
using HttpParamsUtility;
using MercadoLibre.SDK.Meta;

namespace MercadoLibre.SDK
{
/// <summary>
/// Interface for the service wrapping access to the Mercado Libre REST API.
/// </summary>
public interface IMeliApiService
{
/// <summary>
/// Gets or sets the crendentials.
/// </summary>
/// <value>
/// The crendentials.
/// </value>
/// <remarks>
/// - Don't forget to set this before calling methods that require an API token.
/// - You can subscribe to events on <see cref="MeliCredentials"/> to track when the tokens are refreshed.
/// </remarks>
MeliCredentials Credentials { get; set; }

/// <summary>
/// Generate an URL to get an access token for other users.
/// </summary>
/// <param name="clientId">The client identifier (meli app ID).</param>
/// <param name="site">The site.</param>
/// <param name="redirectUri">The call back URI redirect URL to (Mercado Libre with append ?code=YOUR_SECRET_CODE to this URL).</param>
/// <returns>The authentication URL to redirect your user to.</returns>
string GetAuthUrl(long clientId, MeliSite site, string redirectUri);

/// <summary>
/// Requests an access and refresh token from the code provided by the mercado libre callback.
/// </summary>
/// <param name="code">The code.</param>
/// <param name="redirectUri">The redirect URI.</param>
/// <returns>Return True when the operation is successful.</returns>
Task<bool> AuthorizeAsync(string code, string redirectUri);

/// <summary>
/// Sends a GET request.
/// </summary>
/// <param name="resource">The resource.</param>
/// <param name="parameters">The parameters.</param>
/// <returns></returns>
Task<HttpResponseMessage> GetAsync(string resource, HttpParams parameters = null);

/// <summary>
/// Sends a GET request and deserialises the JSON response.
/// </summary>
/// <typeparam name="T">The class to use to deserialise the JSON response.</typeparam>
/// <param name="resource">The resource.</param>
/// <param name="parameters">The parameters.</param>
/// <returns></returns>
Task<T> GetAsync<T>(string resource, HttpParams parameters = null);

/// <summary>
/// Sends a POST request.
/// </summary>
/// <param name="resource">The resource.</param>
/// <param name="parameters">The parameters.</param>
/// <param name="content">The payload for the content of the HTTP request.</param>
/// <returns></returns>
Task<HttpResponseMessage> PostAsync(string resource, HttpParams parameters = null, object content = null);

/// <summary>
/// Sends a POST request and deserialises the JSON response.
/// </summary>
/// <typeparam name="T">The class to use to deserialise the JSON response.</typeparam>
/// <param name="resource">The resource.</param>
/// <param name="parameters">The parameters.</param>
/// <param name="content">The payload for the content of the HTTP request.</param>
/// <returns></returns>
Task<T> PostAsync<T>(string resource, HttpParams parameters = null, object content = null);

/// <summary>
/// Sends a PUT request.
/// </summary>
/// <param name="resource">The resource.</param>
/// <param name="parameters">The parameters.</param>
/// <param name="content">The content.</param>
/// <returns></returns>
Task<HttpResponseMessage> PutAsync(string resource, HttpParams parameters = null, object content = null);

/// <summary>
/// Sends a PUT request and deserialises the JSON response.
/// </summary>
/// <typeparam name="T">The class to use to deserialise the JSON response.</typeparam>
/// <param name="resource">The resource.</param>
/// <param name="parameters">The parameters.</param>
/// <param name="content">The content.</param>
/// <returns></returns>
Task<T> PutAsync<T>(string resource, HttpParams parameters = null, object content = null);

/// <summary>
/// Sends a DELETE request.
/// </summary>
/// <param name="resource">The resource.</param>
/// <param name="parameters">The parameters.</param>
/// <returns></returns>
Task<HttpResponseMessage> DeleteAsync(string resource, HttpParams parameters = null);

/// <summary>
/// Sends a DELETE request and deserialises the JSON response.
/// </summary>
/// <typeparam name="T">The class to use to deserialise the JSON response.</typeparam>
/// <param name="resource">The resource.</param>
/// <param name="parameters">The parameters.</param>
/// <returns></returns>
Task<T> DeleteAsync<T>(string resource, HttpParams parameters = null);
}
}
Loading

0 comments on commit b706ac0

Please sign in to comment.