Skip to content

Commit

Permalink
[feat] Add custom parameter sets for FedEx and UPS account creation (#…
Browse files Browse the repository at this point in the history
…509)

- Add custom parameter sets for FedEx and UPS account creation
- Prevent users from using generic account creation parameter set for FedEx and UPS
- Add unit tests, cassettes to confirm required/optional parameter set validation, confirm custom workflow carrier account parameter set usage
- Add custom message option in MissingParameter exception
  • Loading branch information
nwithan8 authored Aug 29, 2023
1 parent 1bc9700 commit 15143b5
Show file tree
Hide file tree
Showing 15 changed files with 425 additions and 178 deletions.
2 changes: 1 addition & 1 deletion EasyPost.Tests/ExceptionsTests/ExceptionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ public void TestExceptionConstructors()
Assert.Equal(testMessage, invalidObjectError.Message);

InvalidParameterError invalidParameterError = new(testPropertyName);
Assert.Equal(string.Format(CultureInfo.InvariantCulture, Constants.ErrorMessages.InvalidParameter, testPropertyName), invalidParameterError.Message);
Assert.Equal($"{string.Format(CultureInfo.InvariantCulture, Constants.ErrorMessages.InvalidParameter, testPropertyName)}. ", invalidParameterError.Message);

JsonDeserializationError jsonDeserializationError = new(testType);
Assert.Equal(string.Format(CultureInfo.InvariantCulture, Constants.ErrorMessages.JsonDeserializationError, testType.FullName), jsonDeserializationError.Message);
Expand Down
32 changes: 31 additions & 1 deletion EasyPost.Tests/Fixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,36 @@ internal static ParameterSets.CarrierAccount.Create Create(Dictionary<string, ob
RegistrationData = fixture.GetOrNull<Dictionary<string, object?>>("registration_data"),
};
}

internal static ParameterSets.CarrierAccount.CreateFedEx CreateFedEx(Dictionary<string, object>? fixture)
{
fixture ??= new Dictionary<string, object>();

return new ParameterSets.CarrierAccount.CreateFedEx
{
Description = fixture.GetOrNull<string>("description"),
Reference = fixture.GetOrNull<string>("reference"),
Credentials = fixture.GetOrNull<Dictionary<string, object?>>("credentials"),
TestCredentials = fixture.GetOrNull<Dictionary<string, object?>>("test_credentials"),
AccountNumber = "123456789",
CorporateAddressCity = "San Francisco",
CorporateAddressCountryCode = "US",
CorporateAddressPostalCode = "94105",
CorporateAddressState = "CA",
CorporateAddressStreet = "345 California St",
CorporateCompanyName = "EasyPost",
CorporateEmailAddress = "[email protected]",
CorporateFirstName = "Demo",
CorporateLastName = "User",
CorporateJobTitle = "Developer",
CorporatePhoneNumber = "5555555555",
ShippingAddressCity = "San Francisco",
ShippingAddressCountryCode = "US",
ShippingAddressPostalCode = "94105",
ShippingAddressState = "CA",
ShippingAddressStreet = "345 California St",
};
}
}

internal static class CustomsInfo
Expand Down Expand Up @@ -618,7 +648,7 @@ internal static ParameterSets.Shipment.Create Create(Dictionary<string, object>?
Parcel = Parcels.Create(parcelFixture),
CustomsInfo = CustomsInfo.Create(customsInfoFixture),
Options = Options(optionsFixture),
CarbonOffset = fixture.GetOrDefault<bool>("carbon_offset"), // this will always be true or false, never null
CarbonOffset = fixture.GetOrDefault<bool>("carbon_offset"), // this will always be true or false, never null
CarrierAccountIds = fixture.GetOrNull<List<string>>("carrier_accounts"),
Carrier = fixture.GetOrNull<string>("carrier"),
Service = fixture.GetOrNull<string>("service"),
Expand Down
40 changes: 39 additions & 1 deletion EasyPost.Tests/ParametersTests/ParametersTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,44 @@ public void TestParametersToDictionaryWithSubDictionary()
Assert.Equal(streetB, addressData["street1"]);
}

