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

[feat] Add ability to compare parameter set to an EasyPost object #511

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
60 changes: 56 additions & 4 deletions EasyPost.Tests/ParametersTests/ParametersTest.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyPost._base;
using EasyPost.Models.API;
using EasyPost.Parameters;
using EasyPost.Tests._Utilities;
using EasyPost.Tests._Utilities.Attributes;
using EasyPost.Utilities.Internal.Attributes;
Expand Down Expand Up @@ -135,7 +137,7 @@ public void TestRequiredAndOptionalParameterValidation()
Assert.Throws<Exceptions.General.MissingParameterError>(() => parametersWithOnlyOptionalParameterSet.ToDictionary());
}

private sealed class ParameterSetWithRequiredAndOptionalParameters : Parameters.BaseParameters
private sealed class ParameterSetWithRequiredAndOptionalParameters : Parameters.BaseParameters<EasyPostObject>
{
[TopLevelRequestParameter(Necessity.Required, "test", "required")]
public string? RequiredParameter { get; set; }
Expand Down Expand Up @@ -255,9 +257,9 @@ public async Task TestDisallowUsingParameterObjectDictionariesInDictionaryFuncti
[Testing.Logic]
public void TestParameterToDictionaryAccountsForNonPublicProperties()
{
ExampleParameters exampleParameters = new ExampleParameters();
ExampleDecoratorParameters exampleDecoratorParameters = new ExampleDecoratorParameters();

Dictionary<string, object> dictionary = exampleParameters.ToDictionary();
Dictionary<string, object> dictionary = exampleDecoratorParameters.ToDictionary();

// All decorated properties should be present in the dictionary, regardless of their access modifier
Assert.True(dictionary.ContainsKey("decorated_public_property"));
Expand All @@ -270,11 +272,50 @@ public void TestParameterToDictionaryAccountsForNonPublicProperties()
Assert.True(dictionary.Count == 4);
}

/// <summary>
/// This test proves that the .Matches() method will evaluate if a provided EasyPostObject matches the current parameter set, based on the defined match function.
/// </summary>
[Fact]
[Testing.Logic]
public void TestParameterMatchOverrideFunction()
{
ExampleMatchParametersEasyPostObject obj = new ExampleMatchParametersEasyPostObject
{
Prop1 = "prop1",
// uses default match function at base level (returns false)
// this can also be implemented on a per-parameter set basis
// users can also override the match function to implement custom logic (see examples below)
};

// The default match function should return false
ExampleMatchParameters parameters = new ExampleMatchParameters
{
Prop1 = "prop1",
};
Assert.False(parameters.Matches(obj));

// The overridden match function should return true (because the Prop1 property matches)
parameters = new ExampleMatchParameters
{
Prop1 = "prop1",
MatchFunction = o => o.Prop1 == "prop1",
};
Assert.True(parameters.Matches(obj));

// The overridden match function should return false (because the Prop1 property does not match)
parameters = new ExampleMatchParameters
{
Prop1 = "prop2",
MatchFunction = o => o.Prop1 == "prop2",
};
Assert.False(parameters.Matches(obj));
}

#endregion
}

#pragma warning disable CA1852 // Can be sealed
internal class ExampleParameters : Parameters.BaseParameters
internal class ExampleDecoratorParameters : Parameters.BaseParameters<EasyPostObject>
{
// Default values set to guarantee any property won't be skipped for serialization due to a null value

Expand All @@ -295,5 +336,16 @@ internal class ExampleParameters : Parameters.BaseParameters

private string? UndecoratedPrivateProperty { get; set; } = "undecorated_private";
}

internal class ExampleMatchParametersEasyPostObject : EasyPostObject
{
public string? Prop1 { get; set; }
}

internal class ExampleMatchParameters : Parameters.BaseParameters<ExampleMatchParametersEasyPostObject>
{
public string? Prop1 { get; set; }
}

