Source: api/board.js

'use strict';

module.exports = AgileBoardClient;

/**
 * Used to access Jira REST endpoints in '/rest/agile/1.0/dashboard'
 * @param {JiraClient} jiraClient
 * @constructor AgileBoardClient
 */
function AgileBoardClient(jiraClient) {
  this.jiraClient = jiraClient;

  /**
   * Get a list of all dashboards, optionally filtering them.
   *
   * @method getAllBoards
   * @memberOf AgileBoardClient#
   * @param {Object} [opts] The request options to send to the Jira API
   * @param {string} [opts.type] Limits returning boards of a specific type: `scrum` or `kanban`.
   * @param {number} [opts.startAt] The index of the first dashboard to return (0-based). must be 0 or a multiple of
   *     maxResults
   * @param {string} [opts.name] Filters results to boards that match or partially match the specified name.
   * @param {string} [opts.projectKeyOrId] Filters results to boards that are relevant to a project. Relevance meaning that
   *     the jql filter defined in board contains a reference to a project.
   * @param {number} [opts.maxResults] A hint as to the the maximum number of dashboards to return in each call. Note that the
   *     JIRA server reserves the right to impose a maxResults limit that is lower than the value that a client
   *     provides, dues to lack or resources or any other condition. When this happens, your results will be
   *     truncated. Callers should always check the returned maxResults to determine the value that is effectively
   *     being used.
   * @param {string} [opts.accountIdLocation]
   * @param {string} [opts.userkeyLocation]
   * @param {string} [opts.usernameLocation]
   * @param {string} [opts.projectLocation]
   * @param {boolean} [opts.includePrivate] Appends private boards to the end of the list. The name and type fields are
   *      excluded for security reasons.
   * @param {boolean} [opts.negateLocationFiltering] If set to true, negate filters used for querying by location.
   *      By default false.
   * @param {string} [opts.orderBy] Ordering of the results by a given field. If not provided, values will not be
   *      sorted. Valid values: name.
   * @param {string} [opts.expand] List of fields to expand for each board. Valid values: admins, permissions.
   * @param {function} [callback] Called when the dashboards have been retrieved.
   * @return {Promise} Resolved when the dashboards have been retrieved.
   */
  this.getAllBoards = function (opts, callback) {
    opts = opts || {};

    var options = {
      uri: this.jiraClient.buildAgileURL('/board'),
      method: 'GET',
      json: true,
      followAllRedirects: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults,
        type: opts.type,
        name: opts.name,
        projectKeyOrId: opts.projectKeyOrId,
        accountIdLocation: opts.accountIdLocation,
        userkeyLocation: opts.userkeyLocation,
        usernameLocation: opts.usernameLocation,
        projectLocation: opts.projectLocation,
        includePrivate: opts.includePrivate,
        negateLocationFiltering: opts.negateLocationFiltering,
        orderBy: opts.orderBy,
        expand: opts.expand
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  /**
   * Creates a board
   *
   * @method createBoard
   * @memberOf AgileBoardClient#
   * @param {string} name Must be less than 255 characters.
   * @param {string} type Valid values: scrum, kanban
   * @param {number} filterId ID of a filter that the user has permissions to view. Note, if the
   *  user does not have the 'Create shared objects' permission and tries to create a shared board,
   *  a private board will be created instead (remember that board sharing depends on the filter sharing).
   * @param {object} location The container that the board will be located in. location must include the
   *  type property (Valid values: project, user). If choosing 'project', then a project must be specified
   *  by a projectKeyOrId property in location. If choosing 'user', the current user is chosen by default.
   *  The projectKeyOrId property should not be provided.
   * @param {function} [callback] Called when the sprint has been created.
   * @return {Promise} Resolved when the sprint has been created.
   */
  this.createBoard = function (name, type, filterId, location, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board'),
      method: 'POST',
      followAllRedirects: true,
      json: true,
      body: {
        name,
        type,
        filterId,
        location
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  // TODO add JsDoc
  this.getBoardByFilterId = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('board/filter/' + opts.filterId),
      method: 'GET',
      followAllRedirects: true,
      json: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  /**
   * Get a single agile board.
   *
   * @method getBoard
   * @memberOf AgileBoardClient#
   * @param opts The request options sent to the Jira API.
   * @param {number} opts.boardId The agile board id.
   * @param {function} [callback] Called when the dashboard has been retrieved
   * @return {Promise} Resolved when the dashboard has been retrieved
   */
  this.getBoard = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId),
      method: 'GET',
      json: true,
      followAllRedirects: true,
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  /**
   * Deletes the board. Admin without the view permission can still remove the board.
   *
   * @method deleteBoard
   * @memberOf AgileBoardClient#
   * @param opts The request options sent to the Jira API.
   * @param {number} opts.boardId The agile board id.
   * @param {function} [callback] Called when the dashboard has been retrieved
   * @return {Promise} Resolved when the dashboard has been retrieved
   */
  this.deleteBoard = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId),
      method: 'DELETE',
      json: true,
      followAllRedirects: true
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  /**
   * Get a list of all issues from the board's backlog, for the given board Id.
   *
   * @method getIssuesForBacklog
   * @memberOf AgileBoardClient#
   * @param opts The request options to send to the Jira API
   * @param {number} opts.boardId The agile board id.
   * @param {string} [opts.jql] Filters results using a JQL query.
   * @param {boolean} [opts.validateQuery] Specifies whether to valide the JQL query.
   * @param {Array<string> | string} [opts.fields] The list of fields to return for each issue.
   * @param {number} [opts.startAt] The index of the first dashboard to return (0-based). must be 0 or a multiple of
   *     maxResults
   * @param {number} [opts.maxResults] A hint as to the the maximum number of issues to return in each call. Note that the
   *     JIRA server reserves the right to impose a maxResults limit that is lower than the value that a client
   *     provides, dues to lack or resources or any other condition. When this happens, your results will be
   *     truncated. Callers should always check the returned maxResults to determine the value that is effectively
   *     being used.
   * @param {function} [callback] Called when the backlog issues have been retrieved.
   * @return {Promise} Resolved when the backlog issues have been retrieved.
   */
  this.getIssuesForBacklog = function (opts, callback) {
    let fields;

    if (opts.fields) {
      if (typeof opts.fields === 'string') fields = opts.fields; // backward compatibility
      else fields = opts.fields.join(',');
    }

    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/backlog'),
      method: 'GET',
      json: true,
      followAllRedirects: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults,
        jql: opts.jql,
        validateQuery: opts.validateQuery,
        fields: fields,
        expand: opts.expand
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  /**
   * Get configuration for a board
   *
   * @method getConfiguration
   * @memberOf AgileBoardClient#
   * @param opts The request options to send to the Jira API
   * @param {number} opts.boardId The agile board id.
   * @param {function} [callback] Called when the board configuration has been retrieved.
   * @return {Promise} Resolved when the board configuration has been retrieved.
   */
  this.getConfiguration = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL(
        '/board/' + opts.boardId + '/configuration'
      ),
      method: 'GET',
      json: true,
      followAllRedirects: true
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  /**
   * Returns all epics from the board, for the given board ID. This only includes epics that the user has permission to
   * view. Note, if the user does not have permission to view the board, no epics will be returned at all.
   *
   * @method getEpics
   * @memberOf AgileBoardClient#
   * @param opts The request options to send to the Jira API
   * @param {number} opts.boardId The agile board id.
   * @param {number} [opts.startAt] The starting index of the returned epics. Base index: 0. See the 'Pagination'
   *      section at the top of this page for more details.
   * @param {number} [opts.maxResults] The maximum number of epics to return per page. Default: 50. See the 'Pagination'
   *      section at the top of this page for more details.
   * @param {string} [opts.done] Filters results to epics that are either done or not done. Valid values: true, false.
   * @param {function} [callback] Called when the board configuration has been retrieved.
   * @return {Promise} Resolved when the board configuration has been retrieved.
   */
  this.getEpics = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL(
        '/board/' + opts.boardId + '/epic'
      ),
      method: 'GET',
      json: true,
      followAllRedirects: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults,
        done: opts.done
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  /**
   * Returns all issues that do not belong to any epic on a board, for a given board ID. This only includes issues that
   * the user has permission to view. Issues returned from this resource include Agile fields, like sprint,
   * closedSprints, flagged, and epic. By default, the returned issues are ordered by rank.
   * 
   * @method getIssuesWithoutEpic
   * @memberOf AgileBoardClient#
   * @param opts
   * @param {number | string} opts.boardId
   * @param {number} [opts.startAt] The starting index of the returned issues. Base index: 0. See the 'Pagination'
   *      section at the top of this page for more details.
   * @param {number} [opts.maxResults] The maximum number of issues to return per page. Default: 50. See the
   *      'Pagination' section at the top of this page for more details. Note, the total number of issues returned is
   *      limited by the property 'jira.search.views.default.max' in your Jira instance. If you exceed this limit,
   *      your results will be truncated.
   * @param {string} [opts.jql] Filters results using a JQL query. If you define an order in your JQL query, it will
   *      override the default order of the returned issues.
   *      Note that username and userkey have been deprecated as search terms for this parameter. See the migration guide
   *      for details. Use accountId instead.
   * @param {boolean} [opts.validateQuery] Specifies whether to validate the JQL query or not. Default: true.
   * @param {Array<string>} [opts.fields] The list of fields to return for each issue. By default, all navigable and
   *      Agile fields are returned.
   * @param {string} [opts.expand] A comma-separated list of the parameters to expand.
   * @param {function} [callback] Called when the board configuration has been retrieved.
   * @return {Promise} Resolved when the board configuration has been retrieved.
   */
  this.getIssuesWithoutEpic = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL(
        '/board/' + opts.boardId + '/epic/none/issue'
      ),
      method: 'GET',
      json: true,
      followAllRedirects: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults,
        jql: opts.jql,
        validateQuery: opts.validateQuery,
        fields: opts.fields ? opts.fields.join(',') : undefined,
        expand: opts.expand
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  // TODO add JsDoc
  this.getIssuesForEpic = function (opts, callback) {
    var options = {
      uri: 'board' + opts.boardId + '/epic/' + opts.epicId + '/issue',
      method: 'GET',
      json: true,
      followAllRedirects: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults,
        jql: opts.jql,
        validateQuery: opts.validateQuery,
        fields: opts.fields ? opts.fields.join(',') : undefined,
        expand: opts.expand
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  // TODO add JsDoc
  this.getFeaturesForBoard = function (opts, callback) {
    var options = {
      uri: 'board' + opts.boardId + '/features',
      method: 'GET',
      json: true,
      followAllRedirects: true
    }

    return this.jiraClient.makeRequest(options, callback);
  };

  // TODO add JsDoc
  this.toggleFeatures = function (opts, callback) {
    var options = {
      uri: 'board' + opts.boardId + '/features',
      method: 'PUT',
      json: true,
      followAllRedirects: true,
      body: {
        boardId: opts.boardIdBody,
        feature: opts.feature,
        enabling: opts.enabling
      }
    }

    return this.jiraClient.makeRequest(options, callback);
  };

  /**
   * Get a list of all issues associated with an agile board
   *
   * @method getIssuesForBoard
   * @memberOf AgileBoardClient#
   * @param opts The request options to send to the Jira API
   * @param opts.boardId The agile board id.
   * @param [opts.startAt] The index of the first issue to return (0-based). must be 0 or a multiple of
   *     maxResults
   * @param [opts.maxResults] A hint as to the the maximum number of issues to return in each call. Note that the
   *     JIRA server reserves the right to impose a maxResults limit that is lower than the value that a client
   *     provides, dues to lack or resources or any other condition. When this happens, your results will be
   *     truncated. Callers should always check the returned maxResults to determine the value that is effectively
   *     being used.
   * @param [opts.jql] Filters results using a JQL query. If you define an order in your JQL query, it will override
   *     the default order of the returned issues. Note that username and userkey have been deprecated as search terms
   *     for this parameter. See the migration guide for details. Use accountId instead.
   * @param [opts.fields] The list of fields to return for each issue. By default, all navigable and Agile fields are
   *     returned.
   * @param [opts.expand] The parameters to expand
   * @param [callback] Called when the issues have been retrieved.
   * @return {Promise} Resolved when the issues have been retrieved.
   */
  this.getIssuesForBoard = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/issue'),
      method: 'GET',
      json: true,
      followAllRedirects: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults,
        jql: opts.jql,
        validateQuery: opts.validateQuery,
        fields: opts.fields ? opts.fields.join(',') : undefined,
        expand: opts.expand
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  // TODO add JsDoc
  this.moveIssuesToBoard = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/issue'),
      method: 'POST',
      json: true,
      followAllRedirects: true,
      qs: {
        issues: opts.issues ? opts.issues.join(',') : undefined,
        rankBeforeIssue: opts.rankBeforeIssue,
        rankAfterIssue: opts.rankAfterIssue,
        rankCustomFieldId: opts.rankCustomFieldId
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  // TODO add JsDoc
  this.getProjects = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/project'),
      method: 'GET',
      json: true,
      followAllRedirects: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  // TODO add JsDoc
  this.getProjectsFull = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/project/full'),
      method: 'GET',
      json: true,
      followAllRedirects: true
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  // TODO add JsDoc
  this.getBoardPropertyKeys = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/properties'),
      method: 'GET',
      json: true,
      followAllRedirects: true
    };

    return this.jiraClient.makeRequest(options, callback);
  }

  // TODO add JsDoc
  this.getBoardProperty = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/properties/' + opts.propertyKey),
      method: 'GET',
      json: true,
      followAllRedirects: true
    };

    return this.jiraClient.makeRequest(options, callback);
  }

  /**
   * Sets the value of the specified board's property. You can use this resource to store a
   * custom data against the board identified by the id. The user who stores the data is required
   * to have permissions to modify the board.
   * 
   * @method setBoardProperty
   * @memberof AgileBoardClient
   * @param {Object} opts
   * @param {string | number} opts.boardId
   * @param {string | number} opts.propertyKey
   * @param {any} opts.property specified board's property.
   * @param {callback} [callback]
   * @returns {Promise}
   */
  this.setBoardProperty = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/properties/' + opts.propertyKey),
      method: 'PUT',
      json: true,
      followAllRedirects: true,
      body: opts.property
    };

    return this.jiraClient.makeRequest(options, callback);
  }

  // TODO add JsDoc
  this.deleteBoardProperty = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/properties/' + opts.propertyKey),
      method: 'DELETE',
      json: true,
      followAllRedirects: true
    };

    return this.jiraClient.makeRequest(options, callback);
  }

  // TODO add JsDoc
  this.getAllQuickFilters = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/quickfilter'),
      method: 'GET',
      json: true,
      followAllRedirects: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  }

  // TODO add JsDoc
  this.getQuickFilter = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/quickfilter/' + opts.quickFilterId),
      method: 'GET',
      json: true,
      followAllRedirects: true
    };

    return this.jiraClient.makeRequest(options, callback);
  }

  /**
   * Get a list of sprints associated with an agile board
   * 
   * @deprecated Use board.getAllSprints
   *
   * @method getSprintsForBoard
   * @memberOf AgileBoardClient#
   * @param opts The request options to send to the Jira API
   * @param opts.boardId The agile board id.
   * @param [opts.startAt] The index of the first sprint to return (0-based). must be 0 or a multiple of
   *     maxResults
   * @param [opts.maxResults] A hint as to the the maximum number of sprints to return in each call. Note that the
   *     JIRA server reserves the right to impose a maxResults limit that is lower than the value that a client
   *     provides, dues to lack or resources or any other condition. When this happens, your results will be
   *     truncated. Callers should always check the returned maxResults to determine the value that is effectively
   *     being used.
   * @param [opts.state] Optionally filter by state, e.g. 'active'.
   * @param callback Called when the sprints have been retrieved.
   * @return {Promise} Resolved when the sprints have been retrieved.
   */
  this.getSprintsForBoard = function (opts, callback) {
    return this.getAllSprints(opts, callback);
  };

  /**
   * Get a list of projects associated board
   *
   * @method getProjectsForBoard
   * @memberOf AgileBoardClient#
   * @param opts The request options to send to the Jira API
   * @param opts.boardId The agile board id.
   * @param [opts.startAt] The index of the first sprint to return (0-based). must be 0 or a multiple of
   *     maxResults
   * @param [opts.maxResults] A hint as to the the maximum number of sprints to return in each call. Note that the
   *     JIRA server reserves the right to impose a maxResults limit that is lower than the value that a client
   *     provides, dues to lack or resources or any other condition. When this happens, your results will be
   *     truncated. Callers should always check the returned maxResults to determine the value that is effectively
   *     being used.
   * @param callback Called when the sprints have been retrieved.
   * @return {Promise} Resolved when the sprints have been retrieved.
   */
  this.getProjectsForBoard = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/project'),
      method: 'GET',
      json: true,
      followAllRedirects: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  /**
   * Get reports for associated board
   *
   * @method getReportsForBoard
   * @memberOf AgileBoardClient#
   * @param {Object} opts The request options to send to the Jira API
   * @param {number} opts.boardId The agile board id.
   * @param {function} [callback] Called when the sprints have been retrieved.
   * @return {Promise} Resolved when the sprints have been retrieved.
   */
  this.getReportsForBoard = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/reports'),
      method: 'GET',
      json: true,
      followAllRedirects: true
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  // TODO add JsDoc
  this.getAllSprints = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/sprint'),
      method: 'GET',
      json: true,
      followAllRedirects: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults,
        state: opts.state
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  // TODO add JsDoc
  this.getIssuesForSprint = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/sprint/' + opts.sprintId + '/issue'),
      method: 'GET',
      json: true,
      followAllRedirects: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults,
        jql: opts.jql,
        validateQuery: opts.validateQuery,
        fields: opts.fields ? opts.fields.join(',') : undefined,
        expand: opts.expand
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };

  // TODO add JsDoc
  this.getAllVersions = function (opts, callback) {
    var options = {
      uri: this.jiraClient.buildAgileURL('/board/' + opts.boardId + '/version'),
      method: 'GET',
      json: true,
      followAllRedirects: true,
      qs: {
        startAt: opts.startAt,
        maxResults: opts.maxResults,
        released: opts.released
      }
    };

    return this.jiraClient.makeRequest(options, callback);
  };
}