Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Requestor with StripeClient and HttpClient #1487

Merged
merged 1 commit into from
Feb 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Stripe.net/Entities/EphemeralKeys/EphemeralKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class EphemeralKey : StripeEntity<EphemeralKey>, IHasId, IHasObject
// that they'll be able to decode an object that's current according to their version.
public string RawJson
{
get { return this.StripeResponse?.ResponseJson; }
get { return this.StripeResponse?.Content; }
}

[JsonProperty("created")]
Expand Down
20 changes: 20 additions & 0 deletions src/Stripe.net/Infrastructure/Public/IHttpClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Stripe
{
using System;
using System.Threading;
using System.Threading.Tasks;

/// <summary>
/// Interface for HTTP clients used to make requests to Stripe's API.
/// </summary>
public interface IHttpClient
{
/// <summary>Sends a request to Stripe's API as an asynchronous operation.</summary>
/// <param name="request">The parameters of the request to send.</param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
/// <returns>The task object representing the asynchronous operation.</returns>
Task<StripeResponse> MakeRequestAsync(
StripeRequest request,
CancellationToken cancellationToken = default(CancellationToken));
}
}
29 changes: 29 additions & 0 deletions src/Stripe.net/Infrastructure/Public/IStripeClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace Stripe
{
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

/// <summary>
/// Interface for a Stripe client.
/// </summary>
public interface IStripeClient
{
/// <summary>Sends a request to Stripe's API as an asynchronous operation.</summary>
/// <typeparam name="T">Type of the Stripe entity returned by the API.</typeparam>
/// <param name="method">The HTTP method.</param>
/// <param name="path">The path of the request.</param>
/// <param name="options">The parameters of the request.</param>
/// <param name="requestOptions">The special modifiers of the request.</param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
/// <returns>The task object representing the asynchronous operation.</returns>
/// <exception cref="StripeException">Thrown if the request fails.</exception>
Task<T> RequestAsync<T>(
HttpMethod method,
string path,
BaseOptions options,
RequestOptions requestOptions,
CancellationToken cancellationToken = default(CancellationToken))
where T : IStripeEntity;
}
}
93 changes: 93 additions & 0 deletions src/Stripe.net/Infrastructure/Public/StripeClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
namespace Stripe
{
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

/// <summary>
/// A Stripe client, used to issue requests to Stripe's API and deserialize responses.
/// </summary>
public class StripeClient : IStripeClient
{
/// <summary>Initializes a new instance of the <see cref="StripeClient"/> class.</summary>
/// <param name="httpClient">
/// The <see cref="IHttpClient"/> client to use. If <c>null</c>, an HTTP client will be
/// created with default parameters.
/// </param>
public StripeClient(IHttpClient httpClient = null)
{
this.HttpClient = httpClient ?? BuildDefaultHttpClient();
}

/// <summary>Gets the <see cref="IHttpClient"/> used to send HTTP requests.</summary>
/// <value>The <see cref="IHttpClient"/> used to send HTTP requests.</value>
public IHttpClient HttpClient { get; }

/// <summary>Sends a request to Stripe's API as an asynchronous operation.</summary>
/// <typeparam name="T">Type of the Stripe entity returned by the API.</typeparam>
/// <param name="method">The HTTP method.</param>
/// <param name="path">The path of the request.</param>
/// <param name="options">The parameters of the request.</param>
/// <param name="requestOptions">The special modifiers of the request.</param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
/// <returns>The task object representing the asynchronous operation.</returns>
/// <exception cref="StripeException">Thrown if the request fails.</exception>
public async Task<T> RequestAsync<T>(
HttpMethod method,
string path,
BaseOptions options,
RequestOptions requestOptions,
CancellationToken cancellationToken = default(CancellationToken))
where T : IStripeEntity
{
var request = new StripeRequest(method, path, options, requestOptions);

var response = await this.HttpClient.MakeRequestAsync(request);

return ProcessResponse<T>(response);
}

private static IHttpClient BuildDefaultHttpClient()
{
return new SystemNetHttpClient();
}

private static T ProcessResponse<T>(StripeResponse response)
where T : IStripeEntity
{
if (response.StatusCode != HttpStatusCode.OK)
{
throw BuildStripeException(response);
}

var obj = StripeEntity.FromJson<T>(response.Content);
obj.StripeResponse = response;

return obj;
}

private static StripeException BuildStripeException(StripeResponse response)
{
// If the value of the `error` key is a string, then the error is an OAuth error
// and we instantiate the StripeError object with the entire JSON.
// Otherwise, it's a regular API error and we instantiate the StripeError object
// with just the nested hash contained in the `error` key.
var errorToken = JObject.Parse(response.Content)["error"];
var stripeError = errorToken.Type == JTokenType.String
? StripeError.FromJson(response.Content)
: StripeError.FromJson(errorToken.ToString());

stripeError.StripeResponse = response;

return new StripeException(
response.StatusCode,
stripeError,
stripeError.Message)
{
StripeResponse = response,
};
}
}
}
51 changes: 32 additions & 19 deletions src/Stripe.net/Infrastructure/Public/StripeConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ namespace Stripe
{
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Reflection;
using Newtonsoft.Json;
using Stripe.Infrastructure;
Expand All @@ -14,9 +13,11 @@ public static class StripeConfiguration
{
private static string apiKey;

private static IStripeClient stripeClient;

static StripeConfiguration()
{
StripeNetVersion = new AssemblyName(typeof(Requestor).GetTypeInfo().Assembly.FullName).Version.ToString(3);
StripeNetVersion = new AssemblyName(typeof(StripeConfiguration).GetTypeInfo().Assembly.FullName).Version.ToString(3);
}

/// <summary>API version used by Stripe.net.</summary>
Expand Down Expand Up @@ -71,12 +72,6 @@ public static string ApiKey
/// <summary>Gets or sets the base URL for Stripe's Files API.</summary>
public static string FilesBase { get; set; } = DefaultFilesBase;

/// <summary>Gets or sets a custom <see cref="HttpMessageHandler"/>.</summary>
public static HttpMessageHandler HttpMessageHandler { get; set; }

/// <summary>Gets or sets the timespan to wait before the request times out.</summary>
public static TimeSpan HttpTimeout { get; set; } = DefaultHttpTimeout;

/// <summary>
/// Gets or sets the settings used for deserializing JSON objects returned by Stripe's API.
/// It is highly recommended you do not change these settings, as doing so can produce
Expand All @@ -87,22 +82,40 @@ public static string ApiKey
/// </summary>
public static JsonSerializerSettings SerializerSettings { get; set; } = DefaultSerializerSettings();

/// <summary>Gets the version of the Stripe.net client library.</summary>
public static string StripeNetVersion { get; }

/// <summary>
/// Gets or sets the timespan to wait before the request times out.
/// This property is deprecated and will be removed in a future version, please use the
/// <see cref="HttpTimeout"/> property instead.
/// Gets or sets a custom <see cref="StripeClient"/> for sending requests to Stripe's
/// API. You can use this to use a custom message handler, set proxy parameters, etc.
/// </summary>
// TODO: remove this property in a future major version
[Obsolete("Use StripeConfiguration.HttpTimeout instead.")]
public static TimeSpan? HttpTimeSpan
/// <example>
/// To use a custom message handler:
/// <code>
/// System.Net.Http.HttpMessageHandler messageHandler = ...;
/// var httpClient = new System.Net.HttpClient(messageHandler);
/// var stripeClient = new Stripe.StripeClient(new Stripe.SystemNetHttpClient(httpClient));
/// Stripe.StripeConfiguration.StripeClient = stripeClient;
/// </code>
/// </example>
public static IStripeClient StripeClient
{
get { return HttpTimeout; }
set { HttpTimeout = value ?? DefaultHttpTimeout; }
get
{
if (stripeClient == null)
{
stripeClient = new StripeClient();
}

return stripeClient;
}

set
{
stripeClient = value;
}
}

/// <summary>Gets the version of the Stripe.net client library.</summary>
public static string StripeNetVersion { get; }

/// <summary>
/// Returns a new instance of <see cref="Newtonsoft.Json.JsonSerializerSettings"/> with
/// the default settings used by Stripe.net.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
namespace Stripe.Infrastructure
namespace Stripe
{
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Stripe.Infrastructure.Extensions;
using Stripe.Infrastructure.FormEncoding;

/// <summary>
/// Represents a request to Stripe's API.
/// </summary>
public class Request
public class StripeRequest
{
/// <summary>Initializes a new instance of the <see cref="Request"/> class.</summary>
/// <summary>Initializes a new instance of the <see cref="StripeRequest"/> class.</summary>
/// <param name="method">The HTTP method.</param>
/// <param name="path">The path of the request.</param>
/// <param name="options">The parameters of the request.</param>
/// <param name="requestOptions">The special modifiers of the request.</param>
public Request(
public StripeRequest(
HttpMethod method,
string path,
BaseOptions options,
Expand Down Expand Up @@ -62,6 +61,17 @@ public Request(
/// </summary>
public HttpContent Content { get; }

/// <summary>Returns a string that represents the <see cref="StripeRequest"/>.</summary>
/// <returns>A string that represents the <see cref="StripeRequest"/>.</returns>
public override string ToString()
{
return string.Format(
"<{0} Method={1} Uri={2}>",
this.GetType().FullName,
this.Method,
this.Uri.ToString());
}

private static Uri BuildUri(
HttpMethod method,
string path,
Expand Down
83 changes: 80 additions & 3 deletions src/Stripe.net/Infrastructure/Public/StripeResponse.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,90 @@
namespace Stripe
{
using System;
using System.Linq;
using System.Net;
using System.Net.Http.Headers;

/// <summary>
/// Represents a response from Stripe's API.
/// </summary>
public class StripeResponse
{
public string ResponseJson { get; set; }
/// <summary>Initializes a new instance of the <see cref="StripeResponse"/> class.</summary>
/// <param name="statusCode">The HTTP status code.</param>
/// <param name="headers">The HTTP headers of the response.</param>
/// <param name="content">The body of the response.</param>
public StripeResponse(HttpStatusCode statusCode, HttpResponseHeaders headers, string content)
{
this.StatusCode = statusCode;
this.Headers = headers;
this.Content = content;
}

public string RequestId { get; set; }
/// <summary>Gets the HTTP status code of the response.</summary>
/// <value>The HTTP status code of the response.</value>
public HttpStatusCode StatusCode { get; }

public DateTime RequestDate { get; set; }
/// <summary>Gets the HTTP headers of the response.</summary>
/// <value>The HTTP headers of the response.</value>
public HttpResponseHeaders Headers { get; }

/// <summary>Gets the body of the response.</summary>
/// <value>The body of the response.</value>
public string Content { get; }

/// <summary>Gets the date of the request, as returned by Stripe.</summary>
/// <value>The date of the request, as returned by Stripe.</value>
public DateTimeOffset? Date => this.Headers?.Date;

/// <summary>Gets the idempotency key of the request, as returned by Stripe.</summary>
/// <value>The idempotency key of the request, as returned by Stripe.</value>
public string IdempotencyKey => MaybeGetHeader(this.Headers, "Idempotency-Key");

/// <summary>Gets the ID of the request, as returned by Stripe.</summary>
/// <value>The ID of the request, as returned by Stripe.</value>
public string RequestId => MaybeGetHeader(this.Headers, "Request-Id");

/// <summary>
/// Gets the body of the response.
/// This method is deprecated and will be removed in a future version, please use the
/// <see cref="Content"/> property getter instead.
/// </summary>
/// <value>The body of the response.</value>
// TODO: remove this in a future a major version
[Obsolete("Use Content instead")]
public string ResponseJson => this.Content;
ob-stripe marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Gets the date of the request, as returned by Stripe.
/// This method is deprecated and will be removed in a future version, please use the
/// <see cref="Date"/> property getter instead.
/// </summary>
/// <value>The date of the request, as returned by Stripe.</value>
// TODO: remove this in a future a major version
[Obsolete("Use Date instead")]
public DateTime RequestDate => this.Date?.DateTime ?? default(DateTime);

/// <summary>Returns a string that represents the <see cref="StripeResponse"/>.</summary>
/// <returns>A string that represents the <see cref="StripeResponse"/>.</returns>
public override string ToString()
{
return string.Format(
"<{0} status={1} Request-Id={2} Date={3}>",
this.GetType().FullName,
(int)this.StatusCode,
this.RequestId,
this.Date?.ToString("s"));
}

private static string MaybeGetHeader(HttpHeaders headers, string name)
{
if ((headers == null) || (!headers.Contains(name)))
{
return null;
}

return headers.GetValues(name).First();
}
}
}
Loading