Skip to content

Commit

Permalink
Merge pull request #43 from easykeys/feature/fedex/api
Browse files Browse the repository at this point in the history
Feature/fedex/api
  • Loading branch information
ucrengineer authored May 8, 2024
2 parents 829283c + a45bb28 commit f88f5bf
Show file tree
Hide file tree
Showing 173 changed files with 3,379 additions and 756 deletions.
65 changes: 65 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,68 @@ dotnet_diagnostic.SA1300.severity = silent

# SA1117: Parameters should be on same line or separate lines
dotnet_diagnostic.SA1117.severity = suggestion
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:warning
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_style_expression_bodied_methods = false:warning
csharp_style_expression_bodied_constructors = false:warning
csharp_style_expression_bodied_operators = true:warning
csharp_indent_labels = no_change
csharp_style_expression_bodied_properties = true:warning
csharp_space_around_binary_operators = before_and_after

[*.{cs,vb}]
#### Naming styles ####

# Naming rules

dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i

dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case

dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case

# Symbol specifications

dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =

dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =

dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =

# Naming styles

dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
end_of_line = lf
4 changes: 2 additions & 2 deletions .github/workflows/feature.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ jobs:
echo "CommitsSinceVersionSource: ${{ steps.gitversion.outputs.commitsSinceVersionSource }}"
echo "CommitDate: ${{ steps.gitversion.outputs.commitDate }}"
- name: Install .net7.0
- name: Install .net8.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: "7.0.x"
dotnet-version: "8.0.x"
include-prerelease: true

- name: Clear Nuget Feeds
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/master-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ jobs:
with:
fetch-depth: 0

- name: Install .net7.0
- name: Install .net8.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: "7.0.x"
dotnet-version: "8.0.x"
include-prerelease: true

- name: Restore dependencies
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
- name: Install net7.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: "7.0.x"
dotnet-version: "8.0.x"
include-prerelease: true

