Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions src/Stripe.net/Infrastructure/Public/ApiRequestorAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ internal static ApiRequestor Adapt(IStripeClient client)
return stripeClient.Requestor;
}

if (client is DefaultStripeClient defaultStripeClient)
{
return defaultStripeClient.Requestor;
}

return new ApiRequestorAdapter(client);
}

Expand Down
80 changes: 80 additions & 0 deletions src/Stripe.net/Infrastructure/Public/DefaultStripeClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
namespace Stripe
{
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

/// <summary>
/// A default implementation of the <see cref="IStripeClient"/> interface. This is used by
/// StripeConfiguration to provide a default client, which is used in conjuction with Service
/// instances when no client is provided.
/// </summary>
internal class DefaultStripeClient : IStripeClient
{
public DefaultStripeClient(string apiKey, string clientId, IHttpClient httpClient)
{
this.Requestor = new LiveApiRequestor(
new StripeClientOptions
{
ApiKey = apiKey,
ClientId = clientId,
HttpClient = httpClient,
}, new List<string>());
}

/// <summary>Gets the base URL for Stripe's API.</summary>
/// <value>The base URL for Stripe's API.</value>
public string ApiBase => this.Requestor?.ApiBase;

/// <summary>Gets the API key used by the client to send requests.</summary>
/// <value>The API key used by the client to send requests.</value>
public string ApiKey => this.Requestor?.ApiKey;

/// <summary>Gets the client ID used by the client in OAuth requests.</summary>
/// <value>The client ID used by the client in OAuth requests.</value>
public string ClientId => this.Requestor?.ClientId;

/// <summary>Gets the base URL for Stripe's OAuth API.</summary>
/// <value>The base URL for Stripe's OAuth API.</value>
public string ConnectBase => this.Requestor?.ConnectBase;

/// <summary>Gets the base URL for Stripe's Files API.</summary>
/// <value>The base URL for Stripe's Files API.</value>
public string FilesBase => this.Requestor?.FilesBase;

/// <summary>Gets the base URL for Stripe's Meter Events API.</summary>
/// <value>The base URL for Stripe's Meter Events API.</value>
public string MeterEventsBase => this.Requestor?.MeterEventsBase;

/// <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 => this.Requestor?.HttpClient;

internal ApiRequestor Requestor { get; }

/// <inheritdoc/>
public async Task<T> RequestAsync<T>(
HttpMethod method,
string path,
BaseOptions options,
RequestOptions requestOptions,
CancellationToken cancellationToken = default)
where T : IStripeEntity
{
return await this.Requestor.RequestAsync<T>(BaseAddress.Api, method, path, options, requestOptions, cancellationToken).ConfigureAwait(false);
}

/// <inheritdoc/>
public async Task<Stream> RequestStreamingAsync(
HttpMethod method,
string path,
BaseOptions options,
RequestOptions requestOptions,
CancellationToken cancellationToken = default)
{
return await this.Requestor.RequestStreamingAsync(BaseAddress.Api, method, path, options, requestOptions, cancellationToken).ConfigureAwait(false);
}
}
}
12 changes: 10 additions & 2 deletions src/Stripe.net/Infrastructure/Public/LiveApiRequestor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace Stripe
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
Expand All @@ -17,8 +18,9 @@ internal class LiveApiRequestor : ApiRequestor
internal static readonly List<string> RawRequestUsage = new List<string> { "raw_request" };
private JsonSerializerSettings jsonSerializerSettings;
private StripeClientOptions clientOptions;
private List<string> defaultUsage;

public LiveApiRequestor(StripeClientOptions options)
public LiveApiRequestor(StripeClientOptions options, List<string> defaultUsage = null)
{
// Clone the object passed in, or use an empty option object if it is null
options = options?.Clone() ?? new StripeClientOptions();
Expand All @@ -41,6 +43,7 @@ public LiveApiRequestor(StripeClientOptions options)
this.ConnectBase = options.ConnectBase ?? DefaultConnectBase;
this.FilesBase = options.FilesBase ?? DefaultFilesBase;
this.MeterEventsBase = options.MeterEventsBase ?? DefaultMeterEventsBase;
this.defaultUsage = defaultUsage ?? new List<string>();
this.jsonSerializerSettings = StripeConfiguration.DefaultSerializerSettings(this);
}

