3.0.0 • Published 5 years ago

@henson/diplomat v3.0.0

Weekly downloads
1
License
MIT
Repository
-
Last release
5 years ago

Henson Diplomat Library

Table of Contents

Install

yarn add @henson/diplomat

API

rateLimiter - Throttling

Rate limiter runs in either single instance mode or global mode.

In cluster mode, all instances of the same scope in system share a maximum rate. Redis deployment is required in global mode.

Usage

Methods:

  • throttle(options: RatelimiterOptions): Promise<boolean>

    Returns false when request is throttled.

    RateLimiterOptions:

interface RatelimiterOptions {
  readonly scope: string;    // the scope of throttling
  readonly interval: number; // seconds of a rate limit period
  readonly limit: number;    // maximum requests per period
  global?: boolean;
}

Example

  import { rateLimiter } from '@henson/diplomat';

  // create a RatelimiterOptions.
  const options = {
      scope: 'scope1',
      interval: 1,
      limit: 5,
      global: true
  };

  // Use `throttle` function to check whether rate is exceeded.
  if (await rateLimiter.throttle(options)) {
    // normal processing.
  } else {
    // rate is exceeded. response error.
  }

request - Enhanced HTTP client

This API extends https://www.npmjs.com/package/request to support multi-endpoint, rate-limit and service-discovery.

Usage

WARNING Streaming is not supported.

Constants:

const enum ServiceDiscoverPolicy {
  FIRST,
  RANDOM,
}

Interfaces:

interface Agents {
  httpAgent?: http.Agent;
  httpsAgent?: https.Agent;
}

interface EgressLogOptions {
  action: string;
  backendNodeType?: string;
  subscriberId?: egresslog.SubscriberId;
}

interface AlarmOptions {
  name: string;
  handler?: (res: RequestResult | undefined) => boolean;
}

interface MetricsOptions {
  name: string;
  tag?: {
    [key: string]: string | number | boolean
  };
}

interface MultiEndpointRequestOptions extends _request.CoreOptions {
  retryTimes?: number;
  retryResponseCode?: number[];
  rateLimit?: rateLimiter.RatelimiterOptions;
  serviceDiscoverPolicy?: ServiceDiscoverPolicy;
  serviceName?: string;
  agents?: Agents;
  egressLogOpts?: EgressLogOptions;
}

interface UriOptions {
  uri: string | Url | Array<string | Url>;
}

interface UrlOptions {
  url: string | Url | Array<string | Url>;
}

type OptionsWithUri = UriOptions & MultiEndpointRequestOptions;
type OptionsWithUrl = UrlOptions & MultiEndpointRequestOptions;
type Options = OptionsWithUri | OptionsWithUrl;

type InternalOptions = OptionsWithUri & OptionsWithUrl;

