-
Notifications
You must be signed in to change notification settings - Fork 8
/
AbstractDAO.ts
220 lines (198 loc) · 7.49 KB
/
AbstractDAO.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
import {IFilterProcessor} from '../api/IFilterProcessor';
import {IHasHTTP} from '../api/IHasHTTP';
import {IOnmsHTTP} from '../api/IOnmsHTTP';
import {OnmsError} from '../api/OnmsError';
import {Filter} from '../api/Filter';
import {OnmsHTTPOptions} from '../api/OnmsHTTPOptions';
import {SearchProperty} from '../api/SearchProperty';
import {SearchPropertyType} from '../api/SearchPropertyType';
import {log, catDao} from '../api/Log';
import {V1FilterProcessor} from './V1FilterProcessor';
import {V2FilterProcessor} from './V2FilterProcessor';
import {PropertiesCache} from './PropertiesCache';
import {BaseDAO} from './BaseDAO';
/** @hidden */
// tslint:disable-next-line
const moment = require('moment');
/** @hidden */
// tslint:disable-next-line
import {Moment} from 'moment';
import {IValueProvider} from './IValueProvider';
/**
* An abstract data access layer API, meant to (somewhat) mirror the DAO interfaces
* inside OpenNMS. Used to retrieve model data like alarms, events, etc. from the
* OpenNMS ReST API in a consistent way.
*
* @module AbstractDAO
* @param K the ID/key type (number, string, etc.)
* @param T the model type (OnmsAlarm, OnmsEvent, etc.)
*/
export abstract class AbstractDAO<K, T> extends BaseDAO implements IValueProvider {
/**
* Returns the Promise for a [[IFilterProcessor]].
* @returns {Promise}
*/
public async getFilterProcessor(): Promise<IFilterProcessor> {
switch (this.getApiVersion()) {
case 2:
return this.getPropertiesCache().then((cache) => {
return new V2FilterProcessor(cache);
});
default:
return Promise.resolve(new V1FilterProcessor());
}
}
/**
* Retrieve a model object.
* @param id - the ID of the object
*/
public abstract async 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[]>;
/**
* Get the list properties that can be used in queries.
* @version ReST v2
*/
public async searchProperties(): Promise<SearchProperty[]> {
return this.getPropertiesCache().then((cache) => {
return cache.getProperties();
});
}
/**
* Gets the property identified by the id if it exists.
*
* @param id The id to search the property by.
*/
public async searchProperty(id: string): Promise<SearchProperty> {
return this.getPropertiesCache().then((cache) => {
return cache.getProperty(id);
});
}
/**
* Returns or creates the [[PropertiesCache]] for this dao.
*
* @return the [[PropertiesCache]] for this dao. It is created if it does not exist.
*/
public async getPropertiesCache(): Promise<PropertiesCache> {
if (this.getApiVersion() === 1) {
throw new OnmsError('Search property metadata is only available in OpenNMS ' +
'versions that support the ReSTv2 API.');
}
// Cache not yet initialized
if (!PropertiesCache.get(this)) {
return this.getOptions().then((opts) => {
opts.headers.accept = 'application/json';
return this.http.get(this.searchPropertyPath(), opts).then((result) => {
const searchProperties = this.parseResultList(result, 'searchProperty',
this.searchPropertyPath(), (prop) => {
return this.toSearchProperty(prop);
});
PropertiesCache.put(this, searchProperties);
return Promise.resolve(PropertiesCache.get(this));
});
});
}
// Cache already initialized, use value
return Promise.resolve(PropertiesCache.get(this));
}
/**
* Finds the values for the given propertyId, if it exists.
*
* @param {string} propertyId The propertyId to find the values for
* @param options Some additional options. May be implementer dependent, such as limit, or value restrictions
* @returns {Promise<any>} A promise containing the values.
*/
public async findValues(propertyId: string, options?: any): Promise<any> {
return this.searchProperty(propertyId).then((property) => {
return this.getOptions().then((opts) => {
const path = this.searchPropertyPath() + '/' + property.id;
opts.headers.accept = 'application/json';
if (options) {
Object.assign(opts, options);
}
return this.http.get(path, opts).then((result) => {
return this.parseResultList(result, 'value', path, (value) => value);
});
});
});
}
/**
* The path to retrieve search properties for this DAO.
*/
protected abstract searchPropertyPath(): string;
/**
* Fetches the data from the result and verfifes that the <code>dataFieldName</code> exists in the data property.
* If it does not exist, an exception is thrown.
*
* @param result The result to fetch the data from
* @param dataFieldName The property name (basically <code>result.data[dataFieldName]</code>.
* @param path The path where the result was fetched from. This is for error handling
* @param mapCallbackFunction Callback function to convert each entry from <code>result.data[dataFieldName]</code>.
*/
protected parseResultList(result: any, dataFieldName: string, path: string, mapCallbackFunction: any): any {
let data = result.data;
if (this.getCount(data) > 0 && data[dataFieldName]) {
data = data[dataFieldName];
} else {
data = [];
}
if (!Array.isArray(data)) {
throw new OnmsError('Expected an array but got "' + (typeof data) + '" instead: ' + path);
}
if (mapCallbackFunction) {
return data.map(mapCallbackFunction);
}
return data;
}
/**
* Create an [[OnmsHTTPOptions]] object for DAO calls given an optional filter.
* @param filter - the filter to use
*/
protected async getOptions(filter?: Filter): Promise<OnmsHTTPOptions> {
return Promise.resolve(new OnmsHTTPOptions())
.then((options) => {
if (this.useJson()) {
options.headers.accept = 'application/json';
} else {
// always use application/xml in DAO calls when we're not sure how
// usable JSON output will be.
options.headers.accept = 'application/xml';
}
if (filter) {
return this.getFilterProcessor().then((processor) => {
options.parameters = processor.getParameters(filter);
return options;
});
}
return options;
});
}
/**
* Generate a [[SearchProperty]] from the given dictionary.
* @hidden
*/
protected toSearchProperty(data: any): SearchProperty {
if (!data) {
return null;
}
const prop = new SearchProperty(this);
prop.id = data.id;
prop.name = data.name;
prop.orderBy = !!data.orderBy;
prop.type = SearchPropertyType.forId(data.type);
prop.values = data.values;
return prop;
}
/**
* Retrieve the API version from the currently configured server.
*/
protected getApiVersion(): number {
if (this.http === undefined || this.http.server === undefined || this.http.server.metadata === undefined) {
throw new OnmsError('Server meta-data must be populated prior to making DAO calls.');
}
return this.http.server.metadata.apiVersion();
}
}