Expand Down Expand Up @@ -216,6 +219,11 @@ private StripeRequest MakeStripeRequest(
RequestOptions requestOptions,
ApiMode apiMode)
{
if (this.defaultUsage.Count > 0)
{
requestOptions = requestOptions.WithUsage(this.defaultUsage);
}

var uri = StripeRequest.BuildUri(
requestOptions?.BaseUrl ?? this.GetBaseUrl(baseAddress),
method,
Expand Down Expand Up @@ -297,7 +305,7 @@ public override async Task<StripeResponse> RawRequestAsync(
throw new InvalidOperationException("content is not allowed for non-POST requests.");
}

requestOptions = requestOptions.WithUsage(RawRequestUsage);
requestOptions = requestOptions.WithUsage(this.defaultUsage.Concat(RawRequestUsage).ToList());
var apiMode = ApiModeUtils.GetApiMode(path);
var uri = StripeRequest.BuildUri(
requestOptions?.BaseUrl ?? this.GetBaseUrl(BaseAddress.Api),
Expand Down
5 changes: 4 additions & 1 deletion src/Stripe.net/Infrastructure/Public/StripeClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace Stripe
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
Expand All @@ -13,6 +14,8 @@ namespace Stripe
/// </summary>
public class StripeClient : IStripeClient
{
internal static readonly List<string> StripeClientUsage = new List<string> { "stripe_client" };

private JsonSerializerSettings jsonSerializerSettings;

// Fields: The beginning of the section generated from our OpenAPI spec
Expand Down Expand Up @@ -71,7 +74,7 @@ public StripeClient(
}

public StripeClient(StripeClientOptions options)
: this(new LiveApiRequestor(options))
: this(new LiveApiRequestor(options, StripeClientUsage))
{
}

Expand Down
5 changes: 2 additions & 3 deletions src/Stripe.net/Infrastructure/Public/StripeConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ namespace Stripe
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Stripe.Infrastructure;
Expand Down Expand Up @@ -243,7 +242,7 @@ public static void SetApiKey(string newApiKey)
ApiKey = newApiKey;
}

private static StripeClient BuildDefaultStripeClient()
private static IStripeClient BuildDefaultStripeClient()
{
if (ApiKey != null && ApiKey.Length == 0)
{
Expand All @@ -268,7 +267,7 @@ private static StripeClient BuildDefaultStripeClient()
maxNetworkRetries: MaxNetworkRetries,
appInfo: AppInfo,
enableTelemetry: EnableTelemetry);
return new StripeClient(ApiKey, ClientId, httpClient: httpClient);
return new DefaultStripeClient(ApiKey, ClientId, httpClient);
}
}
}
30 changes: 17 additions & 13 deletions src/StripeTests/BaseStripeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,42 +69,46 @@ public BaseStripeTest(
if ((this.StripeMockFixture != null) && (this.MockHttpClientFixture != null))
{
// Set up StripeClient to use stripe-mock with the mock HTTP client
var requestor = this.StripeMockFixture.BuildApiRequestor(this.MockHttpClientFixture.MockHandler.Object);
this.StripeClient = new StripeClient(requestor);
this.Requestor = requestor;
var clientOptions = this.StripeMockFixture.BuildStripeClientOptions(this.MockHttpClientFixture.MockHandler.Object);
var client = new StripeClient(clientOptions);
this.StripeClient = client;
this.Requestor = client.Requestor;

// Reset the mock before each test
this.MockHttpClientFixture.Reset();
}
else if (this.StripeMockFixture != null)
{
// Set up StripeClient to use stripe-mock
var requestor = this.StripeMockFixture.BuildApiRequestor();
this.StripeClient = new StripeClient(requestor);
this.Requestor = requestor;
var clientOptions = this.StripeMockFixture.BuildStripeClientOptions();
var client = new StripeClient(clientOptions);
this.StripeClient = client;
this.Requestor = client.Requestor;
}
else if (this.MockHttpClientFixture != null)
{
// Set up StripeClient with the mock HTTP client
var httpClient = new SystemNetHttpClient(
new HttpClient(this.MockHttpClientFixture.MockHandler.Object));
var requestor = new LiveApiRequestor(new StripeClientOptions
var clientOptions = new StripeClientOptions
{
ApiKey = "sk_test_123",
HttpClient = httpClient,
});
this.StripeClient = new StripeClient(requestor);
this.Requestor = requestor;
};
var client = new StripeClient(clientOptions);
this.StripeClient = client;
this.Requestor = client.Requestor;

// Reset the mock before each test
this.MockHttpClientFixture.Reset();
}
else
{
// Use the default StripeClient
var requestor = new LiveApiRequestor(new StripeClientOptions { ApiKey = "sk_test_123" });
this.StripeClient = new StripeClient(requestor);
this.Requestor = requestor;
var clientOptions = new StripeClientOptions { ApiKey = "sk_test_123" };
var client = new StripeClient(clientOptions);
this.StripeClient = client;
this.Requestor = client.Requestor;
}
}

