@nuskin/product-status-client

JavaScript client for the Nu Skin Product Status Service web API

Usage no npm install needed!

<script type="module">
  import nuskinProductStatusClient from 'https://cdn.skypack.dev/@nuskin/product-status-client';
</script>

README

Product Status Service Client

JavaScript client for the Nu Skin Product Status Service web API

Installing

Using npm:

$ npm install @nuskin/product-status-client

Using Yarn:

$ yarn add @nuskin/product-status-client

Initializing the Client

Initialize a new instance of the client by providing the following parameters to the constructor:

  • baseURL - The web API's base URL. Currently, this is the base URL of Nu Skin's cloud web services.
  • clientID - The web API client ID.
  • clientSecret - The web API client secret.
  • country - Country code of the market that requests will be made for.

The following optional parameters may also be provided:

{
  // Additional query parameters to include in product status requests.
  params: {
    paramName: "paramValue",
    otherParamName: 12345
  },

  // The requested product quantity.
  // By default, the requested product quantity is 1000.
  quantity: 1000, // Default

  // The maximum wait time for batching requests together.
  // Wait time is given in milliseconds.
  // By default, the batch wait time is 1 millisecond.
  // See the section on batch wait time for further details.
  maxWait: 1, // Default

  // The maximum size of a request batch.
  // By default, the maximum batch size is 36.
  // See the section on batch size for further details.
  batchSize: 36, // Default

  // Storage provider for locally caching product status data.
  // Provider must expose get and set methods.
  // See the section on status caching for further details.
  store: {
    // Key argument is string
    get: function(key) {
      // Retrieve value for given key
      return value;
    },
    // Key and value arguments are strings
    set: function(key, value) {
      /* ... */
    }
  },

  // Array of time-to-live settings for cached product status data.
  // Each entry should have a minimum quantity and a time-to-live value 
  // in seconds for that quantity.
  // See the section on status caching for further details.
  ttl: [
    { qty: 100, ttl: 60 },
    { qty: 250, ttl: 300 }
  ]
}

Example:

import { ProductStatusClient } from "@nuskin/product-status-client";

const client = new ProductStatusClient({
  baseURL: "https://api.cloud.nuskin.com",
  clientID: "0123...aeae",
  clientSecret: "8888...aa00",
  country: "US",
  params: {
    foo: "bar"
  },
  maxWait: 25,
  store: {
    get: sessionStorage.get,
    set: sessionStorage.set
  },
  ttl: [
    { qty: 100, ttl: 30 },
    { qty: 500, ttl: 120 },
    { qty: 1000, ttl: 300 }
  ]
});

Using the Client

Call .getProductStatuses() to retrieve product status information for one or more product SKUs.

This function can be called with either a single SKU string or an array of SKU strings:

client.getProductStatuses("80801234").then(...)

client.getProductStatuses(["55555555", "55555556", "55555557"]).then(...)

The function returns a promise that resolves to an array of product statuses:

client.getProductStatuses(["12345678", "99999999"])
.then(statuses => {

  console.log(statuses[0]); // {SKU: "12345678", availableQuantity: 1000, ...}

  console.log(statuses[1]); // {SKU: "99999999", availableQuantity: 565, ...}
});

Product Status Structure

Product statuses returned by the client take the following form:

// Product status information object, version 2
interface ProductStatusV2 {

  // Product SKU
  SKU: string;

  // Product available quantity
  availableQuantity: number;

  // Flag indicating whether product is enabled for sale on the web
  webEnabled: boolean;
  
  // List of attribute flags
  attribute: Array<{

    // Attribute flag name
    type: string;

    // Attribute flag value
    value: boolean;
  }>;
  
  // Product availability information
  // Possible values include "RELEASED_FOR_SALE", "DISCONTINUED", "PHASE_OUT", "APPROVED_FOR_SALE", "NOT_FOUND"
  status: {

    // Retail availability status
    retail: string;

    // Wholesale availability status
    wholesale: string;
  };
  
  // List of allowed order types for this product
  orderType: string[];
  
  // Product pricing breakdown
  pricing: {

    // Pricing details map for the retail channel
    // Map keys are order types
    retail: {
      [orderType: string]: PricingDetail
    };

    // Pricing details map for the wholesale channel
    // Map keys are order types
    wholesale: {
      [orderType: string]: PricingDetail
    };
  };
}


// Pricing details specific to an order type
interface PricingDetail {

  // Product price
  price: number;

  // Product personal volume (PV) value
  pv?: number;

  // Product commissionable volume (CV) value
  cv?: number; 

  // Product price in ADR points
  Points?: number;
}

Legacy Support for Version 1 Product Statuses

Legacy support is offered for Version 1 product statuses.

Version 1 product statuses take the following form:

// Product status information object, version 1
interface ProductStatusV1 {

  // Product SKU
  SKU: string;
  
  // Product available quantity
  availableQuantity: number;
  
