Skip to content

Commit

Permalink
[bug] Add missing claims parameters, fix interdependency parameter en…
Browse files Browse the repository at this point in the history
…forcement (#603)

- Account for enum comparison during inter-dependency checks on Parameter sets
- Add missing `CheckDeliveryAddress` parameter to claims creation parameters
- Add missing `ACH` payment option for claims
  • Loading branch information
nwithan8 authored Nov 18, 2024
1 parent 95dab8f commit b738965
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 20 deletions.
1 change: 1 addition & 0 deletions EasyPost.Tests/Fixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ internal static ParameterSets.Claim.Create Create(Dictionary<string, object>? fi
ContactEmail = fixture.GetOrNull<string>("contact_email"),
PaymentMethod = fixture.GetOrNullEnum<ClaimPaymentMethod>("payment_method"),
RecipientName = fixture.GetOrNull<string>("recipient_name"),
CheckDeliveryAddress = fixture.GetOrNull<string>("check_delivery_address"),
};
}

Expand Down
69 changes: 69 additions & 0 deletions EasyPost.Tests/ParametersTests/ParametersTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using EasyPost.Models.API;
using EasyPost.Tests._Utilities;
using EasyPost.Tests._Utilities.Attributes;
using EasyPost.Utilities.Internal;
using EasyPost.Utilities.Internal.Attributes;
using Xunit;

Expand Down Expand Up @@ -577,6 +578,52 @@ public void TestDependentNestedParameters()
}
}

[Fact]
[Testing.Custom]
public async Task TestEnumUsageInInterdependentParameterEnforcement()
{
// Should pass because Param1 and Param2 are set correctly
ParameterSetWithInterdependentEnums parameters = new()
{
Param1 = ParameterEnum.Value1,
Param2 = "value2"
};

try
{
parameters.ToDictionary();
}
catch (Exceptions.General.InvalidParameterPairError)
{
Assert.Fail("Should not throw exception if both Param1 and Param2 are set correctly.");
}

// Should fail because Param1 is set to Value1, but Param2 is not set to "value2"
parameters = new ParameterSetWithInterdependentEnums
{
Param1 = ParameterEnum.Value1,
Param2 = "value3"
};

Assert.Throws<Exceptions.General.InvalidParameterPairError>(() => parameters.ToDictionary());

// Should pass because Param1 is not set to a value that enforces a restriction on Param2
parameters = new ParameterSetWithInterdependentEnums
{
Param1 = ParameterEnum.Value2,
Param2 = "value3"
};

try
{
parameters.ToDictionary();
}
catch (Exceptions.General.InvalidParameterPairError)
{
Assert.Fail("Should not throw exception if Param1 is not set to a value that enforces a restriction on Param2.");
}
}

/// <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 @@ -889,6 +936,28 @@ internal sealed class ParameterSetWithMixedDependentPresenceAndValueBasedTopLeve
public string? BParam { get; set; }
}

internal sealed class ParameterEnum : ValueEnum
{
public static readonly ParameterEnum Value1 = new(1, "value1");

public static readonly ParameterEnum Value2 = new(2, "value2");

private ParameterEnum(int id, object value)
: base(id, value)
{
}
}

internal sealed class ParameterSetWithInterdependentEnums : Parameters.BaseParameters<EasyPostObject>
{
[TopLevelRequestParameter(Necessity.Optional, "param1")]
[TopLevelRequestParameterDependents(IndependentStatus.IfValue, "value1", DependentStatus.MustBeValue, dependentValue: "value2", "Param2")]
public ParameterEnum? Param1 { get; set; }

[TopLevelRequestParameter(Necessity.Optional, "param2")]
public string? Param2 { get; set; }
}

#pragma warning restore CA1852 // Can be sealed

#endregion
Expand Down
6 changes: 6 additions & 0 deletions EasyPost/Models/API/ClaimPaymentMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ public class ClaimPaymentMethod : ValueEnum
/// </summary>
public static readonly ClaimPaymentMethod EasyPostWallet = new(2, "easypost_wallet");

/// <summary>
/// An enum representing paying a claim reimbursement via a bank transfer.
/// </summary>
// ReSharper disable once InconsistentNaming
public static readonly ClaimPaymentMethod ACH = new(3, "ach");

