From e779a225478f992012f60dd593842e81499a4026 Mon Sep 17 00:00:00 2001 From: Louis Lecaroz Date: Thu, 6 Dec 2018 12:38:13 +0100 Subject: [PATCH 1/4] Externalizing request options Be able to set request options @ the JiraApi level, will make more flexible that package allowing it to sign in on all infra types: authenticated proxy, jira, oauth or whatever https://github.com/steves/node-jira/issues/152 --- lib/jira.js | 108 +++++++++++----------------------- package.json | 10 ++-- spec/jira.spec.coffee | 133 ++++++++++++------------------------------ 3 files changed, 79 insertions(+), 172 deletions(-) diff --git a/lib/jira.js b/lib/jira.js index 190e2661..75577e2c 100644 --- a/lib/jira.js +++ b/lib/jira.js @@ -27,15 +27,30 @@ // // ## Example ## // -// Find the status of an issue. +// Find the status of an issue. (this example provide also different types of authentication // -// JiraApi = require('jira').JiraApi; -// -// var jira = new JiraApi('https', config.host, config.port, config.user, config.password, '2.0.alpha1'); -// jira.findIssue(issueNumber, function(error, issue) { -// console.log('Status: ' + issue.fields.status.name); -// }); +//const HttpProxyAgent = require("http-proxy-agent"); +//const agent = new HttpProxyAgent("http://proxy"); +//const options = { +// "agent": agent, // proxy setting +// rejectUnauthorized: this.strictSSL, // strict ssl option +// headers: { +// "Proxy-Authorization": "Bearer authentication_token", // proxy authentification +// "Authorization": 'Basic ' + Buffer.from(username + ':' + password).toString('base64') // basic authentication +// }, +// oauth: { // in the case of oauth instead of basic +// consumer_key: oauth.consumer_key, +// consumer_secret: oauth.consumer_secret, +// token: oauth.access_token, +// token_secret: oauth.access_token_secret +// } +//} +//JiraApi = require('jira').JiraApi; // +//var jira = new JiraApi('https', 'server.com', '443', '2.0.alpha1', true, 'jira', options); +//jira.findIssue(issueNumber, function(error, issue) { +// console.log('Status: ' + issue.fields.status.name); +//}); // Currently there is no explicit login call necessary as each API call uses Basic Authentication to authenticate. // // ## Options ## @@ -44,13 +59,10 @@ // * `protocol`: Typically 'http:' or 'https:' // * `host`: The hostname for your jira server // * `port`: The port your jira server is listening on (probably `80` or `443`) -// * `user`: The username to log in with -// * `password`: Keep it secret, keep it safe // * `Jira API Version`: Known to work with `2` and `2.0.alpha1` // * `verbose`: Log some info to the console, usually for debugging -// * `strictSSL`: Set to false if you have self-signed certs or something non-trustworthy -// * `oauth`: A dictionary of `consumer_key`, `consumer_secret`, `access_token` and `access_token_secret` to be used for OAuth authentication. // * `base`: Add base slug if your JIRA install is not at the root of the host +// * `options`: request Options standard parameter (see request doc) // // ## Implemented APIs ## // @@ -132,21 +144,14 @@ var url = require('url'), logger = console, OAuth = require("oauth"); - -var JiraApi = exports.JiraApi = function(protocol, host, port, username, password, apiVersion, verbose, strictSSL, oauth, base) { +var JiraApi = exports.JiraApi = function(protocol, host, port, apiVersion, verbose, base, requestOptions) { this.protocol = protocol; this.host = host; this.port = port; - this.username = username; - this.password = password; this.apiVersion = apiVersion; this.base = base; - // Default strictSSL to true (previous behavior) but now allow it to be - // modified - if (strictSSL == null) { - strictSSL = true; - } - this.strictSSL = strictSSL; + this.requestOptions = requestOptions===undefined?{}:requestOptions; + // This is so we can fake during unit tests this.request = require('request'); if (verbose !== true) { logger = { log: function() {} }; } @@ -176,21 +181,16 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor return decodeURIComponent(uri); }; + this.extend = function(src, obj) { + for (var key in src) { + if (src.hasOwnProperty(key)) obj[key] = src[key]; + } + return obj; + } + this.doRequest = function(options, callback) { - if(oauth && oauth.consumer_key && oauth.consumer_secret) { - options.oauth = { - consumer_key: oauth.consumer_key, - consumer_secret: oauth.consumer_secret, - token: oauth.access_token, - token_secret: oauth.access_token_secret - }; - } else if(this.username && this.password) { - options.auth = { - 'user': this.username, - 'pass': this.password - }; - } - this.request(options, callback); + clonedOptions = require('lodash.clonedeep')(this.requestOptions) + this.request(this.extend(options,clonedOptions), callback); }; }; @@ -211,7 +211,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.findIssue = function(issueNumber, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issue/' + issueNumber), method: 'GET' }; @@ -256,7 +255,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor // [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id288524) this.getUnresolvedIssueCount = function(version, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/version/' + version + '/unresolvedIssueCount'), method: 'GET' }; @@ -298,7 +296,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.getProject = function(project, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/project/' + project), method: 'GET' }; @@ -345,7 +342,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.findRapidView = function(projectName, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/rapidviews/list', 'rest/greenhopper/'), method: 'GET', json: true @@ -400,7 +396,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.getLastSprintForRapidView = function(rapidViewId, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/sprintquery/' + rapidViewId, 'rest/greenhopper/'), method: 'GET', json:true @@ -453,7 +448,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.getSprintIssues = function getSprintIssues(rapidViewId, sprintId, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/rapid/charts/sprintreport?rapidViewId=' + rapidViewId + '&sprintId=' + sprintId, 'rest/greenhopper/'), method: 'GET', json: true @@ -509,7 +503,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.addIssueToSprint = function(issueId, sprintId, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/sprint/' + sprintId + '/issues/add', 'rest/greenhopper/'), method: 'PUT', followAllRedirects: true, @@ -573,7 +566,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.issueLink = function(link, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issueLink'), method: 'POST', followAllRedirects: true, @@ -612,7 +604,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.getRemoteLinks = function getRemoteLinks(issueNumber, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issue/' + issueNumber + '/remotelink'), method: 'GET', json: true @@ -649,7 +640,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.createRemoteLink = function createRemoteLink(issueNumber, remoteLink, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issue/' + issueNumber + '/remotelink'), method: 'POST', json: true, @@ -697,7 +687,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.getVersions = function(project, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/project/' + project + '/versions'), method: 'GET' }; @@ -751,7 +740,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.createVersion = function(version, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/version'), method: 'POST', followAllRedirects: true, @@ -811,7 +799,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor */ this.updateVersion = function(version, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/version/'+version.id), method: 'PUT', followAllRedirects: true, @@ -874,7 +861,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor } var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/search'), method: 'POST', json: true, @@ -934,7 +920,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor includeInactive = (includeInactive !== undefined) ? includeInactive : false; var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri( '/user/search?username=' + username + '&startAt=' + startAt + @@ -985,7 +970,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor maxResults = (maxResults !== undefined) ? maxResults : 50; var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri( '/group?groupname=' + groupName + '&expand=users[' + startAt + ':' + maxResults + ']'), @@ -1053,7 +1037,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor // [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290028) this.addNewIssue = function(issue, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issue'), method: 'POST', followAllRedirects: true, @@ -1106,7 +1089,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.addWatcher = function (issueKey, username, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issue/' + issueKey + '/watchers'), method: 'POST', followAllRedirects: true, @@ -1143,7 +1125,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor // [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290791) this.deleteIssue = function(issueNum, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issue/' + issueNum), method: 'DELETE', followAllRedirects: true, @@ -1181,7 +1162,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor // [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290878) this.updateIssue = function(issueNum, issueUpdate, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issue/' + issueNum), body: issueUpdate, method: 'PUT', @@ -1242,7 +1222,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor */ this.listComponents = function(project, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/project/' + project + '/components'), method: 'GET', json: true @@ -1282,7 +1261,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor // [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290028) this.addNewComponent = function (component, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/component'), method: 'POST', followAllRedirects: true, @@ -1325,7 +1303,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor // [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290791) this.deleteComponent = function (componentNum, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/component/' + componentNum), method: 'DELETE', followAllRedirects: true, @@ -1375,7 +1352,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor */ this.listFields = function(callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/field'), method: 'GET', json: true @@ -1423,7 +1399,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor */ this.listPriorities = function(callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/priority'), method: 'GET', json: true @@ -1500,7 +1475,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor */ this.listTransitions = function(issueId, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issue/' + issueId + '/transitions?expand=transitions.fields'), method: 'GET', json: true @@ -1541,7 +1515,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor // [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290489) this.transitionIssue = function(issueNum, issueTransition, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issue/' + issueNum + '/transitions'), body: issueTransition, method: 'POST', @@ -1591,7 +1564,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor */ this.listProjects = function(callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/project'), method: 'GET', json: true @@ -1631,7 +1603,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor // [Jira Doc](https://docs.atlassian.com/jira/REST/latest/#id108798) this.addComment = function(issueId, comment, callback){ var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issue/' + issueId + '/comment'), body: { "body": comment @@ -1705,7 +1676,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor newEstimate = false; } var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issue/' + issueId + '/worklog' + (newEstimate ? "?adjustEstimate=new&newEstimate=" + newEstimate : "")), body: worklog, method: 'POST', @@ -1754,7 +1724,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor this.deleteWorklog = function(issueId, worklogId, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issue/' + issueId + '/worklog/' + worklogId), method: 'DELETE', followAllRedirects: true, @@ -1802,7 +1771,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor */ this.listIssueTypes = function(callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/issuetype'), method: 'GET', json: true @@ -1853,7 +1821,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor */ this.registerWebhook = function(webhook, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/webhook', 'rest/webhooks/', '1.0'), method: 'POST', json: true, @@ -1904,7 +1871,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor */ this.listWebhooks = function(callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/webhook', 'rest/webhooks/', '1.0'), method: 'GET', json: true @@ -1940,7 +1906,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor // [Jira Doc](https://developer.atlassian.com/display/JIRADEV/JIRA+Webhooks+Overview) this.getWebhook = function(webhookID, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/webhook/' + webhookID, 'rest/webhooks/', '1.0'), method: 'GET', json: true @@ -1976,7 +1941,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor // [Jira Doc](https://developer.atlassian.com/display/JIRADEV/JIRA+Webhooks+Overview) this.deleteWebhook = function(webhookID, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/webhook/' + webhookID, 'rest/webhooks/', '1.0'), method: 'DELETE', json: true @@ -2025,7 +1989,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor */ this.getCurrentUser = function(callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/session', 'rest/auth/', '1'), method: 'GET', json: true @@ -2116,7 +2079,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, username, passwor */ this.getBacklogForRapidView = function(rapidViewId, callback) { var options = { - rejectUnauthorized: this.strictSSL, uri: this.makeUri('/xboard/plan/backlog/data?rapidViewId=' + rapidViewId, 'rest/greenhopper/'), method: 'GET', json: true diff --git a/package.json b/package.json index 7e56d2b2..152db7ea 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,9 @@ } ], "dependencies": { - "request": "<2.16.0", - "oauth": "^0.9.11" + "lodash.clonedeep": "^4.5.0", + "oauth": "^0.9.11", + "request": "<2.16.0" }, "scripts": { "test": "grunt test" @@ -34,8 +35,9 @@ "grunt-bump": "0.0.13", "grunt-docco": "^0.3.3", "grunt-jasmine-node": "^0.2.1", - "coffee-script": "^1.7.1", + "coffeescript": "^2.3.2", "grunt-jslint": "^1.1.8", - "rewire": "^2.0.0" + "rewire": "^2.0.0", + "buffer-from": "^1.1.1" } } diff --git a/spec/jira.spec.coffee b/spec/jira.spec.coffee index 90638c1f..7cc3d5f2 100644 --- a/spec/jira.spec.coffee +++ b/spec/jira.spec.coffee @@ -1,4 +1,5 @@ url = require 'url' +bufferFrom = require('buffer-from') rewire = require 'rewire' nodeJira = rewire '../lib/jira' @@ -20,56 +21,42 @@ describe "Node Jira Tests", -> OAuth.OAuth.prototype = jasmine.createSpyObj 'OAuth', ['getOAuthRequestToken', '_encodeData'] nodeJira.__set__ "OAuth", OAuth - @jira = new nodeJira.JiraApi 'http', 'localhost', 80, 'test', 'test', 2 + options = + rejectUnauthorized: true + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64') + @jira = new nodeJira.JiraApi 'http', 'localhost', 80, 2, false, null, options spyOn @jira, 'request' @cb = jasmine.createSpy 'callback' it "Sets basic auth if oauth is not passed in", -> options = - auth = - user: 'test' - pass: 'test' + rejectUnauthorized: false + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.doRequest options, @cb expect(@jira.request) .toHaveBeenCalledWith(options, jasmine.any(Function)) it "Sets OAuth oauth for the requests if oauth is passed in", -> options = - oauth = + oauth: consumer_key: 'ck' consumer_secret: 'cs' access_token: 'ac' access_token_secret: 'acs' # oauth = new OAuth.OAuth(null, null, oauth.consumer_key, oauth.consumer_secret, null, null, "RSA-SHA1") - @jira = new nodeJira.JiraApi 'http', 'localhost', 80, 'test', 'test', 2, false, false, options.oauth + @jira = new nodeJira.JiraApi 'http', 'localhost', 80, 2, false, null, options spyOn @jira, 'request' @jira.doRequest options, @cb expect(@jira.request) .toHaveBeenCalledWith(options, jasmine.any(Function)) - it "Sets strictSSL to false when passed in", -> - expected = false - jira = new nodeJira.JiraApi 'http', 'localhost', 80, 'test', 'test', 2, false, expected - expect(jira.strictSSL).toEqual(expected) - - it "Sets strictSSL to true when passed in", -> - expected = true - jira = new nodeJira.JiraApi 'http', 'localhost', 80, 'test', 'test', 2, false, expected - expect(jira.strictSSL).toEqual(expected) - - it "Sets strictSSL to true when not passed in", -> - expected = true - expect(@jira.strictSSL).toEqual(expected) - it "Finds an issue", -> options = rejectUnauthorized: true uri: makeUrl "issue/1" method: 'GET' - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.findIssue 1, @cb expect(@jira.request) .toHaveBeenCalledWith(options, jasmine.any(Function)) @@ -93,9 +80,7 @@ describe "Node Jira Tests", -> rejectUnauthorized: true uri: makeUrl "version/1/unresolvedIssueCount" method: 'GET' - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.getUnresolvedIssueCount 1, @cb expect(@jira.request) @@ -120,9 +105,7 @@ describe "Node Jira Tests", -> rejectUnauthorized: true uri: makeUrl "project/ABC" method: 'GET' - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.getProject 'ABC', @cb expect(@jira.request) @@ -143,9 +126,7 @@ describe "Node Jira Tests", -> uri: makeUrl("rapidviews/list", true) method: 'GET' json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.findRapidView 'ABC', @cb expect(@jira.request) @@ -173,9 +154,7 @@ describe "Node Jira Tests", -> uri: makeUrl("sprintquery/1", true) method: 'GET' json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.getLastSprintForRapidView 1, @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -205,9 +184,7 @@ describe "Node Jira Tests", -> followAllRedirects: true body: issueKeys: [2] - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.addIssueToSprint 2, 1, @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -228,9 +205,7 @@ describe "Node Jira Tests", -> json: true body: 'test' followAllRedirects: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.issueLink 'test', @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -252,9 +227,7 @@ describe "Node Jira Tests", -> rejectUnauthorized: true uri: makeUrl "project/ABC/versions" method: 'GET' - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.getVersions 'ABC', @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -280,9 +253,7 @@ describe "Node Jira Tests", -> json: true body: 'ABC' followAllRedirects: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.createVersion 'ABC', @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -308,6 +279,7 @@ describe "Node Jira Tests", -> it "Passes a search query to Jira, default options", -> fields = ["summary", "status", "assignee", "description"] + expand = ["schema", "names"] options = rejectUnauthorized: true uri: makeUrl "search" @@ -319,9 +291,8 @@ describe "Node Jira Tests", -> startAt: 0 maxResults: 50 fields: fields - auth: - user: 'test' - pass: 'test' + expand: expand + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.searchJira 'aJQLstring', {}, @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -341,6 +312,7 @@ describe "Node Jira Tests", -> it "Passes a search query to Jira, non-default options", -> fields = ["assignee", "description", "test"] + expand = ["schema", "names"] options = rejectUnauthorized: true uri: makeUrl "search" @@ -352,9 +324,8 @@ describe "Node Jira Tests", -> startAt: 200 maxResults: 100 fields: fields - auth: - user: 'test' - pass: 'test' + expand: expand + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.searchJira 'aJQLstring', { maxResults: 100, fields: fields, startAt: 200 }, @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -383,9 +354,7 @@ describe "Node Jira Tests", -> uri: makeUrl("rapid/charts/sprintreport?rapidViewId=1&sprintId=1", true) method: 'GET' json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.getSprintIssues 1, 1, @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -408,9 +377,7 @@ describe "Node Jira Tests", -> method: 'DELETE' json: true followAllRedirects: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.deleteIssue 1, @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -430,9 +397,7 @@ describe "Node Jira Tests", -> method: 'PUT' json: true followAllRedirects: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.updateIssue 1, 'updateGoesHere', @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -450,9 +415,7 @@ describe "Node Jira Tests", -> uri: makeUrl "issue/1/transitions?expand=transitions.fields" method: 'GET' json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.listTransitions 1, @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -476,9 +439,7 @@ describe "Node Jira Tests", -> method: 'POST' followAllRedirects: true json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.transitionIssue 1, 'someTransition', @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -496,9 +457,7 @@ describe "Node Jira Tests", -> uri: makeUrl "project" method: 'GET' json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.listProjects @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -523,9 +482,7 @@ describe "Node Jira Tests", -> method: 'POST' followAllRedirects: true json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.addComment 1, 'aComment', @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -546,9 +503,7 @@ describe "Node Jira Tests", -> method: 'POST' followAllRedirects: true json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.addWatcher 1, "testuser", @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -568,9 +523,7 @@ describe "Node Jira Tests", -> method: 'POST' followAllRedirects: true json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.addWorklog 1, 'aWorklog', @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -597,9 +550,7 @@ describe "Node Jira Tests", -> method: 'POST' followAllRedirects: true json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.addWorklog 1, 'aWorklog', '1h', @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -624,9 +575,7 @@ describe "Node Jira Tests", -> uri: makeUrl "issuetype" method: 'GET' json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.listIssueTypes @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -644,9 +593,7 @@ describe "Node Jira Tests", -> uri: makeUrl("xboard/plan/backlog/data?rapidViewId=123", true) method: 'GET' json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.getBacklogForRapidView 123, @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) @@ -664,9 +611,7 @@ describe "Node Jira Tests", -> uri: makeUrl "issue/1/remotelink" method: 'GET' json: true - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.getRemoteLinks 1, @cb expect(@jira.request) .toHaveBeenCalledWith(options, jasmine.any(Function)) @@ -692,9 +637,7 @@ describe "Node Jira Tests", -> method: 'POST' json: true body: 'test' - auth: - user: 'test' - pass: 'test' + headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64'); @jira.createRemoteLink 1, 'test', @cb expect(@jira.request).toHaveBeenCalledWith options, jasmine.any(Function) From ab8832816e5b9af7007ce7091588f7bd029760a1 Mon Sep 17 00:00:00 2001 From: Louis Lecaroz Date: Thu, 6 Dec 2018 20:49:08 +0100 Subject: [PATCH 2/4] Adding backward compatibility with old JiraApi constructor --- lib/jira.js | 29 +++++++++++++++++++++++++---- spec/jira.spec.coffee | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/lib/jira.js b/lib/jira.js index 75577e2c..3b47cea9 100644 --- a/lib/jira.js +++ b/lib/jira.js @@ -144,7 +144,7 @@ var url = require('url'), logger = console, OAuth = require("oauth"); -var JiraApi = exports.JiraApi = function(protocol, host, port, apiVersion, verbose, base, requestOptions) { +var JiraApiWithOptions = exports.JiraApiWithOptions = function(protocol, host, port, apiVersion, verbose, base, requestOptions) { this.protocol = protocol; this.host = host; this.port = port; @@ -187,7 +187,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, apiVersion, verbo } return obj; } - this.doRequest = function(options, callback) { clonedOptions = require('lodash.clonedeep')(this.requestOptions) this.request(this.extend(options,clonedOptions), callback); @@ -195,7 +194,27 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, apiVersion, verbo }; -(function() { +var JiraApi = exports.JiraApi = function(protocol, host, port, username, password, apiVersion, verbose, strictSSL, oauth, base) { + options = { + rejectUnauthorized: strictSSL == null?true:strictSSL + }; + if(oauth && oauth.consumer_key && oauth.consumer_secret) { + options.oauth = { + consumer_key: oauth.consumer_key, + consumer_secret: oauth.consumer_secret, + token: oauth.access_token, + token_secret: oauth.access_token_secret + }; + } else if(username && password) { + options.auth = { + 'user': username, + 'pass': password + }; + } + JiraApiWithOptions.call(this, protocol, host, port, apiVersion, verbose, base, options) +}; + +_jiraMethods=(function() { // ## Find an issue in jira ## // ### Takes ### // @@ -2101,4 +2120,6 @@ var JiraApi = exports.JiraApi = function(protocol, host, port, apiVersion, verbo }); }; -}).call(JiraApi.prototype); +}); +_jiraMethods.call(JiraApiWithOptions.prototype); +_jiraMethods.call(JiraApi.prototype); diff --git a/spec/jira.spec.coffee b/spec/jira.spec.coffee index 7cc3d5f2..9ea4840a 100644 --- a/spec/jira.spec.coffee +++ b/spec/jira.spec.coffee @@ -24,10 +24,39 @@ describe "Node Jira Tests", -> options = rejectUnauthorized: true headers: 'Authorization': 'Basic ' + bufferFrom('test:test').toString('base64') - @jira = new nodeJira.JiraApi 'http', 'localhost', 80, 2, false, null, options + @jira = new nodeJira.JiraApiWithOptions 'http', 'localhost', 80, 2, false, null, options spyOn @jira, 'request' @cb = jasmine.createSpy 'callback' + it "Sets basic auth if oauth is not passed in with JiraApi old method", -> + options = + rejectUnauthorized: false, + auth : + user: 'test' + pass: 'test' + @jira = new nodeJira.JiraApi 'http', 'localhost', 80, 'test', 'test', 2 + spyOn @jira, 'request' + + @jira.doRequest options, @cb + expect(@jira.request) + .toHaveBeenCalledWith(options, jasmine.any(Function)) + + it "Sets OAuth oauth for the requests if oauth is passed in with JiraApi old method", -> + options = + rejectUnauthorized: false, + oauth : + consumer_key: 'ck' + consumer_secret: 'cs' + access_token: 'ac' + access_token_secret: 'acs' + # oauth = new OAuth.OAuth(null, null, oauth.consumer_key, oauth.consumer_secret, null, null, "RSA-SHA1") + @jira = new nodeJira.JiraApi 'http', 'localhost', 80, 'test', 'test', 2, false, false, options.oauth + spyOn @jira, 'request' + + @jira.doRequest options, @cb + expect(@jira.request) + .toHaveBeenCalledWith(options, jasmine.any(Function)) + it "Sets basic auth if oauth is not passed in", -> options = rejectUnauthorized: false @@ -44,7 +73,7 @@ describe "Node Jira Tests", -> access_token: 'ac' access_token_secret: 'acs' # oauth = new OAuth.OAuth(null, null, oauth.consumer_key, oauth.consumer_secret, null, null, "RSA-SHA1") - @jira = new nodeJira.JiraApi 'http', 'localhost', 80, 2, false, null, options + @jira = new nodeJira.JiraApiWithOptions 'http', 'localhost', 80, 2, false, null, options spyOn @jira, 'request' @jira.doRequest options, @cb From edcf0e15dd980d8a314502fa51fdd8fb4c61d505 Mon Sep 17 00:00:00 2001 From: Louis Lecaroz Date: Fri, 7 Dec 2018 15:03:25 +0100 Subject: [PATCH 3/4] Fixing node version used for const in strict mode (this is for travis error in github) --- .travis.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 44562d65..35fffc7e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: node_js node_js: - - "0.10" + - "6.4.0" before_install: npm install -g grunt-cli install: npm install diff --git a/package.json b/package.json index 152db7ea..fbe7f58f 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "devDependencies": { "grunt": "^0.4.4", "grunt-bump": "0.0.13", - "grunt-docco": "^0.3.3", + "grunt-docco": "^0.5.0", "grunt-jasmine-node": "^0.2.1", "coffeescript": "^2.3.2", "grunt-jslint": "^1.1.8", From ba025cd58491f15dba8bf2d1cc045c53b9d200fb Mon Sep 17 00:00:00 2001 From: Louis Lecaroz Date: Fri, 7 Dec 2018 16:35:53 +0100 Subject: [PATCH 4/4] Add JiraApiWithOptions documentation in the README.md --- README.md | 27 +++++++++++++++++++++++++++ lib/jira.js | 4 ++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c8f94ed4..204eb57e 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,37 @@ or ### Create the JIRA client ### +#### Create a simple JIRA client #### + JiraApi = require('jira').JiraApi; var jira = new JiraApi('https', config.host, config.port, config.user, config.password, '2.0.alpha1'); +#### Need special HTTP settings #### + +In the case of, if some particular settings are needed to sign in like proxy authentication, for example. This can be done by passing the HTTP request options as parameter of the jiraApiWithOptions method. + + const HttpProxyAgent = require("http-proxy-agent"); + const agent = new HttpProxyAgent("http://proxy"); + const options = { + "agent": agent, // proxy setting + rejectUnauthorized: this.strictSSL, // strict ssl option + headers: { + "Proxy-Authorization": "Bearer authentication_token", // proxy authentification + "Authorization": 'Basic ' + Buffer.from(username + ':' + password).toString('base64') // basic authentication + }, + oauth: { // in the case of oauth instead of basic + consumer_key: oauth.consumer_key, + consumer_secret: oauth.consumer_secret, + token: oauth.access_token, + token_secret: oauth.access_token_secret + } + } + JiraApiWithOptions = require('jira').JiraApiWithOptions; + + var jira = new JiraApiWithOptions('https', 'server.com', '443', '2.0.alpha1', true, 'jira', options); + + ### Find the status of an issue ### jira.findIssue(issueNumber, function(error, issue) { diff --git a/lib/jira.js b/lib/jira.js index 3b47cea9..a6c2d8c5 100644 --- a/lib/jira.js +++ b/lib/jira.js @@ -45,9 +45,9 @@ // token_secret: oauth.access_token_secret // } //} -//JiraApi = require('jira').JiraApi; +//JiraApiWithOptions = require('jira').JiraApiWithOptions; // -//var jira = new JiraApi('https', 'server.com', '443', '2.0.alpha1', true, 'jira', options); +//var jira = new JiraApiWithOptions('https', 'server.com', '443', '2.0.alpha1', true, 'jira', options); //jira.findIssue(issueNumber, function(error, issue) { // console.log('Status: ' + issue.fields.status.name); //});