#pragma warning restore CA1852 // Can be sealed
}
2 changes: 1 addition & 1 deletion EasyPost/Models/API/Batch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public class BatchCollection : PaginatedCollection<Batch>
/// <returns>A TParameters-type parameters set.</returns>
protected internal override TParameters BuildNextPageParameters<TParameters>(IEnumerable<Batch> entries, int? pageSize = null)
{
Parameters.Shipment.All parameters = Filters != null ? (Parameters.Shipment.All)Filters : new Parameters.Shipment.All();
Parameters.Batch.All parameters = Filters != null ? (Parameters.Batch.All)Filters : new Parameters.Batch.All();
nwithan8 marked this conversation as resolved.
Show resolved Hide resolved

// TODO: Batches get returned in reverse order from everything else (oldest first instead of newest first), so this needs to be "after_id" instead of "before_id"
parameters.AfterId = entries.Last().Id;
Expand Down
3 changes: 2 additions & 1 deletion EasyPost/Models/API/Beta/CarrierMetadata.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using EasyPost._base;
using EasyPost.Utilities.Internal;
using Newtonsoft.Json;

Expand All @@ -10,7 +11,7 @@ namespace EasyPost.Models.API.Beta
/// Class representing an <a href="https://www.easypost.com/docs/api#carriermetadata-object">EasyPost carrier metadata summary</a>.
/// </summary>
[Obsolete("This class is deprecated. Please use EasyPost.Models.API.CarrierMetadata instead. This class will be removed in a future version.", false)]
public class CarrierMetadata
public class CarrierMetadata : EasyPostObject
{
#region JSON Properties

Expand Down
3 changes: 2 additions & 1 deletion EasyPost/Models/API/CarrierMetadata.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using EasyPost._base;
using EasyPost.Utilities.Internal;
using Newtonsoft.Json;

Expand All @@ -8,7 +9,7 @@ namespace EasyPost.Models.API
/// <summary>
/// Class representing an <a href="https://www.easypost.com/docs/api#carriermetadata-object">EasyPost carrier metadata summary</a>.
/// </summary>
public class CarrierMetadata
public class CarrierMetadata : EphemeralEasyPostObject
{
#region JSON Properties

Expand Down
7 changes: 4 additions & 3 deletions EasyPost/Models/Shared/PaginatedCollection.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyPost._base;
using EasyPost.Exceptions.General;
using Newtonsoft.Json;

Expand All @@ -21,7 +22,7 @@
/// <summary>
/// The filter parameters used to retrieve this collection.
/// </summary>
internal Parameters.BaseParameters? Filters { get; set; }
internal Parameters.BaseParameters<TEntries>? Filters { get; set; }

/// <summary>
/// Get the next page of a paginated collection.
Expand All @@ -30,10 +31,10 @@
/// <param name="currentEntries">The results on the current page. Used to determine the API call parameters to retrieve the next page.</param>
/// <param name="pageSize">The size of the next page.</param>
/// <typeparam name="TCollection">The type of <see cref="PaginatedCollection{TCollection}"/> to get the next page of.</typeparam>
/// <typeparam name="TParameters">The type of <see cref="Parameters.BaseParameters"/> to construct for the API call.</typeparam>

Check warning on line 34 in EasyPost/Models/Shared/PaginatedCollection.cs

View workflow job for this annotation

GitHub Actions / Roslyn_Static_Analysis

XML comment has cref attribute 'BaseParameters' that could not be resolved

Check warning on line 34 in EasyPost/Models/Shared/PaginatedCollection.cs

View workflow job for this annotation

GitHub Actions / Roslyn_Static_Analysis

XML comment has cref attribute 'BaseParameters' that could not be resolved

Check warning on line 34 in EasyPost/Models/Shared/PaginatedCollection.cs

View workflow job for this annotation

GitHub Actions / Roslyn_Static_Analysis

XML comment has cref attribute 'BaseParameters' that could not be resolved

Check warning on line 34 in EasyPost/Models/Shared/PaginatedCollection.cs

View workflow job for this annotation

GitHub Actions / Roslyn_Static_Analysis

XML comment has cref attribute 'BaseParameters' that could not be resolved

Check warning on line 34 in EasyPost/Models/Shared/PaginatedCollection.cs

View workflow job for this annotation

GitHub Actions / Integration_Tests

XML comment has cref attribute 'BaseParameters' that could not be resolved
/// <returns>The next page of a paginated collection.</returns>
/// <exception cref="EndOfPaginationError">Thrown if there is no next page to retrieve.</exception>
internal async Task<TCollection> GetNextPage<TCollection, TParameters>(Func<TParameters, Task<TCollection>> apiCallFunction, List<TEntries>? currentEntries, int? pageSize = null) where TCollection : PaginatedCollection<TEntries> where TParameters : Parameters.BaseParameters
internal async Task<TCollection> GetNextPage<TCollection, TParameters>(Func<TParameters, Task<TCollection>> apiCallFunction, List<TEntries>? currentEntries, int? pageSize = null) where TCollection : PaginatedCollection<TEntries> where TParameters : Parameters.BaseParameters<TEntries>
{
if (currentEntries == null || currentEntries.Count == 0)
{
Expand All @@ -55,10 +56,10 @@
/// </summary>
/// <param name="entries">The entries of the collection.</param>
/// <param name="pageSize">The size of the next page.</param>
/// <typeparam name="TParameters">The type of <see cref="Parameters.BaseParameters"/> to construct for the API call.</typeparam>

Check warning on line 59 in EasyPost/Models/Shared/PaginatedCollection.cs

View workflow job for this annotation

GitHub Actions / Roslyn_Static_Analysis

XML comment has cref attribute 'BaseParameters' that could not be resolved

Check warning on line 59 in EasyPost/Models/Shared/PaginatedCollection.cs

View workflow job for this annotation

GitHub Actions / Roslyn_Static_Analysis

XML comment has cref attribute 'BaseParameters' that could not be resolved

Check warning on line 59 in EasyPost/Models/Shared/PaginatedCollection.cs

View workflow job for this annotation

GitHub Actions / Roslyn_Static_Analysis

XML comment has cref attribute 'BaseParameters' that could not be resolved

Check warning on line 59 in EasyPost/Models/Shared/PaginatedCollection.cs

View workflow job for this annotation

GitHub Actions / Roslyn_Static_Analysis

XML comment has cref attribute 'BaseParameters' that could not be resolved

Check warning on line 59 in EasyPost/Models/Shared/PaginatedCollection.cs

View workflow job for this annotation

GitHub Actions / Integration_Tests

XML comment has cref attribute 'BaseParameters' that could not be resolved
/// <returns>A TParameters-type set of parameters to use for the subsequent API call.</returns>
/// <exception cref="EndOfPaginationError">Thrown if there are no more items to retrieve for the paginated collection.</exception>
// This method is abstract and must be implemented for each collection.
protected internal abstract TParameters BuildNextPageParameters<TParameters>(IEnumerable<TEntries> entries, int? pageSize = null) where TParameters : Parameters.BaseParameters;
protected internal abstract TParameters BuildNextPageParameters<TParameters>(IEnumerable<TEntries> entries, int? pageSize = null) where TParameters : Parameters.BaseParameters<TEntries>;
}
}
2 changes: 1 addition & 1 deletion EasyPost/Parameters/Address/All.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace EasyPost.Parameters.Address
/// <a href="https://www.easypost.com/docs/api#retrieve-a-list-of-addresses">Parameters</a> for <see cref="EasyPost.Services.AddressService.All(All, System.Threading.CancellationToken)"/> API calls.
/// </summary>
[ExcludeFromCodeCoverage]
public class All : BaseAllParameters
public class All : BaseAllParameters<Models.API.Address>
{
#region Request Parameters

Expand Down
2 changes: 1 addition & 1 deletion EasyPost/Parameters/Address/Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace EasyPost.Parameters.Address
/// <a href="https://www.easypost.com/docs/api#create-and-verify-addresses">Parameters</a> for <see cref="EasyPost.Services.AddressService.Create(Create, System.Threading.CancellationToken)"/> API calls.
/// </summary>
[ExcludeFromCodeCoverage]
public class Create : BaseParameters, IAddressParameter
public class Create : BaseParameters<Models.API.Address>, IAddressParameter
{
#region Request Parameters

Expand Down
9 changes: 6 additions & 3 deletions EasyPost/Parameters/BaseAllParameters.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
using System;
using System.Collections.Generic;
using EasyPost._base;

namespace EasyPost.Parameters;

/// <summary>
/// Base class for parameter sets used in `All` methods.
/// </summary>
public abstract class BaseAllParameters : BaseParameters
public abstract class BaseAllParameters<TMatchInputType> : BaseParameters<TMatchInputType> where TMatchInputType : EphemeralEasyPostObject
{
/// <summary>
/// Construct a new <see cref="BaseAllParameters"/>-based instance from a <see cref="Dictionary{TKey,TValue}"/>.
/// Construct a new <see cref="BaseAllParameters{TMatchInputType}"/>-based instance from a <see cref="Dictionary{TKey,TValue}"/>.
/// </summary>
/// <param name="dictionary">The dictionary to parse.</param>
/// <returns>A BaseAllParameters-subtype object.</returns>
public static BaseAllParameters FromDictionary(Dictionary<string, object>? dictionary)
#pragma warning disable CA1000
public static BaseAllParameters<TMatchInputType> FromDictionary(Dictionary<string, object>? dictionary)
#pragma warning restore CA1000
{
throw new NotImplementedException();
}
Expand Down
21 changes: 17 additions & 4 deletions EasyPost/Parameters/BaseParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace EasyPost.Parameters
/// <summary>
/// Base class for all parameters used in functions.
/// </summary>
public abstract class BaseParameters
public abstract class BaseParameters<TMatchInputType> : IBaseParameters where TMatchInputType : EphemeralEasyPostObject
{
/*
* NOTES:
Expand All @@ -31,10 +31,23 @@ public abstract class BaseParameters
private Dictionary<string, object?> _parameterDictionary;

/// <summary>
/// Initializes a new instance of the <see cref="BaseParameters"/> class for a new set of request parameters.
/// A function to determine if a given object matches this parameter set.
/// Defaults to always returning false, but can be overridden by child classes and end-users.
/// </summary>
public Func<TMatchInputType, bool> MatchFunction { get; set; } = _ => false;

/// <summary>
/// Initializes a new instance of the <see cref="BaseParameters{TMatchInputType}"/> class for a new set of request parameters.
/// </summary>
protected BaseParameters() => _parameterDictionary = new Dictionary<string, object?>();

/// <summary>
/// Execute the match function on a given object.
/// </summary>
/// <param name="obj">The <see cref="EasyPostObject"/> to compare this parameter set against.</param>
/// <returns>The result of the <see cref="MatchFunction"/></returns>
public bool Matches(TMatchInputType obj) => MatchFunction(obj);

/// <summary>
/// Convert this parameter object to a dictionary for an HTTP request.
/// </summary>
Expand Down Expand Up @@ -91,7 +104,7 @@ public virtual Dictionary<string, object> ToDictionary()
/// embedded.
/// </param>
/// <returns><see cref="Dictionary{TKey,TValue}" /> of parameters.</returns>
protected virtual Dictionary<string, object> ToSubDictionary(Type parentParameterObjectType)
public virtual Dictionary<string, object> ToSubDictionary(Type parentParameterObjectType)
{
// Construct the dictionary of all parameters
PropertyInfo[] properties = GetType().GetProperties(BindingFlags.Instance |
Expand Down Expand Up @@ -161,7 +174,7 @@ private void Add(RequestParameterAttribute requestParameterAttribute, object? va
// If the given value is another base-Parameters object, serialize it as a sub-dictionary for the parent dictionary
// This is because the JSON schema for a sub-object is different than the JSON schema for a top-level object
// e.g. the schema for an address in the address create API call is different than the schema for an address in the shipment create API call
case BaseParameters parameters:
case IBaseParameters parameters: // TODO: if issues arise with this function, look at the type constraint on BaseParameters here
return parameters.ToSubDictionary(GetType());
// If the given value is a list, serialize each element of the list
case IList list:
Expand Down
2 changes: 1 addition & 1 deletion EasyPost/Parameters/Batch/AddShipments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace EasyPost.Parameters.Batch
/// <a href="https://www.easypost.com/docs/api#add-shipments-to-a-batch">Parameters</a> for <see cref="EasyPost.Services.BatchService.AddShipments(string, AddShipments, System.Threading.CancellationToken)"/> API calls.
/// </summary>
[ExcludeFromCodeCoverage]
public class AddShipments : BaseParameters
public class AddShipments : BaseParameters<Models.API.Batch>
{
#region Request Parameters

Expand Down
2 changes: 1 addition & 1 deletion EasyPost/Parameters/Batch/All.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace EasyPost.Parameters.Batch
/// <a href="https://www.easypost.com/docs/api#list-all-batches">Parameters</a> for <see cref="EasyPost.Services.BatchService.All(All, System.Threading.CancellationToken)"/> API calls.
/// </summary>
[ExcludeFromCodeCoverage]
public class All : BaseAllParameters
public class All : BaseAllParameters<Models.API.Batch>
{
#region Request Parameters

Expand Down
2 changes: 1 addition & 1 deletion EasyPost/Parameters/Batch/Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace EasyPost.Parameters.Batch
/// <a href="https://www.easypost.com/docs/api#create-a-batch">Parameters</a> for <see cref="EasyPost.Services.BatchService.Create(Create, System.Threading.CancellationToken)"/> API calls.
/// </summary>
[ExcludeFromCodeCoverage]
public class Create : BaseParameters, IBatchParameter
public class Create : BaseParameters<Models.API.Batch>, IBatchParameter
{
#region Request Parameters

Expand Down
2 changes: 1 addition & 1 deletion EasyPost/Parameters/Batch/GenerateLabel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace EasyPost.Parameters.Batch
/// <a href="https://www.easypost.com/docs/api#batch-labels">Parameters</a> for <see cref="EasyPost.Services.BatchService.GenerateLabel(string, GenerateLabel, System.Threading.CancellationToken)"/> API calls.
/// </summary>
[ExcludeFromCodeCoverage]
public class GenerateLabel : BaseParameters
public class GenerateLabel : BaseParameters<Models.API.Batch>
{
#region Request Parameters

Expand Down
2 changes: 1 addition & 1 deletion EasyPost/Parameters/Batch/GenerateScanForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace EasyPost.Parameters.Batch
/// <a href="https://www.easypost.com/docs/api#manifesting-scan-form">Parameters</a> for <see cref="EasyPost.Services.BatchService.GenerateScanForm(string, GenerateScanForm, System.Threading.CancellationToken)"/> API calls.
/// </summary>
[ExcludeFromCodeCoverage]
public class GenerateScanForm : BaseParameters
public class GenerateScanForm : BaseParameters<Models.API.Batch>
{
#region Request Parameters

Expand Down
2 changes: 1 addition & 1 deletion EasyPost/Parameters/Batch/RemoveShipments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace EasyPost.Parameters.Batch
/// <a href="https://www.easypost.com/docs/api#remove-shipments-from-a-batch">Parameters</a> for <see cref="EasyPost.Services.BatchService.RemoveShipments(string, RemoveShipments, System.Threading.CancellationToken)"/> API calls.
/// </summary>
[ExcludeFromCodeCoverage]
public class RemoveShipments : BaseParameters
public class RemoveShipments : BaseParameters<Models.API.Batch>
{
#region Request Parameters

Expand Down
4 changes: 2 additions & 2 deletions EasyPost/Parameters/Beta/CarrierMetadata/Retrieve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace EasyPost.Parameters.Beta.CarrierMetadata
/// </summary>
[ExcludeFromCodeCoverage]
[Obsolete("This class is deprecated. Please use EasyPost.Parameters.CarrierMetadata.Retrieve instead. This class will be removed in a future version.", false)]
public class Retrieve : BaseParameters
public class Retrieve : BaseParameters<Models.API.Beta.CarrierMetadata>
{
#region Request Parameters

Expand All @@ -28,7 +28,7 @@ public class Retrieve : BaseParameters
#endregion

/// <summary>
/// Override the default <see cref="BaseParameters.ToDictionary"/> method to handle the unique serialization requirements for this parameter set.
/// Override the default <see cref="BaseParameters{TMatchInputType}.ToDictionary"/> method to handle the unique serialization requirements for this parameter set.
/// </summary>
/// <returns>A <see cref="Dictionary{TKey,TValue}"/>.</returns>
public override Dictionary<string, object> ToDictionary()
Expand Down
2 changes: 1 addition & 1 deletion EasyPost/Parameters/Beta/Rate/Retrieve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace EasyPost.Parameters.Beta.Rate
/// <a href="https://www.easypost.com/docs/api#retrieve-rates-for-a-shipment">Parameters</a> for <see cref="EasyPost.Services.Beta.RateService.RetrieveStatelessRates(Retrieve, System.Threading.CancellationToken)"/> API calls.
/// </summary>
[ExcludeFromCodeCoverage]
public class Retrieve : BaseParameters
public class Retrieve : BaseParameters<Models.API.Beta.StatelessRate>
{
#region Request Parameters

Expand Down
2 changes: 1 addition & 1 deletion EasyPost/Parameters/CarrierAccount/Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace EasyPost.Parameters.CarrierAccount
/// <a href="https://www.easypost.com/docs/api#create-a-carrier-account">Parameters</a> for <see cref="EasyPost.Services.CarrierAccountService.Create(Create, System.Threading.CancellationToken)"/> API calls.
/// </summary>
[ExcludeFromCodeCoverage]
public class Create : BaseParameters, ICarrierAccountParameter
public class Create : BaseParameters<Models.API.CarrierAccount>, ICarrierAccountParameter
{
#region Request Parameters

Expand Down
2 changes: 1 addition & 1 deletion EasyPost/Parameters/CarrierAccount/Update.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace EasyPost.Parameters.CarrierAccount
/// <a href="https://www.easypost.com/docs/api#update-a-carrieraccount">Parameters</a> for <see cref="EasyPost.Services.CarrierAccountService.Update(string, Update, System.Threading.CancellationToken)"/> API calls.
/// </summary>
[ExcludeFromCodeCoverage]
public class Update : BaseParameters
public class Update : BaseParameters<Models.API.CarrierAccount>
{
#region Request Parameters

Expand Down
Loading
Loading