text:
import url from 'url';

// Need to use require here for testing
// eslint-disable-next-line no-underscore-dangle
const _request = require('postman-request');

function request(uri, options) {
  return new Promise((resolve, reject) => {
    _request(uri, options, (err, httpResponse) => {
      if (err) {
        reject(err);
      } else {
        if (httpResponse.statusCode >= 400) {
          reject(httpResponse.body);
        }

        // for compatibility with request-promise
        resolve(httpResponse.body);
      }
    });
  });
}
/**
 * @name JiraApi
 * @class
 * Wrapper for the JIRA Rest Api
 * https://docs.atlassian.com/jira/REST/6.4.8/
 */
export default class JiraApi {
  /**
   * @constructor
   * @function
   * @param {JiraApiOptions} options
   */
  constructor(options) {
    this.protocol = options.protocol || 'http';
    this.host = options.host;
    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;
    this.webhookVersion = options.webHookVersion || '1.0';
    this.greenhopperVersion = options.greenhopperVersion || '1.0';
    this.baseOptions = {};

    if (options.ca) {
      this.baseOptions.ca = options.ca;
    }

    if (options.oauth && options.oauth.consumer_key && options.oauth.access_token) {
      this.baseOptions.oauth = {
        consumer_key: options.oauth.consumer_key,
        consumer_secret: options.oauth.consumer_secret,
        token: options.oauth.access_token,
        token_secret: options.oauth.access_token_secret,
        signature_method: options.oauth.signature_method || 'RSA-SHA1',
      };
    } else if (options.bearer) {
      this.baseOptions.auth = {
        user: '',
        pass: '',
        sendImmediately: true,
        bearer: options.bearer,
      };
    } else if (options.username && options.password) {
      this.baseOptions.auth = {
        user: options.username,
        pass: options.password,
      };
    }

    if (options.timeout) {
      this.baseOptions.timeout = options.timeout;
    }
  }

  /**
   * @typedef JiraApiOptions
   * @type {object}
   * @property {string} [protocol=http] - What protocol to use to connect to
   * jira? Ex: http|https
   * @property {string} host - What host is this tool connecting to for the jira
   * instance? Ex: jira.somehost.com
   * @property {string} [port] - What port is this tool connecting to jira with? Only needed for
   * none standard ports. Ex: 8080, 3000, etc
   * @property {string} [username] - Specify a username for this tool to authenticate all
   * requests with.
   * @property {string} [password] - Specify a password for this tool to authenticate all
   * requests with. Cloud users need to generate an [API token](https://confluence.atlassian.com/cloud/api-tokens-938839638.html) for this value.
   * @property {string} [apiVersion=2] - What version of the jira rest api is the instance the
   * 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?
   * Defaults to request from request-promise
   * @property {number} [timeout] - Integer containing the number of milliseconds to wait for a
   * server to send response headers (and start the response body) before aborting the request. Note
   * that if the underlying TCP connection cannot be established, the OS-wide TCP connection timeout
   * will overrule the timeout option ([the default in Linux can be anywhere from 20-120 *
   * seconds](http://www.sekuda.com/overriding_the_default_linux_kernel_20_second_tcp_socket_connect_timeout)).
   * @property {string} [webhookVersion=1.0] - What webhook version does this api wrapper need to
   * hit?
   * @property {string} [greenhopperVersion=1.0] - What webhook version does this api wrapper need
   * to hit?
   * @property {string} [ca] - Specify a CA certificate
   * @property {OAuth} [oauth] - Specify an OAuth object for this tool to authenticate all requests
   * using OAuth.
   * @property {string} [bearer] - Specify an OAuth bearer token to authenticate all requests with.
   */

  /**
   * @typedef OAuth
   * @type {object}
   * @property {string} consumer_key - The consumer entered in Jira Preferences.
   * @property {string} consumer_secret - The private RSA file.
   * @property {string} access_token - The generated access token.
   * @property {string} access_token_secret - The generated access toke secret.
   * @property {string} signature_method [signature_method=RSA-SHA1] - OAuth signurate methode
   * 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
   * Creates a requestOptions object based on the default template for one
   * @param {string} uri
   * @param {object} [options] - an object containing fields and formatting how the
   */
  makeRequestHeader(uri, options = {}) {
    return {
      rejectUnauthorized: this.strictSSL,
      method: options.method || 'GET',
      uri,
      json: true,
      ...options,
    };
  }

  /**
   * @typedef makeRequestHeaderOptions
   * @type {object}
   * @property {string} [method] - HTTP Request Method. ie GET, POST, PUT, DELETE
   */

