Skip to content
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();

// 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 @@ public abstract class PaginatedCollection<TEntries> : _base.EasyPostObject where
/// <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 @@ -33,7 +34,7 @@ public abstract class PaginatedCollection<TEntries> : _base.EasyPostObject where
/// <typeparam name="TParameters">The type of <see cref="Parameters.BaseParameters"/> to construct for the API call.</typeparam>
/// <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 @@ -59,6 +60,6 @@ internal async Task<TCollection> GetNextPage<TCollection, TParameters>(Func<TPar
/// <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