Skip to content

Commit

Permalink
feat(api): add full DAO filtering support
Browse files Browse the repository at this point in the history
  • Loading branch information
Benjamin Reed committed Jun 14, 2017
1 parent c064d07 commit fe2204c
Show file tree
Hide file tree
Showing 23 changed files with 476 additions and 122 deletions.
38 changes: 24 additions & 14 deletions src/API.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,57 @@
import {Comparator, COMPARATORS} from './api/Comparator';
import {Filter} from './api/Filter';
import {OnmsAuthConfig} from './api/OnmsAuthConfig';
import {OnmsError} from './api/OnmsError';
import {OnmsHTTPOptions} from './api/OnmsHTTPOptions';
import {OnmsResult} from './api/OnmsResult';
import {OnmsServer} from './api/OnmsServer';
import {OnmsVersion} from './api/OnmsVersion';
import {Restriction} from './api/Restriction';
import {ServerMetadata} from './api/ServerMetadata';
import {ServerType, SERVER_TYPES} from './api/ServerType';

import {AlarmDAO} from './dao/AlarmDAO';
import {EventDAO} from './dao/EventDAO';
import {V1FilterProcessor} from './dao/V1FilterProcessor';

import {OnmsAlarm} from './model/OnmsAlarm';
import {OnmsAlarmType} from './model/OnmsAlarmType';
import {OnmsEvent} from './model/OnmsEvent';
import {OnmsParm} from './model/OnmsParm';
import {OnmsServiceType} from './model/OnmsServiceType';
import {OnmsSeverity} from './model/OnmsSeverity';
import {OnmsServiceType, SERVICE_TYPES} from './model/OnmsServiceType';
import {OnmsSeverity, SEVERITIES} from './model/OnmsSeverity';
import {OnmsTroubleTicketState} from './model/OnmsTroubleTicketState';

import {AxiosHTTP} from './rest/AxiosHTTP';
import {GrafanaHTTP} from './rest/GrafanaHTTP';
import {SuperAgentHTTP} from './rest/SuperAgentHTTP';

import {Filter} from './dao/criteria/Filter';

import {AlarmDAO} from './dao/AlarmDAO';

import {Client} from './Client';

/* tslint:disable:object-literal-sort-keys */

/** @hidden */
const API = Object.freeze({
Comparator,
COMPARATORS,
Filter,
OnmsAuthConfig,
OnmsError,
OnmsHTTPOptions,
OnmsResult,
OnmsServer,
OnmsVersion,
SERVER_TYPES,
Restriction,
ServerMetadata,
ServerType,
SERVER_TYPES,
});

/** @hidden */
const DAO = Object.freeze({
AlarmDAO,
EventDAO,
V1FilterProcessor,
});

/** @hidden */
Expand All @@ -47,7 +61,9 @@ const Model = Object.freeze({
OnmsEvent,
OnmsParm,
OnmsServiceType,
SERVICE_TYPES,
OnmsSeverity,
SEVERITIES,
OnmsTroubleTicketState,
});

Expand All @@ -58,13 +74,7 @@ const Rest = Object.freeze({
SuperAgentHTTP,
});

/** @hidden */
const DAO = Object.freeze({
Filter,
AlarmDAO,
});

/* tslint:enable:object-literal-sort-keys */

/** @hidden */
export {API, Model, Rest, DAO, Client};
export {API, DAO, Model, Rest, Client};
57 changes: 50 additions & 7 deletions src/CLI.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as startCase from 'lodash.startcase';

import {API, Rest, DAO, Client} from './API';
import {API, Model, Rest, DAO, Client} from './API';

