Skip to content
Open
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
131 changes: 89 additions & 42 deletions src/AvaTaxClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,25 @@ namespace Avalara.AvaTax.RestClient
/// </remarks>
public partial class AvaTaxClient
{
private Dictionary<string, string> _clientHeaders = new Dictionary<string, string>();
private Uri _envUri;
private UserConfiguration _userConfiguration = new UserConfiguration();

private readonly Dictionary<string, string> _clientHeaders = new Dictionary<string, string>();
private readonly Uri _envUri;
private readonly UserConfiguration _userConfiguration;
#if PORTABLE
private static HttpClient _httpClientStatic;

private readonly HttpClient _httpClient;
private HttpClient httpClient => _httpClient ?? _httpClientStatic;
#endif
/// <summary>
/// Tracks the amount of time spent on the most recent API call
/// </summary>
public CallDuration LastCallTime { get; set; }
public CallDuration LastCallTime { get; set; }

/// <summary>
/// Returns the version of the SDK that was compiled
/// </summary>
#if NET45
public static string SDK_TYPE { get { return "NET45"; } }
public static string SDK_TYPE { get { return "NET45"; } }
#endif
#if NET461
public static string SDK_TYPE { get { return "NET461"; } }
Expand All @@ -50,6 +55,16 @@ public partial class AvaTaxClient
#if NET20
public static string SDK_TYPE { get { return "NET20"; } }
#endif

private AvaTaxClient()
{
#if PORTABLE
if (_httpClientStatic == null)
{
_httpClientStatic = new HttpClient() {Timeout = TimeSpan.FromMinutes(20)};
}
#endif
}

#region Constructor
/// <summary>
Expand All @@ -63,13 +78,10 @@ public partial class AvaTaxClient
public AvaTaxClient(string appName, string appVersion, string machineName, AvaTaxEnvironment environment, UserConfiguration userConfiguration = null)
{
// Redo the client identifier
WithClientIdentifier(appName, appVersion, machineName);
if (userConfiguration != null)
{
_userConfiguration = userConfiguration;
}
WithClientIdentifier(appName, appVersion, machineName);
_userConfiguration = userConfiguration ?? new UserConfiguration();

// Setup the URI
// Setup the URI
switch (environment) {
case AvaTaxEnvironment.Sandbox: _envUri = new Uri(Constants.AVATAX_SANDBOX_URL); break;
case AvaTaxEnvironment.Production: _envUri = new Uri(Constants.AVATAX_PRODUCTION_URL); break;
Expand All @@ -89,14 +101,55 @@ public AvaTaxClient(string appName, string appVersion, string machineName, Uri c
{
// Redo the client identifier
WithClientIdentifier(appName, appVersion, machineName);
if (userConfiguration != null)
{
_userConfiguration = userConfiguration;
_userConfiguration = userConfiguration ?? new UserConfiguration();
_envUri = customEnvironment;
}

#if NETSTANDARD
/// <summary>
/// Generate a client that connects to one of the standard AvaTax servers
/// </summary>
/// <param name="httpClient"></param>
/// <param name="appName"></param>
/// <param name="appVersion"></param>
/// <param name="machineName"></param>
/// <param name="environment"></param>
public AvaTaxClient(HttpClient httpClient, string appName, string appVersion, string machineName, AvaTaxEnvironment environment, UserConfiguration userConfiguration = null)
{

_httpClient = httpClient;
// Redo the client identifier
WithClientIdentifier(appName, appVersion, machineName);
_userConfiguration = userConfiguration ?? new UserConfiguration();

// Setup the URI
switch (environment) {
case AvaTaxEnvironment.Sandbox: _envUri = new Uri(Constants.AVATAX_SANDBOX_URL); break;
case AvaTaxEnvironment.Production: _envUri = new Uri(Constants.AVATAX_PRODUCTION_URL); break;
default: throw new Exception("Unrecognized Environment");
}
}

/// <summary>
/// Generate a client that connects to a custom server
/// </summary>
/// <param name="httpClient"></param>
/// <param name="appName"></param>
/// <param name="appVersion"></param>
/// <param name="machineName"></param>
/// <param name="customEnvironment"></param>
public AvaTaxClient(HttpClient httpClient, string appName, string appVersion, string machineName, Uri customEnvironment, UserConfiguration userConfiguration = null)
{
_httpClient = httpClient;
_userConfiguration = userConfiguration ?? new UserConfiguration();

// Redo the client identifier
WithClientIdentifier(appName, appVersion, machineName);
_envUri = customEnvironment;
}
}
#endif
#endregion

#region Security
/// <summary>
/// Sets the default security header string
Expand Down Expand Up @@ -178,9 +231,9 @@ public AvaTaxClient WithClientIdentifier(string appName, string appVersion, stri
{
_clientHeaders.Add(Constants.AVALARA_CLIENT_HEADER, String.Format("{0}; {1}; {2}; {3}; {4}", appName, appVersion, "CSharpRestClient", API_VERSION, machineName));
return this;
}
}
#endregion

#region REST Call Interface
#if PORTABLE
/// <summary>
Expand Down Expand Up @@ -238,8 +291,8 @@ public async Task<T> RestCallAsync<T>(string verb, AvaTaxPath relativePath, obje
public T RestCall<T>(string verb, AvaTaxPath relativePath, object content = null)
{
try {
return RestCallAsync<T>(verb, relativePath, content).Result;
return RestCallAsync<T>(verb, relativePath, content).Result;

// Unroll single-exception aggregates for ease of use
} catch (AggregateException ex) {
if (ex.InnerExceptions.Count == 1)
Expand Down Expand Up @@ -273,10 +326,10 @@ public FileResult RestCallFile(string verb, AvaTaxPath relativePath, object payl
}
throw;
}
}
}
#endif
#endregion

#region Implementation
private JsonSerializerSettings _serializer_settings = null;
private JsonSerializerSettings SerializerSettings
Expand Down Expand Up @@ -312,22 +365,22 @@ private async Task<FileResult> RestCallFileAsync(string verb, AvaTaxPath relativ
try
{
CallDuration cd = new CallDuration();

// Convert the JSON payload, if any
string jsonPayload = null;
string mimeType = null;

if (content != null && content is FileResult) {
content = ((FileResult)content).Data;
mimeType = ((FileResult)content).ContentType;
} else if (content != null) {
jsonPayload = JsonConvert.SerializeObject(content, SerializerSettings);
mimeType = "application/json";
}

// Call REST
using (var result = await InternalRestCallAsync(cd, verb, relativePath, jsonPayload, mimeType).ConfigureAwait(false)) {

// Read the result
if (result.IsSuccessStatusCode) {
string contentType = null;
Expand All @@ -343,23 +396,23 @@ private async Task<FileResult> RestCallFileAsync(string verb, AvaTaxPath relativ
Filename = filename,
Data = await result.Content.ReadAsByteArrayAsync().ConfigureAwait(false)
};

// Capture timings
cd.FinishParse();
this.LastCallTime = cd;

// Capture information about this API call and make it available for logging
var eventargs = new AvaTaxCallEventArgs() { HttpVerb = verb.ToUpper(), Code = result.StatusCode, RequestUri = new Uri(_envUri, relativePath.ToString()), RequestBody = jsonPayload, ResponseBody = fr.Data, Duration = cd };
OnCallCompleted(eventargs);
return fr;

// Handle exceptions and convert them to AvaTax results
} else {
var errorResponseString = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
var err = DeserializeErrorResult(errorResponseString, result.StatusCode);
cd.FinishParse();
this.LastCallTime = cd;

// Capture information about this API call error and make it available for logging
var eventargs = new AvaTaxCallEventArgs() { HttpVerb = verb.ToUpper(), Code = result.StatusCode, RequestUri = new Uri(_envUri, relativePath.ToString()), RequestBody = jsonPayload, ResponseString = errorResponseString, Duration = cd };
OnCallCompleted(eventargs);
Expand Down Expand Up @@ -423,21 +476,15 @@ private async Task<HttpResponseMessage> InternalRestCallAsync(CallDuration cd, s
request.Content = jsonPayload as MultipartFormDataContent;
} else if (jsonPayload != null) {
request.Content = new StringContent(jsonPayload as string, Encoding.UTF8, mimeType);
}
}

// Send
// Send
cd.FinishSetup();

HttpResponseMessage response;
using (var client = new HttpClient() { Timeout = TimeSpan.FromMinutes(_userConfiguration.TimeoutInMinutes) })
{
response = await client.SendAsync(request).ConfigureAwait(false);
}

return response;
return await httpClient.SendAsync(request).ConfigureAwait(false);
}
}

/// <summary>
/// Implementation of raw string-returning async API
/// </summary>
Expand Down
1 change: 0 additions & 1 deletion src/Avalara.AvaTax.netstandard11.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
<PackageReference Include="System.Net.Http" Version="4.3.2" />
</ItemGroup>

Expand Down