Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug] Add missing claims parameters, fix interdependency parameter enforcement #603

Merged
merged 4 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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;
}
}
}
Loading