Skip to content

Commit cf776be

Browse files
Add recording of telemetry to ApiClient
1 parent 7458826 commit cf776be

File tree

9 files changed

+79
-16
lines changed

9 files changed

+79
-16
lines changed

src/CheckoutSdk/AbstractCheckoutSdkBuilder.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ namespace Checkout
99
public abstract class AbstractCheckoutSdkBuilder<T>
1010
{
1111
protected Environment Env = Checkout.Environment.Sandbox;
12+
13+
private bool _recordTelemetry = true;
1214
private EnvironmentSubdomain _envSubdomain;
1315
protected IHttpClientFactory ClientFactory = new DefaultHttpClientFactory();
1416

@@ -24,6 +26,12 @@ public AbstractCheckoutSdkBuilder<T> EnvironmentSubdomain(string subdomain)
2426
return this;
2527
}
2628

29+
public AbstractCheckoutSdkBuilder<T> RecordTelemetry(bool recordTelemetry)
30+
{
31+
_recordTelemetry = recordTelemetry;
32+
return this;
33+
}
34+
2735
#if (NETSTANDARD2_0_OR_GREATER || NETCOREAPP3_1_OR_GREATER)
2836
public AbstractCheckoutSdkBuilder<T> LogProvider(ILoggerFactory loggerFactory)
2937
{
@@ -40,7 +48,7 @@ public AbstractCheckoutSdkBuilder<T> HttpClientFactory(IHttpClientFactory httpCl
4048

4149
protected CheckoutConfiguration GetCheckoutConfiguration()
4250
{
43-
return new CheckoutConfiguration(GetSdkCredentials(), Env, _envSubdomain, ClientFactory);
51+
return new CheckoutConfiguration(GetSdkCredentials(), Env, _envSubdomain, ClientFactory, _recordTelemetry);
4452
}
4553

4654
protected abstract SdkCredentials GetSdkCredentials();

src/CheckoutSdk/ApiClient.cs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
using System.Web;
44
#endif
55
using System;
6+
using System.Collections.Concurrent;
67
using System.Collections.Generic;
8+
using System.Diagnostics;
79
using System.Linq;
810
using System.Net;
911
using System.Net.Http;
@@ -19,16 +21,21 @@ public class ApiClient : IApiClient
1921
private readonly ILogger _log = LogProvider.GetLogger(typeof(ApiClient));
2022
#endif
2123

24+
private static string sdkTelemetryHeader = "cko-sdk-telemetry";
25+
private static int maxCountInTelemetryQueue = 10;
2226
private readonly HttpClient _httpClient;
2327
private readonly Uri _baseUri;
2428
private readonly ISerializer _serializer = new JsonSerializer();
29+
private ConcurrentQueue<RequestMetrics> requestMetricsQueue = new ConcurrentQueue<RequestMetrics>();
30+
private readonly bool _enableTelemetry;
2531

26-
public ApiClient(IHttpClientFactory httpClientFactory, Uri baseUri)
32+
public ApiClient(IHttpClientFactory httpClientFactory, Uri baseUri, bool enableTelemetry)
2733
{
2834
CheckoutUtils.ValidateParams("httpClientFactory", httpClientFactory, "baseUri", baseUri);
2935
var httpClient = httpClientFactory.CreateClient();
3036
_baseUri = baseUri;
3137
_httpClient = httpClient;
38+
_enableTelemetry = enableTelemetry;
3239
}
3340

3441
public async Task<TResult> Get<TResult>(
@@ -170,7 +177,7 @@ public async Task<TResult> Query<TResult>(
170177
$"{WebUtility.UrlEncode(kvp.Key)}={WebUtility.UrlEncode(kvp.Value)}"));
171178
#endif
172179

173-
path = $"{path}?{queryString}";
180+
path = $"{path}?{queryString}";
174181
}
175182
}
176183

