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

@henson/diplomat

v3.0.0

Published

A node.js HTTP client for accessing external systems and distributed services, with service discovery, auto failover and rate limiting builtin

Downloads

3

Readme

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