diff --git a/QuickBooksSharp/Authentication/AuthenticationService.cs b/QuickBooksSharp/Authentication/AuthenticationService.cs index 79d4bab..33178b7 100644 --- a/QuickBooksSharp/Authentication/AuthenticationService.cs +++ b/QuickBooksSharp/Authentication/AuthenticationService.cs @@ -1,4 +1,5 @@ using Flurl; +using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Net.Http; @@ -10,7 +11,8 @@ namespace QuickBooksSharp { public class AuthenticationService : IAuthenticationService { - private readonly QuickBooksHttpClient _client = new QuickBooksHttpClient(null, null, new NoRetryRunPolicy()); + private readonly QuickBooksHttpClient _client = new QuickBooksHttpClient(null, null, new NoRetryRunPolicy(), null); + private readonly ILogger? _logger; //TODO: retrieve the endpoints URLs dynamically //See https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-openid-discovery-doc @@ -20,6 +22,12 @@ public class AuthenticationService : IAuthenticationService private const string USER_INFO_ENDPOINT_URL = "https://accounts.platform.intuit.com/v1/openid_connect/userinfo"; private const string USER_INFO_ENDPOINT_SANDBOX_URL = "https://sandbox-accounts.platform.intuit.com/v1/openid_connect/userinfo"; + public AuthenticationService(ILogger? logger = null) + { + _logger = logger; + _client = new QuickBooksHttpClient(null, null, new NoRetryRunPolicy(), logger); + } + public string GenerateAuthorizationPromptUrl(string clientId, IEnumerable scopes, string redirectUrl, string state) { return new Url("https://appcenter.intuit.com/connect/oauth2") @@ -33,7 +41,7 @@ public string GenerateAuthorizationPromptUrl(string clientId, IEnumerable GetUserInfo(string accessToken, bool useSandbox) { - return await new QuickBooksHttpClient(accessToken, null, RunPolicy.DefaultRunPolicy).GetAsync(useSandbox ? USER_INFO_ENDPOINT_SANDBOX_URL : USER_INFO_ENDPOINT_URL); + return await new QuickBooksHttpClient(accessToken, null, RunPolicy.DefaultRunPolicy, _logger).GetAsync(useSandbox ? USER_INFO_ENDPOINT_SANDBOX_URL : USER_INFO_ENDPOINT_URL); } public async Task GetOAuthTokenAsync(string clientId, string clientSecret, string code, string redirectUrl) diff --git a/QuickBooksSharp/Infrastructure/QuickBooksHttpClient.cs b/QuickBooksSharp/Infrastructure/QuickBooksHttpClient.cs index 1478e4c..5d47e95 100644 --- a/QuickBooksSharp/Infrastructure/QuickBooksHttpClient.cs +++ b/QuickBooksSharp/Infrastructure/QuickBooksHttpClient.cs @@ -1,4 +1,5 @@ using Flurl; +using Microsoft.Extensions.Logging; using System; using System.Net; using System.Net.Http; @@ -16,6 +17,7 @@ public class QuickBooksHttpClient : IQuickBooksHttpClient private readonly string? _accessToken; private readonly long? _realmId; private IRunPolicy _runPolicy; + private ILogger? _logger; private static HttpClient _httpClient = new HttpClient(new HttpClientHandler { @@ -43,11 +45,12 @@ static QuickBooksHttpClient() _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); } - public QuickBooksHttpClient(string? accessToken, long? realmId, IRunPolicy runPolicy) + public QuickBooksHttpClient(string? accessToken, long? realmId, IRunPolicy runPolicy, ILogger? logger) { _accessToken = accessToken; _realmId = realmId; _runPolicy = runPolicy; + _logger = logger; } public async Task GetAsync(Url url) @@ -80,7 +83,40 @@ public async Task SendAsync(Func makeRe if (_accessToken != null) request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken); + Guid requestGuid = Guid.NewGuid(); + + if (_logger != null) + { + string requestString = string.Empty; + if (request.Content != null) + { + await request.Content.LoadIntoBufferAsync(); //Ensure content is buffered for logging in case of retries + requestString = await request.Content.ReadAsStringAsync(); + } + _logger?.LogDebug(100, "Sending request {requestGuid} to {RequestUrl} with body {RequestBody} and headers {RequestHeaders}", + requestGuid, + request.RequestUri, + requestString, + request.Headers); + } + var response = await _httpClient.SendAsync(request); + + if (_logger != null) + { + string responseString = string.Empty; + if (response.Content != null) + { + await response.Content.LoadIntoBufferAsync(); //Ensure content is buffered for logging in case of retries + responseString = await response.Content.ReadAsStringAsync(); + } + _logger?.LogDebug(101, "Received response {StatusCode} for request {requestGuid} with body {ResponseBody} and headers {ResponseHeaders}", + response.StatusCode, + requestGuid, + responseString, + response.Headers); + } + var ex = response.IsSuccessStatusCode ? null : new QuickBooksException(request, response, await response.Content.ReadAsStringAsync()); if (ex?.IsRateLimit == true) diff --git a/QuickBooksSharp/QuickBooksSharp.csproj b/QuickBooksSharp/QuickBooksSharp.csproj index 5c14c60..5aa6624 100644 --- a/QuickBooksSharp/QuickBooksSharp.csproj +++ b/QuickBooksSharp/QuickBooksSharp.csproj @@ -19,6 +19,7 @@ + diff --git a/QuickBooksSharp/Services/DataService.cs b/QuickBooksSharp/Services/DataService.cs index 2cdb8f6..4afdab2 100644 --- a/QuickBooksSharp/Services/DataService.cs +++ b/QuickBooksSharp/Services/DataService.cs @@ -1,4 +1,5 @@ using Flurl; +using Microsoft.Extensions.Logging; using QuickBooksSharp.Entities; using System; using System.Collections.Generic; @@ -16,9 +17,9 @@ public class DataService : IDataService protected readonly Url _serviceUrl; - public DataService(string accessToken, long realmId, bool useSandbox, IRunPolicy? runPolicy = null) + public DataService(string accessToken, long realmId, bool useSandbox, IRunPolicy? runPolicy = null, ILogger? logger = null) { - _client = new QuickBooksHttpClient(accessToken, realmId, runPolicy ?? RunPolicy.DefaultRunPolicy); + _client = new QuickBooksHttpClient(accessToken, realmId, runPolicy ?? RunPolicy.DefaultRunPolicy, logger); _serviceUrl = QuickBooksUrl.Build(useSandbox, realmId); }