From a584bcac5dc62a674634524c1f595de16a9698b1 Mon Sep 17 00:00:00 2001 From: Nate Harris Date: Fri, 12 Apr 2024 11:55:29 -0600 Subject: [PATCH] [fix] Invalid `CarrierFields` structure (#558) - Fix CarrierFields serialization structure - Add unit test to confirm de/serialization works as expected --- CHANGELOG.md | 1 + .../CarrierAccountServiceTest.cs | 94 +++++++++++++++++++ EasyPost/Models/API/CarrierAccount.cs | 7 +- EasyPost/Models/API/CarrierFields.cs | 11 +-- 4 files changed, 104 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a65b48a76..e4de49833 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Next Release +- Fix `CarrierFields` serialization bug causing carrier account operations to fail - Fix accessibility for `CreateFedExSmartPost` parameter set ## v6.3.0 (2024-04-10) diff --git a/EasyPost.Tests/ServicesTests/CarrierAccountServiceTest.cs b/EasyPost.Tests/ServicesTests/CarrierAccountServiceTest.cs index 5e91d9b88..bd87b4873 100644 --- a/EasyPost.Tests/ServicesTests/CarrierAccountServiceTest.cs +++ b/EasyPost.Tests/ServicesTests/CarrierAccountServiceTest.cs @@ -1,11 +1,14 @@ using System; using System.Collections.Generic; +using System.Net; using System.Threading.Tasks; using EasyPost.Exceptions.API; +using EasyPost.Http; using EasyPost.Models.API; using EasyPost.Tests._Utilities; using EasyPost.Tests._Utilities.Attributes; using EasyPost.Utilities.Internal.Attributes; +using Newtonsoft.Json.Linq; using Xunit; namespace EasyPost.Tests.ServicesTests @@ -144,6 +147,97 @@ public async Task TestDelete() #endregion + /// + /// Test that the CarrierAccount fields are correctly deserialized from the API response. + /// None of the demo carrier accounts used in the above tests have credentials or test credentials fields, so we need to use some mock data. + /// + [Fact] + [Testing.EdgeCase] + public async Task TestCarrierFieldsJsonDeserialization() + { + UseMockClient(new List + { + new( + new TestUtils.MockRequestMatchRules(Method.Get, @"v2\/carrier_accounts$"), + new TestUtils.MockRequestResponseInfo(HttpStatusCode.OK, content: "[{\"id\":\"ca_123\",\"object\":\"CarrierAccount\",\"fields\":{\"credentials\":{\"account_number\":{\"visibility\":\"visible\",\"label\":\"DHL Account Number\",\"value\":\"123456\"},\"country\":{\"visibility\":\"visible\",\"label\":\"Account Country Code (2 Letter)\",\"value\":\"US\"},\"site_id\":{\"visibility\":\"visible\",\"label\":\"Site ID (Optional)\",\"value\": null },\"password\":{\"visibility\":\"password\",\"label\":\"Password (Optional)\",\"value\":\"\"},\"is_reseller\":{\"visibility\":\"checkbox\",\"label\":\"Reseller Account? (check if yes)\",\"value\":null}}}}]") + ) + }); + + List carrierAccounts = await Client.CarrierAccount.All(); + + Assert.NotEmpty(carrierAccounts); + Assert.Single(carrierAccounts); + + CarrierAccount carrierAccount = carrierAccounts[0]; + Assert.NotNull(carrierAccount.Fields); + Assert.NotNull(carrierAccount.Fields.Credentials); + Assert.NotNull(carrierAccount.Fields.Credentials["account_number"]); + + CarrierField accountNumberField = carrierAccount.Fields.Credentials["account_number"]; + Assert.Equal("visible", accountNumberField.Visibility); + Assert.Equal("DHL Account Number", accountNumberField.Label); + Assert.Equal("123456", accountNumberField.Value); + } + + /// + /// Test that the CarrierAccount fields are correctly serialized to the API request. + /// + [Fact] + [Testing.EdgeCase] + public async Task TestCarrierFieldsJsonSerialization() + { + UseMockClient(new List + { + new( + new TestUtils.MockRequestMatchRules(Method.Post, @"v2\/pickups"), + new TestUtils.MockRequestResponseInfo(HttpStatusCode.OK, content: "{}") + ) + } + ); + + CarrierAccount carrierAccount = new() + { + Id = "ca_123", + Fields = new CarrierFields + { + Credentials = new Dictionary + { + { + "account_number", + new CarrierField + { + Visibility = "visible", + Label = "DHL Account Number", + Value = "123456" + } + } + } + } + }; + + EasyPost.Parameters.Pickup.Create parameters = new() + { + Shipment = new Shipment(), + CarrierAccounts = new List { carrierAccount } + }; + + // Confirm that the CarrierAccount fields are serialized correctly + Dictionary json = parameters.ToDictionary(); + Dictionary pickupJson = json["pickup"] as Dictionary; + List carrierAccountsJson = pickupJson["carrier_accounts"] as List; + Dictionary carrierAccountJson = carrierAccountsJson[0] as Dictionary; + JObject fieldsJson = carrierAccountJson["fields"] as JObject; + JObject credentialsJson = fieldsJson["credentials"] as JObject; + JObject accountNumberJson = credentialsJson["account_number"] as JObject; + JToken visibility = accountNumberJson["visibility"]; + Assert.Equal("visible", visibility); + + // Test serialization again via an actual API call attempt + // This will throw an exception if the CarrierAccount fields are not serialized correctly + Exception? possibleException = await Record.ExceptionAsync(async () => await Client.Pickup.Create(parameters)); + Assert.Null(possibleException); + } + #endregion } } diff --git a/EasyPost/Models/API/CarrierAccount.cs b/EasyPost/Models/API/CarrierAccount.cs index 822e9d183..598f4b596 100644 --- a/EasyPost/Models/API/CarrierAccount.cs +++ b/EasyPost/Models/API/CarrierAccount.cs @@ -43,6 +43,12 @@ public class CarrierAccount : EasyPostObject, Parameters.ICarrierAccountParamete [JsonProperty("fields")] public CarrierFields? Fields { get; set; } + /// + /// The logo for the associated carrier. + /// + [JsonProperty("logo")] + public string? Logo { get; set; } + /// /// The name used when displaying a readable value for the type of the carrier account. /// @@ -69,7 +75,6 @@ public class CarrierAccount : EasyPostObject, Parameters.ICarrierAccountParamete public string? Type { get; set; } #endregion - } } #pragma warning disable CA1724 // Naming conflicts with Parameters.CarrierAccount diff --git a/EasyPost/Models/API/CarrierFields.cs b/EasyPost/Models/API/CarrierFields.cs index 1a473c796..b404529be 100644 --- a/EasyPost/Models/API/CarrierFields.cs +++ b/EasyPost/Models/API/CarrierFields.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using EasyPost._base; using Newtonsoft.Json; @@ -14,7 +15,7 @@ public class CarrierFields : EasyPostObject /// The credentials used in the production environment. /// [JsonProperty("credentials")] - public CarrierField? Credentials { get; set; } + public Dictionary? Credentials { get; set; } /// /// The credentials used in the test environment. @@ -23,7 +24,7 @@ public class CarrierFields : EasyPostObject #pragma warning disable SA1515 // ReSharper disable once InconsistentNaming #pragma warning restore SA1515 - public CarrierField? TestCredentials { get; set; } + public Dictionary? TestCredentials { get; set; } /// /// For USPS, this designates that no credentials are required. @@ -47,12 +48,6 @@ public class CarrierField : EasyPostObject { #region JSON Properties - /// - /// The key of the field. - /// - [JsonProperty("key")] - public string? Key { get; set; } - /// /// The visibility value is used to control form field types. /// See for more details.