This repository has been archived by the owner on Apr 7, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
arisajs.js
235 lines (182 loc) · 9.77 KB
/
arisajs.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
"use strict";
const Issue = require('./lib/issue.class.js'); // Issue class
const Project = require('./lib/project.class.js'); // Project class
const Check = require('./lib/module.class.js'); // Check class
let Processor = require('./lib/processor.class.js');
let moment = require('moment');
const log = require('./util/logger.js');
const JiraApi = require('jira-connector');
const async = require('async');
const fs = require('fs');
/** Import libraries **/
let project = require('./util/projects.js');
let modules = require('./util/modules.js');
let config = null;
let jira = null;
let projects = {};
let processor;
let mainTimer;
let issueTimer;
let parsedIssueDate;
// Initialize Configuration
function initializeConfig() {
return new Promise((resolve, reject) => {
fs.access('./config/arisa.json', fs.R_OK, (err) => {
if (err) reject(`Can't access config/arisa.json. (${err.message}`);
else {
config = require('./config/arisa.json');
// Check jira authentication configuration. Actual login is done at a later stage.
if (!config.jira || !config.jira.host) reject(`'jira' block missing or lacking host.`);
// Check auth
if (!config.jira.basic_auth && !config.jira.oauth) reject(`'jira' block lacking basic_auth or oauth info.`);
if (config.jira.basic_auth && (!config.jira.basic_auth.username || !config.jira.basic_auth.password)) reject(`'basic_auth' detected but lacking username and or password`);
if (config.jira.oauth && !config.jira.oauth.consumer_key) reject(`'oauth' block lacking consumer_key.`);
// If both auth options are present, prefer oauth and give the user a message.
if (config.jira.basic_auth && config.jira.oauth) {
console.log('Detected both basic_auth and oath. Using oauth.');
console.log('To use basic_auth, remove the oauth block in arisa.json.');
delete config.jira.basic_auth;
}
// Attempt to load the private key.
if(config.jira.oauth){
try{
config.jira.oauth.private_key = fs.readFileSync('./config/keys/jira.pem', 'utf-8');
}catch(error){
console.error('FATAL: Private key missing. Location: config/jira.pem');
process.abort('');
}
}
// Check core-specific settings
if (!config.core) reject(`'core' block missing`);
if (!config.core.update_interval_sec || typeof config.core.update_interval_sec != 'number') reject(`'update_interval_sec' value missing or not a number`);
if (!config.core.check_interval_sec || typeof config.core.check_interval_sec != 'number') reject(`'check_interval_sec' value missing or not a number`);
if (!config.core.startup_offset_sec || typeof config.core.startup_offset_sec != 'number') reject(`'startup_offset_sec' value missing or not a number`);
if (!config.core.project_whitelist || !Array.isArray(config.core.project_whitelist)) reject(`'project_whitelist' value missing or not an array`);
if (config.core.project_whitelist.length == 0) reject(`'project_whitelist' array is empty. Add at least one project`);
// Check processor-specific settings
if (!config.processor) reject(`'processor' block missing`);
if (!config.processor.bot_signature) reject(`'bot_signature' value missing`);
if (config.jira.oauth.token == '') {
JiraApi.oauth_util.getAuthorizeURL(config.jira, (error, oauth) => {
if(error) reject(error);
else{
console.log('== STEP 1 - Your oauth procedure starts here ==');
console.log('You will receive a URL, a token and a token secret.');
console.log('Go to the URL whilst authenticated to the BOT account and approve the request');
console.log('Add the verifier token from this page to the arisa.json file');
console.log('Now add the token and token secret to the arisa.json file.');
console.log('When done, rerun this script.');
console.log('');
console.log('If this is too complex, please use basic_auth instead');
console.log(oauth);
reject('Follow instruction, then rerun');
}
});
reject('Follow the instructions to obtain your Jira authentication token.');
} else if (config.jira.oauth.oauth_verifier) {
JiraApi.oauth_util.swapRequestTokenWithAccessToken(config.jira, (error, token) => {
if(error) reject(error);
else{
console.log('== STEP 2 - Get Jira access token');
console.log('If done correctly, you will receive a new token below.');
console.log('* Remove the oauth_verifier token from the config file.');
console.log('* Replace the token line with the new token found below.');
console.log('You should be left with a consumer_key, token and token_secret');
console.log(token);
reject("Rerun the bot once more. You should be done");
}
});
} else {
// Set defaults
jira = new JiraApi(config.jira);
parsedIssueDate = moment().subtract(config.core.startup_offset_sec, 'seconds');
resolve();
}
}
})
})
}
// Initializing the script
function initialize() {
function handleProjects(data) {
return new Promise((resolve, reject) => {
if (data !== null || data !== undefined) return resolve(data);
else return reject('No projects were returned.')
})
}
project.initProjects({jira, config})
.then(handleProjects) // Initiate whitelisted projects
.then((data) => projects = data) // Store projects locally
.then((projects) => project.renewSecurity({projects, jira})) // Get project security levels if applicable
.then(() => processor = new Processor({projects, jira, config})) // Intiate the processor
.then(() => log.info('Processor created'))
.then(() => processor.loadModules()) // Load all modules
.then(() => log.info('Modules loaded'))
.then(mainLoop) // Start issue checking
.catch(msg => console.error('rejected: ' + msg)); // Catch any errors
}
/*Program loop, switches between issueloop and version checking*/
function mainLoop() {
clearTimeout(issueTimer);
project.renewVersions(projects, jira)
// .then(() => project.renewSecurityLevels(projects, jira))
.then(function () {
setTimeout(mainLoop, config.core.update_interval_sec * 1000);
issueLoop();
}).catch(error => console.error(error));
}
/*Issue loop. Continually check for issues*/
function issueLoop() {
queueIssues();
}
/*Fetch issues, populate a queue*/
function queueIssues() {
let projectstring = config.core.project_whitelist.join(',');
let opts = {
jql: `project in (${projectstring}) AND resolution in (Unresolved, "Awaiting Response") AND updated >= -5m`,
fields: ['resolution', 'description', 'labels', 'assignee', 'environment', 'attachment', 'versions', 'updated', 'created', 'resolutiondate', 'resolution', 'comment', 'reporter', 'customfield_10701', 'customfield_10500', 'security'],
expand: ['transitions'],
startAt: 0,
maxResults: 50
};
let tmpUpdateDate = parsedIssueDate;
jira.search.search(opts, function (err, data) {
if (!data) {
log.warn('Connection error, skipping.');
issueTimer = setTimeout(issueLoop, config.core.check_interval_sec * 1000);
} else {
if (data.total === 0) {
issueTimer = setTimeout(issueLoop, config.core.check_interval_sec * 1000);
} else {
log.trace(`Found ${data.total} issue(s)`);
async.each(data.issues, function (issue, callback) {
let issueUpdateDate = (new Date(issue.fields.updated));
if (issueUpdateDate > parsedIssueDate) {
processor.addItem(new Issue({
key: issue.key,
id: issue.id,
fields: issue.fields,
transitions: issue.transitions,
project: projects[issue.key.replace(/[^A-Z].*/, '')],
jira
}));
if (issueUpdateDate > tmpUpdateDate) {
tmpUpdateDate = issueUpdateDate;
}
} else log.debug('Already parsed ' + issue.key);
callback()
}, function (error) {
if (error) {
log.error('Error: ' + error)
}
else parsedIssueDate = tmpUpdateDate;
});
issueTimer = setTimeout(issueLoop, config.core.check_interval_sec * 1000);
}
}
});
}
/* Module related stuff */
initializeConfig()
.then(initialize)
.catch((error) => log.error(`Error initializing configuration: ${error}`));