/// <summary>
/// Initializes a new instance of the <see cref="ClaimPaymentMethod"/> class.
/// </summary>
Expand Down
9 changes: 8 additions & 1 deletion EasyPost/Parameters/Claim/Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,18 @@ public class Create : BaseParameters<Models.API.Claim>, IClaimParameter
public string? ContactEmail { get; set; }

/// <summary>
/// The <see cref="ClaimPaymentMethod"/> for the claim reimbursement.
/// The <see cref="ClaimPaymentMethod"/> for the claim reimbursement. If set to <see cref="ClaimPaymentMethod.MailedCheck"/>, the <see cref="CheckDeliveryAddress"/> must be provided.
/// </summary>
[TopLevelRequestParameter(Necessity.Optional, "payment_method")]
[TopLevelRequestParameterDependents(IndependentStatus.IfValue, "mailed_check", DependentStatus.MustBeSet, "CheckDeliveryAddress")]
public ClaimPaymentMethod? PaymentMethod { get; set; }

/// <summary>
/// The destination address for a reimbursement check. Required if the <see cref="PaymentMethod"/> is <see cref="ClaimPaymentMethod.MailedCheck"/>.
/// </summary>
[TopLevelRequestParameter(Necessity.Optional, "check_delivery_address")]
public string? CheckDeliveryAddress { get; set; }

#endregion
}
}
2 changes: 2 additions & 0 deletions EasyPost/Utilities/Cryptography.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public static byte[] AsByteArray(this string str, Encoding? encoding = null)
/// </summary>
/// <param name="bytes">Byte array to convert to hex string.</param>
/// <returns>Hex string equivalent of input byte array.</returns>
#pragma warning disable CA1859 // Use byte[] instead of IReadOnlyList<byte>
private static string AsHexString(this IReadOnlyList<byte> bytes)
{
// Fastest safe way to convert a byte array to hex string,
Expand All @@ -61,6 +62,7 @@ private static string AsHexString(this IReadOnlyList<byte> bytes)

return new string(result).ToLowerInvariant();
}
#pragma warning restore CA1859 // Use byte[] instead of IReadOnlyList<byte>

