UNPKG

chatops-knight-js

Version:

A node.js module to interact with the Kyndryl ChatOps Knight API.

268 lines (253 loc) 8 kB
/* * The accompanying program is provided under the terms of the Eclipse Public License 2.0 ("agreement"). * Written by Rod Anami <rod.anami@kyndryl.com>, September 2021. */ const config = require('./Config.js'); // I personally prefer request, but it's deprecated now const axios = require('axios'); //const _ = require('underscore'); const https = require('https'); const fs = require('fs'); const assert = require('assert'); const jwt = require('jwt-simple'); const moment = require('moment'); // Internal function - create new https agent const getHttpsAgent = function(certificate, key, cacert, rejectUnauthorized) { if(!certificate && !key && cacert) { return new https.Agent({ // CA cert from Hashicorp Vault PKI ca: fs.readFileSync(cacert), rejectUnauthorized: rejectUnauthorized }); } else { return new https.Agent({ // Client certificate cert: fs.readFileSync(certificate), key: fs.readFileSync(key), // CA cert from Hashicorp Vault PKI ca: fs.readFileSync(cacert), rejectUnauthorized: rejectUnauthorized }); } } // Internal function - creates new axios instance const getAxiosInstance = function(baseurl, timeout, agent, rootPath, proxy) { return axios.create({ baseURL: baseurl, timeout: timeout, headers: { 'X-Application-Name': config.appName }, httpsAgent: agent, proxy: proxy }); } // Internal function - parse axios response const parseAxiosResponse = function(response){ let message = {}; if (response.data.auth) { message = response.data.auth; } else if (response.data.data){ message = response.data.data; } else if (response.data) { message = response.data; } else { message['status']= response.status; message['statusText'] = response.statusText; } return message; } // Internal function - parse axios error const parseAxiosError = function(error){ let helpMessage = ""; // Fix the stack // passing parseAxiosError as the second param will leave this function out of the trace Error.captureStackTrace(error, parseAxiosError); if (error.response && error.response.status && error.response.status != 404) { error.isKnightError = true; if (error.response.data.error) { error.knightHelpMessage = error.response.data.error; } } return error; } // Internal function - generate CK API Token out of the Source Token const generateAPIToken = function(signPayload, sourceId, sourceToken){ const TimeStamp = moment().format(); signPayload.timestamp = TimeStamp; // console.log(signPayload); // console.log(sourceToken); const Token = jwt.encode(signPayload, sourceToken); // console.log(Token); return Token; } // main class constructor class Knight { constructor(params) { this.https = params.https || false; this.cert = params.cert; this.key = params.key; this.cacert = params.cacert; this.baseUrl = params.baseUrl || config.baseUrl; this.timeout = params.timeout || config.timeout; this.proxy = params.proxy || config.proxy; this.rootPath = params.rootPath || config.rootPath; this.rejectUnauthorized = params.rejectUnauthorized; try { if (this.https) { this.agent = getHttpsAgent(this.cert, this.key, this.cacert, this.rejectUnauthorized); } else { this.agent = false; } this.instance = getAxiosInstance(this.baseUrl, this.timeout, this.agent, this.proxy); } catch (error) { console.error('Error initiating Knight class:\n', error); } } // /ping API endpoint /** * @returns {Promise<Object>} */ async healthCheck(){ const Options = { url: config.ckHealth[0], method: config.ckHealth[1] } try { const response = await this.instance(Options); return parseAxiosResponse(response); } catch(err) { throw parseAxiosError(err); } } // /api/v1/initiateTicket API endpoint /** * @param {String<required>} sourceId * @param {String<required>} sourceToken * @param {String} transactionId * @param {Object<required>} payload * @returns {Promise<Object>} */ async initiateTicket(sourceId, sourceToken, transactionId, payload){ const signPayload = { 'X-Chatops-Source-Id': sourceId, ticketId: payload.ticketId, callbackAddress: payload.callbackAddress }; const APIToken = generateAPIToken(signPayload, sourceId, sourceToken); const Options = { url: `${this.rootPath}/${config.ckInitiateTicket[0]}`, method: config.ckInitiateTicket[1], headers: { 'X-Chatops-Source-Id': sourceId, 'X-Chatops-Source-Api-Token': APIToken, 'X-Transaction-Id': transactionId, }, data: payload } try { const response = await this.instance(Options); return parseAxiosResponse(response); } catch(err) { throw parseAxiosError(err); } } // /api/v1/updateTicket API endpoint /** * @param {String<required>} sourceId * @param {String<required>} sourceToken * @param {String} transactionId * @param {Object<required>} payload * @returns {Promise<Object>} */ async updateTicket(sourceId, sourceToken, transactionId, payload){ const signPayload = { 'X-Chatops-Source-Id': sourceId, ticketId: payload.ticketId, callbackAddress: payload.callbackAddress }; const APIToken = generateAPIToken(signPayload, sourceId, sourceToken); const Options = { url: `${this.rootPath}/${config.ckUpdateTicket[0]}`, method: config.ckUpdateTicket[1], headers: { 'X-Chatops-Source-Id': sourceId, 'X-Chatops-Source-Api-Token': APIToken, 'X-Transaction-Id': transactionId, }, data: payload } try { const response = await this.instance(Options); return parseAxiosResponse(response); } catch(err) { throw parseAxiosError(err); } } // /api/v2/postMessage API endpoint /** * @param {String<required>} sourceId * @param {String<required>} sourceToken * @param {String<required>} transactionId * @param {Object<required>} payload * @returns {Promise<Object>} */ async postMessage(sourceId, sourceToken, transactionId, payload){ const signPayload = { 'X-Chatops-Source-Id': sourceId, 'X-Transaction-Id': transactionId, channelId: payload.channelId }; const APIToken = generateAPIToken(signPayload, sourceId, sourceToken); const Options = { url: `${this.rootPath}/${config.ckPostMessage[0]}`, method: config.ckPostMessage[1], headers: { 'X-Chatops-Source-Id': sourceId, 'X-Chatops-Source-Api-Token': APIToken, 'X-Transaction-Id': transactionId, }, data: payload } try { const response = await this.instance(Options); return parseAxiosResponse(response); } catch(err) { throw parseAxiosError(err); } } // /api/v1/sendDirectMessageToUser API endpoint /** * @param {String<required>} sourceId * @param {String<required>} sourceToken * @param {String} transactionId * @param {Object<required>} payload * @returns {Promise<Object>} */ async sendMessageToUser(sourceId, sourceToken, transactionId, payload){ const signPayload = { 'X-Chatops-Source-Id': sourceId, 'X-Transaction-Id': transactionId, emailId: payload.emailId, }; const APIToken = generateAPIToken(signPayload, sourceId, sourceToken); const Options = { url: `${this.rootPath}/${config.ckSendMessageToUser[0]}`, method: config.ckSendMessageToUser[1], headers: { 'X-Chatops-Source-Id': sourceId, 'X-Chatops-Source-Api-Token': APIToken, 'X-Transaction-Id': transactionId }, data: payload } try { const response = await this.instance(Options); return parseAxiosResponse(response); } catch(err) { throw parseAxiosError(err); } } } module.exports = Knight;