@@ -246,8 +253,37 @@ private async Task<HttpResponseMessage> Invoke(
246253
{
247254
httpRequest.Headers.Add("Cko-Idempotency-Key", idempotencyKey);
248255
}
256+
257+
if (_enableTelemetry)
258+
{
259+
var currentRequestId = Guid.NewGuid().ToString();
260+
if (requestMetricsQueue.TryDequeue(out var lastRequestMetric))
261+
{
262+
lastRequestMetric.RequestId = currentRequestId;
263+
httpRequest.Headers.TryAddWithoutValidation(sdkTelemetryHeader, _serializer.Serialize(lastRequestMetric));
264+
}
249265

250-
return await _httpClient.SendAsync(httpRequest, cancellationToken);
266+
Stopwatch stopwatch = new Stopwatch();
267+
stopwatch.Start();
268+
var result = await _httpClient.SendAsync(httpRequest, cancellationToken);
269+
stopwatch.Stop();
270+
271+
if (requestMetricsQueue.Count < maxCountInTelemetryQueue)
272+
{
273+
lastRequestMetric.PrevRequestDuration = stopwatch.ElapsedMilliseconds;
274+
requestMetricsQueue.Enqueue(new RequestMetrics()
275+
{
276+
PrevRequestDuration = stopwatch.ElapsedMilliseconds,
277+
PrevRequestId = currentRequestId
278+
});
279+
}
280+
281+
return result;
282+
}
283+
else
284+
{
285+
return await _httpClient.SendAsync(httpRequest, cancellationToken);
286+
}
251287
}
252288

253289
private async Task ValidateResponseAsync(HttpResponseMessage httpResponse)

src/CheckoutSdk/CheckoutApi.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,25 +79,29 @@ private static ApiClient BaseApiClient(CheckoutConfiguration configuration)
7979
return new ApiClient(configuration.HttpClientFactory,
8080
configuration.EnvironmentSubdomain != null
8181
? configuration.EnvironmentSubdomain.ApiUri
82-
: configuration.Environment.GetAttribute<EnvironmentAttribute>().ApiUri);
82+
: configuration.Environment.GetAttribute<EnvironmentAttribute>().ApiUri,
83+
configuration.RecordTelemetry);
8384
}
8485

8586
private static ApiClient FilesApiClient(CheckoutConfiguration configuration)
8687
{
8788
return new ApiClient(configuration.HttpClientFactory,
88-
configuration.Environment.GetAttribute<EnvironmentAttribute>().FilesApiUri);
89+
configuration.Environment.GetAttribute<EnvironmentAttribute>().FilesApiUri,
90+
configuration.RecordTelemetry);
8991
}
9092

9193
private static ApiClient TransfersApiClient(CheckoutConfiguration configuration)
9294
{
9395
return new ApiClient(configuration.HttpClientFactory,
94-
configuration.Environment.GetAttribute<EnvironmentAttribute>().TransfersApiUri);
96+
configuration.Environment.GetAttribute<EnvironmentAttribute>().TransfersApiUri,
97+
configuration.RecordTelemetry);
9598
}
9699

97100
private static ApiClient BalancesApiClient(CheckoutConfiguration configuration)
98101
{
99102
return new ApiClient(configuration.HttpClientFactory,
100-
configuration.Environment.GetAttribute<EnvironmentAttribute>().BalancesApiUri);
103+
configuration.Environment.GetAttribute<EnvironmentAttribute>().BalancesApiUri,
104+
configuration.RecordTelemetry);
101105
}
102106

103107

src/CheckoutSdk/CheckoutConfiguration.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public class CheckoutConfiguration
1010

1111
public IHttpClientFactory HttpClientFactory { get; }
1212

13+
public bool RecordTelemetry {get;}
14+
1315
public CheckoutConfiguration(
1416
SdkCredentials sdkCredentials,
1517
Environment environment,
@@ -28,7 +30,8 @@ public CheckoutConfiguration(
2830
SdkCredentials sdkCredentials,
2931
Environment environment,
3032
EnvironmentSubdomain environmentSubdomain,
31-
IHttpClientFactory httpClientFactory)
33+
IHttpClientFactory httpClientFactory,
34+
bool recordTelemetry)
3235
{
3336
CheckoutUtils.ValidateParams(
3437
"sdkCredentials", sdkCredentials,
@@ -38,6 +41,7 @@ public CheckoutConfiguration(
3841
Environment = environment;
3942
EnvironmentSubdomain = environmentSubdomain;
4043
HttpClientFactory = httpClientFactory;
44+
RecordTelemetry = recordTelemetry;
4145
}
4246
}
4347
}

src/CheckoutSdk/Previous/AbstractCheckoutApmApi.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ protected AbstractCheckoutApmApi(CheckoutConfiguration configuration)
1515
var apiClient = new ApiClient(configuration.HttpClientFactory,
1616
configuration.EnvironmentSubdomain != null
1717
? configuration.EnvironmentSubdomain.ApiUri
18-
: configuration.Environment.GetAttribute<EnvironmentAttribute>().ApiUri);
18+
: configuration.Environment.GetAttribute<EnvironmentAttribute>().ApiUri,
19+
configuration.RecordTelemetry);
1920
_idealClient = new IdealClient(apiClient, configuration);
2021
_klarnaClient = new KlarnaClient(apiClient, configuration);
2122
_sepaClient = new SepaClient(apiClient, configuration);

