Repository: jira-node/node-jira-client
Pull request #179 (2017-05-22 17:03:49)

Adding Agile Board API
This is Part 1 of 5 PRs


Patch:
diff --git a/src/jira.js b/src/jira.js
index c118ac5..6e82102 100644
--- a/src/jira.js
+++ b/src/jira.js
@@ -101,6 +101,13 @@ export default class JiraApi {
    * Possible values RSA-SHA1, HMAC-SHA1, PLAINTEXT. Jira Cloud supports only RSA-SHA1.
    */
 
+  /**
+   *  @typedef {object} UriOptions
+   *  @property {string} pathname - The url after the specific functions path
+   *  @property {object} [query] - An object of all query parameters
+   *  @property {string} [intermediatePath] - Overwrites with specified path
+   */
+
   /**
    * @name makeRequestHeader
    * @function
@@ -226,6 +233,25 @@ export default class JiraApi {
     return decodeURIComponent(uri);
   }
 
+  /**
+   * @name makeAgile1Uri
+   * @function
+   * Creates a URI object for a given pathname
+   * @param {UriOptions} object
+   */
+  makeAgileUri(object) {
+    const intermediateToUse = this.intermediatePath || object.intermediatePath;
+    const tempPath = intermediateToUse || '/rest/agile/1.0';
+    const uri = url.format({
+      protocol: this.protocol,
+      hostname: this.host,
+      port: this.port,
+      pathname: `${this.base}${tempPath}${object.pathname}`,
+      query: object.query,
+    });
+    return decodeURIComponent(uri);
+  }
+
   /**
    * @name doRequest
    * @function
@@ -1141,4 +1167,358 @@ export default class JiraApi {
       },
     })));
   }
+
+  /** Move issues to backlog
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/backlog-moveIssuesToBacklog)
+   * @name moveToBacklog
+   * @function
+   * @param {array} issues - id or key of issues to get
+   */
+  moveToBacklog(issues) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: '/backlog/issue',
+    }), {
+      method: 'POST',
+      body: {
+        issues,
+      },
+    }));
+  }
+
+  /** Get all boards
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getAllBoards)
+   * @name getAllBoards
+   * @function
+   * @param {number} [startAt=0] - The starting index of the returned boards.
+   * @param {number} [maxResults=50] - The maximum number of boards to return per page.
+   * @param {string} [type] - Filters results to boards of the specified type.
+   * @param {string} [name] - Filters results to boards that match the specified name.
+   * @param {string} [projectKeyOrId] - Filters results to boards that are relevant to a project.
+   */
+  getAllBoards(startAt = 0, maxResults = 50, type, name, projectKeyOrId) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: '/board',
+      query: {
+        startAt,
+        maxResults,
+        type,
+        name,
+        projectKeyOrId,
+      },
+    })));
+  }
+
+  /** Create Board
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-createBoard)
+   * @name createBoard
+   * @function
+   * @param {object} boardBody - Board name, type and filter Id is required.
+   * @param {string} boardBody.type - Valid values: scrum, kanban
+   * @param {string} boardBody.name - Must be less than 255 characters.
+   * @param {string} boardBody.filterId - Id of a filter that the user has permissions to view.
+   */
+  createBoard(boardBody) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: '/board',
+    }), {
+      method: 'POST',
+      body: boardBody,
+    }));
+  }
+
+  /** Get Board
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getBoard)
+   * @name getBoard
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   */
+  getBoard(boardId) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}`,
+    })));
+  }
+
+  /** Delete Board
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-deleteBoard)
+   * @name deleteBoard
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   */
+  deleteBoard(boardId) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}`,
+    }), {
+      method: 'DELETE',
+    }));
+  }
+
+  /** Get issues for backlog
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getIssuesForBacklog)
+   * @name getIssuesForBacklog
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   * @param {number} [startAt=0] - The starting index of the returned issues. Base index: 0.
+   * @param {number} [maxResults=50] - The maximum number of issues to return per page. Default: 50.
+   * @param {string} [jql] - Filters results using a JQL query.
+   * @param {boolean} [validateQuery] - Specifies whether to validate the JQL query or not.
+   * Default: true.
+   * @param {string} [fields] - The list of fields to return for each issue.
+   */
+  getIssuesForBacklog(boardId, startAt = 0, maxResults = 50, jql, validateQuery = true, fields) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/backlog`,
+      query: {
+        startAt,
+        maxResults,
+        jql,
+        validateQuery,
+        fields,
+      },
+    })));
+  }
+
+  /** Get Configuration
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getConfiguration)
+   * @name getConfiguration
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   */
+  getConfiguration(boardId) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/configuration`,
+    })));
+  }
+
+  /** Get issues for board
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getIssuesForBoard)
+   * @name getIssuesForBoard
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   * @param {number} [startAt=0] - The starting index of the returned issues. Base index: 0.
+   * @param {number} [maxResults=50] - The maximum number of issues to return per page. Default: 50.
+   * @param {string} [jql] - Filters results using a JQL query.
+   * @param {boolean} [validateQuery] - Specifies whether to validate the JQL query or not.
+   * Default: true.
+   * @param {string} [fields] - The list of fields to return for each issue.
+   */
+  getIssuesForBoard(boardId, startAt = 0, maxResults = 50, jql, validateQuery = true, fields) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/issue`,
+      query: {
+        startAt,
+        maxResults,
+        jql,
+        validateQuery,
+        fields,
+      },
+    })));
+  }
+
+  /** Get Epics
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/epic-getEpics)
+   * @name getEpics
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   * @param {number} [startAt=0] - The starting index of the returned epics. Base index: 0.
+   * @param {number} [maxResults=50] - The maximum number of epics to return per page. Default: 50.
+   * @param {string} [done] - Filters results to epics that are either done or not done.
+   * Valid values: true, false.
+   */
+  getEpics(boardId, startAt = 0, maxResults = 50, done) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/epic`,
+      query: {
+        startAt,
+        maxResults,
+        done,
+      },
+    })));
+  }
+
+  /** Get board issues for epic
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/epic-getIssuesForEpic)
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/epic-getIssuesWithoutEpic)
+   * @name getBoardIssuesForEpic
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   * @param {string} epicId - Id of epic to retrieve, specify 'none' to get issues without an epic.
+   * @param {number} [startAt=0] - The starting index of the returned issues. Base index: 0.
+   * @param {number} [maxResults=50] - The maximum number of issues to return per page. Default: 50.
+   * @param {string} [jql] - Filters results using a JQL query.
+   * @param {boolean} [validateQuery] - Specifies whether to validate the JQL query or not.
+   * Default: true.
+   * @param {string} [fields] - The list of fields to return for each issue.
+   */
+  getBoardIssuesForEpic(boardId, epicId, startAt = 0, maxResults = 50, jql,
+    validateQuery = true, fields) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/epic/${epicId}/issue`,
+      query: {
+        startAt,
+        maxResults,
+        jql,
+        validateQuery,
+        fields,
+      },
+    })));
+  }
+
+  /** Get Projects
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/project-getProjects)
+   * @name getProjects
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   * @param {number} [startAt=0] - The starting index of the returned projects. Base index: 0.
+   * @param {number} [maxResults=50] - The maximum number of projects to return per page.
+   * Default: 50.
+   */
+  getProjects(boardId, startAt = 0, maxResults = 50) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/project`,
+      query: {
+        startAt,
+        maxResults,
+      },
+    })));
+  }
+
+  /** Get Projects Full
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/project-getProjectsFull)
+   * @name getProjectsFull
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   */
+  getProjectsFull(boardId) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/project/full`,
+    })));
+  }
+
+  /** Get Board Properties Keys
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/properties-getPropertiesKeys)
+   * @name getBoardPropertiesKeys
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   */
+  getBoardPropertiesKeys(boardId) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/properties`,
+    })));
+  }
+
+  /** Delete Board Property
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/properties-deleteProperty)
+   * @name deleteBoardProperty
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   * @param {string} propertyKey - Id of property to delete
+   */
+  deleteBoardProperty(boardId, propertyKey) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/properties/${propertyKey}`,
+    }), {
+      method: 'DELETE',
+    }));
+  }
+
+  /** Set Board Property
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/properties-setProperty)
+   * @name setBoardProperty
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   * @param {string} propertyKey - Id of property to delete
+   * @param {string} body - value to set, for objects make sure to stringify first
+   */
+  setBoardProperty(boardId, propertyKey, body) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/properties/${propertyKey}`,
+    }), {
+      method: 'PUT',
+      body,
+    }));
+  }
+
+  /** Get Board Property
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/properties-getProperty)
+   * @name getBoardProperty
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   * @param {string} propertyKey - Id of property to retrieve
+   */
+  getBoardProperty(boardId, propertyKey) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/properties/${propertyKey}`,
+    })));
+  }
+
+  /** Get All Sprints
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/sprint-getAllSprints)
+   * @name getAllSprints
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   * @param {number} [startAt=0] - The starting index of the returned sprints. Base index: 0.
+   * @param {number} [maxResults=50] - The maximum number of sprints to return per page.
+   * Default: 50.
+   * @param {string} [state] - Filters results to sprints in specified states.
+   * Valid values: future, active, closed.
+   */
+  getAllSprints(boardId, startAt = 0, maxResults = 50, state) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/sprint`,
+      query: {
+        startAt,
+        maxResults,
+        state,
+      },
+    })));
+  }
+
+  /** Get Board issues for sprint
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/sprint-getIssuesForSprint)
+   * @name getBoardIssuesForSprint
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   * @param {string} sprintId - Id of sprint to retrieve
+   * @param {number} [startAt=0] - The starting index of the returned issues. Base index: 0.
+   * @param {number} [maxResults=50] - The maximum number of issues to return per page. Default: 50.
+   * @param {string} [jql] - Filters results using a JQL query.
+   * @param {boolean} [validateQuery] - Specifies whether to validate the JQL query or not.
+   * Default: true.
+   * @param {string} [fields] - The list of fields to return for each issue.
+   */
+  getBoardIssuesForSprint(boardId, sprintId, startAt = 0, maxResults = 50, jql,
+    validateQuery = true, fields) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/sprint/${sprintId}/issue`,
+      query: {
+        startAt,
+        maxResults,
+        jql,
+        validateQuery,
+        fields,
+      },
+    })));
+  }
+
+  /** Get All Versions
+   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/version-getAllVersions)
+   * @name getAllVersions
+   * @function
+   * @param {string} boardId - Id of board to retrieve
+   * @param {number} [startAt=0] - The starting index of the returned versions. Base index: 0.
+   * @param {number} [maxResults=50] - The maximum number of versions to return per page.
+   * Default: 50.
+   * @param {string} [released] - Filters results to versions that are either released or
+   * unreleased.Valid values: true, false.
+   */
+  getAllVersions(boardId, startAt = 0, maxResults = 50, released) {
+    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
+      pathname: `/board/${boardId}/version`,
+      query: {
+        startAt,
+        maxResults,
+        released,
+      },
+    })));
+  }
 }


Test patch:
diff --git a/test/jira-tests.js b/test/jira-tests.js
index 2adcd91..6f7db03 100644
--- a/test/jira-tests.js
+++ b/test/jira-tests.js
@@ -726,6 +726,104 @@ describe('Jira API Tests', () => {
       });
     });
 
+    // Agile APIs Suite Tests
+    describe('Agile APIs Suite Tests', () => {
+      it('moveToBacklog hits proper url', async () => {
+        const result = await dummyURLCall('moveToBacklog');
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/backlog/issue');
+      });
+
+      it('getAllBoards hits proper url', async () => {
+        const result = await dummyURLCall('getAllBoards');
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board?startAt=0&maxResults=50&type=&name=&projectKeyOrId=');
+      });
+
+      it('createBoard hits proper url', async () => {
+        const result = await dummyURLCall('createBoard');
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board');
+      });
+
+      it('getBoard hits proper url', async () => {
+        const result = await dummyURLCall('getBoard', ['someBoardId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId');
+      });
+
+      it('deleteBoard hits proper url', async () => {
+        const result = await dummyURLCall('deleteBoard', ['someBoardId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId');
+      });
+
+      it('getIssuesForBacklog hits proper url', async () => {
+        const result = await dummyURLCall('getIssuesForBacklog', ['someBoardId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/backlog?startAt=0&maxResults=50&jql=&validateQuery=true&fields=');
+      });
+
+      it('getConfiguration hits proper url', async () => {
+        const result = await dummyURLCall('getConfiguration', ['someBoardId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/configuration');
+      });
+
+      it('getIssuesForBoard hits proper url', async () => {
+        const result = await dummyURLCall('getIssuesForBoard', ['someBoardId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/issue?startAt=0&maxResults=50&jql=&validateQuery=true&fields=');
+      });
+
+      it('getEpics hits proper url', async () => {
+        const result = await dummyURLCall('getEpics', ['someBoardId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/epic?startAt=0&maxResults=50&done=');
+      });
+
+      it('getBoardIssuesForEpic hits proper url', async () => {
+        const result = await dummyURLCall('getBoardIssuesForEpic', ['someBoardId', 'someEpicId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/epic/someEpicId/issue?startAt=0&maxResults=50&jql=&validateQuery=true&fields=');
+      });
+
+      it('getProjects hits proper url', async () => {
+        const result = await dummyURLCall('getProjects', ['someBoardId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/project?startAt=0&maxResults=50');
+      });
+
+      it('getProjectsFull hits proper url', async () => {
+        const result = await dummyURLCall('getProjectsFull', ['someBoardId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/project/full');
+      });
+
+      it('getBoardPropertiesKeys hits proper url', async () => {
+        const result = await dummyURLCall('getBoardPropertiesKeys', ['someBoardId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/properties');
+      });
+
+      it('deleteBoardProperty hits proper url', async () => {
+        const result = await dummyURLCall('deleteBoardProperty', ['someBoardId', 'somePropertyKey']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/properties/somePropertyKey');
+      });
+
+      it('setBoardProperty hits proper url', async () => {
+        const result = await dummyURLCall('setBoardProperty', ['someBoardId', 'somePropertyKey']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/properties/somePropertyKey');
+      });
+
+      it('getBoardProperty hits proper url', async () => {
+        const result = await dummyURLCall('getBoardProperty', ['someBoardId', 'somePropertyKey']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/properties/somePropertyKey');
+      });
+
+      it('getAllSprints hits proper url', async () => {
+        const result = await dummyURLCall('getAllSprints', ['someBoardId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/sprint?startAt=0&maxResults=50&state=');
+      });
+
+      it('getBoardIssuesForSprint hits proper url', async () => {
+        const result = await dummyURLCall('getBoardIssuesForSprint', ['someBoardId', 'someSprintId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/sprint/someSprintId/issue?startAt=0&maxResults=50&jql=&validateQuery=true&fields=');
+      });
+
+      it('getAllVersions hits proper url', async () => {
+        const result = await dummyURLCall('getAllVersions', ['someBoardId']);
+        result.should.eql('http://jira.somehost.com:8080/rest/agile/1.0/board/someBoardId/version?startAt=0&maxResults=50&released=');
+      });
+    });
+
     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');