Methods:

  • request(options, callback): void

    Extended options:

    • uri || url: string | Url | Array<string | Url>, fully qualified uris or parsed urls object from url.parse()
    • retryTimes: number, retry times when the request url can't connected or response code is in retryResponseCode array
    • retryResponseCode: Array<number>, define the response code to retry the request for one url.
    • rateLimit - object, define the throttling options, refer to module rateLimiter.
    • serviceName - string, service name that is used to discovery and communicate with service automatically.
    • serviceDiscoverPolicy - ServiceDiscoverPolicy, policy of the behavior for service discovery, there are 2 options.
      • ServiceDiscoverPolicy.FIRST, choose the first service node from the service discovery result to send the request. It is the default behavior.
      • ServiceDiscoverPolicy.RANDOM, choose a service node randomly from the service discovery result to send the request.
    • agents - Agents, the agents that are used to send HTTP/HTTPS requests and can include 2 optional items.
      • httpAgent - http.Agent, the agent that is used to send HTTP requests.
      • httpsAgent - https.Agent, the agent that is used to send HTTPS requests.
    • egressLogOpts - EgressLogOptions, the options includes neceessary informations of the outgoing API, and if egressLogOpts is not undefined, request will generate egress logs for this outgoing API.
    • alarm - AlarmOptions, the options for alarm. The alarm is based on the number of the function request calls.
      • name - string, the alarm name from configuration file.
      • handler - function, customized function for alarm handling. The default handler raises alarm when the final http response is error or http status code is 5xx.
    • metrics - MetricsOptions, the options for metrics. The metrics are based on the number of attempts to send.
      • name - string, the unique metrics name in current module.
      • tags - object, the customized tags to append. The default metrics tags include statusCode and statusType. The statusType includes Error, Information, Success, Redirection, ClientError and ServerError.

    For original options please reference to: request parameters

  • request.defaults(options)

    Returns a wrapper around the normal request API that defaults to whatever options you pass to it.

    For example:

    //requests using baseRequest() will set the 'x-token' header
    const baseRequest = request.defaults({
      headers: {'x-token': 'my-token'}
    });
    
    //requests using specialRequest() will include the 'x-token' header set in
    //baseRequest and will also include the 'special' header
    const specialRequest = baseRequest.defaults({
      headers: {special: 'special value'}
    })
  • request.METHOD(options, callback): void

    These HTTP method convenience functions act just like request() but with a default method already set for you:

    • get: Defaults to method: "GET".
    • post: Defaults to method: "POST".
    • request.put: Defaults to method: "PUT".
    • patch: Defaults to method: "PATCH".
    • del / delete: Defaults to method: "DELETE".
    • head: Defaults to method: "HEAD".
    • options: Defaults to method: "OPTIONS".
import { Options, request } from '@henson/diplomat';

// uri is an array in options
// request will retry one by one untill any uri server could connected
// and response code not 502 or 504
request({
  method: 'GET',
  uri: ['http://localhost:1608/v9',
    'http://localhost:1608/v1/msisdn?imsi=IMSI0123456789',
    'http://localhost:1608/v0'],
  retryTimes: 3,
  retryStatusCode: [502, 504],
  timeout: 5000
}, (error: any, response: http.IncomingMessage, body: any) => {
  if(error) {
    console.log('Request failed:' + error.message);
  } else {
    console.log('Request succeed:', response.statusCode, body);
  }
});
import * as http from 'http';
import * as https from 'https';
import { Options, request } from '@henson/diplomat';

const agentOptions: http.AgentOptions = {
  keepAlive: true,
  maxSockets: 20,
};
const httpAgent = new http.Agent(agentOptions);
const httpsAgent = new https.Agent(agentOptions);
const keepAliveAgents: Agents = {
  httpAgent,
  httpsAgent,
};

// uri is an array in the paramater
// request will retry one by one untill any uri server could connected
// and response code not 502 or 504
// agents can hold both HTTP and HTTPS agents and the right one will be choosen to send the request. 
request(['http://localhost:1608/v9',
  'https://localhost:1608/v1/msisdn?imsi=IMSI0123456789',
  'http://localhost:1608/v0'], {
  method: 'GET',
  retryTimes: 3,
  retryStatusCode: [502, 504],
  agents: keepAliveAgents,
  timeout: 5000
}, (error: any, response: http.IncomingMessage, body: any) => {
  if(error) {
    console.log('Request failed:' + error.message);
  } else {
    console.log('Request succeed:', response.statusCode, body);
  }
});
import * as http from 'http';
import * as https from 'https';
import { request, Agents, ServiceDiscoverPolicy } from '@henson/diplomat';

const agentOptions: http.AgentOptions = {
  keepAlive: true,
  maxSockets: 20,
};
const httpAgent = new http.Agent(agentOptions);
const httpsAgent = new https.Agent(agentOptions);
const keepAliveAgents: Agents = {
  httpAgent,
  httpsAgent,
};

