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

Issue:
Add "dev-status" API
JIRA has an undocumented "dev-status" API that is usual when JIRA is integrated with other tools like Stash (Bitbucket Server).  For example:


### To get info about branches and pull requests related to an issue:

https://{jiraHost}/rest/dev-status/latest/issue/detail?issueId={jiraIssueNumericId}&applicationType=stash&dataType=pullrequest


### To get info about commits related to an issue:

https://{jiraHost}/rest/dev-status/latest/issue/detail?issueId={jiraIssueNumericId}&applicationType=stash&dataType=repository



I can also supply scrubbed example response bodies if desired.

Pull request description:
Dev status
Fixes #142 

Added support for Dev-Status URLs

Also Added test class for bearer auth

Patch:
diff --git a/src/jira.js b/src/jira.js
index 1b47657..c118ac5 100644
--- a/src/jira.js
+++ b/src/jira.js
@@ -203,6 +203,29 @@ export default class JiraApi {
    * @property {string} intermediatePath - will overwrite the /rest/greenhopper/version section
    */
 
+  /**
+   * @typedef makeDevStatusUri
+   * @function
+   * Creates a URI object for a given pathname
+   * @arg {pathname, query, intermediatePath} obj1
+   * @param {string} pathname obj1.pathname - The url after the /rest/api/version
+   * @param {object} query obj1.query - a query object
+   * @param {string} intermediatePath obj1.intermediatePath - If specified will overwrite the
+   * /rest/dev-status/latest/issue/detail section
+   */
+  makeDevStatusUri({ pathname, query, intermediatePath }) {
+    const intermediateToUse = this.intermediatePath || intermediatePath;
+    const tempPath = intermediateToUse || '/rest/dev-status/latest/issue';
+    const uri = url.format({
+      protocol: this.protocol,
+      hostname: this.host,
+      port: this.port,
+      pathname: `${this.base}${tempPath}${pathname}`,
+      query,
+    });
+    return decodeURIComponent(uri);
+  }
+
   /**
    * @name doRequest
    * @function
@@ -1086,4 +1109,36 @@ export default class JiraApi {
       pathname: '/status',
     })));
   }
+
+  /** Get a Dev-Status summary by issue ID
+   * @name getDevStatusSummary
+   * @function
+   * @param {string} issueId - id of issue to get
+   */
+  getDevStatusSummary(issueId) {
+    return this.doRequest(this.makeRequestHeader(this.makeDevStatusUri({
+      pathname: '/summary',
+      query: {
+        issueId,
+      },
+    })));
+  }
+
+  /** Get a Dev-Status detail by issue ID
+   * @name getDevStatusDetail
+   * @function
+   * @param {string} issueId - id of issue to get
+   * @param {string} applicationType - type of application (stash, bitbucket)
+   * @param {string} dataType - info to return (repository, pullrequest)
+   */
+  getDevStatusDetail(issueId, applicationType, dataType) {
+    return this.doRequest(this.makeRequestHeader(this.makeDevStatusUri({
+      pathname: '/detail',
+      query: {
+        issueId,
+        applicationType,
+        dataType,
+      },
+    })));
+  }
 }


Test patch:
diff --git a/test/jira-tests.js b/test/jira-tests.js
index 7a2573c..2adcd91 100644
--- a/test/jira-tests.js
+++ b/test/jira-tests.js
@@ -14,6 +14,7 @@ function getOptions(options) {
     request: actualOptions.request,
     oauth: actualOptions.oauth || null,
     intermediatePath: actualOptions.intermediatePath,
+    bearer: actualOptions.bearer || null,
   };
 }
 
@@ -41,6 +42,21 @@ describe('Jira API Tests', () => {
       expect(jira.baseOptions.auth).to.be.undefined;
     });
 
+    it('Constructor with bearer credentials', () => {
+      const options = getOptions({
+        bearer: 'testBearer',
+      });
+
+      const jira = new JiraApi(options);
+
+      expect(jira.baseOptions.auth).to.eql({
+        user: '',
+        pass: '',
+        sendImmediately: true,
+        bearer: options.bearer,
+      });
+    });
+
     it('Constructor with oauth credentials', () => {
       const options = getOptions({
         oauth: {
@@ -692,6 +708,24 @@ describe('Jira API Tests', () => {
       result.should.eql('http://jira.somehost.com:8080/rest/api/2.0/status');
     });
 
+    // Field Option APIs Suite Tests
+    describe('Dev-Status APIs Suite Tests', () => {
+      it('getDevStatusSummary hits proper url', async () => {
+        const result = await dummyURLCall('getDevStatusSummary', ['someIssueId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/dev-status/latest/issue/summary?issueId=someIssueId');
+      });
+
+      it('getDevStatusDetail hits proper url - repo', async () => {
+        const result = await dummyURLCall('getDevStatusDetail', ['someIssueId', 'someApplicationType', 'repository']);
+        result.should.eql('http://jira.somehost.com:8080/rest/dev-status/latest/issue/detail?issueId=someIssueId&applicationType=someApplicationType&dataType=repository');
+      });
+
+      it('getDevStatusDetail hits proper url - pullrequest', async () => {
+        const result = await dummyURLCall('getDevStatusDetail', ['someIssueId', 'someApplicationType', 'pullrequest']);
+        result.should.eql('http://jira.somehost.com:8080/rest/dev-status/latest/issue/detail?issueId=someIssueId&applicationType=someApplicationType&dataType=pullrequest');
+      });
+    });
+
     it('issueNotify hits proper url', async () => {
       const result = await dummyURLCall('issueNotify', ['someIssueId', {}]);
       result.should.eql('http://jira.somehost.com:8080/rest/api/2.0/issue/someIssueId/notify');