Skip to content

Commit b738965

Browse files
authored
[bug] Add missing claims parameters, fix interdependency parameter enforcement (#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
1 parent 95dab8f commit b738965

File tree

8 files changed

+132
-20
lines changed

8 files changed

+132
-20
lines changed

EasyPost.Tests/Fixture.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ internal static ParameterSets.Claim.Create Create(Dictionary<string, object>? fi
298298
ContactEmail = fixture.GetOrNull<string>("contact_email"),
299299
PaymentMethod = fixture.GetOrNullEnum<ClaimPaymentMethod>("payment_method"),
300300
RecipientName = fixture.GetOrNull<string>("recipient_name"),
301+
CheckDeliveryAddress = fixture.GetOrNull<string>("check_delivery_address"),
301302
};
302303
}
303304

EasyPost.Tests/ParametersTests/ParametersTest.cs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using EasyPost.Models.API;
77
using EasyPost.Tests._Utilities;
88
using EasyPost.Tests._Utilities.Attributes;
9+
using EasyPost.Utilities.Internal;
910
using EasyPost.Utilities.Internal.Attributes;
1011
using Xunit;
1112

@@ -577,6 +578,52 @@ public void TestDependentNestedParameters()
577578
}
578579
}
579580

581+
[Fact]
582+
[Testing.Custom]
583+
public async Task TestEnumUsageInInterdependentParameterEnforcement()
584+
{
585+
// Should pass because Param1 and Param2 are set correctly
586+
ParameterSetWithInterdependentEnums parameters = new()
587+
{
588+
Param1 = ParameterEnum.Value1,
589+
Param2 = "value2"
590+
};
591+
592+
try
593+
{
594+
parameters.ToDictionary();
595+
}
596+
catch (Exceptions.General.InvalidParameterPairError)
597+
{
598+
Assert.Fail("Should not throw exception if both Param1 and Param2 are set correctly.");
599+
}
600+
601+
// Should fail because Param1 is set to Value1, but Param2 is not set to "value2"
602+
parameters = new ParameterSetWithInterdependentEnums
603+
{
604+
Param1 = ParameterEnum.Value1,
605+
Param2 = "value3"
606+
};
607+
608+
Assert.Throws<Exceptions.General.InvalidParameterPairError>(() => parameters.ToDictionary());
609+
610+
// Should pass because Param1 is not set to a value that enforces a restriction on Param2
611+
parameters = new ParameterSetWithInterdependentEnums
612+
{
613+
Param1 = ParameterEnum.Value2,
614+
Param2 = "value3"
615+
};
616+
617+
try
618+
{
619+
parameters.ToDictionary();
620+
}
621+
catch (Exceptions.General.InvalidParameterPairError)
622+
{
623+
Assert.Fail("Should not throw exception if Param1 is not set to a value that enforces a restriction on Param2.");
624+
}
625+
}
626+
580627
/// <summary>
581628
/// This test proves that we can reuse the Addresses.Create parameter object,
582629
/// with its serialization logic adapting to whether it is a top-level parameter object
@@ -889,6 +936,28 @@ internal sealed class ParameterSetWithMixedDependentPresenceAndValueBasedTopLeve
889936
public string? BParam { get; set; }
890937
}
891938

939+
internal sealed class ParameterEnum : ValueEnum
940+
{
941+
public static readonly ParameterEnum Value1 = new(1, "value1");
942+
943+
public static readonly ParameterEnum Value2 = new(2, "value2");
944+
945+
private ParameterEnum(int id, object value)
946+
: base(id, value)
947+
{
948+
}
949+
}
950+
951+
internal sealed class ParameterSetWithInterdependentEnums : Parameters.BaseParameters<EasyPostObject>
952+
{
953+
[TopLevelRequestParameter(Necessity.Optional, "param1")]
954+
[TopLevelRequestParameterDependents(IndependentStatus.IfValue, "value1", DependentStatus.MustBeValue, dependentValue: "value2", "Param2")]
955+
public ParameterEnum? Param1 { get; set; }
956+
957+
[TopLevelRequestParameter(Necessity.Optional, "param2")]
958+
public string? Param2 { get; set; }
959+
}
960+
892961
#pragma warning restore CA1852 // Can be sealed
893962

894963
#endregion

EasyPost/Models/API/ClaimPaymentMethod.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ public class ClaimPaymentMethod : ValueEnum
1717
/// </summary>
1818
public static readonly ClaimPaymentMethod EasyPostWallet = new(2, "easypost_wallet");
1919