src/CheckoutSdk/Previous/CheckoutApi.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ public CheckoutApi(CheckoutConfiguration configuration) : base(configuration)
3333
var apiClient = new ApiClient(configuration.HttpClientFactory,
3434
configuration.EnvironmentSubdomain != null
3535
? configuration.EnvironmentSubdomain.ApiUri
36-
: configuration.Environment.GetAttribute<EnvironmentAttribute>().ApiUri);
36+
: configuration.Environment.GetAttribute<EnvironmentAttribute>().ApiUri,
37+
configuration.RecordTelemetry);
3738
_tokensClient = new TokensClient(apiClient, configuration);
3839
_customersClient = new CustomersClient(apiClient, configuration);
3940
_sourcesClient = new SourcesClient(apiClient, configuration);

src/CheckoutSdk/RequestMetrics.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Checkout
2+
{
3+
public struct RequestMetrics
4+
{
5+
public string PrevRequestId { get; set; }
6+
public string RequestId { get; set; }
7+
public long PrevRequestDuration { get; set; }
8+
}
9+
}

test/CheckoutSdkTest/CheckoutApiTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public void ShouldInstantiateAndRetrieveClientsPrevious()
1616
httpClientFactoryMock.Setup(mock => mock.CreateClient())
1717
.Returns(new HttpClient());
1818
var checkoutConfiguration = new CheckoutConfiguration(sdkCredentialsMock.Object, Environment.Sandbox, null,
19-
httpClientFactoryMock.Object);
19+
httpClientFactoryMock.Object, false);
2020

2121
//Act
2222
Previous.ICheckoutApi checkoutApi = new Previous.CheckoutApi(checkoutConfiguration);
@@ -48,7 +48,7 @@ public void ShouldInstantiateAndRetrieveClientsDefault()
4848
httpClientFactoryMock.Setup(mock => mock.CreateClient())
4949
.Returns(new HttpClient());
5050
var checkoutConfiguration = new CheckoutConfiguration(sdkCredentialsMock.Object, Environment.Sandbox, null,
51-
httpClientFactoryMock.Object);
51+
httpClientFactoryMock.Object, false);
5252

5353
//Act
5454
ICheckoutApi checkoutApi = new CheckoutApi(checkoutConfiguration);

test/CheckoutSdkTest/CheckoutConfigurationTest.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ private void ShouldCreateConfiguration()
1313
var credentials = new StaticKeysSdkCredentials(ValidDefaultSk, ValidDefaultPk);
1414
var httpClientFactoryMock = new Mock<IHttpClientFactory>();
1515
var configuration =
16-
new CheckoutConfiguration(credentials, Environment.Production, null, httpClientFactoryMock.Object);
16+
new CheckoutConfiguration(credentials, Environment.Production, null, httpClientFactoryMock.Object, false);
1717
configuration.Environment.ShouldBe(Environment.Production);
1818
configuration.SdkCredentials.ShouldBeAssignableTo(typeof(StaticKeysSdkCredentials));
1919
}
@@ -30,7 +30,7 @@ public void ShouldCreateConfigurationWithSubdomain(string subdomain, string expe
3030
var httpClientFactoryMock = new Mock<IHttpClientFactory>();
3131
var environmentSubdomain = new EnvironmentSubdomain(Environment.Sandbox, subdomain);
3232
var configuration = new CheckoutConfiguration(credentials, Environment.Sandbox, environmentSubdomain,
33-
httpClientFactoryMock.Object);
33+
httpClientFactoryMock.Object, false);
3434

3535
configuration.Environment.ShouldBe(Environment.Sandbox);
3636
configuration.EnvironmentSubdomain.ApiUri.ToString().ShouldBe(expectedUri);
@@ -50,7 +50,7 @@ public void ShouldCreateConfigurationWithBadSubdomain(string subdomain, string e
5050
var httpClientFactoryMock = new Mock<IHttpClientFactory>();
5151
var environmentSubdomain = new EnvironmentSubdomain(Environment.Sandbox, subdomain);
5252
var configuration = new CheckoutConfiguration(credentials, Environment.Sandbox, environmentSubdomain,
53-
httpClientFactoryMock.Object);
53+
httpClientFactoryMock.Object, false);
5454

5555
configuration.Environment.ShouldBe(Environment.Sandbox);
5656
configuration.EnvironmentSubdomain.ApiUri.ToString().ShouldBe(expectedUri);

0 commit comments

Comments
 (0)