Skip to content

Commit

Permalink
#68 FedExOAuthService
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeybusygin committed Aug 27, 2024
1 parent 8fa0421 commit 878949e
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 3 deletions.
18 changes: 18 additions & 0 deletions ShippingRates/Models/FedEx/FedExErrorResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;

namespace ShippingRates.Models.FedEx
{
class FedExErrorResponse
{
[JsonPropertyName("errors")]
public FedExErrorItem[] Errors { get; set; }
}

class FedExErrorItem
{
[JsonPropertyName("code")]
public string Code { get; set; }
[JsonPropertyName("message")]
public string Message { get; set; }
}
}
77 changes: 77 additions & 0 deletions ShippingRates/Services/FedEx/FedExOAuthService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using ShippingRates.ShippingProviders;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace ShippingRates.Services.FedEx
{
internal class FedExOAuthService
{
static string GetOAuthRequestUri(bool isProduction)
=> $"https://{(isProduction ? "apis" : "apis-sandbox")}.fedex.com/oauth/token";

public static async Task<string> GetTokenAsync(FedExProviderConfiguration configuration, HttpClient httpClient, Action<Error> reportError)
{
var token = TokenCacheService.GetToken(configuration.ClientId);
if (!string.IsNullOrEmpty(token))
return token;

var requestMessage = new HttpRequestMessage(HttpMethod.Post, GetOAuthRequestUri(configuration.UseProduction));
var postData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", configuration.ClientId),
new KeyValuePair<string, string>("client_secret", configuration.ClientSecret)
};
requestMessage.Content = new FormUrlEncodedContent(postData);

var responseMessage = await httpClient.SendAsync(requestMessage);
var response = await responseMessage.Content.ReadAsStringAsync();

if (responseMessage.IsSuccessStatusCode)
{
var result = JsonSerializer.Deserialize<FedExOAuthResponse>(response);

TokenCacheService.AddToken(configuration.ClientId, result.AccessToken, result.ExpiresIn);

return result.AccessToken;
}
else
{
var errorResponse = JsonSerializer.Deserialize<Models.FedEx.FedExErrorResponse>(response);
if ((errorResponse?.Errors?.Length ?? 0) > 0)
{
foreach (var error in errorResponse.Errors)
{
reportError(new Error()
{
Number = error.Code,
Description = error.Message
});
}
}
else
{
reportError(new Error() { Description = $"Unknown error while fetching FedEx OAuth token: {responseMessage.StatusCode} {response}" });
}

return null;
}
}

class FedExOAuthResponse
{
[JsonPropertyName("token_type")]
public string TokenType { get; set; }
[JsonPropertyName("access_token")]
public string AccessToken { get; set; }
[JsonPropertyName("expires_in")]
public int ExpiresIn { get; set; }
[JsonPropertyName("scope")]
public string Scope { get; set; }
}
}
}
6 changes: 3 additions & 3 deletions ShippingRates/Services/TokenCacheService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
namespace ShippingRates.Services
{
/// <summary>
/// Token caching for UPS
/// Token caching for UPS, FedEx
/// </summary>
internal class TokenCacheService
{
static readonly ConcurrentDictionary<string, CacheItem> _cache = new ConcurrentDictionary<string, CacheItem>();
/// <summary>
/// Get token for a given client ID
/// </summary>
/// <param name="clientId">UPS Client ID</param>
/// <param name="clientId">Client ID</param>
/// <returns>Token string or null</returns>
public static string GetToken(string clientId)
{
Expand All @@ -29,7 +29,7 @@ public static string GetToken(string clientId)
/// <summary>
/// Add token
/// </summary>
/// <param name="clientId">UPS Client ID</param>
/// <param name="clientId">Client ID</param>
/// <param name="token">Token</param>
/// <param name="expiresIn">Expiration interval in seconds</param>
public static void AddToken(string clientId, string token, int expiresIn)
Expand Down
11 changes: 11 additions & 0 deletions ShippingRates/ShippingProviders/FedExProviderConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ namespace ShippingRates.ShippingProviders
{
public class FedExProviderConfiguration
{
/// <summary>
/// FedEx Account Number
/// </summary>
public string AccountNumber { get; set; }
public string Key { get; set; }
public string MeterNumber { get; set; }
Expand All @@ -19,5 +22,13 @@ public class FedExProviderConfiguration
/// If not using the production Rate API, you can use 5531 as the HubID per FedEx documentation.
/// </summary>
public string HubId { get; set; }
/// <summary>
/// FedEx Client Id (required for REST API)
/// </summary>
public string ClientId { get; set; }
/// <summary>
/// FedEx Client Secret (required for REST API)
/// </summary>
public string ClientSecret { get; set; }
}
}

0 comments on commit 878949e

Please sign in to comment.