  /**
   * @name makeUri
   * @function
   * Creates a URI object for a given pathname
   * @param {object} [options] - an object containing path information
   */
  makeUri({
    pathname, query, intermediatePath, encode = false,
  }) {
    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}${tempPath}${pathname}`,
      query,
    });
    return encode ? encodeURI(uri) : 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 {object} [options] - An options object specifying uri information
   */
  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}${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 {object} [options] - The url after the /rest/
   */
  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}${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
   */

  /**
   * @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 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
   * Does a request based on the requestOptions object
   * @param {object} requestOptions - fields on this object get posted as a request header for
   * requests to jira
   */
  async doRequest(requestOptions) {
    const options = {
      ...this.baseOptions,
      ...requestOptions,
    };

    try {
      const response = await this.request(options);

      if (response) {
        if (Array.isArray(response.errorMessages) && response.errorMessages.length > 0) {
          throw new Error(response.errorMessages.join(', '));
        }
      }

      return response;
    } catch (e) {
      throw new Error(JSON.stringify(e));
    }
  }

  /**
   * @name findIssue
   * @function
   * Find an issue in jira
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290709)
   * @param {string} issueNumber - The issue number to search for including the project key
   * @param {string} expand - The resource expansion to return additional fields in the response
   * @param {string} fields - Comma separated list of field ids or keys to retrieve
   * @param {string} properties - Comma separated list of properties to retrieve
   * @param {boolean} fieldsByKeys - False by default, used to retrieve fields by key instead of id
   */
  findIssue(issueNumber, expand, fields, properties, fieldsByKeys) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueNumber}`,
      query: {
        expand: expand || '',
        fields: fields || '*all',
        properties: properties || '*all',
        fieldsByKeys: fieldsByKeys || false,
      },
    })));
  }

  /**
   * @name downloadAttachment
   * @function
   * Download an attachment
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id288524)
   * @param {object} attachment - the attachment
   */
  downloadAttachment(attachment) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/attachment/${attachment.id}/${attachment.filename}`,
      intermediatePath: '/secure',
      encode: true,
    }), { json: false, encoding: null }));
  }

  /**
   * @name deleteAttachment
   * @function
   * Remove the attachment
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-attachments/#api-rest-api-3-attachment-id-delete)
   * @param {string} attachmentId - the attachment id
   */
  deleteAttachment(attachmentId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/attachment/${attachmentId}`,
    }), { method: 'DELETE', json: false, encoding: null }));
  }

  /**
   * @name getUnresolvedIssueCount
   * @function
   * Get the unresolved issue count
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id288524)
   * @param {string} version - the version of your product you want to find the unresolved
   * issues of.
   */
  async getUnresolvedIssueCount(version) {
    const requestHeaders = this.makeRequestHeader(
      this.makeUri({
        pathname: `/version/${version}/unresolvedIssueCount`,
      }),
    );
    const response = await this.doRequest(requestHeaders);
    return response.issuesUnresolvedCount;
  }

  /**
   * @name getProject
   * @function
   * Get the Project by project key
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id289232)
   * @param {string} project - key for the project
   */
  getProject(project) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/project/${project}`,
    })));
  }

  /**
   * @name createProject
   * @function
   * Create a new Project
   * [Jira Doc](https://docs.atlassian.com/jira/REST/latest/#api/2/project-createProject)
   * @param {object} project - with specs
   */
  createProject(project) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/project/',
    }), {
      method: 'POST',
      body: project,
    }));
  }

  /** Find the Rapid View for a specified project
   * @name findRapidView
   * @function
   * @param {string} projectName - name for the project
   */
  async findRapidView(projectName) {
    const response = await this.doRequest(this.makeRequestHeader(this.makeSprintQueryUri({
      pathname: '/rapidviews/list',
    })));

    if (typeof projectName === 'undefined' || projectName === null) return response.views;

    const rapidViewResult = response.views
      .find((x) => x.name.toLowerCase() === projectName.toLowerCase());

    return rapidViewResult;
  }

  /** Get the most recent sprint for a given rapidViewId
   * @name getLastSprintForRapidView
   * @function
   * @param {string} rapidViewId - the id for the rapid view
   */
  async getLastSprintForRapidView(rapidViewId) {
    const response = await this.doRequest(
      this.makeRequestHeader(this.makeSprintQueryUri({
        pathname: `/sprintquery/${rapidViewId}`,
      })),
    );
    return response.sprints.pop();
  }

  /** Get the issues for a rapidView / sprint
   * @name getSprintIssues
   * @function
   * @param {string} rapidViewId - the id for the rapid view
   * @param {string} sprintId - the id for the sprint
   */
  getSprintIssues(rapidViewId, sprintId) {
    return this.doRequest(this.makeRequestHeader(this.makeSprintQueryUri({
      pathname: '/rapid/charts/sprintreport',
      query: {
        rapidViewId,
        sprintId,
      },
    })));
  }

  /** Get a list of Sprints belonging to a Rapid View
   * @name listSprints
   * @function
   * @param {string} rapidViewId - the id for the rapid view
   */
  listSprints(rapidViewId) {
    return this.doRequest(this.makeRequestHeader(this.makeSprintQueryUri({
      pathname: `/sprintquery/${rapidViewId}`,
    })));
  }

  /** Get details about a Sprint
   * @name getSprint
   * @function
   * @param {string} sprintId - the id for the sprint view
   */
  getSprint(sprintId) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/sprint/${sprintId}`,
    })));
  }

  /** Add an issue to the project's current sprint
   * @name addIssueToSprint
   * @function
   * @param {string} issueId - the id of the existing issue
   * @param {string} sprintId - the id of the sprint to add it to
   */
  addIssueToSprint(issueId, sprintId) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/sprint/${sprintId}/issue`,
    }), {
      method: 'POST',
      body: {
        issues: [issueId],
      },
    }));
  }

  /** Create an issue link between two issues
   * @name issueLink
   * @function
   * @param {object} link - a link object formatted how the Jira API specifies
   */
  issueLink(link) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/issueLink',
    }), {
      method: 'POST',
      followAllRedirects: true,
      body: link,
    }));
  }

  /** List all issue link types jira knows about
   * [Jira Doc](https://docs.atlassian.com/software/jira/docs/api/REST/8.5.0/#api/2/issueLinkType-getIssueLinkTypes)
   * @name listIssueLinkTypes
   * @function
   */
  listIssueLinkTypes() {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/issueLinkType',
    })));
  }

  /** Retrieves the remote links associated with the given issue.
   * @name getRemoteLinks
   * @function
   * @param {string} issueNumber - the issue number to find remote links for.
   */
  getRemoteLinks(issueNumber) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueNumber}/remotelink`,
    })));
  }

  /**
   * @name createRemoteLink
   * @function
   * Creates a remote link associated with the given issue.
   * @param {string} issueNumber - The issue number to create the remotelink under
   * @param {object} remoteLink - the remotelink object as specified by the Jira API
   */
  createRemoteLink(issueNumber, remoteLink) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueNumber}/remotelink`,
    }), {
      method: 'POST',
      body: remoteLink,
    }));
  }

  /**
   * @name deleteRemoteLink
   * @function
   * Delete a remote link with given issueNumber and id
   * @param {string} issueNumber - The issue number to delete the remotelink under
   * @param {string} id the remotelink id
   */
  deleteRemoteLink(issueNumber, id) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueNumber}/remotelink/${id}`,
    }), {
      method: 'DELETE',
      followAllRedirects: true,
    }));
  }

  /** Get Versions for a project
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id289653)
   * @name getVersions
   * @function
   * @param {string} project - A project key to get versions for
   * @param {object} query - An object containing the query params
   */
  getVersions(project, query = {}) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/project/${project}/versions`,
      query,
    })));
  }

  /** Get details of single Version in project
   * [Jira Doc](https://docs.atlassian.com/jira/REST/cloud/#api/2/version-getVersion)
   * @name getVersion
   * @function
   * @param {string} version - The id of this version
   */
  getVersion(version) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/version/${version}`,
    })));
  }

  /** Create a version
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id288232)
   * @name createVersion
   * @function
   * @param {object} version - an object of the new version
   */
  createVersion(version) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/version',
    }), {
      method: 'POST',
      followAllRedirects: true,
      body: version,
    }));
  }

  /** Update a version
   * [Jira Doc](https://docs.atlassian.com/jira/REST/latest/#d2e510)
   * @name updateVersion
   * @function
   * @param {object} version - an new object of the version to update
   */
  updateVersion(version) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/version/${version.id}`,
    }), {
      method: 'PUT',
      followAllRedirects: true,
      body: version,
    }));
  }

  /** Delete a version
   * [Jira Doc](https://docs.atlassian.com/jira/REST/latest/#api/2/version-delete)
   * @name deleteVersion
   * @function
   * @param {string} versionId - the ID of the version to delete
   * @param {string} moveFixIssuesToId - when provided, existing fixVersions will be moved
   *                 to this ID. Otherwise, the deleted version will be removed from all
   *                 issue fixVersions.
   * @param {string} moveAffectedIssuesToId - when provided, existing affectedVersions will
   *                 be moved to this ID. Otherwise, the deleted version will be removed
   *                 from all issue affectedVersions.
   */
  deleteVersion(versionId, moveFixIssuesToId, moveAffectedIssuesToId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/version/${versionId}`,
    }), {
      method: 'DELETE',
      followAllRedirects: true,
      qs: {
        moveFixIssuesTo: moveFixIssuesToId,
        moveAffectedIssuesTo: moveAffectedIssuesToId,
      },
    }));
  }

  /** Move version
   * [Jira Doc](https://docs.atlassian.com/jira/REST/cloud/#api/2/version-moveVersion)
   * @name moveVersion
   * @function
   * @param {string} versionId - the ID of the version to delete
   * @param {string} position - an object of the new position
   */

  moveVersion(versionId, position) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/version/${versionId}/move`,
    }), {
      method: 'POST',
      followAllRedirects: true,
      body: position,
    }));
  }

  /** Pass a search query to Jira
   * [Jira Doc](https://docs.atlassian.com/jira/REST/latest/#d2e4424)
   * @name searchJira
   * @function
   * @param {string} searchString - jira query string in JQL
   * @param {object} optional - object containing any of the following properties
   * @param {integer} [optional.startAt=0]: optional starting index number
   * @param {integer} [optional.maxResults=50]: optional The maximum number of items to
   *                  return per page. To manage page size, Jira may return fewer items per
   *                  page where a large number of fields are requested.
   * @param {array} [optional.fields]: optional array of string names of desired fields
   * @param {array} [optional.expand]: optional array of string names of desired expand nodes
   */
  searchJira(searchString, optional = {}) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/search',
    }), {
      method: 'POST',
      followAllRedirects: true,
      body: {
        jql: searchString,
        ...optional,
      },
    }));
  }

  /** Create a Jira user
   * [Jira Doc](https://docs.atlassian.com/jira/REST/cloud/#api/2/user-createUser)
   * @name createUser
   * @function
   * @param {object} user - Properly Formatted User object
   */
  createUser(user) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/user',
    }), {
      method: 'POST',
      followAllRedirects: true,
      body: user,
    }));
  }

  /** Search user on Jira
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#d2e3756)
   * @name searchUsers
   * @function
   * @param {SearchUserOptions} options
   */
  searchUsers({
    username, query, startAt, maxResults, includeActive, includeInactive,
  }) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/user/search',
      query: {
        username,
        query,
        startAt: startAt || 0,
        maxResults: maxResults || 50,
        includeActive: includeActive || true,
        includeInactive: includeInactive || false,
      },
    }), {
      followAllRedirects: true,
    }));
  }

  /**
   * @typedef SearchUserOptions
   * @type {object}
   * @property {string} username - (DEPRECATED) A query string used to search username, name or
   * e-mail address
   * @property {string} query - A query string that is matched against user attributes
   * (displayName, and emailAddress) to find relevant users. The string can match the prefix of
   * the attribute's value. For example, query=john matches a user with a displayName of John
   * Smith and a user with an emailAddress of johnson@example.com. Required, unless accountId
   * or property is specified.
   * @property {integer} [startAt=0] - The index of the first user to return (0-based)
   * @property {integer} [maxResults=50] - The maximum number of users to return
   * @property {boolean} [includeActive=true] - If true, then active users are included
   * in the results
   * @property {boolean} [includeInactive=false] - If true, then inactive users
   * are included in the results
   */

  /** Get all users in group on Jira
   * @name getUsersInGroup
   * @function
   * @param {string} groupname - A query string used to search users in group
   * @param {integer} [startAt=0] - The index of the first user to return (0-based)
   * @param {integer} [maxResults=50] - The maximum number of users to return (defaults to 50).
   */
  getUsersInGroup(groupname, startAt = 0, maxResults = 50) {
    return this.doRequest(
      this.makeRequestHeader(this.makeUri({
        pathname: '/group',
        query: {
          groupname,
          expand: `users[${startAt}:${maxResults}]`,
        },
      }), {
        followAllRedirects: true,
      }),
    );
  }

  /** Get issues related to a user
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id296043)
   * @name getUsersIssues
   * @function
   * @param {string} username - username of user to search for
   * @param {boolean} open - determines if only open issues should be returned
   */
  getUsersIssues(username, open) {
    const openJql = open ? ' AND status in (Open, \'In Progress\', Reopened)' : '';
    return this.searchJira(`assignee = ${username.replace('@', '\\u0040')}${openJql}`, {});
  }

  /** Returns a user.
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-user-get)
   * @name getUser
   * @function
   * @param {string} accountId - The accountId of user to search for
   * @param {string} expand - The expand for additional info (groups,applicationRoles)
   */
  getUser(accountId, expand) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/user',
      query: {
        accountId,
        expand,
      },
    })));
  }

  /** Returns a list of all (active and inactive) users.
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-users-search-get)
   * @name getUsers
   * @function
   * @param {integer} [startAt=0] - The index of the first user to return (0-based)
   * @param {integer} [maxResults=50] - The maximum number of users to return (defaults to 50).
   */
  getUsers(startAt = 0, maxResults = 100) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/users',
      query: {
        startAt,
        maxResults,
      },
    })));
  }

  /** Add issue to Jira
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290028)
   * @name addNewIssue
   * @function
   * @param {object} issue - Properly Formatted Issue object
   */
  addNewIssue(issue) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/issue',
    }), {
      method: 'POST',
      followAllRedirects: true,
      body: issue,
    }));
  }

  /** Add a user as a watcher on an issue
   * @name addWatcher
   * @function
   * @param {string} issueKey - the key of the existing issue
   * @param {string} username - the jira username to add as a watcher to the issue
   */
  addWatcher(issueKey, username) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueKey}/watchers`,
    }), {
      method: 'POST',
      followAllRedirects: true,
      body: username,
    }));
  }

  /** Change an assignee on an issue
   * [Jira Doc](https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-assign)
   * @name assignee
   * @function
   * @param {string} issueKey - the key of the existing issue
   * @param {string} assigneeName - the jira username to add as a new assignee to the issue
   */
  updateAssignee(issueKey, assigneeName) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueKey}/assignee`,
    }), {
      method: 'PUT',
      followAllRedirects: true,
      body: { name: assigneeName },
    }));
  }

  /** Change an assignee on an issue
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-assignee-put)
   * @name updateAssigneeWithId
   * @function
   * @param {string} issueKey - the key of the existing issue
   * @param {string} userId - the jira username to add as a new assignee to the issue
   */
  updateAssigneeWithId(issueKey, userId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueKey}/assignee`,
    }), {
      method: 'PUT',
      followAllRedirects: true,
      body: { accountId: userId },
    }));
  }

  /** Delete issue from Jira
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290791)
   * @name deleteIssue
   * @function
   * @param {string} issueId - the Id of the issue to delete
   */
  deleteIssue(issueId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}`,
    }), {
      method: 'DELETE',
      followAllRedirects: true,
    }));
  }

  /** Update issue in Jira
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290878)
   * @name updateIssue
   * @function
   * @param {string} issueId - the Id of the issue to update
   * @param {object} issueUpdate - update Object as specified by the rest api
   * @param {object} query - adds parameters to the query string
   */
  updateIssue(issueId, issueUpdate, query = {}) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}`,
      query,
    }), {
      body: issueUpdate,
      method: 'PUT',
      followAllRedirects: true,
    }));
  }

  /** List Components
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290489)
   * @name listComponents
   * @function
   * @param {string} project - key for the project
   */
  listComponents(project) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/project/${project}/components`,
    })));
  }

  /** Add component to Jira
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290028)
   * @name addNewComponent
   * @function
   * @param {object} component - Properly Formatted Component
   */
  addNewComponent(component) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/component',
    }), {
      method: 'POST',
      followAllRedirects: true,
      body: component,
    }));
  }

  /** Update Jira component
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#api/2/component-updateComponent)
   * @name updateComponent
   * @function
   * @param {string} componentId - the Id of the component to update
   * @param {object} component - Properly Formatted Component
   */
  updateComponent(componentId, component) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/component/${componentId}`,
    }), {
      method: 'PUT',
      followAllRedirects: true,
      body: component,
    }));
  }

  /** Delete component from Jira
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-api-2-component-id-delete)
   * @name deleteComponent
   * @function
   * @param {string} id - The ID of the component.
   * @param {string} moveIssuesTo - The ID of the component to replace the deleted component.
   *                                If this value is null no replacement is made.
   */
  deleteComponent(id, moveIssuesTo) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/component/${id}`,
    }), {
      method: 'DELETE',
      followAllRedirects: true,
      qs: moveIssuesTo ? { moveIssuesTo } : null,
    }));
  }

  /** Get count of issues assigned to the component.
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-component-id-relatedIssueCounts-get)
   * @name relatedIssueCounts
   * @function
   * @param {string} id - Component Id.
   */
  relatedIssueCounts(id) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/component/${id}/relatedIssueCounts`,
    })));
  }

  /** Create custom Jira field
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#api/2/field-createCustomField)
   * @name createCustomField
   * @function
   * @param {object} field - Properly formatted Field object
   */
  createCustomField(field) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/field',
    }), {
      method: 'POST',
      followAllRedirects: true,
      body: field,
    }));
  }

  /** List all fields custom and not that jira knows about.
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290489)
   * @name listFields
   * @function
   */
  listFields() {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/field',
    })));
  }

  /** Add an option for a select list issue field.
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#api/2/field/{fieldKey}/option-createOption)
   * @name createFieldOption
   * @function
   * @param {string} fieldKey - the key of the select list field
   * @param {object} option - properly formatted Option object
   */
  createFieldOption(fieldKey, option) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/field/${fieldKey}/option`,
    }), {
      method: 'POST',
      followAllRedirects: true,
      body: option,
    }));
  }

  /** Returns all options defined for a select list issue field.
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#api/2/field/{fieldKey}/option-getAllOptions)
   * @name listFieldOptions
   * @function
   * @param {string} fieldKey - the key of the select list field
   */
  listFieldOptions(fieldKey) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/field/${fieldKey}/option`,
    })));
  }

  /** Creates or updates an option for a select list issue field.
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#api/2/field/{fieldKey}/option-putOption)
   * @name upsertFieldOption
   * @function
   * @param {string} fieldKey - the key of the select list field
   * @param {string} optionId - the id of the modified option
   * @param {object} option - properly formatted Option object
   */
  upsertFieldOption(fieldKey, optionId, option) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/field/${fieldKey}/option/${optionId}`,
    }), {
      method: 'PUT',
      followAllRedirects: true,
      body: option,
    }));
  }

  /** Returns an option for a select list issue field.
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#api/2/field/{fieldKey}/option-getOption)
   * @name getFieldOption
   * @function
   * @param {string} fieldKey - the key of the select list field
   * @param {string} optionId - the id of the option
   */
  getFieldOption(fieldKey, optionId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/field/${fieldKey}/option/${optionId}`,
    })));
  }

  /** Deletes an option from a select list issue field.
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#api/2/field/{fieldKey}/option-delete)
   * @name deleteFieldOption
   * @function
   * @param {string} fieldKey - the key of the select list field
   * @param {string} optionId - the id of the deleted option
   */
  deleteFieldOption(fieldKey, optionId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/field/${fieldKey}/option/${optionId}`,
    }), {
      method: 'DELETE',
      followAllRedirects: true,
    }));
  }

  /**
   * @name getIssueProperty
   * @function
   * Get Property of Issue by Issue and Property Id
   * [Jira Doc](https://docs.atlassian.com/jira/REST/cloud/#api/2/issue/{issueIdOrKey}/properties-getProperty)
   * @param {string} issueNumber - The issue number to search for including the project key
   * @param {string} property - The property key to search for
   */
  getIssueProperty(issueNumber, property) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueNumber}/properties/${property}`,
    })));
  }

  /**
   * @name getIssueChangelog
   * @function
   * List all changes for an issue, sorted by date, starting from the latest
   * [Jira Doc](https://docs.atlassian.com/jira/REST/cloud/#api/2/issue/{issueIdOrKey}/changelog)
   * @param {string} issueNumber - The issue number to search for including the project key
   * @param {integer} [startAt=0] - optional starting index number
   * @param {integer} [maxResults=50] - optional ending index number
   */
  getIssueChangelog(issueNumber, startAt = 0, maxResults = 50) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueNumber}/changelog`,
      query: {
        startAt,
        maxResults,
      },
    })));
  }

  /**
   * @name getIssueWatchers
   * @function
   * List all watchers for an issue
   * [Jira Doc](http://docs.atlassian.com/jira/REST/cloud/#api/2/issue-getIssueWatchers)
   * @param {string} issueNumber - The issue number to search for including the project key
   */
  getIssueWatchers(issueNumber) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueNumber}/watchers`,
    })));
  }

  /** List all priorities jira knows about
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290489)
   * @name listPriorities
   * @function
   */
  listPriorities() {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/priority',
    })));
  }

  /** List Transitions for a specific issue that are available to the current user
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290489)
   * @name listTransitions
   * @function
   * @param {string} issueId - get transitions available for the issue
   */
  listTransitions(issueId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}/transitions`,
      query: {
        expand: 'transitions.fields',
      },
    })));
  }

  /** Transition issue in Jira
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id290489)
   * @name transitionsIssue
   * @function
   * @param {string} issueId - the Id of the issue to delete
   * @param {object} issueTransition - transition object from the jira rest API
   */
  transitionIssue(issueId, issueTransition) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}/transitions`,
    }), {
      body: issueTransition,
      method: 'POST',
      followAllRedirects: true,
    }));
  }

  /** List all Viewable Projects
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id289193)
   * @name listProjects
   * @function
   */
  listProjects() {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/project',
    })));
  }

  /** Add a comment to an issue
   * [Jira Doc](https://docs.atlassian.com/jira/REST/latest/#id108798)
   * @name addComment
   * @function
   * @param {string} issueId - Issue to add a comment to
   * @param {string} comment - string containing comment
   */
  addComment(issueId, comment) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}/comment`,
    }), {
      body: {
        body: comment,
      },
      method: 'POST',
      followAllRedirects: true,
    }));
  }

  /** Add a comment to an issue, supports full comment object
   * [Jira Doc](https://docs.atlassian.com/jira/REST/latest/#id108798)
   * @name addCommentAdvanced
   * @function
   * @param {string} issueId - Issue to add a comment to
   * @param {object} comment - The object containing your comment data
   */
  addCommentAdvanced(issueId, comment) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}/comment`,
    }), {
      body: comment,
      method: 'POST',
      followAllRedirects: true,
    }));
  }

  /** Update comment for an issue
   * [Jira Doc](https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-updateComment)
   * @name updateComment
   * @function
   * @param {string} issueId - Issue with the comment
   * @param {string} commentId - Comment that is updated
   * @param {string} comment - string containing new comment
   * @param {object} [options={}] - extra options
   */
  updateComment(issueId, commentId, comment, options = {}) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}/comment/${commentId}`,
    }), {
      body: {
        body: comment,
        ...options,
      },
      method: 'PUT',
      followAllRedirects: true,
    }));
  }

  /**
   * @name getComments
   * @function
   * Get Comments by IssueId.
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-comment-list-post)
   * @param {string} issueId - this issue this comment is on
   */
  getComments(issueId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}/comment`,
    })));
  }

  /**
   * @name getComment
   * @function
   * Get Comment by Id.
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-comment-list-post)
   * @param {string} issueId - this issue this comment is on
   * @param {number} commentId - the id of the comment
   */
  getComment(issueId, commentId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}/comment/${commentId}`,
    })));
  }

  /**
   * @name deleteComment
   * @function
   * Delete Comments by Id.
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-comment-list-post)
   * @param {string} issueId - this issue this comment is on
   * @param {number} commentId - the id of the comment
   */
  deleteComment(issueId, commentId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}/comment/${commentId}`,
    }), {
      method: 'DELETE',
      followAllRedirects: true,
    }));
  }

  /** Add a worklog to a project
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id291617)
   * @name addWorklog
   * @function
   * @param {string} issueId - Issue to add a worklog to
   * @param {object} worklog - worklog object from the rest API
   * @param {object} newEstimate - the new value for the remaining estimate field
   * @param {object} [options={}] - extra options
   */
  addWorklog(issueId, worklog, newEstimate = null, options = {}) {
    const query = {
      adjustEstimate: newEstimate ? 'new' : 'auto',
      ...newEstimate ? { newEstimate } : {},
      ...options,
    };

    const header = {
      uri: this.makeUri({
        pathname: `/issue/${issueId}/worklog`,
        query,
      }),
      body: worklog,
      method: 'POST',
      'Content-Type': 'application/json',
      json: true,
    };

    return this.doRequest(header);
  }

  /** Get ids of worklogs modified since
   * [Jira Doc](https://docs.atlassian.com/jira/REST/cloud/#api/2/worklog-getWorklogsForIds)
   * @name updatedWorklogs
   * @function
   * @param {number} since - a date time in unix timestamp format since when updated worklogs
   * will be returned.
   * @param {string} expand - ptional comma separated list of parameters to expand: properties
   * (provides worklog properties).
   */
  updatedWorklogs(since, expand) {
    const header = {
      uri: this.makeUri({
        pathname: '/worklog/updated',
        query: { since, expand },
      }),
      method: 'GET',
      'Content-Type': 'application/json',
      json: true,
    };

    return this.doRequest(header);
  }

  /** Delete worklog from issue
   * [Jira Doc](https://docs.atlassian.com/jira/REST/latest/#d2e1673)
   * @name deleteWorklog
   * @function
   * @param {string} issueId - the Id of the issue to delete
   * @param {string} worklogId - the Id of the worklog in issue to delete
   */
  deleteWorklog(issueId, worklogId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}/worklog/${worklogId}`,
    }), {
      method: 'DELETE',
      followAllRedirects: true,
    }));
  }

  /** Update worklog from issue
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-worklogs/#api-rest-api-2-issue-issueidorkey-worklog-id-put)
   * @name updateWorklog
   * @function
   * @param {string} issueId - the Id of the issue to update
   * @param {string} worklogId - the Id of the worklog in issue to update
   * @param {string} body - value to set
   */
  updateWorklog(issueId, worklogId, body) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}/worklog/${worklogId}`,
    }), {
      method: 'PUT',
      body,
      followAllRedirects: true,
    }));
  }

  /** Deletes an issue link.
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-issueLink-linkId-delete)
   * @name deleteIssueLink
   * @function
   * @param {string} linkId - the Id of the issue link to delete
   */
  deleteIssueLink(linkId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issueLink/${linkId}`,
    }), {
      method: 'DELETE',
      followAllRedirects: true,
    }));
  }

  /** Returns worklog details for a list of worklog IDs.
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-worklog-list-post)
   * @name getWorklogs
   * @function
   * @param {array} worklogsIDs - a list of worklog IDs.
   * @param {string} expand - expand to include additional information about worklogs
   *
   */
  getWorklogs(worklogsIDs, expand) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/worklog/list',
      query: {
        expand,
      },
    }), {
      method: 'POST',
      body: {
        ids: worklogsIDs,
      },
    }));
  }

  /** Get worklogs list from a given issue
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-issue-issueIdOrKey-worklog-get)
   * @name getIssueWorklogs
   * @function
   * @param {string} issueId - the Id of the issue to find worklogs for
   * @param {integer} [startAt=0] - optional starting index number
   * @param {integer} [maxResults=1000] - optional ending index number
   */
  getIssueWorklogs(issueId, startAt = 0, maxResults = 1000) {
    return this.doRequest(this.makeRequestHeader(
      this.makeUri({
        pathname: `/issue/${issueId}/worklog`,
        query: {
          startAt,
          maxResults,
        },
      }),
    ));
  }

  /** List all Issue Types jira knows about
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id295946)
   * @name listIssueTypes
   * @function
   */
  listIssueTypes() {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/issuetype',
    })));
  }

  /** Register a webhook
   * [Jira Doc](https://developer.atlassian.com/display/JIRADEV/JIRA+Webhooks+Overview)
   * @name registerWebhook
   * @function
   * @param {object} webhook - properly formatted webhook
   */
  registerWebhook(webhook) {
    return this.doRequest(this.makeRequestHeader(this.makeWebhookUri({
      pathname: '/webhook',
    }), {
      method: 'POST',
      body: webhook,
    }));
  }

  /** List all registered webhooks
   * [Jira Doc](https://developer.atlassian.com/display/JIRADEV/JIRA+Webhooks+Overview)
   * @name listWebhooks
   * @function
   */
  listWebhooks() {
    return this.doRequest(this.makeRequestHeader(this.makeWebhookUri({
      pathname: '/webhook',
    })));
  }

  /** Get a webhook by its ID
   * [Jira Doc](https://developer.atlassian.com/display/JIRADEV/JIRA+Webhooks+Overview)
   * @name getWebhook
   * @function
   * @param {string} webhookID - id of webhook to get
   */
  getWebhook(webhookID) {
    return this.doRequest(this.makeRequestHeader(this.makeWebhookUri({
      pathname: `/webhook/${webhookID}`,
    })));
  }

  /** Delete a registered webhook
   * [Jira Doc](https://developer.atlassian.com/display/JIRADEV/JIRA+Webhooks+Overview)
   * @name issueLink
   * @function
   * @param {string} webhookID - id of the webhook to delete
   */
  deleteWebhook(webhookID) {
    return this.doRequest(this.makeRequestHeader(this.makeWebhookUri({
      pathname: `/webhook/${webhookID}`,
    }), {
      method: 'DELETE',
    }));
  }

  /** Describe the currently authenticated user
   * [Jira Doc](http://docs.atlassian.com/jira/REST/latest/#id2e865)
   * @name getCurrentUser
   * @function
   */
  getCurrentUser() {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/myself',
    })));
  }

  /** Retrieve the backlog of a certain Rapid View
   * @name getBacklogForRapidView
   * @function
   * @param {string} rapidViewId - rapid view id
   */
  getBacklogForRapidView(rapidViewId) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/xboard/plan/backlog/data',
      query: {
        rapidViewId,
      },
    })));
  }

  /** Add attachment to a Issue
   * [Jira Doc](https://docs.atlassian.com/jira/REST/latest/#api/2/issue/{issueIdOrKey}/attachments-addAttachment)
   * @name addAttachmentOnIssue
   * @function
   * @param {string} issueId - issue id
   * @param {object} readStream - readStream object from fs
   */
  addAttachmentOnIssue(issueId, readStream) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}/attachments`,
    }), {
      method: 'POST',
      headers: {
        'X-Atlassian-Token': 'nocheck',
      },
      formData: {
        file: readStream,
      },
    }));
  }

  /** Notify people related to issue
   * [Jira Doc](https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-notify)
   * @name issueNotify
   * @function
   * @param {string} issueId - issue id
   * @param {object} notificationBody - properly formatted body
   */
  issueNotify(issueId, notificationBody) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/issue/${issueId}/notify`,
    }), {
      method: 'POST',
      body: notificationBody,
    }));
  }

  /** Get list of possible statuses
   * [Jira Doc](https://docs.atlassian.com/jira/REST/latest/#api/2/status-getStatuses)
   * @name listStatus
   * @function
   */
  listStatus() {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      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,
      },
    })));
  }

  /** Get issue
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/issue-getIssue)
   * @name getIssue
   * @function
   * @param {string} issueIdOrKey - Id of issue
   * @param {string} [fields] - The list of fields to return for each issue.
   * @param {string} [expand] - A comma-separated list of the parameters to expand.
   */
  getIssue(issueIdOrKey, fields, expand) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/issue/${issueIdOrKey}`,
      query: {
        fields,
        expand,
      },
    })));
  }

  /** 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 && { 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 issue estimation for board
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/issue-getIssueEstimationForBoard)
   * @name getIssueEstimationForBoard
   * @function
   * @param {string} issueIdOrKey - Id of issue
   * @param {number} boardId - The id of the board required to determine which field
   * is used for estimation.
   */
  getIssueEstimationForBoard(issueIdOrKey, boardId) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/issue/${issueIdOrKey}/estimation`,
      query: {
        boardId,
      },
    })));
  }

  /** 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,
      },
    })));
  }

  /** Estimate issue for board
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/issue-estimateIssueForBoard)
   * @name estimateIssueForBoard
   * @function
   * @param {string} issueIdOrKey - Id of issue
   * @param {number} boardId - The id of the board required to determine which field
   * is used for estimation.
   * @param {string} body - value to set
   */
  estimateIssueForBoard(issueIdOrKey, boardId, body) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/issue/${issueIdOrKey}/estimation`,
      query: {
        boardId,
      },
    }), {
      method: 'PUT',
      body,
    }));
  }

  /** Rank Issues
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/issue-rankIssues)
   * @name rankIssues
   * @function
   * @param {string} body - value to set
   */
  rankIssues(body) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: '/issue/rank',
    }), {
      method: 'PUT',
      body,
    }));
  }

  /** 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://developer.atlassian.com/cloud/jira/software/rest/api-group-board/#api-agile-1-0-board-boardid-sprint-sprintid-issue-get)
   * @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.
   * @param {string} [expand] - A comma-separated list of the parameters to expand.
   */
  getBoardIssuesForSprint(
    boardId,
    sprintId,
    startAt = 0,
    maxResults = 50,
    jql,
    validateQuery = true,
    fields,
    expand,
  ) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/board/${boardId}/sprint/${sprintId}/issue`,
      query: {
        startAt,
        maxResults,
        jql,
        validateQuery,
        fields,
        expand,
      },
    })));
  }

  /** 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,
      },
    })));
  }

  /** Get Filter
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/filter)
   * @name getFilter
   * @function
   * @param {string} filterId - Id of filter to retrieve
   */

  getFilter(filterId) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/filter/${filterId}`,
    })));
  }

  /** Get Epic
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/epic-getEpic)
   * @name getEpic
   * @function
   * @param {string} epicIdOrKey - Id of epic to retrieve
   */
  getEpic(epicIdOrKey) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/epic/${epicIdOrKey}`,
    })));
  }

  /** Partially update epic
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/epic-partiallyUpdateEpic)
   * @name partiallyUpdateEpic
   * @function
   * @param {string} epicIdOrKey - Id of epic to retrieve
   * @param {string} body - value to set, for objects make sure to stringify first
   */
  partiallyUpdateEpic(epicIdOrKey, body) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/epic/${epicIdOrKey}`,
    }), {
      method: 'POST',
      body,
    }));
  }

  /** Get issues for epic
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/epic-getIssuesForEpic)
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/epic-getIssuesWithoutEpic)
   * @name getIssuesForEpic
   * @function
   * @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.
   */
  getIssuesForEpic(
    epicId,
    startAt = 0,
    maxResults = 50,
    jql,
    validateQuery = true,
    fields,
  ) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/epic/${epicId}/issue`,
      query: {
        startAt,
        maxResults,
        jql,
        validateQuery,
        fields,
      },
    })));
  }

  /** Move Issues to Epic
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/epic-moveIssuesToEpic)
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/epic-removeIssuesFromEpic)
   * @name moveIssuesToEpic
   * @function
   * @param {string} epicIdOrKey - Id of epic to move issue to, or 'none' to remove from epic
   * @param {array} issues - array of issues to move
   */
  moveIssuesToEpic(epicIdOrKey, issues) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/epic/${epicIdOrKey}/issue`,
    }), {
      method: 'POST',
      body: {
        issues,
      },
    }));
  }

  /** Rank Epics
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/epic-rankEpics)
   * @name rankEpics
   * @function
   * @param {string} epicIdOrKey - Id of epic
   * @param {string} body - value to set
   */
  rankEpics(epicIdOrKey, body) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/epic/${epicIdOrKey}/rank`,
    }), {
      method: 'PUT',
      body,
    }));
  }

  /**
   * @name getServerInfo
   * @function
   * Get server info
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-api-2-serverInfo-get)
   */
  getServerInfo() {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/serverInfo',
    })));
  }

  /**
   * @name getIssueCreateMetadata
   * @param {object} optional - object containing any of the following properties
   * @param {array} [optional.projectIds]: optional Array of project ids to return metadata for
   * @param {array} [optional.projectKeys]: optional Array of project keys to return metadata for
   * @param {array} [optional.issuetypeIds]: optional Array of issuetype ids to return metadata for
   * @param {array} [optional.issuetypeNames]: optional Array of issuetype names to return metadata
   * for
   * @param {string} [optional.expand]: optional Include additional information about issue
   * metadata. Valid value is 'projects.issuetypes.fields'
   * Get metadata for creating an issue.
   * [Jira Doc](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-createmeta-get)
   */
  getIssueCreateMetadata(optional = {}) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: '/issue/createmeta',
      query: optional,
    })));
  }

  /** Generic Get Request
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/2/)
   * @name genericGet
   * @function
   * @param {string} endpoint - Rest API endpoint
   */
  genericGet(endpoint) {
    return this.doRequest(this.makeRequestHeader(this.makeUri({
      pathname: `/${endpoint}`,
    })));
  }

  /** Generic Get Request to the Agile API
   * [Jira Doc](https://docs.atlassian.com/jira-software/REST/cloud/2/)
   * @name genericGet
   * @function
   * @param {string} endpoint - Rest API endpoint
   */
  genericAgileGet(endpoint) {
    return this.doRequest(this.makeRequestHeader(this.makeAgileUri({
      pathname: `/${endpoint}`,
    })));
  }
}


text:
package org.jetbrains.jps.classFilesIndex.indexer.api.storage;

import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.jps.builders.java.dependencyView.Mappings;

import java.io.File;
import java.io.IOException;

/**
 * @author Dmitry Batkovich
 */
public class ClassFilesIndexStorageWriter<K, V> extends ClassFilesIndexStorageBase<K, V> {
  private final Mappings myMappings;

  public ClassFilesIndexStorageWriter(final File indexDir,
                                      final KeyDescriptor<K> keyDescriptor,
                                      final DataExternalizer<V> valueExternalizer,
                                      final Mappings mappings) throws IOException {
    super(indexDir, keyDescriptor, valueExternalizer);
    myMappings = mappings;
  }

  public void putData(final K key, final V value, final String containingClass) {
    final int id = myMappings.getName(containingClass);
    try {
      myWriteLock.lock();
      final CompiledDataValueContainer<V> container = myCache.get(key);
      container.putValue(id, value);
    }
    finally {
      myWriteLock.unlock();
    }
  }
}

redpajama_set_name: RedPajamaGithub


text:
package org.aplikator.client.shared.descriptor;

import org.aplikator.client.local.widgets.HasFields;
import org.aplikator.client.local.widgets.SimpleBadgeWidget;

import com.google.gwt.user.client.ui.Widget;

/**
 * // Bound to DATA ??
 *
 * @author Pavel Stastny <pavel.stastny at gmail.com>
 */
//@Portable
public class SimpleBadgeDTO extends WidgetPropertyDTOBase {

    private String badgeType;

    @SuppressWarnings("unused")
    public SimpleBadgeDTO() {//TODO custom marshaller
    }

    public SimpleBadgeDTO(PropertyDTO property) {
        super(property);
    }

    public String getBadgeType() {
        return this.badgeType;
    }

    public void setBadgeType(String ltype) {
        this.badgeType = ltype;
    }

    @Override
    public Widget getWidget(HasFields form) {
        SimpleBadgeWidget simpleWidget = new SimpleBadgeWidget(this.badgeType, property, getSize());
        if (form != null) {
            form.registerDataField(simpleWidget);
        }
        return simpleWidget;
    }
}


text:
<?php
/**
 * Created by PhpStorm.
 * User: GreenPlugin
 * Date: 10.01.17
 * Time: 1:30
 */

namespace YandexPDD;

use GuzzleHttp\Client;
use Psr\Http\Message\ResponseInterface;
use YandexPDD\Entity\FillableInterface;

/**
 * Class AbstractBaseManager
 * @package YandexPDD
 */
Abstract class AbstractBaseManager
{
	/**
	 * @var string
	 */
	protected $domain;

	/**
	 * @var string
	 */
	protected $apiKey;

	/**
	 * @var Client
	 */
	protected $client;

	/**
	 * @var bool
	 */
	protected $debug = false;

	/**
	 * @var null|string
	 */
	protected $error = null;

	/**
	 * @var bool
	 */
	protected $status = true;

	/**
	 * @var null|mixed
	 */
	protected $response = null;

	/**
	 * BaseManager constructor.
	 *
	 * @param string $apiKey
	 * @param string $domain
	 */
	public function __construct($apiKey, $domain)
	{
		$this->apiKey = $apiKey;
		$this->domain = $domain;
		$this->client = new Client();
	}

	/**
	 * @param string $request
	 *
	 * @param mixed  $response
	 *
	 * @return mixed|ResponseInterface
	 */
	protected function getQuery($request, $response)
	{
		$result = $this->client->request(
			'GET',
			$request,
			[
				'headers'     => ['PddToken' => $this->apiKey],
				'http_errors' => $this->debug,
			]
		);

		return $this->processResponse($result, $response);
	}

	/**
	 * @param string $requestUrl
	 * @param array  $requestBody
	 *
	 * @param mixed  $response
	 *
	 * @return mixed|null
	 */
	protected function postQuery($requestUrl, $requestBody, $response)
	{
		$result = $this->client->request(
			'POST',
			$requestUrl,
			[
				'headers'     => ['PddToken' => $this->apiKey],
				'form_params' => $requestBody,
				'http_errors' => $this->debug,
			]
		);
		return $this->processResponse($result, $response);

	}

	/**
	 * @param ResponseInterface $response
	 *
	 * @param FillableInterface $responseObject
	 *
	 * @return mixed|null
	 */
	protected function processResponse(ResponseInterface $response, FillableInterface $responseObject)
	{
		if($response->getStatusCode() != 200){
			$this->error = $response->getStatusCode();
			$this->response = null;
			return null;
		}

		$result = json_decode($response->getBody()->getContents(), true);

		if($result == null){
			$this->error = 'bad_json';
			$this->response = null;
			return null;
		}

		if($result['success'] === 'error'){
			$this->error = $result['error'];
			$this->response = null;
			return null;
		}

		$this->response = $responseObject->fill($result);

		return $responseObject;
	}

	/**
	 * @return boolean
	 */
	public function isDebug()
	{
		return $this->debug;
	}

	/**
	 * @param boolean $debug
	 *
	 * @return AbstractBaseManager
	 */
	public function setDebug($debug)
	{
		$this->debug = $debug;

		return $this;
	}

	/**
	 * @return string
	 */
	public function getDomain()
	{
		return $this->domain;
	}

	/**
	 * @param string $domain
	 *
	 * @return AbstractBaseManager
	 */
	public function setDomain($domain)
	{
		$this->domain = $domain;

		return $this;
	}

	/**
	 * @return null|string
	 */
	public function getError()
	{
		return $this->error;
	}

	/**
	 * @return boolean
	 */
	public function isStatus()
	{
		return $this->status;
	}

	/**
	 * @return null|mixed
	 */
	abstract public function getResponse();

}


text:
- Introduced new (/message_protocols) Endpoint which returns the available message protocols list and their respective edition names.
- Updated all curl commands in documentation

## 2.1.4
- Fixed issue related to ER lookup strategy in REMReM-generate.

## 2.1.3
- Updated the eiffel-remrem-parent version from 2.0.5 to 2.0.6
- Updated the eiffel-remrem-semantics version from 2.1.0 to 2.2.1
- Excluded the log4j-to-slf4j dependency to resolve vulnerability issues.
- Uplifted eiffel-remrem-parent version from 2.0.4 to 2.0.5
- Uplifted eiffel-remrem-shared version from 2.0.4 to 2.0.5
- Removed ER retry mechanism

## 2.1.2
- Implemented the functionality to read the Jasypt encryption key from jasypt.key file

## 2.1.1
- Fixed the test case for REMReM Generate to support lenient validation for Eiffel Protocol.

## 2.1.0
- Updated REMReM Generate to support lenient validation for Eiffel Protocol.

## 2.0.18
- ER lookup control parameter lookupLimit has been set to 1 or higher.

## 2.0.17
- Fixed failed testcase in EiffelRemERLookupControllerUnitTest class.

## 2.0.16
- REMReM lookups controlled 'failIfNoneFound' and 'failIfMultipleFound' lookups per lookup
  object within an event instead of globally per call.

## 2.0.15
- Fixed the broken link which points to generate documentation in swagger.

## 2.0.14
- Uplifted eiffel-remrem-semantics version from 2.0.12 to 2.0.13.

## 2.0.13
- Added the lookupInExternalERs and lookupLimit parameters to ER lookup.

## 2.0.12
- Uplifted eiffel-remrem-parent version from 2.0.2 to 2.0.4.
- Uplifted eiffel-remrem-shared version from 2.0.2 to 2.0.4.
- Uplifted eiffel-remrem-semantics version from 2.0.9 to 2.0.12.

## 2.0.11
- Added REMReM Generate documentation in master branch
- Uplifted eiffel-remrem-semantics version from 2.0.8 to 2.0.9.

## 2.0.10
- Uplifted eiffel-remrem-semantics version from 2.0.6 to 2.0.8.

## 2.0.9
- Fixed for ER Lookup functionality do not work due to hardcoded context path.

## 2.0.8
- REMReM generate service is being started when ER not configured.

## 2.0.7
- Update pom.xml

## 2.0.6
- Added ER Lookup configurations for a link and code to handle the lookup towards ER when generating events.
- Uplifted eiffel-remrem-generate version from 2.0.5 to 2.0.6

## 2.0.5
- Uplifted eiffel-remrem-parent version from 2.0.1 to 2.0.2.
- Uplifted eiffel-remrem-shared version from 2.0.1 to 2.0.2.
- Uplifted eiffel-remrem-semantics version from 2.0.5 to 2.0.6.

## 2.0.4
- Uplifted eiffel-remrem-parent version from 2.0.0 to 2.0.1.
- Uplifted eiffel-remrem-shared version from 2.0.0 to 2.0.1.
- Uplifted eiffel-remrem-semantics version from 2.0.4 to 2.0.5.

## 2.0.3
- Upgraded eiffel-remrem-semantics version from 2.0.3 to 2.0.4

## 2.0.2
- Upgraded eiffel-remrem-semantics version from 2.0.2 to 2.0.3.

## 2.0.1
- Upgraded eiffel-remrem-semantics version from 2.0.1 to 2.0.2.

## 2.0.0
- Upgraded eiffel-remrem-semantics version from 1.0.1 to 2.0.1.
- Parent group changed from Ericsson to eiffel-community.

## 1.0.1
- Changed Link from http://ericsson.github.io to https://eiffel-community.github.io.
- Upgraded eiffel-remrem-semantics version from 1.0.0 to 1.0.1.

## 1.0.0
- Upgraded eiffel-remrem-parent version from 0.0.8 to 1.0.0.
- Upgraded eiffel-remrem-shared version from 0.4.2 to 1.0.0.
- Upgraded eiffel-remrem-semantics version from 0.5.3 to 1.0.0.

## 0.10.9
- Upgraded eiffel-remrem-parent version from 0.0.7 to 0.0.8.
- Upgraded eiffel-remrem-shared version from 0.4.1 to 0.4.2.
- Upgraded eiffel-remrem-semantics version from 0.5.2 to 0.5.3.

## 0.10.8
- Adaptation of Spring application to execute without external Tomcat installation.

## 0.10.7
- Upgraded eiffel-remrem-parent version from 0.0.6 to 0.0.7.
- Upgraded eiffel-remrem-shared version from 0.4.0 to 0.4.1.
- Upgraded eiffel-remrem-semantics version from 0.5.1 to 0.5.2.

## 0.10.6
- Updated versions of parent from 0.0.5 to 0.0.6 and shared from 0.3.9 to 0.4.0
- Uplifted semantics version from 0.5.0 to 0.5.1

## 0.10.5
- Updated parent, shared and semantics version

## 0.10.4
- Updated remrem shared and semantics version

## 0.10.3
- Updated parent version

## 0.10.2
- Migrated from gradle to maven

## 0.10.1
- Updated versions of few dependencies.

## 0.10.0
- Removed based64 encryption mechanism for ldap manager password

## 0.9.9
- - Added jasypt-spring-boot-starter dependency to support open text encryption

## 0.9.8
- Removed Protocol Interface dependency
- Uplifted semantics version to 0.4.1

## 0.9.7
- Added more information in error messages for generate service
- Uplifted semantics version to 0.4.0

## 0.9.6
- Added swagger for generate service
- Added more verifications for generate service
- Added presentation in html for all GET endpoints
- Changed year in copyright headers from 2017 to 2018

## 0.9.5
- CLI changed to spring boot application and added logback support to remrem generate

## 0.9.4
- Moved ldap related functionality from shared
- Added functionality to disable the Remrem Generate Authentication.
- Uplifted remrem-semantics version to 0.3.7

## 0.9.3
- Added endpoint to get event templates
- Uplifted remrem-semantics to 0.3.5
- Uplifted remrem-protocol-interface to 0.0.7

## 0.9.2
- Uplifted remrem-semantics version to 0.3.2 to support continuous operation events

## 0.9.1
- Uplifted generate version to resolve versions endpoint issue

## 0.9.0
- Uplifted remrem-semantics to 0.3.1 to override given input meta.source.serializer GAV information

## 0.8.9
- Uplifted remrem-protocol version to 0.0.5 and remrem-semantics version to 0.3.0

## 0.8.8
- Uplifted remrem-semantics version to 0.2.9 to accept case insensitive eventType through query params.

## 0.8.7
- Added validation that can detect duplicate keys in the JSON request body

## 0.8.6
- Uplift remrem semantics to 0.2.8 and eiffelinputEventType checking.

## 0.8.5
- Uplift remrem semantics to 0.2.7 and eiffelevents as camelcase.

## 0.8.4
- Removed spring configurations from config.properties file and handled through code.

## 0.8.3
- Changed REMReM generate to read required properties from tomcat/conf/config.properties for service.

## 0.8.2
- Uplifted remrem-semantics version to 0.2.6 to support links validation.

## 0.8.1
- Added copyright headers to the source code.
- Uplifted remrem-shared version to 0.3.3 to get the versions of generate and all loaded protocols.

## 0.8.0
- Updated remrem-shared version to 0.3.2 to support base64 encryption functionality for Ldap manager password.

## 0.7.9
- Moved ldap related functionality to shared

## 0.7.8
- Added HttpStatus codes for generated ouput

## 0.7.7
- Changed the project structure to build seperate
  binaries for CLI and Service.

## 0.7.6
- Updated the documentation for generate.

## 0.7.5
- upgraded semantics version in build.gradle to support all
  EiffelEvents in the eiffel repo from topic-drop4 branch
- Changed json inputs for test cases

## 0.7.4
- Fixed domain property injection in SecurityConfig

## 0.7.3
- Added single protocol end-point in remrem generate.

## 0.7.2
- Explicitly stated some dependencies.

## 0.7.1
- Fix authentication required even when activedirectory.enabled=false

## 0.7.0
- Added functionality to list endpoint versions

## 0.6.3
- Removed unused dependency.

## 0.6.2
- added optional authentication to an Active Directory server for all
  REST endpoints

## 0.6.1
- Removed printstack traces to stdout.

## 0.6.0
- added functionality to load dynamic protocol jars in both cli as well
  as service

## 0.5.9
- update gradle.build to resolve transient dependecies conflicts during
  integration tests
- refactoring to fix error when help flag passed
- update gradle and version of eiffel semantics library
- added unit and integration tests for cli


text:
#ifndef BOOST_ASIO_BUFFERED_WRITE_STREAM_FWD_HPP
#define BOOST_ASIO_BUFFERED_WRITE_STREAM_FWD_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

namespace boost {
namespace asio {

template <typename Stream>
class buffered_write_stream;

} // namespace asio
} // namespace boost

#endif // BOOST_ASIO_BUFFERED_WRITE_STREAM_FWD_HPP


text:
//import { APP_BASE_HREF } from '@angular/common';
import { disableDeprecatedForms, provideForms } from '@angular/forms';
import { enableProdMode } from '@angular/core';
import { bootstrap } from '@angular/platform-browser-dynamic';

import { APP_ROUTER_PROVIDERS } from './app.routes';
import {LocationStrategy, HashLocationStrategy} from '@angular/common';
import { AppComponent } from './app.component';

if ('<%= ENV %>' === 'prod') { enableProdMode(); }

/**
 * Bootstraps the application and makes the ROUTER_PROVIDERS and the APP_BASE_HREF available to it.
 * @see https://angular.io/docs/ts/latest/api/platform-browser-dynamic/index/bootstrap-function.html
 */
bootstrap(AppComponent, [
  disableDeprecatedForms(),
  provideForms(),
  APP_ROUTER_PROVIDERS,
  {provide: LocationStrategy, useClass: HashLocationStrategy}
]);

// In order to start the Service Worker located at "./worker.js"
// uncomment this line. More about Service Workers here
// https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers
//
// if ('serviceWorker' in navigator) {
//   (<any>navigator).serviceWorker.register('./worker.js').then((registration: any) =>
//       console.log('ServiceWorker registration successful with scope: ', registration.scope))
//     .catch((err: any) =>
//       console.log('ServiceWorker registration failed: ', err));
// }


text:
__author__ = 'rvuine'

from micropsi_core.nodenet.netapi import NetAPI

class TheanoNetAPI(NetAPI):
    """
    Theano / numpy extension of the NetAPI, giving native modules access to bulk operations and efficient
    data structures for machine learning purposes.
    """

    def __init__(self, nodenet):
        super(TheanoNetAPI, self).__init__(nodenet)
        self.__nodenet = nodenet

    @property
    def floatX(self):
        return self.__nodenet.numpyfloatX

    def announce_nodes(self, nodespace_uid, numer_of_nodes, average_element_per_node):
        self.__nodenet.announce_nodes(nodespace_uid, numer_of_nodes, average_element_per_node)

    def get_selectors(self, group):
        """
        Returns The indices for the elements for the given group, as an ndarray of ints.
        These indices are valid in a, w, and theta.
        """
        return self.__nodenet.nodegroups[group]

    def get_a(self):
        """
        Returns the theano shared variable with the activation vector
        """
        return self.__nodenet.a

    def get_w(self):
        """
        Returns the theano shared variable with the link weights
        Caution: Changing non-zero values to zero or zero-values to non-zero will lead to inconsistencies.
        """
        return self.__nodenet.w

    def get_theta(self):
        """
        Returns the theano shared variable with the "theta" parameter values
        """
        return self.__nodenet.g_theta


text:
import Leaflet from 'leaflet';

import latlngListType from './types/latlngList';
import PopupContainer from './PopupContainer';

export default class Polygon extends PopupContainer {
  componentWillMount() {
    super.componentWillMount();
    const { map, positions, ...props } = this.props;
    this.leafletElement = Leaflet.polygon(positions, props);
  }

  componentDidUpdate(prevProps) {
    if (this.props.positions !== prevProps.positions) {
      this.leafletElement.setLatLngs(this.props.positions);
    }
  }
}

Polygon.propTypes = {
  positions: latlngListType.isRequired
};


text:
<?php

namespace VdtPlus\Training;

class Smuggling extends AbstractTraining {
    protected $weapons = 0;
    protected $ammunition = 0;
    protected $dollar = 1500;
    protected $duration = 9600;
    protected $points = 23.5;
    protected $requirements = array("Encargos" => 1);
}


text:
layout: post
title: "Balti and Bioinformatics On Air: 21st January 2015"
description: ""
category: balti
tags: [balti bioinformatics]
---

The plan this year for the triumphant **Balti and Bioinformatics**
series is to alternate between virtual, "on-air" meetings (where
sadly you will need to provide your own balti curry) and real
life ones which will be mainly held in Birmingham, but may be
in other places in England or Wales. Ideally I would plan to run
6 meetings a year.

So ... to kick us off:

**Balti and Bioinformatics On-Air**

This meeting's theme is *open data and reproducible bioinformatics*.

<iframe width="560" height="315" src="https://www.youtube.com/embed/ZACVcJt0oJA" frameborder="0" allowfullscreen></iframe>

Wednesday 21st January, 4pm GMT (=11am EST, =8am PST, 00:00 China)

20 minute talks each (interactive Q&A through Google Hangouts enabled)

Draft schedule:

+0m C. Titus Brown, UC Davis: Self-interest: can it be a strategy for convincing scientists to share pre-publication data in a useful way?

+30m Scott Edmunds, GigaScience: New models for open data publishing

+50m Jane Landolin, Pacific Biosciences: Open Pacific Biosciences data for model organisms

+70m Michael Barton, JGI: nucleotid.es for de novo assembly benchmarking and Docker

+90m Nick Loman, U. Birmingham: Nanopore data updates and the "poreathon".

+100m Dave Lunt, University of Hull: ReproPhylo - Reproducible phylogenetics.

+110m Discussion (are we on the right track? Challenges? Containers and VMs - beneficial or the wrong direction?)


text:
module Licensee
  module ProjectFiles
    class ReadmeFile < Licensee::ProjectFiles::LicenseFile
      EXTENSIONS = %w[md markdown mdown txt rdoc rst].freeze
      SCORES = {
        /\AREADME\z/i                                       => 1.0,
        /\AREADME\.(#{Regexp.union(EXTENSIONS).source})\z/i => 0.9
      }.freeze

      TITLE_REGEX = /licen[sc]e:?/i.freeze
      UNDERLINE_REGEX = /\n[-=]+/m.freeze
      CONTENT_REGEX = /^
          (?:                                # Header lookbehind
            [\#=]+\s#{TITLE_REGEX}\s*[\#=]*  # Start of hashes or rdoc header
          |
            #{TITLE_REGEX}#{UNDERLINE_REGEX} # Start of underlined header
          )$
          (.*?)                              # License content
          (?=^                               # Header or end of file lookahead
            (?:
              [\#=]+                         # Next hash or rdoc header
            |
              [^\n]+#{UNDERLINE_REGEX}       # Next of underlined header
            )
          |
            \z                               # End of file
          )
        /mix.freeze

      def possible_matchers
        super.push(Matchers::Reference)
      end

      def self.name_score(filename)
        SCORES.each do |pattern, score|
          return score if pattern&.match?(filename)
        end
        0.0
      end

      def self.license_content(content)
        match = CONTENT_REGEX.match(content)
        match[1].strip if match
      end
    end
  end
end


text:
const fs = require('fs');
const { execSync } = require('child_process');

const buffer = fs.readFileSync(0);
const { component, env } = JSON.parse(buffer.toString());

let command = `terrahub build --format json --include ${component}`;
if (env && env !== 'default') {
  command += ` --env ${env}`;
}

const result = execSync(command, {
  cwd: `${__dirname}/..`,
  shell: true
}).toString().trim();

console.log(result);


text:
Android:
* Rewrote a large portion of the code
* Migrated away from beacon 'ranging' and towards device 'scanning'

iOS:
* Removed iOS support (temporary)

## Version 0.9.0 (August 29, 2016) (Adam)
Forked Original Repository.
Android:
* Fixed issues for Estimote Android SDK (0.11.0)
* Added toggleVerbose method.
* Removed iOS code (temporary)


text:
import * as React from 'react';
import * as Popper from 'popper.js';
import { CSSModule } from '../index';

export interface UncontrolledTooltipProps extends React.HTMLAttributes<HTMLElement> {
  [key: string]: any;
  target: string | HTMLElement;
  container?: string | HTMLElement;
  delay?: number | {show: number, hide: number};
  className?: string;
  innerClassName?: string;
  autohide?: boolean;
  placement?: Popper.Placement;
  modifiers?: Popper.Modifiers;
  cssModule?: CSSModule;
  fade?: boolean;
  flip?: boolean;
}

export interface TooltipProps extends UncontrolledTooltipProps {
  toggle?: () => void;
  isOpen?: boolean;
}

declare class Tooltip<T> extends React.Component<TooltipProps> {}
export default Tooltip;