opennms
Version:
Client API for the OpenNMS network monitoring platform
354 lines (321 loc) • 10.4 kB
text/typescript
/* eslint-disable max-classes-per-file */
import {OnmsAuthConfig} from './OnmsAuthConfig';
import {OnmsServer} from './OnmsServer';
import {IHash} from '../internal/IHash';
import {Util} from '../internal/Util';
import cloneDeep from 'lodash/cloneDeep';
/** @hidden */
export const DEFAULT_TIMEOUT = 10000;
/** @hidden */
export const TIMEOUT_PROP = Symbol.for('timeout');
/** @hidden */
export const AUTH_PROP = Symbol.for('auth');
/** @hidden */
const isString = (v?: any) => {
return v && (typeof v === 'string' || v instanceof String);
};
/**
* A builder for [[OnmsHTTPOptions]]. Create a new one with [[OnmsHTTPOptions.newBuilder]].
* @category Rest
*/
/* eslint-disable , @typescript-eslint/naming-convention,no-underscore-dangle,id-denylist,id-match, */
export class OnmsHTTPOptionsBuilder {
/** @hidden */
private _timeout?: number;
/** @hidden */
private _server?: OnmsServer;
/** @hidden */
private _auth?: OnmsAuthConfig;
/** @hidden */
private _headers = {} as IHash<string>;
/** @hidden */
private _parameters = {} as IHash<string | string[]>;
/** @hidden */
private _data?: any;
/**
* Construct a new builder from an existing options object, if provided.
*
* NOTE: server, auth, headers, and parameters are cloned, but data is left alone
* and assumed to be mutable autside of the builder or elsewhere.
*/
public constructor(options?: OnmsHTTPOptions) {
if (options) {
this._timeout = options.timeout;
this._server = options.server ? options.server.clone() : undefined;
this._auth = options.auth ? options.auth.clone() : undefined;
this._headers = options.headers ? cloneDeep(options.headers) : {};
this._parameters = options.parameters ? cloneDeep(options.parameters) : {};
this._data = options.data;
}
}
/** Build the [[OnmsHTTPOptions]] object. */
public build(): OnmsHTTPOptions {
return new OnmsHTTPOptions(
this._timeout,
this._server,
this._auth,
cloneDeep(this._headers),
cloneDeep(this._parameters),
this._data,
);
}
/**
* Merge the contents of the provided [[OnmsHTTPOptions]] object, additively.
* Timeout, server, auth, and data will be replaced only if set, and headers and parameters
* will be overlayed on top of existing.
* @param options the options to merge with this builder's current values
*/
public merge(options?: OnmsHTTPOptions) {
if (options) {
if (options.timeout) {
this.setTimeout(options.timeout);
}
if (options.server) {
this.setServer(options.server);
}
if (options.auth) {
this.setAuth(options.auth);
}
if (options.headers) {
for (const header of Object.keys(options.headers)) {
this.setHeader(header, options.headers[header]);
}
}
if (options.parameters) {
for (const parameter of Object.keys(options.parameters)) {
this.addParameter(parameter, options.parameters[parameter]);
}
}
if (options.data) {
this.setData(options.data);
}
}
return this;
}
/**
* The connection timeout for the request.
*
* If `undefined` is passed, the default timeout will be used.
* @param timeout the new timeout
*/
public setTimeout(timeout?: number) {
this._timeout = timeout;
return this;
}
/**
* The [[OnmsServer]] to connect to.
*
* If `undefined` is passed, the default server will be used.
* @param server the new server
*/
public setServer(server?: OnmsServer) {
this._server = server;
return this;
}
/**
* The authentication config to use when connecting.
*
* If `undefined` is passed, the default authentication settings will be used.
* @param auth the authentication config
*/
public setAuth(auth?: OnmsAuthConfig) {
this._auth = auth;
return this;
}
/**
* The headers to set in the request.
*
* If `undefined` is passed, all headers in the builder will be reset and the default headers will be used.
* @param headers the headers to use (or `undefined`)
*/
public setHeaders(headers?: IHash<string>) {
this._headers = headers || {};
return this;
}
/**
* A header to set in the request.
*
* If `undefined` is passed, that header will be reset to defaults.
* @param header the header name
* @param value the value of the header
*/
public setHeader(header: string, value?: string | number | boolean) {
const v = value ? String(value) : undefined;
const actualKey = Util.insensitiveKey(header, this._headers);
delete this._headers[header];
if (actualKey) {
delete this._headers[actualKey];
}
if (v !== undefined) {
this._headers[header] = v;
}
return this;
}
/**
* A header to set in the request only if it is not already set.
* @param header the header name
* @param value the value of the header
*/
public setDefaultHeader(header: string, value: string | number | boolean) {
const actualKey = Util.insensitiveKey(header, this._headers);
if (!actualKey) {
this._headers[header] = String(value);
}
return this;
}
/**
* The parameters to pass to the request.
*
* If `undefined` is passed, all parameters in the builder will be reset.
* @param parameters the parameters to use (or `undefined`)
*/
public setParameters(parameters?: IHash<string|string[]>) {
if (!parameters) {
this._parameters = {};
} else {
this._parameters = parameters;
}
return this;
}
/**
* A parameter to add or append to the request.
*
* If `undefined` is passed, that parameter will be reset to defaults.
* If the value is a string array, the existing value in the builder will be replaced.
* Otherwise, if the parameter already exists in the builder, the parameter will be converted to an array
* if necessary and this parameter will be added to it.
* @param parameter the parameter name
* @param value the value of the parameter to add (or `undefined`)
*/
public addParameter(parameter: string, value?: string | string[] | number | boolean) {
// if it's already an array, coerce the contents to a string
// otherwise, coerce it as a scalar string
const v = Array.isArray(value) ?
value.map((vv) => String(vv)) :
(value && !isString(value)) ? String(value) : value;
// Since parameters can be repeated an arbitrary number of times we will store them in an array in the map
// as soon as the occurrence of a given key is > 1
if (this._parameters[parameter]) {
if (v === undefined) {
delete this._parameters[parameter];
} else if (Array.isArray(v)) {
this._parameters[parameter] = v.map((obj) => String(obj));
} else {
const currentValue = this._parameters[parameter];
if (Array.isArray(currentValue)) {
currentValue.push(String(v));
} else {
const newArrayValue = [];
newArrayValue.push(currentValue);
newArrayValue.push(String(v));
this._parameters[parameter] = newArrayValue;
}
}
} else {
if (v) {
if (Array.isArray(v)) {
this._parameters[parameter] = v.map((obj) => String(obj));
} else {
this._parameters[parameter] = String(v);
}
}
}
return this;
}
/**
* The data to use in the request.
*
* If `undefined` is passed, the data will be cleared.
* @param data the data
*/
public setData(data?: any) {
this._data = data;
return this;
}
}
/* eslint-enable @typescript-eslint/naming-convention, no-underscore-dangle, id-denylist, id-match */
/**
* Options to be used when making HTTP ReST calls.
* @category Rest
*/
export class OnmsHTTPOptions {
/**
* Create a new builder for an [[OnmsHTTPOptions]] object.
* @param options if an existing options object is passed, the builder will be pre-populated
*/
public static newBuilder(options?: OnmsHTTPOptions) {
return new OnmsHTTPOptionsBuilder(options);
}
/** How long to wait for ReST calls to time out. */
public get timeout(): number | undefined {
return this[TIMEOUT_PROP] || undefined;
}
/** The authentication config that should be used. */
public get auth(): OnmsAuthConfig | undefined {
const auth = this[AUTH_PROP];
if (auth !== null && auth !== undefined) {
return auth;
}
if (this.server && this.server.auth) {
return this.server.auth;
}
return undefined;
}
/** The server to use instead of that provided by the HTTP implementation. */
public readonly server: OnmsServer | null;
/** HTTP headers to be passed to the request. */
public readonly headers = {} as IHash<string>;
/** HTTP parameters to be passed on the URL. */
public readonly parameters = {} as IHash<string | string[]>;
/** HTTP data to be passed when POSTing */
public readonly data: any;
/**
* The default timeout associated with these options.
*
* This is a trick for making sure serialization to JSON happens properly
* without exposing internals.
*/
private readonly [TIMEOUT_PROP]: number | null;
/**
* The default authentication credentials associated with these options.
*
* This is a trick for making sure serialization to JSON happens properly
* without exposing internals.
*/
private readonly [AUTH_PROP]: OnmsAuthConfig | null;
/**
* Construct a new OnmsHTTPOptions object.
* @constructor
*/
constructor(
timeout?: number,
server?: OnmsServer,
auth?: OnmsAuthConfig,
headers?: IHash<string>,
parameters?: IHash<string | string[]>,
data?: any,
) {
this[TIMEOUT_PROP] = timeout || DEFAULT_TIMEOUT;
this.server = server || null;
this[AUTH_PROP] = null;
if (server && auth && auth !== server.auth) {
this[AUTH_PROP] = auth;
}
this.headers = headers || {} as IHash<string>;
this.parameters = parameters || {} as IHash<string | string[]>;
this.data = data;
}
/**
* Convert the options to a plain JSON object.
*/
public toJSON(): object {
const ret = Object.assign({} as any, this);
if (this[TIMEOUT_PROP]) {
ret.timeout = this[TIMEOUT_PROP];
}
if (this[AUTH_PROP]) {
ret.auth = this[AUTH_PROP];
}
return ret;
}
}