lib/jira.js   A
last analyzed

Complexity

Total Complexity 35
Complexity/F 1.75

Size

Lines of Code 153
Function Count 20

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
cc 0
wmc 35
c 4
b 1
f 0
nc 2
mnd 4
bc 30
fnc 20
dl 0
loc 153
rs 9
bpm 1.5
cpm 1.75
noi 3

1 Function

Rating   Name   Duplication   Size   Complexity  
A jira.js ➔ ??? 0 12 2
1
const requestify = require('requestify');
2
const config = require('./config');
3
const cookieAuthentication = require('./cookieAuthentication');
4
let sessionCookie = null;
5
6
/**
7
 * Used to build an url to get more results than 50 when required
8
 * @param  {String} oldUrl  the previous executed url
9
 * @param  {Number} startAt new start for the url to build
10
 * @return {String}         the new url to get next results
11
 */
12
let buildUrlToGetNextPage = (oldUrl, startAt) => {
13
  let url = '';
14
15
  if (oldUrl.search('startAt') === -1) {
16
    url = oldUrl + '&startAt=' + startAt;
17
  } else {
18
    let index = oldUrl.search('&startAt=');
19
    url = oldUrl.replace('&startAt=' + oldUrl.substr(index + 9, oldUrl.length - index + 9), '&startAt=' + startAt);
20
  }
21
22
  return url;
23
};
24
/**
25
 * Builds the header that will be added all requests to Jira
26
 * @param  {String} url the url that will be executed - this is mandatory for cookie based authentication and useless for basic authentication
0 ignored issues
show
Documentation introduced by
The parameter url does not exist. Did you maybe forget to remove this comment?
Loading history...
27
 * @return {Promise}     returns the header object and rejects as an {Error}
28
 */
29
let getHeader = (url) => new Promise((resolve, reject) => {
30
  let user = process.env.JIRA_USER;
31
  let password = process.env.JIRA_PASSWORD;
32
33
  if (module.exports.config.authentication === 'cookie') {
34
    if (sessionCookie) {
35
      resolve(cookieAuthentication.getHeader(sessionCookie));
36
    } else {
37
      cookieAuthentication.login(user, password, url)
38
        .then((cookie) => {
39
          sessionCookie = cookie;
40
          resolve(cookieAuthentication.getHeader(sessionCookie));
41
        })
42
        .catch((error) => {
43
          reject(error);
44
        });
45
    }
46
  } else {
47
    let token = 'Basic ' + new Buffer(user + ':' + password).toString('base64');
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
48
    resolve({
49
      'Authorization': token,
50
      'Cache-Control': 'public, max-age=60',
51
    });
52
  }
53
});
54
55
let execJiraQueryUntilRecursive = (url, retrieveAllPages, resolve, reject, maxRetry, count) => {
56
  console.log('try to execute request (' + url + '): ' + count + '/' + maxRetry);
57
58
  if (count === maxRetry) {
59
    console.error('too much retries, stopping here');
60
    reject({code: -1, url: url, message: 'Too much retries'});
61
  } else {
62
    getHeader(url)
63
      .then((header) => {
64
        let options = {
65
          cache: {
66
            cache: false,
67
            expires: 30000,
68
          },
69
          headers: header,
70
        };
71
72
        requestify.get(url, options)
73
          .then((response) => {
74
            console.log('Request OK, code = ' + response.code);
75
            let jsonBody;
76
            try {
77
              jsonBody = JSON.parse(response.body);
78
            } catch (error) {
79
              console.error('Error parsing response. Url = ' + url + ', Error = ' + error.message);
80
              return Promise.reject({code: -1, url: url, message: error.message});
81
            }
82
            return jsonBody;
83
          })
84
          .then((response) => {
85
            if (retrieveAllPages && response.startAt !== null && response.startAt + response.maxResults <= response.total) {
86
              let startAt = response.startAt + response.maxResults;
87
              let nextPageUrl = buildUrlToGetNextPage(url, startAt);
88
              execJiraQueryUntil(nextPageUrl, true, 5)  // eslint-disable-line no-use-before-define
89
                .then((nextPageResponse) => {
90
                  response.issues = response.issues.concat(nextPageResponse.issues);
91
                  response.maxResults = response.total;
92
                  response.startAt = 0;
93
                  resolve(response);
94
                })
95
                .catch((error) => reject(error));
96
            } else {
97
              resolve(response);
98
            }
99
          })
100
          .catch((error) => {
101
            if (error.code === 401 && module.exports.config.authentication === 'cookie') { // cookie expired, gets a new one
102
              console.log('Error executing request, cookie expired, but I\'ll log in again');
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
103
              sessionCookie = null;
104
              setTimeout(execJiraQueryUntilRecursive, module.exports.config.retryTimeout, url, retrieveAllPages, resolve, reject, maxRetry, count + 1);
105
            } else if ((error.code === 'ECONNRESET') || (error.code === 'ENOTCONN') || (error.body === 'timeout exceeded') || (Math.floor(error.code / 100) === 5)) {
106
              console.error('Error executing request, but I\'ll try again... Url = ' + url + ', Error = ' + JSON.stringify(error, null, '\t'));
107
              setTimeout(execJiraQueryUntilRecursive, module.exports.config.retryTimeout, url, retrieveAllPages, resolve, reject, maxRetry, count + 1);
108
            } else {
109
              reject({code: error.code, url: url, message: error.body ? error.body : error.message});
110
            }
111
          });
112
      })
113
      .catch((error) => {
114
        reject({code: -1, url: url, message: error.message});
115
      });
116
  }
117
};
118
119
let execJiraQueryUntil = (url, retrieveAllPages, maxRetry) => {
120
  let count = 1;
121
  return new Promise((resolve, reject) => {
122
    execJiraQueryUntilRecursive(url, retrieveAllPages, resolve, reject, maxRetry, count);
123
  });
124
};
125
126
module.exports = {
127
  /**
128
   * Configuration
129
   */
130
  config,
131
  /**
132
   * Return true if Jira credentials are not set as environment variables (JIRA_USER and JIRA_PASSWORD)
133
   * @return {boolean} true if credentials are missing
134
   */
135
  areJiraCredentialsMissing: () => {
136
    let user = process.env.JIRA_USER;
137
    let password = process.env.JIRA_PASSWORD;
138
    let check = (!user || !password);
139
140
    return check;
141
  },
142
  /** Execute a Jira Rest api and returns the response as a Promise.
143
   * When the request returns an error 5xx, it is retried 5 times with a 1s timeout in between before returning
144
   * @param  {String} url               the url to execute
145
   * @param  {boolean} retrieveAllPages if true and if the query returns more than 50 items, all other items are automatically retrieved and
146
   * @return {Promise}                  returns the json response, or in case of error an object with 3 properties: code {Number}, url {String}, message {String}
147
   */
148
  execJiraQuery: (url, retrieveAllPages) => new Promise((resolve, reject) => {
149
    execJiraQueryUntil(url, retrieveAllPages, config.maxRetry)
150
      .then((response) => resolve(response))
151
      .catch((error) => reject(error));
152
  }),
153
};
154