Connect carrier API utils package
npm install connect-carrier-api-utilsConnect carrier-api utils is a package that support a carrier integration process on SE Connect platform.
Keep in mind it is not officially supporting package by ShipStation or ShipEngine organization. Due to this fact, we are not responsible for any issues related to this package.
Implementation based on SE-connect models and provide unified and easy to use:
* validation rules,
* third party connector implementation,
* default implementations,
* SE logic helpers,
* generic helpers, strings, parsers, etc.,
* fake tests object builders and addresses ready to use,
* implementation for connecting with rendering service to creating labels.
Only what you need is just run npm i @shipengine/connect-carrier-api-utils command inside your project directory.
The validation process include 3 steps.
1. Make a reference to services definition data. Define when the service is selected if there should be custom logic behind that.
2. Connect services with validations. Determinate validations for each service.
3. Execute validations.
At the beginning you should be familiar with following objects: ServiceDetails, ValidationPlan, ValidatonExecutor and validations rules.
#### Quick overview ####
Instance of ValidationExecutor based on the ValidationPlan to execute validations and return a validation Result object or throw error in case of validation error.
The ValidationPlan is a pair of the specific ServiceDetails object and validation list related to them.
To give a module more information about services you can create a new class that decorate a ShippingService definitions. This class should inherit from the ServiceDetails - it is an abstract class which contains logic to check if service is selected.
Using the ShippingService as a data source allows to use in module exactly the same value, which is provided in definition. E.g you can keep additional Code property.
class StandardService extends ServiceDetails {
SERVICE_IDENTIFIER = "Standard";
constructor(shippingService: ShippingService) {
super(shippingService);
}
readonly Code: string = this.shippingService.Code;
}
As a constructor parameter you should use ShippingService instance, which is implementation for ShipStation definitions.
export const SaturdayService: ShippingService = {
Id: "3448ebe0-e70d-48ec-8ec2-5dc813e57888",
Name: "Saturday",
Code: "saturday_delivery",
International: false,
Abbreviation: "saturdayService"
};
The base method from a ServiceDetails delivery isServiceSelected method which is used to determine if the service is selected by validation logic and name property which is using to determinate that.
export abstract class ServiceDetails {
protected abstract SERVICE_IDENTIFIER: string;
isServiceSelected(serviceIdentifier: string): boolean {
return serviceIdentifier === this.shippingService.Name;
}
constructor(protected shippingService: ShippingService) { }
}
You can easily override this method to change logic that checks if service is selected.
class StandardService extends ServiceDetails {
SERVICE_IDENTIFIER = "Standard";
constructor(shippingService: ShippingService) {
super(shippingService);
}
override isServiceSelected(serviceIdentifier: string): boolean{
return serviceIdentifier === this.shippingService.Code;
}
}
Plan executor is a class that is responsible for executing provided validation rules.
To configure validation plan executor you can use the following code:
const validationPlan = new ValidationPlan(
new Map(
[
[new ServiceDetails(StandardService), [
new FakeFirstValidation(),
new FakeSecondValidation(),
new validateParcelWeight(1, 2000)]]
]));
The Validation plan contains map of the ServiceDetails and the validations. You can add new validations to the plan in a runtime.
validationPlan.addValidation(StandardServiceInstance, new ValidateCustomsItemsRequiredIfOneSelected());
The ValidationExecutor is a class that is responsible for finding out what service is selected and executing provided validation rules.
To activate the validationExecutor you can use the following code:
const validationExecutor = new ValidationExecutor(validationPlan);
validationExecutor.execute(CreateLabelRequest);
After that the rules are executed in the order they are provided. As an output for this method you can receive Result object with will be handled and errors will be throw in case of validation error.
To add custom validation rule you need to implement CustomValidationRule interface and add instance to the ValidatorPlan to related service.
class CustomValidationRule implements CustomValidationRule {
validate(request: CreateLabelRequest): Result {
// your logic here
return Result.Success;
}
}
validationPlan.addValidation(StandardServiceInstance, new CustomValidationRule());
CustomsItemsRequiredIfOneSelectedValidator | None | If any customs value is selected ensure all of them should be providedLabelSizeAndFormatValidator | supportedLayouts: LabelLayouts[], supportedDocuments: DocumentFormat[] | If layout or documents is not supported the error will be returned. |MandatoryAddressParametersValidator | addressType: AddressType, fieldsToValidate: AddressFields | Chose which address should be validate, and which parameter should be provided.NumberOfPackageValidator | max: number, min: number = 0 | The number of the package should be in range provided in arguments listParcelWeightValidator | minWeight: number, maxWeight: number | Weight of the parcels should be in range provided in arguments listCheckAllowedAddressCountry | allowedCountries: string[], addressType: AddressType = AddressType.Receiver | Check country code for provided address type is allowed for provided values listValidateParametersBySchema | validateSchema: object | Validate any object by provided valid JSON schemaCheckPackagesMaxDimensions | maxDimensions: number[] | Validate all packages dimensions by provided maximum valuesThe solution delivered by this package is a set of helper functions that can be used to implement third party support.
To create TPS connection you need make 2 things:
1. Inheritance ThirdPartyConnector to make sure logs will contains correct module oriented prefix and refresh token logic will be implemented.
2. Make a ThirdPartyRequest instance that contains all the data you need to make a request to the third party service.
#### Step 1. Create ThirdPartyConnector ####
First of all, you need to implement the ThirdPartyConnector generic class.
export class ModuleThirdPartyConnector extends ThirdPartyConnector {
constructor() {
super("ModuleThirdPartyConnector");
}
}
Value provided as a argument of the ThirdPartyConnector constructor is a identifier of the module. It is used to identify the module in the logs.
#### Step 2. Create ThirdPartyRequest ####
Next you need to create a ThirdPartyRequest instance.
The request object is a generic object that is used to send to carrier API.
To create and configure request object you can use following implementation:
const standardRequest = new ThirdPartyRequest().Method(ThirdPartyMethods.GET).Url("https://www.google.com").Body({})
ThirdPartyRequest support following fluent configuration methods:
| Method | Description
|------- | -----------
| Method | Set method of the request
| Url | Set url of the request
| Body | Set body of the request
| Headers | Set headers of the request
| Query | Set query of the request
| Tiemout | Set timeout of the request
#### Refresh Token ####
In case the API required any type of authentication you can decorate your request, in a way that handle check logic and refresh logic out of the box.
In case of custom authorization you need to implement IRefreshAuthProcess interface.
| Method | Description
| --- | ---
| IsRefreshRequired() | Logic that check if refresh logic is required, depends on time or something.The metadata can be pass as an constructor.
| RefreshBehaviour() | In this method you want to determinate how the refresh request looks like
| ApplyRefreshBehaviour() | In case refresh was done it might be required to override some data in base request.In this method you got as an param response form the refresh token and your main request.
| UpdateMetadata() | This method should override auth data in metadata.
Example of the usage:
const refreshTokenImplementation = new RefreshTokenAlwaysRequiredRefreshTestImpl();
const connector = new ThirdPartyConnectorAuthentication(baseConnector, refreshTokenImplementation, [404]);
var response = await connector.SendRequest
refreshTokenImplementation.UpdateMetadata(createLabelRequest.metadata);
#### Handle response based on status code ####
If you expect specific status code returned from the API in case of success or fail, then you can use SendRequestWithStatuses method on the ThirdPartyConnector object. As an argument you have to provide RequestExpectedStatuses object.
const response = await moduleThirdPartyConnectorImplementation.SendRequestWithExpectedStatuses
#### Example of usage ####
Since the ThirdPartyConnector class is responsible for handling communication with web service, action is executed by calling send method.
public async sendRequest
Promise
As you can see on definition Send method requires a request object. E.g
const standardRequest = new ThirdPartyRequest()
.Method(ThirdPartyMethods.GET)
.Url('http://localhost:5099/Test')
.Body({}); // create a standard request
Next you can call a method. As a generic objects, you can use any model.
const response = await moduleThirdPartyConnectorImplementation.sendRequest
In the example above ResponseTestModel and ErrorTestModel are declared as a expected model in order: response, error from carrier API.
Those models are defined in the module, depends on a carrier documentation. E.g:
export class ResponseTestModel {
public barcode: string;
}
export class ErrorTestModel {
public message: string;
}
As a result of calling send method, ThirdPartyConnector will return ResponseTestModel or ErrorTestModel object depending on TPS connection response and parsing response.
In case you want to add custom features to request, you can decorate request like this base decorator:
export class ThirdPartyRequestResponseTypeBase64 extends ThirdPartyRequest {
constructor(public thirdPartyRequest: ThirdPartyRequest) {
super();
this.thirdPartyRequest = thirdPartyRequest;
this.thirdPartyRequest.ThirdPartyRequestProvider.responseEncoding = 'arraybuffer';
}
}
Usage example:
const standardRequest = new ThirdPartyRequest().Method(ThirdPartyMethods.GET).Url("https://www.google.com").Body({})
const decoratedRequest = new ThirdPartyRequestResponseTypeBase64(standardRequest);
#### Step 1
Create an instance of class ThirdPartySoapConnector and initialize it. For each SOAP service provided by the carrier API a separate instance of the ThirdPartySoapConnector should be created.
const connector = new ThirdPartySoapConnector();
await connector.Initialize('URL_TO_WSDL');
#### Step 2
Set desired SOAP headers required by the carrier API, these are often used for authentication.
connector.SetSoapHeaders({
'ClientId': 'test_client_id',
'AuthenticationTicket': 'test_auth_ticket'
});
#### Step 3
Sometimes it may be required to use custom namespaces in requests, in that case it's necessarry to manually add them to the xml envelope.
connector.AddCustomNamespaceToEnvelope('dat', 'URL_TO_THE_NAMESPACE_DEFINITION');
#### Step 4
Define a request by providing the SOAP method it should call and the data. The ThirdPartySoapRequest class is parametrized and you need to provide the type of the data it contains, in the example it's the AuthenticateRequest type.
const requestData: AuthenticateRequest = {
'UserName': 'dummy_username',
'Password': 'dummy_password'
};
return new ThirdPartySoapRequest
.Method('Authenticate')
.Data(requestData);
#### Step 5
Send the request. The SendRequest method is parametrized and you need to provide the type of data that will be returned from the carrier.
const response = await connector.SendRequest
#### Step 6
During the module's lifetime it might be necessary to update existing SOAP headers, in that case you can use the UpdateSoapHeader method:
await connector.UpdateSoapHeader(':AuthenticationTicket', 'new_auth_ticket');
| Method | Description
|------------------------|-------------
| GetFirstNotEmpty | Pass the number of strings and it will return the first not empty string.
| CheckAndSubstringText | Check if the text is longer than the max length and if so, it will be truncated.
| FormatUrl | Format url with query parameters.
| GetSafeString | Get safe string from the string.
| HandleError | Handle error in preferred by SE Connect way.
| LogInfo | Log info in preferred by SE Connect way.
| LogError | Log error in preferred by SE Connect way.
| GetGUID | Get GUID.
| GetSafeValueFromMap | Get safe value from map.
| GetTrackingNumber | Get tracking number.
| GetValueFromMetadata | Get value from metadata in a safe way.
| CheckIfMandatoryParametersAreProvided | Check list of the parameters if are provided
| FormatUtcDate | Formats the given date to the given format converted to UTC time zone
| StringToBase64 | Encode string to base64.
| ArrayBufferToBase64 | Encode provided ArrayBuffer to base64 string
| GetSortedPackageDimensions | Returns sorted package dimensions in ascending or descending order
| IsValueAllowed | Check if value is allowed from the provided values
| RoundNumberTo | Round number to two digits accuretly for monetary values
| GetBaseUrl | Get carrier API BaseUrl based on connection name
Every class contains a Build() method that return SE object.
You can easy set property of the object using fluent interface methods. E.g
const fakeObject = new FakeAddress().SetCountry().SetCity().Build();
Or
const fakeObject = new FakeAddress().SetDefault("PL001").Build();
| Class | Description
|---------------------------------|------------
| FakeAddress | Fake object for address.
| FakePackage | Fake object for package.
| FakeCreateLabelRequestBuilder | Fake object for create label.
| FakeCreateManifestRequestBuilder | Fake object for create manifest.
| FakeCustomItem | Fake object for custom item.
| FakeGetTrackRequest | Fake object for get track.
| FakeRegisterRequestBuilder | Fake object for register.
| FakeShipFrom | Fake object for ship from.
| FakeShipTo | Fake object for ship to.
DynamoDBConnectClient provides an interface that simplifies the usage of some commonly used DynamoDB features.#### Connecting to a DynamoDB database
First create an instance of DynamoDBConnectClient. If you wish to connect to our AWS hosted instance of DynamoDB, leave out the endpoint.
``ts`
const dynamoDB = new DynamoDBConnectClient('table-name', 'aws-region-name (e.g eu-west-1)', 'endpoint (e.g. http://localhost:8000)');
#### Writing data into DynamoDB
To write data to DynamoDB use the method Write with an array of objects as the parameter. The array will be automatically split into batches of 25 items and will be saved in DynamoDB.
`ts
const exampleItems = [
{
pk: 1,
name: 'name_1'
},
{
pk: 2,
name: 'name_2'
}
];
await dynamoDB.Write(records);
`
#### Retrieving data from DynamoDB
To retreive data from DynamoDB use the method Query with suitable parameters. For more information about the parameters, see the AWS documentation.
The returned data will be automatically parsed into an array of objects of the desired type.
` ts
const keyConditionExpression = 'pk = :pk';
const filterExpression = 'name = :name';
const expressionAttributeValues = {
':pk': 1,
':name': 'name_1',
};
const queryResult = await dynamoDB.Query
`
#### Deleting data from DynamoDB
To delete data from DynamoDB use the method Delete with suitable parameters. For more information about the parameters, see the AWS documentation.
` ts
const keys = [
{
pk: 1
},
{
pk: 2
}
];
dynamoDB.Delete(keys);
`
The Proof of Delivery Service provides functionality to handle proof of delivery documents. It implements a two-step process that first attempts to retrieve an existing document URL using a hash of the file, and if that fails, uploads the document and returns the URL.
#### Prerequisites ####
Before using the ProofOfDeliveryService, you need to set the following environment variable:
``
PROOF_OF_DELIVERY_API_URL=
If not provided, it will use a default URL: 'http://tracking-service-dev.sslocal.com'
#### Step 1. Create an instance of ProofOfDeliveryService ####
First, create a new instance of the ProofOfDeliveryService:
`ts
import { ProofOfDeliveryService } from '@shipengine/connect-carrier-api-utils';
const podService = new ProofOfDeliveryService();
`
#### Step 2. Use the service to get or upload a proof of delivery document ####
The service provides a single method ProvisionProofOfDeliveryUrl that handles both retrieving existing documents and uploading new ones:
`ts
import { ProofOfDeliveryFileType } from '@shipengine/connect-carrier-api-utils';
async function getProofOfDeliveryUrl(
carrierCode: string,
trackingNumber: string,
documentData: string,
fileType: ProofOfDeliveryFileType
): Promise
try {
// This will first try to get an existing URL, and if that fails, upload the document
const url = await podService.ProvisionProofOfDeliveryUrl(
carrierCode,
trackingNumber,
documentData, // Base64-encoded file data
fileType
);
return url;
} catch (error) {
console.error('Error getting proof of delivery URL:', error);
throw error;
}
}
`
#### Parameters ####
The ProvisionProofOfDeliveryUrl method takes the following parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| carrierCode | string | A platform-standard API code that identifies the tracking carrier for a shipment |trackingNumber
| | string | The carrier-provided tracking identifier for a shipment |base64EncodedFile
| | string | The file data encoded as a base64 string |fileType` | ProofOfDeliveryFileType | The file type (PDF or PNG) |
|
#### Return Value ####
The method returns a Promise that resolves to a string containing the URL where the proof of delivery document can be accessed.
#### Limitations ####
- Maximum file size: 5MB
- Supported file types: PDF and PNG only
* Team Spartans on slack channel
v1.1.5 - 2022-08-22 - Extending base validations for address model- city, country, address line1.
v2.0.4 - 2023-03-12 - Change error message handling for third party communicator and authorization process.
v2.1.0 - 2023-04-21 - Added new base validation's rules and new text and custom validation helpers. Additional code improvements.
v4.0.2 - 2023-08-17 - Add json serialization for handling carrier response during error in third party connector.
v4.0.3 - 2023-08-24 - Changed error message format so that it won't be badly formatted on SE side.
v4.0.4 - 2023-08-25 - Removed new line characters from message so that it won't be badly formatted on SE side.
v4.1.0 - 2023-08-29 - POC; support for launch darkly flags added.
v4.1.1 - 2023-08-29 - fix to provide launch darkly key.
v4.1.2 - 2023-09-01 - fix to features for boolean values and additional logging added.
v.4.1.3 - 2023-09-08 - fix features flags to work asynchronically - Bartosz Musielak
v4.1.4 - 2023-10-09 - Added base methods for splitting ZPL and PDF labels, changed Queries method to not add '?' after each parameter
v4.2.0 - 2023-10-23 - Move @shipengine/connect-carrier-api, @shipengine/connect-runtime to peer dependencies
v4.2.1 - 2023-10-24 - Corrected workflow file for label splitting methods
v4.3.0 - 2023-10-25 - Add generic SOAP client - Marcin Karwat
v4.3.1 - 2023-10-25 - Brought back label splitting tests - Magdalena Niwczyk
v4.3.2 - 2023-10-30 - GOLD-2731 - Improve returned datetime for event_datetime parameter for track method.
v4.4.0 - 2023-11-14 - GOLD-2184 - Add generic DynamoDB client
v4.4.1 - 2024-03-22 - GOLD-4340 - Remove unnecessary request field in DefaultVoidImplementation response
v4.4.2 - 2024-04-24 - GOLD-5152 - Add a method to check if the country is from European Union - Bartosz Zurawski
v4.5.0 - 2024-06-14 - GOLD-6337 - Add AlternativeIdentifier - Kajetan Starobrzanski
v4.5.7 - 2024-07-26 - GOLD-7453 - Fix response logging and error handling in base ThirdPartyConnector implementation - Jakub Hypki
v4.5.8 - 2024-08-02 - GOLD-7453 - Add method to handle response stringifying and improve error handling in ThirdPartyConenctor - Jakub Hypki
v4.5.9 - 2024-09-02 - GOLD-8215 - Remove FF (FeatureFlag) support - Karolina Kleciak
v.4.6.0 - 2025-02-09 - GOLD-4914 - Add timeout handling for ThirdPartyRequest - Bartosz Musielak
v.4.6.1 - 2025-03-09 - GOLD-10557 - Add accurate rounding of values for monetary purposes - Bartosz Musielak
v.4.7.0 - 2025-05-05 - GOLD-4224 - Add Proof of Delivery Service (POD) support - Marcin Karwat
v.4.8.0 - 2025-04-01 - GOLD-5240 - Add limiter - Kajetan Starobrzanski
v.4.8.1 - 2025-04-01 - GOLD-5240 - Fix unit tests - Kajetan Starobrzanski
v.4.8.2 - 2025-06-25 - GOLD-5240 - Fix limiter configuration storage - Marcin Karwat
v.4.8.3 - 2025-06-26 - GOLD-5240 - Add detailed logs to limiter for debugging - Marcin Karwat
v.4.8.6 - 2025-08-13 - GOLD-7778 - Add a method to get base url based on connection name - Iqra Zeenat
v.4.8.7 - 2025-09-02 - GOLD-14335 && GOLD-16828 - Added base limit configuration for BRT-IT and Australia Post My Post Business - Ameer Hamza
v.4.9.0 - 2025-10-30 - GOLD-18550 - Removed no longer used rate limiter implementation - Jakub Hypki