20+
/// <summary>
21+
/// An enum representing paying a claim reimbursement via a bank transfer.
22+
/// </summary>
23+
// ReSharper disable once InconsistentNaming
24+
public static readonly ClaimPaymentMethod ACH = new(3, "ach");
25+
2026
/// <summary>
2127
/// Initializes a new instance of the <see cref="ClaimPaymentMethod"/> class.
2228
/// </summary>

EasyPost/Parameters/Claim/Create.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,18 @@ public class Create : BaseParameters<Models.API.Claim>, IClaimParameter
7171
public string? ContactEmail { get; set; }
7272

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

80+
/// <summary>
81+
/// The destination address for a reimbursement check. Required if the <see cref="PaymentMethod"/> is <see cref="ClaimPaymentMethod.MailedCheck"/>.
82+
/// </summary>
83+
[TopLevelRequestParameter(Necessity.Optional, "check_delivery_address")]
84+
public string? CheckDeliveryAddress { get; set; }
85+
7986
#endregion
8087
}
8188
}

EasyPost/Utilities/Cryptography.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public static byte[] AsByteArray(this string str, Encoding? encoding = null)
4646
/// </summary>
4747
/// <param name="bytes">Byte array to convert to hex string.</param>
4848
/// <returns>Hex string equivalent of input byte array.</returns>
49+
#pragma warning disable CA1859 // Use byte[] instead of IReadOnlyList<byte>
4950
private static string AsHexString(this IReadOnlyList<byte> bytes)
5051
{
5152
// Fastest safe way to convert a byte array to hex string,
@@ -61,6 +62,7 @@ private static string AsHexString(this IReadOnlyList<byte> bytes)
6162

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

6567
/// <summary>
6668
/// Convert a string to a hex string using a specific encoding.

EasyPost/Utilities/Internal/Attributes/RequestParameterAttribute.cs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,9 @@ protected RequestParameterDependentsAttribute(IndependentStatus independentStatu
150150
/// Initializes a new instance of the <see cref="RequestParameterDependentsAttribute"/> class.
151151
/// </summary>
152152
/// <param name="independentStatus">The set status of the independent property.</param>
153-
/// <param name="independentValue">The value of the independent property.</param>
153+
/// <param name="independentValue">The value of the independent property. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
154154
/// <param name="dependentStatus">The set status of the dependent properties.</param>
155-
/// <param name="dependentValue">The value of the dependent properties.</param>
155+
/// <param name="dependentValue">The value of the dependent properties. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
156156
/// <param name="dependentProperties">The names of the dependent properties.</param>
157157
protected RequestParameterDependentsAttribute(IndependentStatus independentStatus, object independentValue, DependentStatus dependentStatus, object dependentValue, params string[] dependentProperties)
158158
{
@@ -168,7 +168,7 @@ protected RequestParameterDependentsAttribute(IndependentStatus independentStatu
168168
/// </summary>
169169
/// <param name="independentStatus">The set status of the independent property.</param>
170170
/// <param name="dependentStatus">The set status of the dependent properties.</param>
171-
/// <param name="dependentValue">The value of the dependent properties.</param>
171+
/// <param name="dependentValue">The value of the dependent properties. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
172172
/// <param name="dependentProperties">The names of the dependent properties.</param>
173173
protected RequestParameterDependentsAttribute(IndependentStatus independentStatus, DependentStatus dependentStatus, object dependentValue, params string[] dependentProperties)
174174
{
@@ -182,7 +182,7 @@ protected RequestParameterDependentsAttribute(IndependentStatus independentStatu
182182
/// Initializes a new instance of the <see cref="RequestParameterDependentsAttribute"/> class.
183183
/// </summary>
184184
/// <param name="independentStatus">The set status of the independent property.</param>
185-
/// <param name="independentValue">The value of the independent property.</param>
185+
/// <param name="independentValue">The value of the independent property. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
186186
/// <param name="dependentStatus">The set status of the dependent properties.</param>
187187
/// <param name="dependentProperties">The names of the dependent properties.</param>
188188
protected RequestParameterDependentsAttribute(IndependentStatus independentStatus, object independentValue, DependentStatus dependentStatus, params string[] dependentProperties)
@@ -197,7 +197,7 @@ protected RequestParameterDependentsAttribute(IndependentStatus independentStatu
197197
/// Check that the expected value state of the property is met.
198198
/// </summary>
199199
/// <param name="propertyValue">Optional, the value of the independent property.</param>
200-
/// <param name="dependentPropertyValue">The value of the dependent property.</param>
200+
/// <param name="dependentPropertyValue">The value of the dependent property. Do not pass in <see cref="ValueEnum"/>; instead, pass in the underlying value.</param>
201201
/// <returns>True if the dependent property meets the dependency condition, false otherwise.</returns>
202202
private bool DependencyConditionPasses(object? propertyValue, object? dependentPropertyValue)
203203
{
@@ -228,10 +228,17 @@ private bool DependencyConditionPasses(object? propertyValue, object? dependentP
228228
/// Check that all dependent properties are compliant with the dependency conditions.
229229
/// </summary>
230230
/// <param name="obj">The object containing the dependent properties.</param>
231-
/// <param name="propertyValue">The value of the independent property.</param>
231+
/// <param name="propertyValue">The value of the independent property. A <see cref="ValueEnum"/> will be converted to its underlying value.</param>
232232
/// <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>
233233
public Tuple<bool, string> DependentsAreCompliant(object obj, object? propertyValue)
234234
{
235+
// 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)
236+
if (propertyValue != null && Objects.IsValueEnum(propertyValue))
237+
{
238+
ValueEnum enumValue = (ValueEnum)propertyValue;
239+
propertyValue = enumValue.Value;
240+
}
241+
235242
// No need to check dependent IfSet properties if the property is not set
236243
if (propertyValue == null && IndependentStatus == IndependentStatus.IfSet)
237244
{
@@ -304,9 +311,9 @@ public TopLevelRequestParameterDependentsAttribute(IndependentStatus independent
304311
/// Initializes a new instance of the <see cref="TopLevelRequestParameterDependentsAttribute"/> class.
305312
/// </summary>
306313
/// <param name="independentStatus">The set status of the independent property.</param>
307-
/// <param name="independentValue">The value of the independent property.</param>
314+
/// <param name="independentValue">The value of the independent property. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
308315
/// <param name="dependentStatus">The set status of the dependent properties.</param>
309-
/// <param name="dependentValue">The value of the dependent properties.</param>
316+
/// <param name="dependentValue">The value of the dependent properties. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
310317
/// <param name="dependentProperties">The names of the dependent properties.</param>
311318
public TopLevelRequestParameterDependentsAttribute(IndependentStatus independentStatus, object independentValue, DependentStatus dependentStatus, object dependentValue, params string[] dependentProperties)
312319
: base(independentStatus, independentValue, dependentStatus, dependentValue, dependentProperties)
@@ -318,7 +325,7 @@ public TopLevelRequestParameterDependentsAttribute(IndependentStatus independent
318325
/// </summary>
319326
/// <param name="independentStatus">The set status of the independent property.</param>
320327
/// <param name="dependentStatus">The set status of the dependent properties.</param>
321-
/// <param name="dependentValue">The value of the dependent properties.</param>
328+
/// <param name="dependentValue">The value of the dependent properties. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
322329
/// <param name="dependentProperties">The names of the dependent properties.</param>
323330
public TopLevelRequestParameterDependentsAttribute(IndependentStatus independentStatus, DependentStatus dependentStatus, object dependentValue, params string[] dependentProperties)
324331
: base(independentStatus, dependentStatus, dependentValue, dependentProperties)
@@ -329,7 +336,7 @@ public TopLevelRequestParameterDependentsAttribute(IndependentStatus independent
329336
/// Initializes a new instance of the <see cref="TopLevelRequestParameterDependentsAttribute"/> class.
330337
/// </summary>
331338
/// <param name="independentStatus">The set status of the independent property.</param>
332-
/// <param name="independentValue">The value of the independent property.</param>
339+
/// <param name="independentValue">The value of the independent property. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
333340
/// <param name="dependentStatus">The set status of the dependent properties.</param>
334341
/// <param name="dependentProperties">The names of the dependent properties.</param>
335342
public TopLevelRequestParameterDependentsAttribute(IndependentStatus independentStatus, object independentValue, DependentStatus dependentStatus, params string[] dependentProperties)
@@ -392,9 +399,9 @@ public NestedRequestParameterDependentsAttribute(IndependentStatus independentSt
392399
/// Initializes a new instance of the <see cref="NestedRequestParameterDependentsAttribute"/> class.
393400
/// </summary>
394401
/// <param name="independentStatus">The set status of the independent property.</param>
395-
/// <param name="independentValue">The value of the independent property.</param>
402+
/// <param name="independentValue">The value of the independent property. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
396403
/// <param name="dependentStatus">The set status of the dependent properties.</param>
397-
/// <param name="dependentValue">The value of the dependent properties.</param>
404+
/// <param name="dependentValue">The value of the dependent properties. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
398405
/// <param name="dependentProperties">The names of the dependent properties.</param>
399406
public NestedRequestParameterDependentsAttribute(IndependentStatus independentStatus, object independentValue, DependentStatus dependentStatus, object dependentValue, params string[] dependentProperties)
400407
: base(independentStatus, independentValue, dependentStatus, dependentValue, dependentProperties)
@@ -406,7 +413,7 @@ public NestedRequestParameterDependentsAttribute(IndependentStatus independentSt
406413
/// </summary>
407414
/// <param name="independentStatus">The set status of the independent property.</param>
408415
/// <param name="dependentStatus">The set status of the dependent properties.</param>
409-
/// <param name="dependentValue">The value of the dependent properties.</param>
416+
/// <param name="dependentValue">The value of the dependent properties. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
410417
/// <param name="dependentProperties">The names of the dependent properties.</param>
411418
public NestedRequestParameterDependentsAttribute(IndependentStatus independentStatus, DependentStatus dependentStatus, object dependentValue, params string[] dependentProperties)
412419
: base(independentStatus, dependentStatus, dependentValue, dependentProperties)
@@ -417,7 +424,7 @@ public NestedRequestParameterDependentsAttribute(IndependentStatus independentSt
417424
/// Initializes a new instance of the <see cref="NestedRequestParameterDependentsAttribute"/> class.
418425
/// </summary>
419426
/// <param name="independentStatus">The set status of the independent property.</param>
420-
/// <param name="independentValue">The value of the independent property.</param>
427+
/// <param name="independentValue">The value of the independent property. If enforcing a custom <see cref="ValueEnum"/>, provide the underlying value.</param>
421428
/// <param name="dependentStatus">The set status of the dependent properties.</param>
422429
/// <param name="dependentProperties">The names of the dependent properties.</param>
423430
public NestedRequestParameterDependentsAttribute(IndependentStatus independentStatus, object independentValue, DependentStatus dependentStatus, params string[] dependentProperties)

EasyPost/Utilities/Internal/Enum.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,11 @@ public override bool Equals(object? obj)
109109
public static IEnumerable<T> GetAll<T>()
110110
where T : IEnum
111111
=>
112-
typeof(T).GetFields(BindingFlags.Public |
113-
BindingFlags.Static |
114-
BindingFlags.DeclaredOnly)
115-
.Select(f => f.GetValue(null))
116-
.Cast<T>();
112+
typeof(T).GetFields(BindingFlags.Public |
113+
BindingFlags.Static |
114+
BindingFlags.DeclaredOnly)
115+
.Select(f => f.GetValue(null))
116+
.Cast<T>();
117117

118118
/// <summary>
119119
/// Compare two objects.

EasyPost/Utilities/Internal/Objects.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,25 @@ public static bool IsPrimitive(object? obj)
3232
{
3333
return obj is string or ValueType or null;
3434
}
35+
36+
/// <summary>
37+
/// Check if an object is an <see cref="Enum"/> or derived from <see cref="Enum"/>.
38+
/// </summary>
39+
/// <param name="obj">The object to evaluate.</param>
40+
/// <returns><c>true</c> if the object is an <see cref="Enum"/> or derived from <see cref="Enum"/>, <c>false</c> otherwise.</returns>
41+
public static bool IsEnum(object? obj)
42+
{
43+
return obj is Enum;
44+
}
45+
46+
/// <summary>
47+
/// Check if an object is a <see cref="ValueEnum"/> or derived from <see cref="ValueEnum"/>.
48+
/// </summary>
49+
/// <param name="obj">The object to evaluate.</param>
50+
/// <returns><c>true</c> if the object is a <see cref="ValueEnum"/> or derived from <see cref="ValueEnum"/>, <c>false</c> otherwise.</returns>
51+
public static bool IsValueEnum(object? obj)
52+
{
53+
return obj is ValueEnum;
54+
}
3555
}
3656
}

0 commit comments

Comments
 (0)