Repository: jira-node/node-jira-client (js)

Issue:
Add override for api version
Hey mang,

we have a jira server we're using, would be nice if I could provide a function or something to override the pathname generated in makeUri because we have
example.com/rest/____
and get 404's when /api/${apiVersion} is in there.

Pull request description:
Intermediate path fix
Should fix #159 

Patch:
diff --git a/src/jira.js b/src/jira.js
index 152d4f5..dd57f1e 100644
--- a/src/jira.js
+++ b/src/jira.js
@@ -19,6 +19,7 @@ export default class JiraApi {
     this.port = options.port || null;
     this.apiVersion = options.apiVersion || '2';
     this.base = options.base || '';
+    this.intermediatePath = options.intermediatePath;
     this.strictSSL = options.hasOwnProperty('strictSSL') ? options.strictSSL : true;
       // This is so we can fake during unit tests
     this.request = options.request || request;
@@ -63,6 +64,8 @@ export default class JiraApi {
    * tool is connecting to?
    * @property {string} [base] - What other url parts exist, if any, before the rest/api/
    * section?
+   * @property {string} [intermediatePath] - If specified, overwrites the default rest/api/version
+   * section of the uri
    * @property {boolean} [strictSSL=true] - Does this tool require each request to be
    * authenticated?  Defaults to true.
    * @property {function} [request] - What method does this tool use to make its requests?
@@ -118,52 +121,81 @@ export default class JiraApi {
    * @name makeUri
    * @function
    * Creates a URI object for a given pathname
-   * @param {string} pathname - The url after the /rest/api/version
+   * @param {object} [options] - an object containing path information
    */
-  makeUri({ pathname, query }) {
+  makeUri({ pathname, query, intermediatePath }) {
+    const intermediateToUse = this.intermediatePath || intermediatePath;
+    const tempPath = intermediateToUse || `/rest/api/${this.apiVersion}`;
     const uri = url.format({
       protocol: this.protocol,
       hostname: this.host,
       port: this.port,
-      pathname: `${this.base}/rest/api/${this.apiVersion}${pathname}`,
+      pathname: `${this.base}${tempPath}${pathname}`,
       query,
     });
     return decodeURIComponent(uri);
   }
 
+  /**
+   * @typedef makeUriOptions
+   * @type {object}
+   * @property {string} pathname - The url after the /rest/api/version
+   * @property {object} query - a query object
+   * @property {string} intermediatePath - If specified will overwrite the /rest/api/version section
+   */
+
   /**
    * @name makeWebhookUri
    * @function
    * Creates a URI object for a given pathName
-   * @param {string} pathname - The url after the /rest/
+   * @param {object} [options] - An options object specifying uri information
    */
-  makeWebhookUri({ pathname }) {
+  makeWebhookUri({ pathname, intermediatePath }) {
+    const intermediateToUse = this.intermediatePath || intermediatePath;
+    const tempPath = intermediateToUse || `/rest/webhooks/${this.webhookVersion}`;
     const uri = url.format({
       protocol: this.protocol,
       hostname: this.host,
       port: this.port,
-      pathname: `${this.base}/rest/webhooks/${this.webhookVersion}${pathname}`,
+      pathname: `${this.base}${tempPath}${pathname}`,
     });
     return decodeURIComponent(uri);
   }
 
+  /**
+   * @typedef makeWebhookUriOptions
+   * @type {object}
+   * @property {string} pathname - The url after the /rest/webhooks
+   * @property {string} intermediatePath - If specified will overwrite the /rest/webhooks section
+   */
+
   /**
    * @name makeSprintQueryUri
    * @function
    * Creates a URI object for a given pathName
-   * @param {string} pathname - The url after the /rest/
+   * @param {object} [options] - The url after the /rest/
    */
-  makeSprintQueryUri({ pathname, query }) {
+  makeSprintQueryUri({ pathname, query, intermediatePath }) {
+    const intermediateToUse = this.intermediatePath || intermediatePath;
+    const tempPath = intermediateToUse || `/rest/greenhopper/${this.greenhopperVersion}`;
     const uri = url.format({
       protocol: this.protocol,
       hostname: this.host,
       port: this.port,
-      pathname: `${this.base}/rest/greenhopper/${this.greenhopperVersion}${pathname}`,
+      pathname: `${this.base}${tempPath}${pathname}`,
       query,
     });
     return decodeURIComponent(uri);
   }
 
+  /**
+   * @typedef makeSprintQueryUriOptions
+   * @type {object}
+   * @property {string} pathname - The url after the /rest/api/version
+   * @property {object} query - a query object
+   * @property {string} intermediatePath - will overwrite the /rest/greenhopper/version section
+   */
+
   /**
    * @name doRequest
    * @function


Test patch:
diff --git a/test/jira-tests.js b/test/jira-tests.js
index f2b32c1..ac4ef29 100644
--- a/test/jira-tests.js
+++ b/test/jira-tests.js
@@ -13,6 +13,7 @@ function getOptions(options) {
     strictSSL: actualOptions.hasOwnProperty('strictSSL') ? actualOptions.strictSSL : true,
     request: actualOptions.request,
     oauth: actualOptions.oauth || null,
+    intermediatePath: actualOptions.intermediatePath,
   };
 }
 
@@ -117,6 +118,22 @@ describe('Jira API Tests', () => {
         .to.eql('http://jira.somehost.com:8080/rest/api/2.0/somePathName');
     });
 
+    it('builds url with intermediatePath', () => {
+      const jira = new JiraApi(getOptions());
+
+      expect(jira.makeUri({ pathname: '/somePathName', intermediatePath: 'intermediatePath' }))
+        .to.eql('http://jira.somehost.com:8080/intermediatePath/somePathName');
+    });
+
+    it('builds url with globally specified intermediatePath', () => {
+      const jira = new JiraApi(getOptions({
+        intermediatePath: 'intermediatePath',
+      }));
+
+      expect(jira.makeUri({ pathname: '/somePathName' }))
+        .to.eql('http://jira.somehost.com:8080/intermediatePath/somePathName');
+    });
+
     it('builds url with query string parameters', () => {
       const jira = new JiraApi(getOptions());
 
@@ -145,6 +162,16 @@ describe('Jira API Tests', () => {
         .to.eql('http://jira.somehost.com:8080/rest/webhooks/1.0/somePathName');
     });
 
+    it('makeWebhookUri functions with intermediate path', () => {
+      const jira = new JiraApi(getOptions());
+
+      expect(jira.makeWebhookUri({
+        pathname: '/somePathName',
+        intermediatePath: '/someIntermediatePath',
+      }))
+        .to.eql('http://jira.somehost.com:8080/someIntermediatePath/somePathName');
+    });
+
     it('makeSprintQueryUri functions properly in the average case', () => {
       const jira = new JiraApi(getOptions());
 
@@ -154,6 +181,16 @@ describe('Jira API Tests', () => {
         .to.eql('http://jira.somehost.com:8080/rest/greenhopper/1.0/somePathName');
     });
 
+    it('makeSprintQueryUri functions properly in the average case', () => {
+      const jira = new JiraApi(getOptions());
+
+      expect(jira.makeSprintQueryUri({
+        pathname: '/somePathName',
+        intermediatePath: '/someIntermediatePath',
+      }))
+        .to.eql('http://jira.somehost.com:8080/someIntermediatePath/somePathName');
+    });
+
     it('makeUri functions properly no port http', () => {
       const { port, ...options } = getOptions();