-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.js
258 lines (240 loc) · 8.89 KB
/
main.js
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
// Import built-in Node.js package path.
const path = require('path');
/**
* Import the ServiceNowConnector class from local Node.js module connector.js
* and assign it to constant ServiceNowConnector.
* When importing local modules, IAP requires an absolute file reference.
* Built-in module path's join method constructs the absolute filename.
*/
const ServiceNowConnector = require(path.join(__dirname, '/connector.js'));
/**
* Import built-in Node.js package events' EventEmitter class and
* assign it to constant EventEmitter. We will create a child class
* from this class.
*/
const EventEmitter = require('events').EventEmitter;
/**
* The ServiceNowAdapter class.
*
* @summary ServiceNow Change Request Adapter
* @description This class contains IAP adapter properties and methods that IAP
* brokers and products can execute. This class inherits the EventEmitter
* class.
*/
class ServiceNowAdapter extends EventEmitter {
/**
* Here we document the ServiceNowAdapter class' callback. It must follow IAP's
* data-first convention.
* @callback ServiceNowAdapter~requestCallback
* @param {(object|string)} responseData - The entire REST API response.
* @param {error} [errorMessage] - An error thrown by REST API call.
*/
/**
* Here we document the adapter properties.
* @typedef {object} ServiceNowAdapter~adapterProperties - Adapter
* instance's properties object.
* @property {string} url - ServiceNow instance URL.
* @property {object} auth - ServiceNow instance credentials.
* @property {string} auth.username - Login username.
* @property {string} auth.password - Login password.
* @property {string} serviceNowTable - The change request table name.
*/
/**
* @memberof ServiceNowAdapter
* @constructs
*
* @description Instantiates a new instance of the Itential ServiceNow Adapter.
* @param {string} id - Adapter instance's ID.
* @param {ServiceNowAdapter~adapterProperties} adapterProperties - Adapter instance's properties object.
*/
constructor(id, adapterProperties) {
// Call super or parent class' constructor.
super();
// Copy arguments' values to object properties.
this.id = id;
this.props = adapterProperties;
// Instantiate an object from the connector.js module and assign it to an object property.
this.connector = new ServiceNowConnector({
url: this.props.url,
username: this.props.auth.username,
password: this.props.auth.password,
serviceNowTable: this.props.serviceNowTable
});
}
/**
* @memberof ServiceNowAdapter
* @method connect
* @summary Connect to ServiceNow
* @description Complete a single healthcheck and emit ONLINE or OFFLINE.
* IAP calls this method after instantiating an object from the class.
* There is no need for parameters because all connection details
* were passed to the object's constructor and assigned to object property this.props.
*/
connect() {
// As a best practice, Itential recommends isolating the health check action
// in its own method.
this.healthcheck();
}
/**
* @memberof ServiceNowAdapter
* @method healthcheck
* @summary Check ServiceNow Health
* @description Verifies external system is available and healthy.
* Calls method emitOnline if external system is available.
*
* @param {ServiceNowAdapter~requestCallback} [callback] - The optional callback
* that handles the response.
*/
healthcheck(callback) {
let callbackData = null;
let callbackError = null;
this.getRecord((result, error) => {
/**
* For this lab, complete the if else conditional
* statements that check if an error exists
* or the instance was hibernating. You must write
* the blocks for each branch.
*/
if (error) {
/**
* Write this block.
* If an error was returned, we need to emit OFFLINE.
* Log the returned error using IAP's global log object
* at an error severity. In the log message, record
* this.id so an administrator will know which ServiceNow
* adapter instance wrote the log message in case more
* than one instance is configured.
* If an optional IAP callback function was passed to
* healthcheck(), execute it passing the error seen as an argument
* for the callback's errorMessage parameter.
*/
this.emitOffline();
log.error('{this.id} : offline');
callbackData = error;
} else {
/**
* Write this block.
* If no runtime problems were detected, emit ONLINE.
* Log an appropriate message using IAP's global log object
* at a debug severity.
* If an optional IAP callback function was passed to
* healthcheck(), execute it passing this function's result
* parameter as an argument for the callback function's
* responseData parameter.
*/
this.emitOnline();
log.debug('{this.id} : online');
callbackData = result;
}
});
}
/**
* @memberof ServiceNowAdapter
* @method emitOffline
* @summary Emit OFFLINE
* @description Emits an OFFLINE event to IAP indicating the external
* system is not available.
*/
emitOffline() {
this.emitStatus('OFFLINE');
log.warn('ServiceNow: Instance is unavailable.');
}
/**
* @memberof ServiceNowAdapter
* @method emitOnline
* @summary Emit ONLINE
* @description Emits an ONLINE event to IAP indicating external
* system is available.
*/
emitOnline() {
this.emitStatus('ONLINE');
log.info('ServiceNow: Instance is available.');
}
/**
* @memberof ServiceNowAdapter
* @method emitStatus
* @summary Emit an Event
* @description Calls inherited emit method. IAP requires the event
* and an object identifying the adapter instance.
*
* @param {string} status - The event to emit.
*/
emitStatus(status) {
this.emit(status, { id: this.id });
}
getRecord(callback) {
/**
* Write the body for this function.
* The function is a wrapper for this.connector's get() method.
* Note how the object was instantiated in the constructor().
* get() takes a callback function.
*/
this.connector.get((data, error) => {
if (error) {
console.error(`\nGET error:\n${JSON.stringify(error)}`);
callback([], error);
}
if (data) {
if (data.body) {
let result = JSON.parse(data.body);
let tickets = [];
console.debug(`\nGET request:\n${JSON.stringify(data.body)}`);
result.result.forEach((change) => {
let newChange = {
change_ticket_number: change.number,
change_ticket_key: change.sys_id,
active: change.active,
priority: change.priority,
description: change.description,
work_start: change.work_start,
work_end: change.work_end
};
tickets.push(newChange);
})
callback(tickets);
}
}
})
}
/**
* @memberof ServiceNowAdapter
* @method postRecord
* @summary Create ServiceNow Record
* @description Creates a record in ServiceNow.
*
* @param {ServiceNowAdapter~requestCallback} callback - The callback that
* handles the response.
*/
postRecord(callback) {
/**
* Write the body for this function.
* The function is a wrapper for this.connector's post() method.
* Note how the object was instantiated in the constructor().
* post() takes a callback function.
*/
this.connector.post({}, (data, error) => {
if (error) {
console.error(`\nPOST request:\n${JSON.stringify(error)}`);
callback(data, error);
}
if (data) {
if (data.body) {
console.debug(`\nPOST request:\n${JSON.stringify(data.body
)}`);
const result = JSON.parse(data.body);
const ticket = result.result;
const newTicket = { change_ticket_number: ticket.number,
change_ticket_key: ticket.sys_id,
active: ticket.active,
priority: ticket.priority,
description: ticket.description,
work_start: ticket.work_start,
work_end: ticket.work_end
};
callback(newTicket, error);
}
}
})
}
}
module.exports = ServiceNowAdapter;