Use the following guide to assist in the upgrade process of the easypost-csharp
library between major versions.
- Upgrading from 5.x to 6.0
- Upgrading from 4.x to 5.0
- Upgrading from 3.x to 4.x
- Upgrading from 2.x to 3.0
Likelihood of Impact: High
.NET Core 3.1 Support Dropped
This library no longer supports .NET Core 3.1, which has reached end-of-life.
.NET 8.0 Support Added
This library now supports .NET 8.0.
Likelihood of Impact: High
EasyPost now offers Carbon Neutral shipments by default for free! Because of this, there is no longer a need to specify if you want to offset the carbon footprint of a shipment.
The withCarbonOffset
parameter of the Create
, Buy
, and RegenerateRates
shipment functions has been removed.
The CarbonOffset
parameter of the Shipment.Create
, Shipment.Buy
and Shipment.RegenerateRates
parameter sets has been removed.
This is a high-impact change for those using EndShippers
, as the signature for the Create
and Buy
shipment function has changed. You will need to inspect these callsites to ensure that the EndShipper parameter is being passed in the correct place.
The CarbonOffset
model has also been removed.
Likelihood of Impact: Low
The create_and_buy
batch endpoint has been deprecated, and the CreateAndBuy
Batch service function has been removed.
The correct procedure is to first create a batch and then purchase it with two separate API calls.
NOTICE: v5 is deprecated.
Likelihood of Impact: High
Previously, only Create
, Retrieve
and All
functions were available in services, with all other functions related to a specific instance of a resource available on the resource itself.
For example, v4.x flow of creating a pickup and then buying it:
var pickup = await myClient.Pickup.Create(parameters);
pickup.Buy(); // pickup variable is updated in-place
In v5.0, the flow is now:
var pickup = await myClient.Pickup.Create(parameters);
purchasedPickup = await myClient.Pickup.Buy(pickup.Id); // need to capture the updated Pickup object
Likelihood of Impact: High
The process of configuring a Client
has been overhauled to allow for more flexibility in the configuration process.
Old method:
Client myClient = new Client("my_api_key");
New method:
Client myClient = new Client(new ClientConfiguration("my_api_key"));
The ClientConfiguration
class has a number of optional parameters that can be set to customize the behavior of the Client
instance.
Client myClient = new Client(new ClientConfiguration("my_api_key") {
ApiBase = "optional_api_base_override",
CustomHttpClient = myOptionalCustomHttpClient,
Timeout = TimeSpan.FromMilliseconds(myCustomTimeoutMilliseconds)
});
Likelihood of Impact: Medium
The RestSharp dependency in this library has been dropped in favor of using the System.Net.Http
and Newtonsoft.Json
libraries directly.
This should have no impact on the end-user experience of using this library, but if you were reliant on the RestSharp dependency in this library being transitively used for another aspect of your codebase, you will need to now install RestSharp directly.
Likelihood of Impact: Medium
The following classes and properties have been renamed or altered:
Smartrate
is nowSmartRate
Likelihood of Impact: Medium
Some exception types have been consolidated or altered:
UnexpectedHttpError
is nowUnknownHttpError
UnknownApiError
has been removed and replaced withUnknownHttpError
ExternalApiError
no longer inherits fromApiError
and instead inherits fromEasyPostError
- Any EasyPost API failure will raise an
ApiError
-based exception - Non-EasyPost API/HTTP failures (e.g. calls to Stripe) will raise an
ExternalApiError
exception
- Any EasyPost API failure will raise an
- All
EasyPostError
exceptions now have aPrettyPrint
getter method that returns a human-readable string representation of the error - An EasyPost API timeout will raise a
TimeoutError
exception rather than aSystem.Threading.Tasks.TaskCanceledException
Likelihood of Impact: Medium
The Parameter objects introduced in v4.5.0
have been moved out of beta. As a result, the classes are available in a different namespace.
Old namespace:
var parameters = new EasyPost.BetaFeatures.Parameters.Addresses.Create();
New namespace:
var parameters = new EasyPost.Parameters.Address.Create();
Note that the namespaces have also changed from plural to singular (e.g. Addresses
to Address
, Shipments
to Shipment
) to better correlate with the service names on a Client
instance.
NOTICE: v4 is deprecated.
- Updating Dependencies
- Client Instance
- Language Conventions
- Nullable Properties
- Error Types
- Namespace Changes
Likelihood of Impact: High
Dependencies
- RestSharp was upgraded from v107 to v108
- All dependencies had minor version bumps and have been locked to the latest (at the time) major version
Likelihood of Impact: High
The library is now designed around the idea of a Client
. Users will initialize a Client
instance with their API key
and then use that instance to make API calls.
This change allows for multiple clients to be instantiated with different API keys and for the library to be used in a multi-threaded environment.
// Old
EasyPost.ClientManager.SetCurrent("EASYPOST_API_KEY"); // global API key
// New
var client1 = new EasyPost.Client("EASYPOST_API_KEY_1"); // per-client API key
var client2 = new EasyPost.Client("EASYPOST_API_KEY_2"); // per-client API key
All functions are now accessed via the Client
instance. For example, to create a shipment:
// Old
EasyPost.Shipment shipment = await EasyPost.Shipment.Create(parameters);
// New
EasyPost.Shipment shipment = await myClient.Shipment.Create(parameters);
Likelihood of Impact: High
All functions, classes and properties have been renamed to follow the C# language conventions. The most notable changes here is that all properties of an object are now PascalCase rather than snake_case.
// Old
var id = myAddress.id;
var toAddress = myShipment.to_address;
var formType = myScanForm.form_file_type;
// New
var id = myAddress.Id;
var toAddress = myShipment.ToAddress;
var formType = myScanForm.FormFileType;
Likelihood of Impact: High
All properties of all objects are now nullable. This means that if a property is not returned by the API, it will be
either null
or the default value for the given type (e.g. false
for bool
).
This change silences compilation warnings for the library.
The majority of properties for an object will likely be populated as a result of an API call, but if not, end-users will
know that the value of a non-populated property is guaranteed to be null
.
End users may need to add additional null checks or nullability markers in their code, although this change will likely play well with any existing null checks.
// Old
string name = myAddress.name; // "name" is non-nullable, but not guaranteed to be populated
bool correctName = name.startsWith("John"); // compilation warning: "name" may be null
// New
string? name = myAddress.Name; // "Name" and "name" are nullable
bool correctName = name?.startsWith("John") ?? false; // no compilation warning
Likelihood of Impact: High
Error handling has been overhauled and a number of specific exception types have been introduced.
All exceptions inherit from EasyPost.Exceptions.EasyPostError
(which extends System.Exception
). Users can catch this exception type to handle all
errors thrown by the library.
Subclasses of EasyPostError
are grouped into two categories:
EasyPost.Exceptions.ApiError
for errors returned by the API. Subclasses of this exception type are:EasyPost.Exceptions.ConnectionError
- thrown when the library is unable to connect to the APIEasyPost.Exceptions.ExternalApiError
- thrown when an issue occurs with an external API (e.g. Stripe)EasyPost.Exceptions.GatewayTimeoutError
- thrown when the API gateway times out (504 status code)EasyPost.Exceptions.InternalServerError
- thrown when an internal server error occurs (500 status code)EasyPost.Exceptions.InvalidRequestError
- thrown when the API received an invalid request (422 status code)EasyPost.Exceptions.MethodNotAllowedError
- thrown when the API receives a request with an invalid HTTP method ( 405 status code)EasyPost.Exceptions.NotFoundError
- thrown when the API receives a request for a resource that does not exist ( 404 status code)EasyPost.Exceptions.PaymentError
- thrown when a payment error occurs (402 status code)EasyPost.Exceptions.ProxyError
- thrown when the library is unable to connect to the API via a proxyEasyPost.Exceptions.RateLimitError
- thrown when the API rate limit is exceeded (429 status code)EasyPost.Exceptions.RetryError
- thrown when an error occurs during a retryEasyPost.Exceptions.ServiceUnavailableError
- thrown when the API is unavailable (503 status code)EasyPost.Exceptions.SslError
- thrown when the library is unable to connect to the API via SSLEasyPost.Exceptions.TimeoutError
- thrown when the API request times out (408 status code)EasyPost.Exceptions.UnauthorizedError
- thrown when the API receives an unauthorized request (401 or 403 status code)EasyPost.Exceptions.UnexpectedHttpError
- thrown when an unknown HTTP error occurs (unexpected 5xx status code)EasyPost.Exceptions.UnknownApiError
- thrown when an unknown API error occurs (unexpected 4xx status code)EasyPost.Exceptions.VcrError
- thrown when an error occurs with the VCR (used for testing only)
- Generic exceptions
EasyPost.Exceptions.FilteringError
- thrown when an error occurs during filtering (e.g. calculating lowest rate)EasyPost.Exceptions.InvalidObjectError
- thrown when an invalid object is being usedEasyPost.Exceptions.JsonError
EasyPost.Exceptions.JsonDeserializationError
- thrown when an error occurs during JSON deserializationEasyPost.Exceptions.JsonSerializationError
- thrown when an error occurs during JSON serializationEasyPost.Exceptions.JsonNoDataError
- thrown when no data is provided for JSON deserialization
EasyPost.Exceptions.MissingPropertyError
- thrown when a required property is missing (e.g.Id
for most objects)EasyPost.Exceptions.SignatureVerificationError
- thrown when the signature for a webhook is invalidEasyPost.Exceptions.ValidationError
EasyPost.Exceptions.MissingParameterError
- thrown when a required parameter is missingEasyPost.Exceptions.InvalidParameterError
- thrown when an invalid parameter is passed to a function
Any exception thrown by the EasyPost library will be one of the above types.
Any EasyPost.Exceptions.ApiError
exception will have the following properties:
int? StatusCode
- the HTTP status code returned by the API call (e.g. 404)string? Code
- the error code returned by the API (e.g. "PARAMETER.INVALID")List<EasyPost.Error>? Errors
- a list of errors returned by the API (e.g. "Invalid parameter: to_address")
Any EasyPost.Exceptions.ApiError
exception can be pretty-printed using the PrettyPrint
string property.
Users can better anticipate exception information through utilities in the EasyPost.Exceptions.Constants
namespace.
- Check what exception type will be thrown for a given HTTP status code by
using
EasyPost.Exceptions.Constants.GetEasyPostExceptionType(HttpStatusCode statusCode)
- Check error message strings/templates in
EasyPost.Exceptions.Constants.ErrorMessages
Likelihood of Impact: High
As a result of organizing the source code of this library, the namespace of several classes has changed.
- Most EasyPost-related objects are now in the
EasyPost.Models.API
namespace- e.g.
EasyPost.Address
is nowEasyPost.Models.API.Address
- e.g.
- Exceptions, as noted in Error Types, are now in the
EasyPost.Exceptions
namespace - Services (static functions) are now in the
EasyPost.Services
namespace (see Services and Resources)- This should be opaque to end users accessing services through the
EasyPost.Client
class.
- This should be opaque to end users accessing services through the
Likelihood of Impact: Low
Following the client instance redesign, beta features are now accessed via the Beta
property of
the Client
instance.
This "beta client" can only be used to access beta features. Using a beta client to access non-beta features may result in unexpected behavior.
// Old
var betaObject = await EasyPost.Beta.SomeService.SomeFunction(parameters);
// New
var betaObject = await myClient.Beta.SomeService.SomeFunction(parameters);
Any beta function will use the same client (and associated API key) from which the beta function was accessed (
i.e. myClient
in the example above).
Likelihood of Impact: Low
Static and instance-based methods have been divided into separate services and resources. For example, the static
method Shipment.Create
is now accessible in the Shipment service via myClient.Shipment.Create
. The instance
method myShipment.Buy
is still accessible only via a Shipment instance.
Instances of an object cannot be initialized directly. Instead, use the service to create the instance via a Create
, Retrieve
, or All
API call.
Outside of the new client instance change and the namespace change, this design change should not have any additional significant impact on usage, but it is worth noting the new organizational structure.
NOTICE: v3 is deprecated.
- Updating Dependencies
- Project is Now Asynchronous
- Address Verification Parameter Changes
- Non-Static
Create()
Methods Removed - Renames
List()
Functions toAll()
- Clarify XList vs XCollection Distinctions
Likelihood of Impact: High
Explicit .NET Framework Support Removed
easypost-csharp no longer explicitly supports .NET Framework.
.NET Standard 2.0 is compatible with .NET Framework 4.6.1+. See this article for more information.
.NET Standard 2.0 & NET 5.0 & 6.0 Support Added
easypost-csharp can now be used with .NET Standard 2.0, NET 5.0 & NET 6.0 along with Visual Basic and F#.
Dependencies
- RestSharp was upgraded from v106 to v107
- All dependencies had minor version bumps
Likelihood of Impact: High
Most functions are now asynchronous which requires prepending await
to any function call and making the
functions async
. This change dramatically improves API response times and prevents deadlocks. Aside from the need to
await for your async calls, the interfaces of methods remain the same (except for a couple notable changes listed below)
. A simple example of how to call an EasyPost function may look like:
// `async` now required
static async Task Main()
{
// Must call `await` on EasyPost function calls
Shipment shipment = await Shipment.Create(new Dictionary<string, object>() {
{ "from_address", fromAddress },
{ "to_address", toAddress },
{ "parcel", parcel },
});
// Must call `await` on EasyPost function calls
await shipment.Buy(shipment.LowestRate());
}
This change may require refactoring the parent methods to also be asynchronous.
Likelihood of Impact: High
The parameters to verify an address have been changed from verification
to verify
and strict_verification
to verify_strict
to match our other libraries and documentation. The new parameter names must be used to verify an
address.
// Old
addressData.Add("verifications_strict", new List<bool> { true });
// New
addressData.Add("verify_strict", new List<bool> { true });
Likelihood of Impact: High
In order to make this library Visual Basic-compatible,
non-static Create()
functions are no longer supported.
This affects the Address
, Order
, Pickup
and Shipment
classes.
Previously, users could create, i.e., an Address
object (myAddress
) locally, set its attributes, and then
call myAddress.Create()
to send the address data to EasyPost's API.
Now, users must call Address.Create()
to create an Address
object, passing in the address attributes as a
dictionary. This will send the data to EasyPost's API and return a local Address
object.
// Old
Address myAddress = new Address();
myAddress.company = "EasyPost";
await myAddress.Create();
// New
Address myAddress = await Address.Create(new Dictionary<string, object>() {
{ "company", "EasyPost" },
});
Likelihood of Impact: High
Functions that called the /all
method have been renamed from List()
to All()
to make function naming uniform (
previously there were a mix of both naming conventions) which also brings these calls inline with our documentation. If
you use any List
functions in your project, you will need to update these.
// Old
ShipmentList shipments = Shipment.List()
// New
ShipmentCollection shipments = Shipment.All()
Likelihood of Impact: High
We unified the names of all "collection" type objects from XList to XCollection (eg: ShipmentList -> ShipmentCollection) . If you are using these objects, you will need to correct their names.
// Old
ShipmentList shipments = Shipment.List()
// New
ShipmentCollection shipments = Shipment.All()
Likelihood of Impact: Medium
Functions that were previously named Destroy
are now named Delete
. This change was made make this library consistent
with our other libraries and documentation while also clarifying what the action is doing. You will need to correct
these names in your code when performing actions such as deleting Webhooks, Users, and Carrier Accounts.
// Old
webhook.Destroy();
// New
webhook.Delete();
Likelihood of Impact: Medium
The HTTP method used for the get_rates
endpoint at the API level has changed from POST
to GET
and will only
retrieve rates for a shipment instead of regenerating them. A new /rerate
endpoint has been introduced to replace this
functionality; In this library, you can now call the shipment.RegenerateRates
method to regenerate rates. Due to the
logic change, the GetRates
method has been removed since a Shipment inherently already has rates associated.
// Old
shipment.GetRates()
// New
shipment.RegenerateRates()
Likelihood of Impact: Low
The Rating
class is unusable as you cannot "create" a rate and has been removed. Rates must be retrieved from a
Shipment after creation.