- name: Clear Nuget Feeds
Expand Down
4 changes: 2 additions & 2 deletions EasyKeys.Shipping.sln
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@


Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31825.309
Expand Down Expand Up @@ -80,7 +80,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Minimal.Apis", "src\Minimal
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyKeys.Shipping.FuncTest", "test\EasyKeys.Shipping.FuncTest\EasyKeys.Shipping.FuncTest.csproj", "{48385BA3-B5AE-47E6-9C41-189EE6334D1E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyKeys.Shipping.FedEx.UploadDocument", "src\EasyKeys.Shipping.FedEx.UploadDocument\EasyKeys.Shipping.FedEx.UploadDocument.csproj", "{CCFE77D6-0A82-4EC9-AB2F-6FCD885FC489}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyKeys.Shipping.FedEx.UploadDocument", "src\EasyKeys.Shipping.FedEx.UploadDocument\EasyKeys.Shipping.FedEx.UploadDocument.csproj", "{CCFE77D6-0A82-4EC9-AB2F-6FCD885FC489}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
2 changes: 1 addition & 1 deletion GitVersion.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mode: Mainline
next-version: 3.11.0
next-version: 4.0.0
branches:
feature:
tag: preview
Expand Down
4 changes: 2 additions & 2 deletions build/dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<PropertyGroup
Condition="'$(TargetFramework)' == 'net7.0' Or '$(TargetFramework)' == 'netstandard2.0'">
<NetCoreVersion>7.0.*</NetCoreVersion>
Condition="'$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'netstandard2.0'">
<NetCoreVersion>8.0.*</NetCoreVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions build/settings.props
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<Project>

<PropertyGroup Label="Basic Settings">
<VersionPrefix>3.0.0-preview1</VersionPrefix>
<VersionPrefix>4.0.0-preview1</VersionPrefix>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
<NoWarn>$(NoWarn);CS1591;NU1605;DS126858;DS137138</NoWarn>
<NoWarn>$(NoWarn);CS1591;NU1605;DS126858;DS137138;SA1010;SA1011</NoWarn>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net7.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup Label="Nuget Package Settings">
<Description>DotNetCore Abstractions of Shipment Library.</Description>
<PackageTags>DotNetCore, Shipment Abstractions</PackageTags>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Humanizer" />
</ItemGroup>

<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Label="Nuget Package Settings">
<Description>DotNetCore Abstractions of Shipment Library.</Description>
<PackageTags>DotNetCore, Shipment Abstractions</PackageTags>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Humanizer" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ public interface IAddressValidationProvider
/// <param name="validateAddress"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<ValidateAddress> ValidateAddressAsync(ValidateAddress validateAddress, CancellationToken cancellationToken);
Task<ValidateAddress> ValidateAddressAsync(ValidateAddress validateAddress, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace EasyKeys.Shipping.FedEx.Abstractions.Api.V1.Auth;

/// <summary>
/// Implmentation of the FedEx Rates and Transit Times client. Can be used to get rates and transit times.
/// <see href="https://developer.fedex.com/api/en-us/catalog/authorization/v1/docs.html"/>.
/// </summary>
public interface IFedExAuthClient
{
/// <summary>
/// Gets the authentication token asynchronously.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The authentication token.</returns>
Task<string> GetTokenAsync(CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System.Collections.Concurrent;
using System.Net.Http.Json;

using EasyKeys.Shipping.FedEx.Abstractions.Api.V1.Auth.Models;
using EasyKeys.Shipping.FedEx.Abstractions.Options;

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace EasyKeys.Shipping.FedEx.Abstractions.Api.V1.Auth.Impl;

public class FedExAuthClient : IFedExAuthClient
{
private readonly HttpClient _httpClient;
private readonly FedExApiOptions _options;
private readonly ILogger<FedExAuthClient> _logger;
private readonly ConcurrentDictionary<string, string> _token = new();
private readonly ConcurrentDictionary<string, DateTimeOffset> _expirationClock = new();

public FedExAuthClient(
IHttpClientFactory clientFactory,
IOptionsMonitor<FedExApiOptions> optionsMonitor,
ILogger<FedExAuthClient> logger)
{
_options = optionsMonitor.CurrentValue;
_httpClient = clientFactory.CreateClient(nameof(IFedExAuthClient));
_logger = logger;
}

public async Task<string> GetTokenAsync(CancellationToken cancellationToken = default)
{
try
{
if (DateTimeOffset.Now < _expirationClock.GetValueOrDefault(nameof(_expirationClock)))
{
_logger.LogDebug("Token is returned");
_token.TryGetValue(nameof(TokenResponse), out var existingToken);
ArgumentNullException.ThrowIfNull(existingToken, nameof(TokenResponse));
return existingToken;
}

_logger.LogDebug("Token being requested.");

// Send a POST request to FedEx's token endpoint
var response = await _httpClient.PostAsync(
$"{_options.Url}oauth/token",
new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "client_id", _options.ClientId },
{ "client_secret", _options.ClientSecret },
{ "grant_type", "client_credentials" }
}),
cancellationToken);

response.EnsureSuccessStatusCode();

var tokenObj = await response.Content.ReadFromJsonAsync<TokenResponse>(cancellationToken);

ArgumentNullException.ThrowIfNull(tokenObj, nameof(TokenResponse));

_logger.LogInformation("Token being updated");

_token.AddOrUpdate(nameof(TokenResponse), tokenObj.Token, (x, y) => tokenObj.Token);

_expirationClock.AddOrUpdate(nameof(_expirationClock), (x) => DateTimeOffset.Now.AddSeconds(tokenObj.ExpiresIn - 5), (x, y) => y.AddSeconds(tokenObj.ExpiresIn - 5));

return tokenObj.Token;
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
return string.Empty;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Text.Json.Serialization;

namespace EasyKeys.Shipping.FedEx.Abstractions.Api.V1.Auth.Models;

public record TokenResponse
{
[JsonPropertyName("token_type")]
public string TokenType { get; init; } = string.Empty;

[JsonPropertyName("scope")]
public string Scope { get; init; } = string.Empty;

[JsonPropertyName("access_token")]
public string AccessToken { get; init; } = string.Empty;

[JsonPropertyName("expires_in")]
public long ExpiresIn { get; init; }

public string Token => $"{TokenType} {AccessToken}";
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using EasyKeys.Shipping.FedEx.Abstractions.Options;
using EasyKeys.Shipping.FedEx.Abstractions.Api.V1.Auth;
using EasyKeys.Shipping.FedEx.Abstractions.Api.V1.Auth.Impl;
using EasyKeys.Shipping.FedEx.Abstractions.Middleware;
using EasyKeys.Shipping.FedEx.Abstractions.Options;
using EasyKeys.Shipping.FedEx.Abstractions.Services;
using EasyKeys.Shipping.FedEx.Abstractions.Services.Impl;

Expand Down Expand Up @@ -26,4 +29,24 @@ public static IServiceCollection AddFedExClient(

return services;
}

/// <summary>
/// Adds <see cref="IFedExAuthClient"/> and <see cref="AuthRequestMiddleware"/> with configuration options <see cref="FedExApiOptions"/>.
/// </summary>
/// <param name="services">The DI services.</param>
/// <param name="sectionName">The section name for the options.</param>
/// <param name="configure">The configuration of options.</param>
/// <returns></returns>
public static IServiceCollection AddFedExAuthApiClient(
this IServiceCollection services,
string sectionName = nameof(FedExApiOptions),
Action<FedExApiOptions, IServiceProvider>? configure = null)
{
services.AddChangeTokenOptions<FedExApiOptions>(sectionName, null, (options, config) => configure?.Invoke(options, config));
services.AddHttpClient(name: nameof(IFedExAuthClient));
services.AddTransient<AuthRequestMiddleware>();
services.AddSingleton<IFedExAuthClient, FedExAuthClient>();

return services;
}
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net7.0</TargetFrameworks>
<NoWarn>$(NoWarn);CS0108</NoWarn>
</PropertyGroup>

<PropertyGroup Label="Nuget Package Settings">
<Description>DotNetCore Implementation of FedEx Web Services 2020 of WCF.</Description>
<PackageTags>DotNetCore, FedEx 2020 WCF</PackageTags>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Ardalis.SmartEnum" />
<PackageReference Include="System.ServiceModel.Http" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Bet.Extensions.Options" />
</ItemGroup>

<ItemGroup>
<ProjectReference
Include="..\EasyKeys.Shipping.Abstractions\EasyKeys.Shipping.Abstractions.csproj" />
</ItemGroup>

<ItemGroup>
<WCFMetadata Include="Connected Services" />
</ItemGroup>

<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
<NoWarn>$(NoWarn);CS0108</NoWarn>
</PropertyGroup>
<PropertyGroup Label="Nuget Package Settings">
<Description>DotNetCore Implementation of FedEx Authorization Api and Web Services 2020 of WCF.</Description>
<PackageTags>DotNetCore, FedEx 2020 WCF, FedEx v1 API, FedEx Authorization Api</PackageTags>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ardalis.SmartEnum" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="System.ServiceModel.Http" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Bet.Extensions.Options" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EasyKeys.Shipping.Abstractions\EasyKeys.Shipping.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Connected Services" />
</ItemGroup>
</Project>
Loading

0 comments on commit f88f5bf

Please sign in to comment.