Skip to content

Commit

Permalink
feat(dao): implement /api/v2/ipinterfaces (HELM-188)
Browse files Browse the repository at this point in the history
  • Loading branch information
Benjamin Reed committed Aug 17, 2021
1 parent b879f5e commit 394afce
Show file tree
Hide file tree
Showing 17 changed files with 1,185 additions and 127 deletions.
2 changes: 2 additions & 0 deletions src/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {TicketerConfig} from './api/TicketerConfig';
import {AlarmDAO} from './dao/AlarmDAO';
import {EventDAO} from './dao/EventDAO';
import {FlowDAO} from './dao/FlowDAO';
import {IpInterfaceDAO} from './dao/IpInterfaceDAO';
import {NodeDAO} from './dao/NodeDAO';
import {SituationFeedbackDAO} from './dao/SituationFeedbackDAO';
import {V1FilterProcessor} from './dao/V1FilterProcessor';
Expand Down Expand Up @@ -96,6 +97,7 @@ const DAO = Object.freeze({
AlarmDAO,
EventDAO,
FlowDAO,
IpInterfaceDAO,
NodeDAO,
SituationFeedbackDAO,
V1FilterProcessor,
Expand Down
8 changes: 7 additions & 1 deletion src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {AlarmDAO} from './dao/AlarmDAO';
import {EventDAO} from './dao/EventDAO';
import {FlowDAO} from './dao/FlowDAO';
import {NodeDAO} from './dao/NodeDAO';
import {IpInterfaceDAO} from './dao/IpInterfaceDAO';
import {SituationFeedbackDAO} from './dao/SituationFeedbackDAO';

import {AxiosHTTP} from './rest/AxiosHTTP';
Expand Down Expand Up @@ -158,6 +159,11 @@ export class Client implements IHasHTTP {
return this.getDao('nodes', NodeDAO) as NodeDAO;
}

/** Get an IP interface DAO for querying interfaces. */
public ipInterfaces() {
return this.getDao('ipInterfaces', IpInterfaceDAO) as IpInterfaceDAO;
}

/** Get a flow DAO for querying flows. */
public flows() {
return this.getDao('flows', FlowDAO) as FlowDAO;
Expand All @@ -176,7 +182,7 @@ export class Client implements IHasHTTP {
*/
private getDao(
key: string,
daoClass: typeof AlarmDAO | typeof EventDAO | typeof NodeDAO | typeof FlowDAO | typeof SituationFeedbackDAO,
daoClass: typeof AlarmDAO | typeof EventDAO | typeof NodeDAO | typeof IpInterfaceDAO | typeof FlowDAO | typeof SituationFeedbackDAO,
) {
const existing = this.daos.get(key);
if (existing) {
Expand Down
25 changes: 20 additions & 5 deletions src/api/ServerMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,37 +119,52 @@ export class ServerMetadata {
}
}

/** Does this version support the api/v2/ipinterfaces ReST endpoint? */
public ipInterfaceRest() {
if (this.type === ServerTypes.MERIDIAN) {
return this.version.ge('2022.0.0');
} else {
return this.version.ge('29.0.0');
}
}

/** Returns a convenient data structure with all capabilities listed. */
public capabilities(): {[key: string]: any} {
return {
ackAlarms: this.ackAlarms(),
version: this.version.toString(),
apiVersion: this.apiVersion(),
type: (this.type === ServerTypes.MERIDIAN ? 'Meridian' : 'Horizon'),

ackAlarms: this.ackAlarms(),
enhancedFlows: this.enhancedFlows(),
flows: this.flows(),
tos: this.tos(),
graphs: this.graphs(),
ipInterfaceRest: this.ipInterfaceRest(),
outageSummaries: this.outageSummaries(),
setNodeLocation: this.setNodeLocation(),
situations: this.situations(),
ticketer: this.ticketer(),
type: (this.type === ServerTypes.MERIDIAN ? 'Meridian' : 'Horizon'),
tos: this.tos(),
};
}

/** A human-readable representation of the metadata. */
public toString() {
return 'ServerMetadata[version=' + this.version.toString()
return 'ServerMetadata['
+ 'version=' + this.version.toString()
+ ',apiVersion=' + this.apiVersion()
+ ',type=' + this.type.toString()

+ ',ackAlarms=' + this.ackAlarms()
+ ',enhancedFlows=' + this.enhancedFlows()
+ ',flows=' + this.flows()
+ ',tos=' + this.tos()
+ ',graphs=' + this.graphs()
+ ',ipInterfaceRest=' + this.ipInterfaceRest()
+ ',outageSummaries=' + this.outageSummaries()
+ ',setNodeLocation=' + this.setNodeLocation()
+ ',situations=' + this.situations()
+ ',ticketer=' + this.ticketer()
+ ',tos=' + this.tos()
+ ']';
}

Expand Down
4 changes: 2 additions & 2 deletions src/dao/AbstractDAO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ export abstract class AbstractDAO<K, T> extends BaseDAO implements IValueProvide
* Retrieve a model object.
* @param id - the ID of the object
*/
public abstract async get(id: K): Promise<T>;
public abstract get(id: K): Promise<T>;

/**
* Find all model objects given an optional filter.
* @param filter - the filter to use when retrieving a list of model objects
*/
public abstract async find(filter?: Filter): Promise<T[]>;
public abstract find(filter?: Filter): Promise<T[]>;

/**
* Get the list properties that can be used in queries.
Expand Down
11 changes: 5 additions & 6 deletions src/dao/BaseDAO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const moment = require('moment');
/** @hidden */
// tslint:disable-next-line
import {Moment} from 'moment';
import { Util } from '../internal/Util';

/**
* A base DAO useful for subclassing to create real DAOs. This differs from
Expand Down Expand Up @@ -155,19 +156,17 @@ export abstract class BaseDAO {

/**
* Convert the given value to a date, or undefined if it cannot be converted.
* @deprecated use {@link Util.toDate} instead.
*/
protected toDate(from: any): Moment|undefined {
if (from === undefined || from === null || from === '') {
return undefined;
}
return moment(from);
return Util.toDate(from);
}

/**
* Convert the given value to a number, or undefined if it cannot be converted.
* @deprecated use {@link Util.toNumber} instead.
*/
protected toNumber(from: any): number|undefined {
const ret = parseInt(from, 10);
return isNaN(ret) ? undefined : ret;
return Util.toNumber(from);
}
}
90 changes: 90 additions & 0 deletions src/dao/IpInterfaceDAO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import {AbstractDAO} from './AbstractDAO';

import {Filter} from '../api/Filter';
import {IHasHTTP} from '../api/IHasHTTP';
import {IOnmsHTTP} from '../api/IOnmsHTTP';
import {OnmsError} from '../api/OnmsError';

import { OnmsIpInterface } from '../model/OnmsIpInterface';

/**
* Data access for [[OnmsIpInterface]] objects.
* @category DAO
*/
export class IpInterfaceDAO extends AbstractDAO<number, OnmsIpInterface> {
constructor(impl: IHasHTTP | IOnmsHTTP) {
super(impl);
}

/**
* Get an IP interface, given the interface's ID.
*
* @param id - The interface's ID.
*/
public async get(id: number): Promise<OnmsIpInterface> {
this.assertV2();
return this.getOptions().then((builder) => {
return this.http.get(this.getRoot() + '/' + id, builder.build()).then((result) => {
const node = OnmsIpInterface.fromData(result.data);

if (!node) {
throw new OnmsError(`IpInterfaceDAO.get id={id} ReST request succeeded, but did not return a valid node.`);
}

return node;
});
});
}

/** Search for IP interfaces, given an optional filter. */
public async find(filter?: Filter): Promise<OnmsIpInterface[]> {
this.assertV2();
return this.getOptions(filter).then((builder) => {
return this.http.get(this.getRoot(), builder.build()).then((result) => {
let data = result.data;

if (data !== null && this.getCount(data, result.code) > 0 && data.ipInterface) {
data = data.ipInterface;
} else {
data = [];
}

if (!Array.isArray(data)) {
if (data.id) {
data = [data];
} else {
throw new OnmsError('Expected an array of IP interfaces but got "' + (typeof data) + '" instead.');
}
}
return data.map((ifaceData: any) => {
return OnmsIpInterface.fromData(ifaceData);
});
});
});
}

/**
* The path to the node search properties endpoint.
*/
protected searchPropertyPath(): string {
return this.getRoot() + '/properties';
}

/**
* The root of the IpInterfaces ReST API.
* @hidden
*/
private getRoot() {
return 'api/v2/ipinterfaces';
}

/**
* Make sure v2 is supported.
* @hidden
*/
private assertV2() {
if (this.getApiVersion() < 2) {
throw new OnmsError('The IP interface ReST API is only available on v2.');
}
}
}
111 changes: 4 additions & 107 deletions src/dao/NodeDAO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,10 @@ import {IHasHTTP} from '../api/IHasHTTP';
import {IOnmsHTTP} from '../api/IOnmsHTTP';
import {OnmsError} from '../api/OnmsError';

import {Util} from '../internal/Util';

import {OnmsCategory} from '../model/OnmsCategory';
import {OnmsCollectType} from '../model/OnmsCollectType';
import {OnmsIpInterface} from '../model/OnmsIpInterface';
import {OnmsManagedType} from '../model/OnmsManagedType';
import {OnmsMonitoredService} from '../model/OnmsMonitoredService';
import {OnmsNode} from '../model/OnmsNode';
import {OnmsNodeLabelSource} from '../model/OnmsNodeLabelSource';
import {OnmsNodeType} from '../model/OnmsNodeType';
import {OnmsPrimaryType} from '../model/OnmsPrimaryType';
import {OnmsServiceType} from '../model/OnmsServiceType';
import {OnmsServiceStatusType} from '../model/OnmsServiceStatusType';
import {OnmsSnmpInterface} from '../model/OnmsSnmpInterface';
import {OnmsSnmpStatusType} from '../model/OnmsSnmpStatusType';
import {PhysAddr} from '../model/PhysAddr';

/**
* Data access for [[OnmsNode]] objects.
Expand Down Expand Up @@ -215,122 +203,31 @@ export class NodeDAO extends AbstractDAO<number, OnmsNode> {
* @hidden
*/
public fromData(data: any) {
const node = new OnmsNode();

if (!data) {
return undefined;
}

node.id = this.toNumber(data.id);
node.label = data.label;
node.location = data.location;
node.foreignSource = data.foreignSource || undefined;
node.foreignId = data.foreignId || undefined;
node.sysContact = data.sysContact;
node.sysDescription = data.sysDescription;
node.sysLocation = data.sysLocation;
node.sysName = data.sysName;
node.sysObjectId = data.sysObjectId;

if (data.labelSource) {
node.labelSource = OnmsNodeLabelSource.forId(data.labelSource);
}
if (data.createTime) {
node.createTime = this.toDate(data.createTime);
}
if (data.lastCapsdPoll) {
node.lastCapsdPoll = this.toDate(data.lastCapsdPoll);
}
if (data.type) {
node.type = OnmsNodeType.forId(data.type);
}

node.categories = [];
if (data.categories) {
node.categories = data.categories.map((c: any) => {
return OnmsCategory.for(c.id, c.name);
});
}

for (const key in data.assetRecord) {
if (data.assetRecord.hasOwnProperty(key)
&& data.assetRecord[key] !== null
&& data.assetRecord[key] !== undefined) {
node.assets[key] = data.assetRecord[key];
}
}

return node;
return OnmsNode.fromData(data);
}

/**
* create an IP interface object from a JSON object
* @hidden
*/
public fromIpInterfaceData(data: any): OnmsIpInterface {
const iface = new OnmsIpInterface();

iface.id = this.toNumber(data.id);
iface.hostname = data.hostName || data.hostname;
iface.ipAddress = Util.toIPAddress(data.ipAddress);
iface.isManaged = OnmsManagedType.forId(data.isManaged);
iface.lastCapsdPoll = this.toDate(data.lastCapsdPoll);
iface.snmpPrimary = OnmsPrimaryType.forId(data.snmpPrimary);

if (data.snmpInterface && data.snmpInterface.id) {
iface.snmpInterfaceId = this.toNumber(data.snmpInterface.id);
}

return iface;
return OnmsIpInterface.fromData(data);
}

/**
* create an SNMP interface object from a JSON object
* @hidden
*/
public fromSnmpData(data: any): OnmsSnmpInterface {
const iface = new OnmsSnmpInterface();

iface.id = this.toNumber(data.id);
iface.ifIndex = this.toNumber(data.ifIndex);
iface.ifDescr = data.ifDescr;
iface.ifType = this.toNumber(data.ifType);
iface.ifName = data.ifName;
iface.ifSpeed = this.toNumber(data.ifSpeed);
iface.ifAdminStatus = OnmsSnmpStatusType.forId(this.toNumber(data.ifAdminStatus));
iface.ifOperStatus = OnmsSnmpStatusType.forId(this.toNumber(data.ifOperStatus));
iface.ifAlias = data.ifAlias;
iface.lastCapsdPoll = this.toDate(data.lastCapsdPoll);
iface.collect = OnmsCollectType.forId(data.collectFlag);
iface.poll = data.poll;
iface.lastSnmpPoll = this.toDate(data.lastSnmpPoll);

if (data.physAddr) {
iface.physAddr = new PhysAddr(data.physAddr);
}

return iface;
return OnmsSnmpInterface.fromData(data);
}

/**
* create a monitored service object from a JSON object
* @hidden
*/
public fromServiceData(data: any): OnmsMonitoredService {
const service = new OnmsMonitoredService();

service.id = this.toNumber(data.id);
service.lastFail = this.toDate(data.lastFail);
service.lastGood = this.toDate(data.lastGood);

if (data.serviceType) {
service.type = OnmsServiceType.for(data.serviceType.id, data.serviceType.name);
}
if (data.status) {
service.status = OnmsServiceStatusType.forId(data.status);
}

return service;
return OnmsMonitoredService.fromData(data);
}

/**
Expand Down
Loading

0 comments on commit 394afce

Please sign in to comment.