Completed
Push — master ( 77afd5...a883f6 )
by Cyril
01:05
created

lib/jira.js   A

Complexity

Total Complexity 35
Complexity/F 1.75

Size

Lines of Code 149
Function Count 20

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

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

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