// uri is a string indicate a path on server
// serviceName is a string indicate a real service in cluster
// if the target service is running under https mode,
// a default CA file, configured by parameter 'palInternalSslCaFile' in ConfigData.json, will be used.
// if ca is assigned explicitly, the default one is ignored.
// if serviceDiscoverPolicy is assigned with ServiceDiscoverPolicy.RANDOM,
// it will choose a service node randomly to send the request.
// agents can hold both HTTP and HTTPS agents and the right one will be choosen to send the request. 
request({
  method: 'DELETE',
  uri: '/spp/token/v2/staletoken',
  serviceName: 'pushtokenmanager',
  ca: fs.readFileSync('/path/to/ca.crt'),
  serviceDiscoverPolicy: ServiceDiscoverPolicy.RANDOM,
  agents: keepAliveAgents,
  timeout: 5000
}, (error: any, response: http.IncomingMessage, body: any) => {
  if(error) {
    console.log('Request failed:' + error.message);
  } else {
    console.log('Request succeed:', response.statusCode, body);
  }
});
import { request, EgressLogOptions, EndpointConfig, parseEndpointConfig, } from '@henson/diplomat';

const configObj = config.get('test', 'multipleEndpoints'); // configObj can be a multiple-endpoints string or object
const multipleEndpoints: EndpointConfig = parseEndpointConfig(configObj);

const egressLogOpts = {
  action: 'aaa',
  backendNodeType: multipleEndpoints.backendNodeType,
  subscriberId: {
    imsi: '121424141',
  },
};

request(
  uri: multipleEndpoints.UrlList, 
  {
    method: 'GET',
    retryTimes: multipleEndpoints.retryTimes,
    retryStatusCode: multipleEndpoints.retryStatusCode,
    timeout: 5000,
    egressLogOpts,
  }, (error: any, response: http.IncomingMessage, body: any) => {
  if(error) {
    console.log('Request failed:' + error.message);
  } else {
    console.log('Request succeed:', response.statusCode, body);
  }
});

rp - Request Promise

This API wrap the request with promise

Usage

WARNING Streaming is not supported.

Methods:

  • rp(options): Promise<RequestResponse> options is same with that are passed in request
import { RequestOptions, rp, RequestResponse } from '@henson/diplomat';

// uri is an array in options
// request will retry one by one untill any uri server could connected
// and response code not 502 or 504
const endpointNormal: RequestOptions {
  method: 'GET',
  uri: ['http://localhost:1608/v9',
    'http://localhost:1608/v1/msisdn?imsi=IMSI0123456789',
    'http://localhost:1608/v0'],
  retryTimes: 3,
  retryStatusCode: [502, 504],
  timeout: 5000
}

try {
  const res: RequestResponse = await rp(endpointNormal);
  console.log('Request succeed:', res.statusCode, res.headers, res.body);
} catch (error) {
  console.log('Request failed:' + error.message);
}
import { RequestOptions, rp, RequestResponse } from '@henson/diplomat';

// uri is an array in options
// request will retry one by one untill any uri server could connected
// and response code not 502 or 504
const endpointNormal: RequestOptions {
  uri: ['http://localhost:1608/v9',
    'http://localhost:1608/v1/msisdn?imsi=IMSI0123456789',
    'http://localhost:1608/v0'],
  retryTimes: 3,
  retryStatusCode: [502, 504],
  timeout: 5000
}

try {
  const res: RequestResponse = await rp.get(endpointNormal);
  console.log('Request succeed:', res.statusCode, res.headers, res.body);
} catch (error) {
  console.log('Request failed:' + error.message);
}

EndpointConfig - Endpoint Config Parser

It is a util to parse/use endpoint config objects.

Usage

Interfaces

interface MultipleEndpoints {
  UrlList: string[];
  requestTimeout?: number;
  retryTimes?: number;
  rateLimit?: {
    interval: number;
    limit: number;
  };
}

interface EndpointConfig extends MultipleEndpoints {
  backendNodeType?: string;
  ca?: string;
}

Methods

  • parseEndpointConfig(confObj: any): EndpointConfig | undefined Parse a JSON string or an object to a EndpointConfig object. If confObj is invalid to parse, undefined is returned.
import { config } from '@henson/common';
import { EndpointConfig, parseEndpointConfig } from '@henson/diplomat';

const configStrOrObj = config.get('configfile', 'dispatchUrl');
const endpointConfig = parseEndpointConfig(configStrOrObj);