import {log, catRoot, setLogLevel} from './api/Log';
import {
Expand Down Expand Up @@ -145,12 +145,18 @@ function CLI() {
return alarms.map((alarm) => {
const severityLabel = ((alarm.severity && alarm.severity.label) ? alarm.severity.label : '');

let logMessage = '';
if (alarm.logMessage) {
logMessage = alarm.logMessage.trim();
if (logMessage.length > logMessageLength) {
logMessage = logMessage.slice(0, logMessageLength) + '…';
}
}

return {
count: alarm.count,
id: alarm.id,
log: (alarm.logMessage && alarm.logMessage.length > logMessageLength)
? alarm.logMessage.slice(0, logMessageLength) + '…'
: alarm.logMessage,
log: logMessage,
node: alarm.nodeLabel || '',
severity: colorify(severityLabel),
time: (alarm.lastEventTime ? alarm.lastEventTime.format('YYYY-MM-DD HH:ss') : ''),
Expand All @@ -160,15 +166,52 @@ function CLI() {

// list current alarms
program
.command('alarms')
.command('alarms [filters...]')
.description('List current alarms')
.action(() => {
.action((filters) => {
const config = readConfig();
const auth = new API.OnmsAuthConfig(config.username, config.password);
const server = new API.OnmsServer('OpenNMS', config.url, auth);
const http = new Rest.AxiosHTTP(server);
const dao = new DAO.AlarmDAO(http);
return dao.find().then((alarms) => {

const namePattern = /^(.*?)\s+(eq|ne|ilike|like|gt|lt|ge|le|null|notnull)\s+(.*?)$/i;
const symbolPattern = /^(.*?)\s*(=|==|!=|>|<|>=|<=)\s*(.*?)$/i;
const filter = new API.Filter<any>();

for (const f of filters) {
let match = f.match(namePattern);
let attribute;
let comparator;
let value;
if (match) {
attribute = match[1];
comparator = match[2];
value = match[3];
} else {
match = f.match(symbolPattern);
if (match) {
attribute = match[1];
comparator = match[2];
value = match[3];
} else {
log.warn('Unable to parse filter "' + f + '"', catCLI);
}
}

if (attribute && comparator) {
for (const type in API.COMPARATORS) {
if (API.COMPARATORS.hasOwnProperty(type)) {
const comp = API.COMPARATORS[type];
if (comp.matches(comparator)) {
filter.restrictions.push(new API.Restriction(attribute, comp, value));
}
}
}
}
}

return dao.find(filter).then((alarms) => {
const headers = ['id', 'severity', 'node', 'count', 'time', 'log'];
console.log(cliff.stringifyObjectRows(formatAlarms(alarms), headers, ['red']));
}).catch((err) => {
Expand Down
11 changes: 5 additions & 6 deletions src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class Client {
* @param httpImpl - the {@link IOnmsHTTP} implementation to use
* @param timeout - how long to wait before giving up when making ReST calls
*/
public static checkServer(server: OnmsServer, httpImpl?: IOnmsHTTP, timeout?: number): Promise<boolean> {
public static async checkServer(server: OnmsServer, httpImpl?: IOnmsHTTP, timeout?: number): Promise<boolean> {
const opts = new OnmsHTTPOptions(timeout, server.auth);
if (!httpImpl) {
if (!Client.http) {
Expand All @@ -59,7 +59,7 @@ export class Client {
* @param httpImpl - the {@link IOnmsHTTP} implementation to use
* @param timeout - how long to wait before giving up when making ReST calls
*/
public static getMetadata(server: OnmsServer, httpImpl?: IOnmsHTTP, timeout?: number):
public static async getMetadata(server: OnmsServer, httpImpl?: IOnmsHTTP, timeout?: number):
Promise<OnmsResult<ServerMetadata>> {
const opts = new OnmsHTTPOptions(timeout, server.auth);
opts.accept = 'application/json';
Expand Down Expand Up @@ -110,11 +110,10 @@ export class Client {
* Connect to an OpenNMS server, check what capabilities it has, and return an {@link OnmsServer}
* for that connection.
*/
public connect(name: string, url: string, username: string, password: string, timeout?: number) {
public async connect(name: string, url: string, username: string, password: string, timeout?: number) {
const server = new OnmsServer(name, url, username, password);
return Client.checkServer(server, undefined, timeout).then(() => {
return Client.getMetadata(server, undefined, timeout);
}).then((result) => {
await Client.checkServer(server, undefined, timeout);
return Client.getMetadata(server, undefined, timeout).then((result) => {
server.metadata = result.data;
return server;
});
Expand Down
47 changes: 47 additions & 0 deletions src/api/Comparator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {OnmsEnum} from '../internal/OnmsEnum';

/**
* Represents a filter comparator.
* @module Comparator
*/ /** */
export class Comparator extends OnmsEnum {
/** aliases for the command-line */
private aliases = [] as string[];

constructor(id: number, label: string, ...aliases: string[]) {
super(id, label);
this.aliases = aliases;
}

/** whether this comparator matches the given comparator string */
public matches(comparator: string) {
return (comparator.toLowerCase() === this.label.toLowerCase())
|| this.aliases.indexOf(comparator) >= 0;
}
}

/* tslint:disable:object-literal-sort-keys */

/** @hidden */
export const COMPARATORS = Object.freeze({
EQ: new Comparator(1, 'EQ', '=', '=='),
NE: new Comparator(2, 'NE', '!='),
ILIKE: new Comparator(3, 'ILIKE'),
LIKE: new Comparator(4, 'LIKE'),
GT: new Comparator(5, 'GT', '>'),
LT: new Comparator(6, 'LT', '<'),
GE: new Comparator(7, 'GE', '>='),
LE: new Comparator(8, 'LE', '<='),
NULL: new Comparator(9, 'NULL'),
NOTNULL: new Comparator(10, 'NOTNULL'),

/*
ALL: new Comparator(9, 'ALL'),
ANY: new Comparator(10, 'ANY'),
BETWEEN: new Comparator(15, 'BETWEEN'),
NOT: new Comparator(14, 'NOT'),
IN: new Comparator(13, 'IN'),
IPLIKE: new Comparator(17, 'IPLIKE'),
SQL: new Comparator(16, 'SQL'),
*/
});
15 changes: 15 additions & 0 deletions src/api/Filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {OnmsHTTPOptions} from '../api/OnmsHTTPOptions';
import {Restriction} from './Restriction';

/**
* A query filter for DAOs.
* @module Filter
* @param T the model type (OnmsAlarm, OnmsEvent, etc.)
*/ /** */
export class Filter<T> {
/** how many results to get back by default */
public limit = 1000;

/** the query restrictions to use when making requests */
public restrictions = [] as Restriction[];
}
12 changes: 12 additions & 0 deletions src/api/IFilterProcessor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {Filter} from './Filter';
import {IHash} from '../internal/IHash';

/**
* Interface that represents a filter processor
* @module IFilterProcessor
* @interface
*/ /** */
export interface IFilterProcessor {
/** given a filter, return a hash of URL parameters */
getParameters(filter: Filter<any>): IHash<string>;
}
7 changes: 6 additions & 1 deletion src/api/IOnmsHTTP.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/** @hidden */
declare const Promise;

import {IFilterProcessor} from './IFilterProcessor';

import {OnmsHTTPOptions} from './OnmsHTTPOptions';
import {OnmsResult} from './OnmsResult';
import {OnmsServer} from '../api/OnmsServer';
import {OnmsServer} from './OnmsServer';

/**
* Interface for making ReST calls to an HTTP server.
Expand All @@ -29,6 +31,9 @@ export interface IOnmsHTTP {
/** the options used when making requests */
options: OnmsHTTPOptions;

/** the filter processor to use when making DAO requests */
filterProcessor: IFilterProcessor;

/**
* Perform an HTTP get to the provided URL.
* @param url the URL to connect to
Expand Down
4 changes: 4 additions & 0 deletions src/api/OnmsError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ export class OnmsError {
/** the error code associated with this error */
public code: number;

/** the stack trace when this error is created */
public stack;

/** the JS Error class associated with this error */
private error: Error;

Expand All @@ -17,6 +20,7 @@ export class OnmsError {
constructor(public message: string, code?: number) {
this.error = new Error(message);
this.code = code;
this.stack = this.error.stack;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/api/OnmsHTTPOptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {OnmsAuthConfig} from './OnmsAuthConfig';
import {IHash} from '../internal/IHash';

/**
* Options to be used when making HTTP ReST calls.
Expand All @@ -15,7 +16,7 @@ export class OnmsHTTPOptions {
public accept = 'application/xml';

/** http parameters to be passed on the URL */
public parameters = {} as any;
public parameters = {} as IHash<string>;

/**
* Construct a new OnmsHTTPOptions object.
Expand Down
27 changes: 27 additions & 0 deletions src/api/Restriction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {Comparator, COMPARATORS} from './Comparator';

/**
* A query restriction.
* @module Restriction
*/ /** */
export class Restriction {
/** the model attribute (name, id, etc.) to query */
public attribute: string;

/** the comparator to use when querying */
public comparator: Comparator;

/** the value to compare the attribute property to */
public value?: any;

constructor(attribute: string, comparator: Comparator, value?: any) {
this.attribute = attribute;
this.comparator = comparator;
this.value = value;
}

/** human-readable string for this restriction */
public toString() {
return this.attribute + ' ' + this.comparator.label + (this.value === undefined ? '' : ' ' + this.value);
}
}
7 changes: 7 additions & 0 deletions src/api/ServerMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,16 @@ export class ServerMetadata {
return this.version.ge('15.0.2');
}

/** what version of the ReST API does this server support */
public apiVersion() {
return 1;
}

/** a convenient data structure with all capabilities listed */
public capabilities() {
return {
ackAlarms: this.ackAlarms(),
apiVersion: this.apiVersion(),
graphs: this.graphs(),
outageSummaries: this.outageSummaries(),
setNodeLocation: this.setNodeLocation(),
Expand All @@ -65,6 +71,7 @@ export class ServerMetadata {
/** a human-readable representation of this version */
public toString() {
return 'ServerMetadata[version=' + this.version.toString()
+ ',apiVersion=' + this.apiVersion()
+ ',type=' + this.type.toString()
+ ',ackAlarms=' + this.ackAlarms()
+ ',graphs=' + this.graphs()
Expand Down
Loading

0 comments on commit fe2204c

Please sign in to comment.