2.3.1 • Published 22 days ago

@keeex/sdk-helper v2.3.1

Weekly downloads
-
License
MIT
Repository
-
Last release
22 days ago

@keeex/sdk-helper

Bugs Code Smells Maintainability Rating Security Rating Vulnerabilities Technical Debt Coverage

General description

Shared code to build SDK libraries.

Features:

  • Simplified raw HTTP calls
  • Authentication
  • Handling of input values transformations into either JSON or FormData
  • Handling of file inputs
  • Handling of generic pagination values
  • Handling of URI arguments and query arguments
  • Handling of file replies
  • Grabing useful data from complex reply
  • Progress callback
  • Concurrent request limitation
  • Separate request queues
  • Input/output filtering

Usage

Setting up a SDK using this library is done by providing settings describing how the API is accessed, as well as individual descriptions for all API calls. These individual descriptions are used to create functions that can be used to call the API directly.

Short example:

import SdkBase from "@keeex/sdk-helper/lib/sdk.js";
import {
  toAPI as dateToAPI,
  fromAPI as dateFromAPI,
} from "@keeex/sdk-helper/lib/filters/date";

const routeFilter = {
  toAPI: {lastKnownDate: dateToAPI},
  fromAPI: {newDate: dateFromAPI},
};

export default class Sdk extends SdkBase {
  public retrieveDate;

  constructor(config) {
    super(
      {baseURL: config.uri},
      {},
      {type: "autocookie"},
    );
    this.retrieveDate = this.directApiCall<{lastKnownDate: Date}, {newDate: Date}}({
      method: "post",
      route: "/api/v1/date/retrieve",
      authenticated: false,
      filters: routeFilter,
      replyDataPath: "",
    });
  }
}

const sdk = new Sdk({uri: "https://invalid.invalid"});
const result = await sdk.retrieveDate({lastKnownDate: new Date()});
console.log("Reply:", result.newDate);

The above example create an SDK that exposes only one function called retrieveDate. This function takes as input an object with all the inputs (in this case there is only one property: lastKnownDate). It then returns the full reply from the server with one property: newDate. The filter automatically convert a Date object into its ISO8601 string representation before the call, and converts the reply back from an ISO8601 string representation to a Date object.

An alternative to directApiCall() named createApiCall() exists; it will return an object exposing the raw reply body as well as the HTTP status code alongside the filtered data.

Pagination

Instead of directApiCall() it is possible to call paginatedApiCall(). In that case the input accepts extra PaginationInput fields, and the reply will follow the PaginatedResponse interface.

Authentication

This library handles two types of authentication: HTTP-header based and cookie based. The third argument of the Sdk class constructor defines the authorization scheme to use. If not provided, no authorization is handled. If provided, routes that are defined with the authenticated property to true requires the user to previously be authenticated.

To indicate to an Sdk instance that authentication completed, call the setAuthToken() method with a suitable value. undefined or any other false-y value indicates no authentication.

Cookie-based authentication

This mode is basically a passthrough. Cookies are handled by the browser. To use this method of authentication, pass a config object with the single property type set to autocookie.

HTTP-header based authentication

This mode allows the caller to provide a value passed as an HTTP header. In this case setAuthToken()) expects a non-empty string argument to indicate authentication.

To use this method, pass a config object with the following properties:

  • type: must be header
  • headerName: name of the header to use. Defaults to Authorization
  • valueTemplate: a template for the value of the header; the word %token% will be replaced by the provided token. Defaults to Bearer %token%

Defining API routes

To create a function that implements an API route, the directApiCall() method must be called. It takes a single parameter which define the behavior of the route. It is an object with the following properties (all properties are optional except for route):

  • route: path to the API route relative to the base URL
  • queue: request queue to use. Defaults to a default queue.
  • method: HTTP method. Defaults to GET.
  • authenticated: true to restrict this call when non authenticated
  • queryParams: list of "input" properties to pass as query arguments
  • bodyParams: list of "input" properties to pass in the body
  • fileParams: list of "input" properties that are expected to be files
  • axiosOptions: custom axios options
  • paginated: true to handle special pagination "input" values (deprecated, see Pagination)
  • replyDataPath: JSON path in the reply where the useful output is located
  • filters: filters to apply to input arguments and output values

The function returned by this call accepts a single parameter which is an "input" object. This "input" object's properties will then be parsed accordingly to the API specification.

URI arguments

It is possible to use some part of the "input" as URI arguments. To do so, sections of the route must start with a : character.

For example, a route defined by /api/v1/item/:id will grab the id property from the "input" object and put it in the URI before the request is performed.

Query arguments

All properties names passed into queryParams will be appended to the route using ?key=value accordingly.

Body arguments

All properties names passed into bodyParams will be put in either a JSON object sent as the body for the request, or into a multipart/form-data, depending on the presence of files or not in that request.

File arguments

If fileParams is present, the body will be a multipart/form-data body and treat the properties names in fileParams as files to be appended to it.

The file who's properties names are in fileParams are required unless the properties names are prefixed with ?.

Getting JSON path from reply

A minor feature is the ability to directly return a specific value from the reply. A "JSON" path can be specified (properties names separated with a .). Once a request complete, this path will be used and the value located in the given property will be returned. This path is specified in the replyDataPath property of an API route definition.

This library have three behavior regarding this:

  • undefined: do not return anything
  • empty: return everything
  • a path: return only the value pointed by the path

File replies

If a route returns a binary blob (a file) a special replyDataPath value should be provided to handle it. The value can be either <stream> or <arraybuffer>. In both case the reply will be returned as-is from axios.

Progress callback

Functions returned by directApiCall() accepts a second parameter: a function that will be called regularly during the upload/download process. This functions takes two arguments: the first is the progress percentage, the second is a boolean set to true if we're in the upload phase.

Queue control

The default setting of this library is to not allow more than two concurrent requests on a single queue. However, to allow for long operations to coexist with short requests, it is possible to specify which "queue" is used by an API call. By putting a queue name on the queue property of an API call description, it can use a separate queue.

There is no restriction to the number of queues; keep in mind however that running too many concurrent requests can results in dropped replies in some circumstances.

If no queue name is provided a default queue is used.

Data filtering

This library have a built-in option to filter/transform data before sending them to the API, and when receiving a reply.

At its core, a filter is a function that takes a value of one type and returns (using a promise) the converted value. The filters property in API route definitions is an object that have two properties:

  • toAPI
  • fromAPI

Each of these properties is an object whose keys represent the keys to filter and values are filter functions.

A special case exists, for API calls that returns a single value and not an object. In that case, a special property named <root> can be set on the filters list to be applied to the reply value itself and not on its properties. If this property is present, no other properties will be handled.

Built-in filters

A list of built-in filters is provided in the lib/filters directory:

  • binary: conversion from/to buffers and hex/base64
  • date: conversion from/to Date objects and ISO8601 string representations
  • fixed: conversion from/to "fixed" decimal numbers, with fixed number of decimals
  • nullstring: unify empty string and null values to/from null

Utility filters

To be able to handle complex objects, special utility filters are available. With them it is possible to filter data behind arrays and properties.

  • array: apply a filter to all values of an array
  • props: apply various filters to the properties of an object