Expand Down
29 changes: 2 additions & 27 deletions src/StripeTests/Functional/TelemetryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ namespace StripeTests
using Stripe;
using Xunit;

using static TelemetryTestUtils;

public class TelemetryTest : BaseStripeTest
{
public TelemetryTest(MockHttpClientFixture mockHttpClientFixture)
Expand Down Expand Up @@ -175,33 +177,6 @@ public void NoTelemetryWhenDisabled()
ItExpr.IsAny<CancellationToken>());
}

private static bool TelemetryHeaderMatcher(
HttpHeaders headers,
Func<string, bool> requestIdMatcher,
Func<long, bool> durationMatcher,
Func<List<string>, bool> usageMatcher)
{
if (!headers.Contains("X-Stripe-Client-Telemetry"))
{
return false;
}

var payload = headers.GetValues("X-Stripe-Client-Telemetry").First();

var deserialized = JToken.Parse(payload);
var requestId = (string)deserialized["last_request_metrics"]["request_id"];
var duration = (long)deserialized["last_request_metrics"]["request_duration_ms"];
var usageRaw = deserialized["last_request_metrics"]["usage"];

List<string> usage = null;
if (usageRaw != null)
{
usage = usageRaw.Select(x => (string)x).ToList();
}

return requestIdMatcher(requestId) && durationMatcher(duration) && usageMatcher(usage);
}

private class TestEntity : StripeEntity<TestEntity>
{
}
Expand Down
47 changes: 47 additions & 0 deletions src/StripeTests/Functional/TelemetryTestUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace StripeTests
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using Moq.Protected;
using Newtonsoft.Json.Linq;
using Stripe;
using Xunit;

internal class TelemetryTestUtils
{
public static bool TelemetryHeaderMatcher(
HttpHeaders headers,
Func<string, bool> requestIdMatcher,
Func<long, bool> durationMatcher,
Func<List<string>, bool> usageMatcher)
{
if (!headers.Contains("X-Stripe-Client-Telemetry"))
{
return false;
}

var payload = headers.GetValues("X-Stripe-Client-Telemetry").First();

var deserialized = JToken.Parse(payload);
var requestId = (string)deserialized["last_request_metrics"]["request_id"];
var duration = (long)deserialized["last_request_metrics"]["request_duration_ms"];
var usageRaw = deserialized["last_request_metrics"]["usage"];

List<string> usage = null;
if (usageRaw != null)
{
usage = usageRaw.Select(x => (string)x).ToList();
}

return requestIdMatcher(requestId) && durationMatcher(duration) && usageMatcher(usage);
}
}
}
Loading
Loading