Source: api/user.js

"use strict";

var fs = require('fs');
var path = require('path');

module.exports = UserClient;

/**
 * Used to access Jira REST endpoints in '/rest/api/2/user'
 *
 * @param {JiraClient} jiraClient
 * @constructor UserClient
 */
function UserClient(jiraClient) {
    this.jiraClient = jiraClient;

    /**
     * Get a user. This resource cannot be accessed anonymously.
     *
     * @method getUser
     * @memberOf UserClient#
     * @param opts The request options sent to the Jira API
     * @param {string} [opts.accountId] The account ID of the user
     * @param {string} [opts.username] The name of the user to retrieve.
     * @param {string} [opts.userKey] The key of the user to retrieve.
     * @param {string} [opts.expand] The fields to be expanded.
     * @param {callback} [callback] Called when the user has been retrieved.
     * @return {Promise} Resolved when the user has been retrieved.
     */
    this.getUser = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user'),
            method: 'GET',
            json: true,
            followAllRedirects: true,
            qs: {
                accountId: opts.accountId,
                username: opts.username,
                key: opts.userKey,
                expand: opts.expand
            }
        };

        if (opts.expand) {
            options.qs.expand = '';
            opts.expand.forEach(function (ex) {
                options.qs.expand += ex + ','
            });
        }

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

    /**
     * Removes user.
     *
     * @method deleteUser
     * @memberOf UserClient#
     * @param opts The request options sent to the Jira API
     * @param opts.username The name of the user to delete.
     * @param opts.userKey The key of the user to delete.
     * @param [callback] Called when the user has been deleted.
     * @return {Promise} Resolved when the user has been deleted.
     */
    this.deleteUser = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user'),
            method: 'DELETE',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username,
                key: opts.userKey
            }
        };

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

    /**
     * Create user. By default created user will not be notified with email. If password field is not set then password
     * will be randomly generated.
     *
     * @method createUser
     * @memberOf UserClient#
     * @param opts The request options sent to the Jira API.
     * @param opts.user See {@link https://docs.atlassian.com/jira/REST/latest/#d2e4049}
     * @param [callback] Called when the user has been created.
     * @return {Promise} Resolved when the user has been created.
     */
    this.createUser = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user'),
            method: 'POST',
            json: true,
            followAllRedirects: true,
            body: opts.user
        };

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

    /**
     * Modify user. The "value" fields present will override the existing value. Fields skipped in request will not be
     * changed.
     *
     * @method editUser
     * @memberOf UserClient#
     * @param opts The request options sent to the Jira API
     * @param opts.user See {@link https://docs.atlassian.com/jira/REST/latest/#d2e4081}
     * @param opts.username The name of the user to edit.
     * @param opts.userKey The key of the user to edit.
     * @param [callback] Called when the user has been edited.
     * @return {Promise} Resolved when the user has been edited.
     */
    this.editUser = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user'),
            method: 'PUT',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username,
                key: opts.userKey
            },
            body: opts.user
        };

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

    /**
     * Returns a list of users that match the search string and can be assigned issues for all the given projects. This
     * resource cannot be accessed anonymously.
     *
     * @method multiProjectSearchAssignable
     * @memberOf UserClient#
     * @param {Object} opts The request options sent to the Jira API
     * @param {string} opts.username The name of the user to search.
     * @param {Array} opts.projectKeys The keys of the projects we are finding assignable users for
     * @param {number} [opts.startAt] The index of the first user to return (0-based)
     * @param {number} [opts.maxResults] The maximum number of users to return (defaults to 50). The maximum allowed
     *     value is 1000. If you specify a value that is higher than this number, your search results will be
     *     truncated.
     * @param [callback] Called when the search results have been retrieved.
     * @return {Promise} Resolved when the search results have been retrieved.
     */
    this.multiProjectSearchAssignable = function (opts, callback) {
        var projectKeyString = '';
        if (opts.projectKeys) {
            opts.projectKeys.forEach(function (key) {
                projectKeyString += key + ',';
            });
            projectKeyString = projectKeyString.slice(0, -1);
        }
        var options = {
            uri: this.jiraClient.buildURL('/user/assignable/multiProjectSearch'),
            method: 'GET',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username,
                projectKeys: projectKeyString,
                startAt: opts.startAt,
                maxResults: opts.maxResults
            }
        };
        return this.jiraClient.makeRequest(options, callback);
    };

    /**
     * Returns a list of users that match the search string. This resource cannot be accessed anonymously. Please note
     * that this resource should be called with an issue key when a list of assignable users is retrieved for editing.
     * For create only a project key should be supplied. The list of assignable users may be incorrect if it's called
     * with the project key for editing.
     *
     * @method searchAssignable
     * @memberOf UserClient#
     * @param {Object} opts The request options sent to the Jira API
     * @param {string} opts.username The username
     * @param {string} opts.project The key of the project we are finding assignable users for
     * @param {string} [opts.issueKey] The issue key for the issue being edited we need to find assignable users for.
     * @param {number} [opts.startAt] The index of the first user to return (0-based)
     * @param {number} [opts.maxResults] The maximum number of users to return (defaults to 50). The maximum allowed
     *     value is 1000. If you specify a value that is higher than this number, your search results will be
     *     truncated.
     * @param {number} [opts.actionDescriptorId]
     * @param [callback] Called when the search results have been retrieved.
     * @return {Promise} Resolved when the search results have been retrieved.
     */
    this.searchAssignable = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user/assignable/search'),
            method: 'GET',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username,
                project: opts.project,
                issueKey: opts.issueKey,
                startAt: opts.startAt,
                maxResults: opts.maxResults,
                actionDescriptorId: opts.actionDescriptorId
            }
        };
        return this.jiraClient.makeRequest(options, callback);
    };

    /**
     * Creates temporary avatar. Creating a temporary avatar is part of a 3-step process in uploading a new avatar for
     * a user: upload, crop, confirm.
     *
     * @method createTemporaryAvatar
     * @memberOf UserClient#
     * @param {Object} opts The request options sent to the Jira API
     * @param {string} opts.username The username
     * @param {string} opts.filepath The path to the file to upload.
     * @param [callback] Called when the temporary avatar has been uploaded.
     * @return {Promise} Resolved when the temporary avatar has been uploaded.
     */
    this.createTemporaryAvatar = function (opts, callback) {
        var extension = path.extname(opts.filepath).slice(1);
        var baseName = path.basename(opts.filepath);
        var fileSize = fs.statSync(opts.filepath).size;

        extension = extension == 'jpg' ? 'jpeg' : extension;

        var options = {
            uri: this.jiraClient.buildURL('/user/avatar/temporary'),
            method: 'POST',
            followAllRedirects: true,
            qs: {
                username: opts.username,
                filename: baseName,
                size: fileSize
            },
            body: fs.readFileSync(opts.filepath),
            headers: {
                "X-Atlassian-Token": 'no-check',
                "Content-Type": 'image/' + extension
            }
        };
        return this.jiraClient.makeRequest(options, callback);
    };

    /**
     * Converts temporary avatar into a real avatar
     *
     * @method convertTemporaryAvatar
     * @memberOf UserClient#
     * @param {Object} opts The request options sent to the Jira API
     * @param {string} opts.username The username
     * @param {Object} opts.avatarId The id of the temporary avatar to convert.
     * @param [callback] Called when the avatar has been converted
     * @return {Promise} Resolved when the avatar has been converted
     */
    this.convertTemporaryAvatar = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user/avatar/'),
            method: 'PUT',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username
            },
            body: { id: opts.avatarId },
            headers: {
                "X-Atlassian-Token": 'no-check'
            }
        };
        return this.jiraClient.makeRequest(options, callback, 'Avatar Converted');
    };

    /**
     * Deletes avatar
     *
     * @method deleteAvatar
     * @memberOf UserClient#
     * @param {Object} opts The request options sent to the Jira API
     * @param {string} opts.username The username
     * @param {Object} opts.avatarId The id of the temporary avatar to delete.
     * @param [callback] Called when the avatar has been deleted.
     * @return {Promise} Resolved when the avatar has been deleted.
     */
    this.deleteAvatar = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user/avatar/' + opts.avatarId),
            method: 'DELETE',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username
            }
        };
        return this.jiraClient.makeRequest(options, callback, 'Avatar Deleted');
    };

    /**
     * Returns all avatars which are visible for the currently logged in user.
     *
     * @method getAvatars
     * @memberOf UserClient#
     * @param {Object} opts The request options sent to the Jira API
     * @param {string} opts.username The username
     * @param [callback] Called when the avatars have been retrieved.
     * @return {Promise} Resolved when the avatars have been retrieved.
     */
    this.getAvatars = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user/avatars'),
            method: 'GET',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username
            }
        };
        return this.jiraClient.makeRequest(options, callback);
    };

    /**
     * Returns the default columns for the given user. Admin permission will be required to get columns for a user
     * other than the currently logged in user.
     *
     * @method getDefaultColumns
     * @memberOf UserClient#
     * @param {Object} opts The request options sent to the Jira API
     * @param {string} opts.username The username
     * @param [callback] Called when the columns have been retrieved.
     * @return {Promise} Resolved when the columns have been retrieved.
     */
    this.getDefaultColumns = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user/columns'),
            method: 'GET',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username
            }
        };
        return this.jiraClient.makeRequest(options, callback);
    };

    /**
     * Sets the default columns for the given user. Admin permission will be required to get columns for a user other
     * than the currently logged in user.
     *
     * @method setDefaultColumns
     * @memberOf UserClient#
     * @param {Object} opts The request options sent to the Jira API
     * @param {string} opts.username The username
     * @param {Array} opts.columns The names of the new columns.  See {@link
        *     https://docs.atlassian.com/jira/REST/latest/#d2e4566}
     * @param [callback] Called when the columns have been set.
     * @return {Promise} Resolved when the columns have been set.
     */
    this.setDefaultColumns = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user/columns'),
            method: 'PUT',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username
            },
            body: {
                columns: opts.columns
            }
        };
        return this.jiraClient.makeRequest(options, callback, 'Default Columns Set');
    };

    /**
     * Reset the default columns for the given user to the system default. Admin permission will be required to get
     * columns for a user other than the currently logged in user.
     *
     * @method resetDefaultColumns
     * @memberOf UserClient#
     * @param {Object} opts The request options sent to the Jira API
     * @param {string} opts.username The username
     * @param [callback] Called when the columns have been reset.
     * @return {Promise} Resolved when the columns have been reset.
     */
    this.resetDefaultColumns = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user/columns'),
            method: 'DELETE',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username
            }
        };
        return this.jiraClient.makeRequest(options, callback, 'Default Columns Reset');
    };

    /**
     * Modify user password.
     *
     * @method changePassword
     * @memberOf UserClient#
     * @param opts The request options sent to the Jira API
     * @param opts.username The name of the user for which to change the password.
     * @param opts.userKey The key of the user for which to change the password.
     * @param opts.password The new password.
     * @param [callback] Called when the password has been set.
     * @return {Promise} Resolved when the password has been set.
     */
    this.changePassword = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user/password'),
            method: 'PUT',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username,
                key: opts.userKey
            },
            body: {
                password: opts.password
            }
        };
        return this.jiraClient.makeRequest(options, callback, 'Password Changed');
    };

    /**
     * Returns a list of active users that match the search string and have all specified permissions for the project
     * or issue.
     *
     * This resource can be accessed by users with ADMINISTER_PROJECT permission for the project or global
     * ADMIN or SYSADMIN rights.
     *
     * @method searchPermissions
     * @memberOf UserClient#
     * @param {Object} opts The request options sent to the jira API
     * @param {string} opts.username The username filter, list includes all users if unspecified
     * @param {Array} opts.permissions Array of permissions for project or issue returned users must have, see
     *     [Permissions]{@link
        *     https://developer.atlassian.com/static/javadoc/jira/6.0/reference/com/atlassian/jira/security/Permissions.Permission.html}
        *     JavaDoc for the list of all possible permissions.
     * @param {string} [opts.issueKey] the issue key for the issue for which returned users have specified permissions.
     * @param {string} [opts.projectKey] the optional project key to search for users with if no issueKey is supplied.
     * @param {number} [opts.startAt] the index of the first user to return (0-based)
     * @param {number} [opts.maxResults] the maximum number of users to return (defaults to 50). The maximum allowed
     *     value is 1000. If you specify a value that is higher than this number, your search results will be
     *     truncated.
     * @param [callback] Called when the search results are retrieved.
     * @return {Promise} Resolved when the search results are retrieved.
     */
    this.searchPermissions = function (opts, callback) {
        var permissions = '';
        if (opts.permissions) {
            opts.permissions.forEach(function (s) {
                permissions += s + ','
            });
            permissions = permissions.slice(0, -1);
        }
        var options = {
            uri: this.jiraClient.buildURL('/user/permission/search'),
            method: 'GET',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username,
                permissions: permissions,
                issueKey: opts.issueKey,
                projectKey: opts.projectKey,
                startAt: opts.startAt,
                maxResults: opts.maxResults
            }
        };
        return this.jiraClient.makeRequest(options, callback);
    };

    /**
     * Returns a list of users matching query with highlighting. This resource cannot be accessed anonymously.
     *
     * @method searchPicker
     * @memberOf UserClient#
     * @param opts The request options sent to the Jira API.
     * @param {string} opts.query
     * @param {number} [opts.maxResults=50]
     * @param {boolean} [opts.showAvatar=false]
     * @param {string} [opts.exclude]
     * @param [callback] Called when the search results are retrieved.
     * @return {Promise} Resolved when the search results are retrieved.
     */
    this.searchPicker = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user/picker'),
            method: 'GET',
            json: true,
            followAllRedirects: true,
            qs: {
                query: opts.query,
                maxResults: opts.maxResults,
                showAvatar: opts.showAvatar,
                exclude: opts.exclude
            }
        };
        return this.jiraClient.makeRequest(options, callback);
    };

    /**
     * Returns a list of users that match the search string. This resource cannot be accessed anonymously.
     *
     * @method search
     * @memberOf UserClient#
     * @param {Object} [opts] The request options sent to the Jira API.
     * @param {string} [opts.query]
     * @param {string} [opts.username] A query string used to search username, name or e-mail address
     * @param {string} [opts.accountId]
     * @param {number} [opts.startAt=0] the index of the first user to return (0-based)
     * @param {number} [opts.maxResults=50] the maximum number of users to return (defaults to 50). The maximum allowed
     *     value is 1000. If you specify a value that is higher than this number, your search results will be
     *     truncated.
     * @param {boolean} [opts.includeActive=true] If true, then active users are included in the results (default true)
     * @param {boolean} [opts.includeInactive=false] If true, then inactive users are included in the results (default
     *     false)
     * @param {string} [opts.property]
     * @param {callback} [callback] Called when the search results are retrieved.
     * @return {Promise} Resolved when the search results are retrieved.
     */
    this.search = function (opts, callback) {
        opts = opts || {};

        var options = {
            uri: this.jiraClient.buildURL('/user/search'),
            method: 'GET',
            json: true,
            followAllRedirects: true,
            qs: {
                query: opts.query,
                username: opts.username,
                accountId: opts.accountId,
                startAt: opts.startAt,
                maxResults: opts.maxResults,
                includeActive: opts.includeActive,
                includeInactive: opts.includeInactive,
                property: opts.property
            }
        };
        return this.jiraClient.makeRequest(options, callback);
    };

    /**
    * Returns a list of users that match the search string. This resource cannot be accessed anonymously.
    *
    * @method all
    * @memberOf UserClient#
    * @param {Object} opts The request options sent to the Jira API.
    * @param {number} [opts.startAt=0] the index of the first user to return (0-based)
    * @param {number} [opts.maxResults=50] the maximum number of users to return (defaults to 50).
    * @param {callback} [callback] Called when the search results are retrieved.
    * @return {Promise} Resolved when the search results are retrieved.
    */
    this.all = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/users/search'),
            method: 'GET',
            followAllRedirects: true,
            json: true,
            qs: {
                maxResults: opts.maxResults,
                startAt: opts.startAt
            }
        };

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

    /**
     * Returns a list of active users that match the search string. This resource cannot be accessed anonymously. Given
     * an issue key this resource will provide a list of users that match the search string and have the browse issue
     * permission for the issue provided.
     *
     * @method viewIssueSearch
     * @memberOf UserClient#
     * @param {Object} opts The request options sent to the Jira API.
     * @param {string} opts.username A query string used to search username, name or e-mail address
     * @param {string} [opts.issueKey] the issue key for the issue being edited we need to find viewable users for.
     * @param {string} [opts.projectKey] the optional project key to search for users with if no issueKey is supplied.
     * @param {number} [opts.startAt=0] the index of the first user to return (0-based)
     * @param {number} [opts.maxResults=50] the maximum number of users to return (defaults to 50). The maximum allowed
     * @param [callback] Called when data has been retrieved
     * @return {Promise} Resolved when data has been retrieved
     */
    this.viewIssueSearch = function (opts, callback) {
        var options = {
            uri: this.jiraClient.buildURL('/user/viewissue/search'),
            method: 'GET',
            json: true,
            followAllRedirects: true,
            qs: {
                username: opts.username,
                issueKey: opts.issueKey,
                projectKey: opts.projectKey,
                startAt: opts.startAt,
                maxResults: opts.maxResults
            }
        };
        return this.jiraClient.makeRequest(options, callback);
    };
}