  // Status of the product available quantity: "ESTIMATED", "CURRENT", "STALE", "UNKNOWN"
  availableQuantityStatus: string;
  
  // Order types that can include this product, e.g. "ORDER", "ADR"
  orderType: string[];
  
  // Product's availability status: "RELEASED_FOR_SALE", "DISCONTINUED", "PHASE_OUT", "APPROVED_FOR_SALE", "NOT_FOUND"
  status: string;
  
  // Flag indicating whether product is enabled for sale on the web
  webEnabled?: boolean;

  // Map of price types to price values.
  // Keys are price types, e.g. "RTL", "WHL", "WADR", "WADW", "WRTL", etc.
  // Values are prices.
  price: { [key: string]: number }[];

  // Map of price types to PV values.
  // Keys are price types, e.g. "RTL", "WHL", "WADR", "WADW", "WRTL", etc.
  // Values are prices.
  psv: { [key: string]: number }[];

  // Map of price types to CV values.
  // Keys are price types, e.g. "RTL", "WHL", "WADR", "WADW", "WRTL", etc.
  // Values are prices.
  csv: { [key: string]: number }[];
}

Call the static method ProductStatusClient.toV1Statuses() to convert Version 2 product statuses to Version 1.

Example:

client.getProductStatuses("99999999")
.then(v2Statuses => ProductStatusClient.toV1Statuses(v2Statuses))
.then(v1Statuses => {
  ...
});

Request Batching

The product status web service that this client encapsulates can accept requests containing multiple SKUs at once. This is also much more efficient for the server than processing each SKU in its own request. Because of this, the Product Status Web Client submits requested SKUs together in batches.

Batch Wait Time

The first call to .getProductStatuses() opens a new batch and begins a countdown timer. Any additional calls to .getProductStatuses() during this countdown are added to the batch.

Once the batch countdown timer expires, a single server request is executed for all SKUs in the batch. The promises that were returned by .getProductStatuses() are then resolved, each with the product statuses requested by its function call.

This is illustrated below:

                Client                    Server
                  |                         |
.getStatuses() => | new batch opened        |
                  |                         |
.getStatuses() => |                         |
.getStatuses() => |                         |
                  | batch timer ends        |
                  |                         |
                  |-------- REQUEST ------->|
Promises resolved |<------------------------|
                  |                         |

The lenth of the batch timer delay can be configured by providing a maxWait parameter to the constructor.

Batch Max Size

Due to server constraints, there is a limit to the number of SKUs that a single server request can contain. Because of this, if a batch reaches its maximum allowed size, it is immediately submitted as a server request and a new batch is begun for any remaining SKUs being requested.

This is illustrated below:

                Client                    Server
                  |                         |
.getStatuses() => | batch 1 opened          |
.getStatuses() => |                         |
.getStatuses() => | batch 1 max size        |
.getStatuses() => | batch 2 opened          |
                  |------- REQUEST 1 ------>|
Promises resolved |<------------------------|
                  |                         |
.getStatuses() => |                         |
                  |                         |
                  |                         |
                  | batch 2 timer ends      |
                  |                         |
                  |------- REQUEST 2 ------>|
Promises resolved |<------------------------|
                  |                         |

Batch size can be configured by providing a batchSize parameter to the constructor.

Caching Product Status Data

Caching support is offered for product status data, to avoid redundant API requests and increase the speed of repeated retrievals for the same statuses.

Cache Provider

You can provide your choice of caching storage provider to the constructor via the store parameter.

store must be an object that exposes a .get() and a .set() method.

.get() must accept a key string and return a value string:

get: (key: string) => string

.set() must accept a key string and a value string:

set: (key: string, value: string) => void

Example 1, in-memory caching:

const cache = new Map();

const client = new ProductStatusClient({
  ...
  store: {
    get: (key) => cache.get(key),
    set: (key, value) => cache.set(key, value)
  }
});

Example 2, browser session storage caching:

const client = new ProductStatusClient({
  ...
  store: {
    get: (key) => sessionStorage.getItem(key),
    set: (key, value) => sessionStorage.setItem(key, value)
  }
});

Cache Time to Live

Cached statuses have a time-to-live (ttl) value. If you provide a caching provider to the constructor via the store parameter, you must also provide ttl settings via the ttl parameter.

ttl must be an array of ttl setting objects. A ttl setting object has the following properties:

  • qty - The minimum available quantity that a product status must have to qualify for this time to live
  • ttl - The time to live for the cached product status, in seconds
const ttlSettings = [
  // Product statuses with an available quantity >= 200 will be cached for up to 60 seconds
  { qty: 200, ttl: 60 },

  // Product statuses with an available quantity >= 500 will be cached for up to 120 seconds
  { qty: 500, ttl: 120 },

  // Product statuses with an available quantity >= 1000 will be cached for up to 300 seconds
  { qty: 1000, ttl: 300 }
];

const client = new ProductStatusClient({
  ...
  ttl: ttlSettings
});