npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

connect-carrier-api-utils

v4.5.13

Published

Connect carrier API utils package

Downloads

1,412

Readme

Connect carrier-api utils

Connect 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.

How to start

Only what you need is just run npm i @shipengine/connect-carrier-api-utils command inside your project directory.

How to use a validation helper

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.

Step 1. Define services

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;
    }
}

Step 2. How to configure validation plan executor

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());

Step 3. How to execute validation plan

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.

How to add custom validation

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());

List of the validations rules

Name of the method | Arguments | Description |-----------------------------------------------|-----------------------------------------------------------------------------------|--------------------------------------------------------------------------| | CustomsItemsRequiredIfOneSelectedValidator | None | If any customs value is selected ensure all of them should be provided | LabelSizeAndFormatValidator | 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 list | ParcelWeightValidator | minWeight: number, maxWeight: number | Weight of the parcels should be in range provided in arguments list | CheckAllowedAddressCountry | allowedCountries: string[], addressType: AddressType = AddressType.Receiver | Check country code for provided address type is allowed for provided values list | ValidateParametersBySchema | validateSchema: object | Validate any object by provided valid JSON schema | CheckPackagesMaxDimensions | maxDimensions: number[] | Validate all packages dimensions by provided maximum values

How to implement third party support

The 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

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<any, any>(new ThirdPartyRequest());

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<ResponseTestModel, ErrorTestModel>(standardRequest, new RequestExpectedStatuses([200], [404])));

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<Response, ErrorResponse>(request: ThirdPartyRequestProvider):
        Promise<Response | ErrorResponse | undefined> { }

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<ResponseTestModel, ErrorTestModel>(standardRequest)

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.

How to add custom third party request builder logic

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);

How to implement SOAP support

This package contains a base implementation of a SOAP connector that may help with creation of SOAP based carrier integrations.

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<AuthenticateRequest>()
        .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<AuthenticateRequest, AuthenticateResponse>(request);

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');

List of helpers

| 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

List of tests fake objects

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.

DynamoDB

Some carrier modules are required to store information such as routing data or PUDO points in a DynamoDB database. Class 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.

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.

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.

const keyConditionExpression = 'pk = :pk';
const filterExpression = 'name = :name';
const expressionAttributeValues = {
    ':pk': 1,
    ':name': 'name_1',
};

const queryResult = await dynamoDB.Query<ItemType>(keyConditionExpression, expressionAttributeValues, filterExpression);

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.

const keys = [
    {
        pk: 1
    },
    {
        pk: 2
    }
];

dynamoDB.Delete(keys);

Who do I talk to?

  • Team Spartans on slack channel

Release notes

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