/// <summary>
/// Convert a string to a hex string using a specific encoding.
Expand Down
35 changes: 21 additions & 14 deletions EasyPost/Utilities/Internal/Attributes/RequestParameterAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,9 @@ protected RequestParameterDependentsAttribute(IndependentStatus independentStatu
/// Initializes a new instance of the <see cref="RequestParameterDependentsAttribute"/> class.
/// </summary>
/// <param name="independentStatus">The set status of the independent property.</param>
/// <param name="independentValue">The value of the independent property.</param>
/// <param name="independentValue">The value of the independent property. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
/// <param name="dependentStatus">The set status of the dependent properties.</param>
/// <param name="dependentValue">The value of the dependent properties.</param>
/// <param name="dependentValue">The value of the dependent properties. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
/// <param name="dependentProperties">The names of the dependent properties.</param>
protected RequestParameterDependentsAttribute(IndependentStatus independentStatus, object independentValue, DependentStatus dependentStatus, object dependentValue, params string[] dependentProperties)
{
Expand All @@ -168,7 +168,7 @@ protected RequestParameterDependentsAttribute(IndependentStatus independentStatu
/// </summary>
/// <param name="independentStatus">The set status of the independent property.</param>
/// <param name="dependentStatus">The set status of the dependent properties.</param>
/// <param name="dependentValue">The value of the dependent properties.</param>
/// <param name="dependentValue">The value of the dependent properties. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
/// <param name="dependentProperties">The names of the dependent properties.</param>
protected RequestParameterDependentsAttribute(IndependentStatus independentStatus, DependentStatus dependentStatus, object dependentValue, params string[] dependentProperties)
{
Expand All @@ -182,7 +182,7 @@ protected RequestParameterDependentsAttribute(IndependentStatus independentStatu
/// Initializes a new instance of the <see cref="RequestParameterDependentsAttribute"/> class.
/// </summary>
/// <param name="independentStatus">The set status of the independent property.</param>
/// <param name="independentValue">The value of the independent property.</param>
/// <param name="independentValue">The value of the independent property. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
/// <param name="dependentStatus">The set status of the dependent properties.</param>
/// <param name="dependentProperties">The names of the dependent properties.</param>
protected RequestParameterDependentsAttribute(IndependentStatus independentStatus, object independentValue, DependentStatus dependentStatus, params string[] dependentProperties)
Expand All @@ -197,7 +197,7 @@ protected RequestParameterDependentsAttribute(IndependentStatus independentStatu
/// Check that the expected value state of the property is met.
/// </summary>
/// <param name="propertyValue">Optional, the value of the independent property.</param>
/// <param name="dependentPropertyValue">The value of the dependent property.</param>
/// <param name="dependentPropertyValue">The value of the dependent property. Do not pass in <see cref="ValueEnum"/>; instead, pass in the underlying value.</param>
/// <returns>True if the dependent property meets the dependency condition, false otherwise.</returns>
private bool DependencyConditionPasses(object? propertyValue, object? dependentPropertyValue)
{
Expand Down Expand Up @@ -228,10 +228,17 @@ private bool DependencyConditionPasses(object? propertyValue, object? dependentP
/// Check that all dependent properties are compliant with the dependency conditions.
/// </summary>
/// <param name="obj">The object containing the dependent properties.</param>
/// <param name="propertyValue">The value of the independent property.</param>
/// <param name="propertyValue">The value of the independent property. A <see cref="ValueEnum"/> will be converted to its underlying value.</param>
/// <returns>A tuple containing a boolean indicating whether the dependency is met, and a string containing the name of the first dependent property that does not meet the dependency conditions.</returns>
public Tuple<bool, string> DependentsAreCompliant(object obj, object? propertyValue)
{
// Convert any value enums to their underlying values (this cannot work with non-value Enums, but those can't be passed to attributes, so it is safe to ignore)
if (propertyValue != null && Objects.IsValueEnum(propertyValue))
{
ValueEnum enumValue = (ValueEnum)propertyValue;
propertyValue = enumValue.Value;
}

// No need to check dependent IfSet properties if the property is not set
if (propertyValue == null && IndependentStatus == IndependentStatus.IfSet)
{
Expand Down Expand Up @@ -304,9 +311,9 @@ public TopLevelRequestParameterDependentsAttribute(IndependentStatus independent
/// Initializes a new instance of the <see cref="TopLevelRequestParameterDependentsAttribute"/> class.
/// </summary>
/// <param name="independentStatus">The set status of the independent property.</param>
/// <param name="independentValue">The value of the independent property.</param>
/// <param name="independentValue">The value of the independent property. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
/// <param name="dependentStatus">The set status of the dependent properties.</param>
/// <param name="dependentValue">The value of the dependent properties.</param>
/// <param name="dependentValue">The value of the dependent properties. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
/// <param name="dependentProperties">The names of the dependent properties.</param>
public TopLevelRequestParameterDependentsAttribute(IndependentStatus independentStatus, object independentValue, DependentStatus dependentStatus, object dependentValue, params string[] dependentProperties)
: base(independentStatus, independentValue, dependentStatus, dependentValue, dependentProperties)
Expand All @@ -318,7 +325,7 @@ public TopLevelRequestParameterDependentsAttribute(IndependentStatus independent
/// </summary>
/// <param name="independentStatus">The set status of the independent property.</param>
/// <param name="dependentStatus">The set status of the dependent properties.</param>
/// <param name="dependentValue">The value of the dependent properties.</param>
/// <param name="dependentValue">The value of the dependent properties. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
/// <param name="dependentProperties">The names of the dependent properties.</param>
public TopLevelRequestParameterDependentsAttribute(IndependentStatus independentStatus, DependentStatus dependentStatus, object dependentValue, params string[] dependentProperties)
: base(independentStatus, dependentStatus, dependentValue, dependentProperties)
Expand All @@ -329,7 +336,7 @@ public TopLevelRequestParameterDependentsAttribute(IndependentStatus independent
/// Initializes a new instance of the <see cref="TopLevelRequestParameterDependentsAttribute"/> class.
/// </summary>
/// <param name="independentStatus">The set status of the independent property.</param>
/// <param name="independentValue">The value of the independent property.</param>
/// <param name="independentValue">The value of the independent property. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
/// <param name="dependentStatus">The set status of the dependent properties.</param>
/// <param name="dependentProperties">The names of the dependent properties.</param>
public TopLevelRequestParameterDependentsAttribute(IndependentStatus independentStatus, object independentValue, DependentStatus dependentStatus, params string[] dependentProperties)
Expand Down Expand Up @@ -392,9 +399,9 @@ public NestedRequestParameterDependentsAttribute(IndependentStatus independentSt
/// Initializes a new instance of the <see cref="NestedRequestParameterDependentsAttribute"/> class.
/// </summary>
/// <param name="independentStatus">The set status of the independent property.</param>
/// <param name="independentValue">The value of the independent property.</param>
/// <param name="independentValue">The value of the independent property. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
/// <param name="dependentStatus">The set status of the dependent properties.</param>
/// <param name="dependentValue">The value of the dependent properties.</param>
/// <param name="dependentValue">The value of the dependent properties. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
/// <param name="dependentProperties">The names of the dependent properties.</param>
public NestedRequestParameterDependentsAttribute(IndependentStatus independentStatus, object independentValue, DependentStatus dependentStatus, object dependentValue, params string[] dependentProperties)
: base(independentStatus, independentValue, dependentStatus, dependentValue, dependentProperties)
Expand All @@ -406,7 +413,7 @@ public NestedRequestParameterDependentsAttribute(IndependentStatus independentSt
/// </summary>
/// <param name="independentStatus">The set status of the independent property.</param>
/// <param name="dependentStatus">The set status of the dependent properties.</param>
/// <param name="dependentValue">The value of the dependent properties.</param>
/// <param name="dependentValue">The value of the dependent properties. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
/// <param name="dependentProperties">The names of the dependent properties.</param>
public NestedRequestParameterDependentsAttribute(IndependentStatus independentStatus, DependentStatus dependentStatus, object dependentValue, params string[] dependentProperties)
: base(independentStatus, dependentStatus, dependentValue, dependentProperties)
Expand All @@ -417,7 +424,7 @@ public NestedRequestParameterDependentsAttribute(IndependentStatus independentSt
/// Initializes a new instance of the <see cref="NestedRequestParameterDependentsAttribute"/> class.
/// </summary>
/// <param name="independentStatus">The set status of the independent property.</param>
/// <param name="independentValue">The value of the independent property.</param>
/// <param name="independentValue">The value of the independent property. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
/// <param name="dependentStatus">The set status of the dependent properties.</param>
/// <param name="dependentProperties">The names of the dependent properties.</param>
public NestedRequestParameterDependentsAttribute(IndependentStatus independentStatus, object independentValue, DependentStatus dependentStatus, params string[] dependentProperties)
Expand Down
10 changes: 5 additions & 5 deletions EasyPost/Utilities/Internal/Enum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ public override bool Equals(object? obj)
public static IEnumerable<T> GetAll<T>()
where T : IEnum
=>
typeof(T).GetFields(BindingFlags.Public |
BindingFlags.Static |
BindingFlags.DeclaredOnly)
.Select(f => f.GetValue(null))
.Cast<T>();
typeof(T).GetFields(BindingFlags.Public |
BindingFlags.Static |
BindingFlags.DeclaredOnly)
.Select(f => f.GetValue(null))
.Cast<T>();

/// <summary>
/// Compare two objects.
Expand Down
20 changes: 20 additions & 0 deletions EasyPost/Utilities/Internal/Objects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,25 @@ public static bool IsPrimitive(object? obj)
{
return obj is string or ValueType or null;
}

/// <summary>
/// Check if an object is an <see cref="Enum"/> or derived from <see cref="Enum"/>.
/// </summary>
/// <param name="obj">The object to evaluate.</param>
/// <returns><c>true</c> if the object is an <see cref="Enum"/> or derived from <see cref="Enum"/>, <c>false</c> otherwise.</returns>
public static bool IsEnum(object? obj)
{
return obj is Enum;
}

/// <summary>
/// Check if an object is a <see cref="ValueEnum"/> or derived from <see cref="ValueEnum"/>.
/// </summary>
/// <param name="obj">The object to evaluate.</param>
/// <returns><c>true</c> if the object is a <see cref="ValueEnum"/> or derived from <see cref="ValueEnum"/>, <c>false</c> otherwise.</returns>
public static bool IsValueEnum(object? obj)
{
return obj is ValueEnum;
}
}
}

0 comments on commit b738965

Please sign in to comment.