[Fact]
[Testing.Exception]
public void TestRequiredAndOptionalParameterValidation()
{
var parametersWithBothParameterSet = new ParameterSetWithRequiredAndOptionalParameters
{
RequiredParameter = "required",
OptionalParameter = "optional",
};
// should not throw an exception when serializing to a dictionary
parametersWithBothParameterSet.ToDictionary();

var parametersWithOnlyRequiredParameterSet = new ParameterSetWithRequiredAndOptionalParameters
{
RequiredParameter = "required",
};

// should not throw an exception when serializing to a dictionary
parametersWithOnlyRequiredParameterSet.ToDictionary();

var parametersWithOnlyOptionalParameterSet = new ParameterSetWithRequiredAndOptionalParameters
{
OptionalParameter = "optional",
};

// should throw an exception when serializing to a dictionary
Assert.Throws<Exceptions.General.MissingParameterError>(() => parametersWithOnlyOptionalParameterSet.ToDictionary());
}

private sealed class ParameterSetWithRequiredAndOptionalParameters : Parameters.BaseParameters
{
[TopLevelRequestParameter(Necessity.Required, "test", "required")]
public string? RequiredParameter { get; set; }

[TopLevelRequestParameter(Necessity.Optional, "test", "optional")]
public string? OptionalParameter { get; set; }
}

/// <summary>
/// This test proves that we can reuse the Addresses.Create parameter object,
/// with its serialization logic adapting to whether it is a top-level parameter object
Expand Down Expand Up @@ -200,7 +238,7 @@ public async Task TestDisallowUsingParameterObjectDictionariesInDictionaryFuncti
Street1 = street,
};

Dictionary<string, object> dictionary = addressCreationParameters.ToDictionary(); // this method is "internal", so end-users won't have access to it, for reasons seen below
Dictionary<string, object> dictionary = addressCreationParameters.ToDictionary(); // this method is "internal", so end-users won't have access to it, for reasons seen below

// At this point, the data has already been wrapped properly by the .ToDictionary() method, and this method expects raw (unwrapped) data
// This will cause a double-wrapping, sending malformed data to the API
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyPost.Exceptions.API;
using EasyPost.Exceptions.General;
using EasyPost.Models.API;
using EasyPost.Tests._Utilities;
using EasyPost.Tests._Utilities.Attributes;
Expand Down Expand Up @@ -57,28 +58,49 @@ public async Task TestCreateWithCustomWorkflow()
UseVCR("create_with_custom_workflow");

// Carriers like FedEx and UPS should hit the `/carrier_accounts/register` endpoint
try
{
Dictionary<string, object> data = Fixtures.BasicCarrierAccount;
data["type"] = "FedexAccount";
data["registration_data"] = new Dictionary<string, object>();
Dictionary<string, object> data = Fixtures.BasicCarrierAccount;

Parameters.CarrierAccount.Create parameters = Fixtures.Parameters.CarrierAccounts.Create(data);
Parameters.CarrierAccount.Create parameters = Fixtures.Parameters.CarrierAccounts.CreateFedEx(data);

try
{
// confirms we can pass in CreateFedEx and CreateUps parameters to the same Create method because they are children of the generic Create class
CarrierAccount carrierAccount = await Client.CarrierAccount.Create(parameters);
CleanUpAfterTest(carrierAccount.Id);
}
catch (InvalidRequestError e)

catch (BadRequestError e)
{
// the data we're sending is invalid, we want to check that the API error is because of malformed data and not due to the endpoint
Assert.Equal(422, e.StatusCode); // 422 is fine. We don't want a 404 not found
Assert.Equal(400, e.StatusCode); // 400 is fine. We don't want a 404 not found
Assert.NotNull(e.Errors);
Assert.Contains(e.Errors, error => error is { Field: "account_number", Message: "must be present and a string" });
Assert.Contains(e.Errors, error => error is { Message: "Invalid Customer Account Nbr" });

// Check the cassette to make sure the endpoint is correct (it should be carrier_accounts/register)
// Check the cassette to make sure the "registration_data" key is populated in the request body
}
}

[Fact]
[CrudOperations.Create]
[Testing.Exception]
public async Task TestPreventUsersUsingGenericParameterSetWithCustomWorkflow()
{
UseVCR("prevent_users_using_generic_parameter_set_with_custom_workflow");

// Generic Create parameter set configured for DHL
Dictionary<string, object> data = Fixtures.BasicCarrierAccount;

// Override the type to be a custom type
data["type"] = Constants.CarrierAccountTypes.FedExAccount;
data["registration_data"] = new Dictionary<string, object>();

Parameters.CarrierAccount.Create parameters = Fixtures.Parameters.CarrierAccounts.Create(data);

// should raise an exception because we're using a generic Create set with a custom workflow type (FedExAccount)
await Assert.ThrowsAsync<InvalidParameterError>(async () => await Client.CarrierAccount.Create(parameters));
}

[Fact]
[CrudOperations.Update]
[Testing.Function]
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 15143b5

Please sign in to comment.