appcenter-cli
Version:
Command line tool for Visual Studio App Center
339 lines (338 loc) • 19.4 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const commandline_1 = require("../../../util/commandline");
const interaction_1 = require("../../../util/interaction");
const util_1 = require("util");
const _ = require("lodash");
const Os = require("os");
const list_of_users_helper_1 = require("../../../util/misc/list-of-users-helper");
const org_users_helper_1 = require("../lib/org-users-helper");
const debug = require("debug")("appcenter-cli:commands:orgs:collaborators:update");
const pLimit = require("p-limit");
let OrgCollaboratorsUpdateCommand = class OrgCollaboratorsUpdateCommand extends commandline_1.Command {
run(client) {
return __awaiter(this, void 0, void 0, function* () {
// validate that string and file properties are not specified simultaneously
this.validateParameters();
// loading user lists and lists of org users and org invitations
const collaboratorsToAddPromise = list_of_users_helper_1.getUsersList(this.collaboratorsToAdd, this.collaboratorsToAddFile, debug);
const collaboratorsToDeletePromise = list_of_users_helper_1.getUsersList(this.collaboratorsToDelete, this.collaboratorsToDeleteFile, debug);
const collaboratorsToMakeAdminsPromise = list_of_users_helper_1.getUsersList(this.collaboratorsToMakeAdmins, this.collaboratorsToMakeAdminsFile, debug);
const adminsToMakeCollaboratorsPromise = list_of_users_helper_1.getUsersList(this.adminsToMakeCollaborators, this.adminsToMakeCollaboratorsFile, debug);
const usersInvitedToOrgPromise = this.getUsersInvitedToOrg(client);
const usersJoinedOrgPromise = org_users_helper_1.getOrgUsers(client, this.name, debug);
// showing spinner while prerequisites are being loaded
const [collaboratorsToAdd, collaboratorsToDelete, collaboratorsToMakeAdmins, adminsToMakeCollaborators, usersInvitedToOrg, usersJoinedOrg,] = yield interaction_1.out.progress("Loading prerequisites...", Promise.all([
collaboratorsToAddPromise,
collaboratorsToDeletePromise,
collaboratorsToMakeAdminsPromise,
adminsToMakeCollaboratorsPromise,
usersInvitedToOrgPromise,
usersJoinedOrgPromise,
]));
let addedCollaborators;
let deletedCollaborators;
if (collaboratorsToAdd.length || collaboratorsToDelete.length) {
const joinedUserEmailsToUserObject = this.toUserEmailMap(usersJoinedOrg);
const userJoinedOrgEmails = Array.from(joinedUserEmailsToUserObject.keys());
addedCollaborators = yield interaction_1.out.progress("Adding collaborators...", this.addCollaborators(client, collaboratorsToAdd, usersInvitedToOrg, userJoinedOrgEmails));
// updating list of invited users
addedCollaborators.forEach((collaborator) => {
if (usersInvitedToOrg.indexOf(collaborator) === -1) {
usersInvitedToOrg.push(collaborator);
}
});
deletedCollaborators = yield interaction_1.out.progress("Deleting collaborators...", this.deleteCollaborators(client, collaboratorsToDelete, usersInvitedToOrg, joinedUserEmailsToUserObject));
}
else {
addedCollaborators = [];
deletedCollaborators = [];
}
let toAdmins;
let toCollaborators;
if (collaboratorsToMakeAdmins.length || adminsToMakeCollaborators.length) {
// just deleted org users should be excluded from role changing
const joinedUserEmailsToUserObject = this.toUserEmailMap(usersJoinedOrg.filter((user) => deletedCollaborators.indexOf(user.email) === -1));
toAdmins = yield interaction_1.out.progress("Changing role to admins...", this.changeUsersRole(client, collaboratorsToMakeAdmins, joinedUserEmailsToUserObject, "admin"));
// updating roles after setting admins
Array.from(joinedUserEmailsToUserObject.values())
.filter((user) => collaboratorsToMakeAdmins.indexOf(user.email) > -1)
.forEach((user) => (user.role = "admin"));
toCollaborators = yield interaction_1.out.progress("Changing role to collaborator...", this.changeUsersRole(client, adminsToMakeCollaborators, joinedUserEmailsToUserObject, "collaborator"));
}
else {
toAdmins = [];
toCollaborators = [];
}
interaction_1.out.text((result) => {
const stringArray = [];
if (result.addedCollaborators.length) {
stringArray.push(`Successfully added ${result.addedCollaborators.length} collaborators to organization`);
}
if (result.deletedCollaborators.length) {
stringArray.push(`Successfully deleted ${result.deletedCollaborators.length} collaborators from organization`);
}
if (result.toAdmins.length) {
stringArray.push(`Successfully changed roles for ${result.toAdmins.length} collaborators to "admin"`);
}
if (result.toCollaborators.length) {
stringArray.push(`Successfully changed roles for ${result.toCollaborators.length} admins to "collaborator"`);
}
return stringArray.join(Os.EOL);
}, { addedCollaborators, deletedCollaborators, toAdmins, toCollaborators });
return commandline_1.success();
});
}
validateParameters() {
if (!(this.collaboratorsToAdd ||
this.collaboratorsToAddFile ||
this.collaboratorsToDelete ||
this.collaboratorsToDeleteFile ||
this.collaboratorsToMakeAdmins ||
this.collaboratorsToMakeAdminsFile ||
this.adminsToMakeCollaborators ||
this.adminsToMakeCollaboratorsFile)) {
throw commandline_1.failure(commandline_1.ErrorCodes.InvalidParameter, "nothing to update");
}
if (this.collaboratorsToAdd && this.collaboratorsToAddFile) {
throw commandline_1.failure(commandline_1.ErrorCodes.InvalidParameter, "parameters '--add-collaborators' and '--add-collaborators-file' are mutually exclusive");
}
if (this.collaboratorsToDelete && this.collaboratorsToDeleteFile) {
throw commandline_1.failure(commandline_1.ErrorCodes.InvalidParameter, "parameters '--delete-collaborators' and '--delete-collaborators-file' are mutually exclusive");
}
if (this.collaboratorsToMakeAdmins && this.collaboratorsToMakeAdminsFile) {
throw commandline_1.failure(commandline_1.ErrorCodes.InvalidParameter, "parameters '--make-admins' and '--make-admins-file' are mutually exclusive");
}
if (this.adminsToMakeCollaborators && this.adminsToMakeCollaboratorsFile) {
throw commandline_1.failure(commandline_1.ErrorCodes.InvalidParameter, "parameters '--make-collaborators' and '--make-collaborators-file' are mutually exclusive");
}
}
getUsersInvitedToOrg(client) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
try {
const result = yield client.orgInvitations.listPending(this.name);
return result.map((invitation) => invitation.email);
}
catch (error) {
if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
throw commandline_1.failure(commandline_1.ErrorCodes.InvalidParameter, `organization ${this.name} doesn't exist`);
}
else {
debug(`Failed to get list of user invitations for organization ${this.name} - ${util_1.inspect(error)}`);
throw commandline_1.failure(commandline_1.ErrorCodes.Exception, `failed to get list of user invitations for organization ${this.name}`);
}
}
});
}
getLimiter() {
return pLimit(10);
}
addCollaborators(client, collaborators, usersInvitedToOrg, usersJoinedOrg) {
return __awaiter(this, void 0, void 0, function* () {
const limiter = this.getLimiter();
const filteredCollaborators = _.difference(collaborators, usersJoinedOrg); // no need to add users already joined org
yield Promise.all(filteredCollaborators.map((collaborator) => limiter(() => usersInvitedToOrg.some((invited) => invited === collaborator)
? this.resendInvitationToUser(client, collaborator)
: this.sendInvitationToUser(client, collaborator))));
return filteredCollaborators;
});
}
sendInvitationToUser(client, collaborator) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
try {
yield client.orgInvitations.create(this.name, collaborator);
}
catch (error) {
if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
throw commandline_1.failure(commandline_1.ErrorCodes.InvalidParameter, `organization ${this.name} doesn't exist`);
}
else {
debug(`Failed to send invitation for ${collaborator} to organization ${this.name} - ${util_1.inspect(error)}`);
throw commandline_1.failure(commandline_1.ErrorCodes.Exception, `failed to send invitation for ${collaborator} to organization ${this.name}`);
}
}
});
}
resendInvitationToUser(client, collaborator) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
try {
yield client.orgInvitations.sendNewInvitation(this.name, collaborator);
}
catch (error) {
if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
throw commandline_1.failure(commandline_1.ErrorCodes.InvalidParameter, `organization ${this.name} doesn't exist`);
}
else {
debug(`Failed to re-send invitation for ${collaborator} to organization ${this.name} - ${util_1.inspect(error)}`);
throw commandline_1.failure(commandline_1.ErrorCodes.Exception, `failed to re-send invitation for ${collaborator} to organization ${this.name}`);
}
}
});
}
deleteCollaborators(client, collaborators, usersInvitedToOrg, joinedUserEmailsToUserObject) {
return __awaiter(this, void 0, void 0, function* () {
const limiter = this.getLimiter();
const userActions = [];
const collaboratorsForDeletion = [];
for (const collaborator of collaborators) {
if (joinedUserEmailsToUserObject.has(collaborator)) {
// user has already joined the org, deleting them
userActions.push(limiter(() => this.deleteUserFromOrganization(client, joinedUserEmailsToUserObject.get(collaborator).name)));
collaboratorsForDeletion.push(collaborator);
}
else if (usersInvitedToOrg.indexOf(collaborator) > -1) {
// user was invited to the org, cancel invite
userActions.push(limiter(() => this.cancelUserInvitation(client, collaborator)));
collaboratorsForDeletion.push(collaborator);
}
// otherwise nothing to do
}
yield Promise.all(userActions);
return collaboratorsForDeletion;
});
}
cancelUserInvitation(client, collaborator) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
try {
yield client.orgInvitations.delete(this.name, collaborator);
}
catch (error) {
if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
throw commandline_1.failure(commandline_1.ErrorCodes.InvalidParameter, `organization ${this.name} doesn't exist`);
}
else {
debug(`Failed to cancel invitation for ${collaborator} to organization ${this.name} - ${util_1.inspect(error)}`);
throw commandline_1.failure(commandline_1.ErrorCodes.Exception, `failed to cancel invitation for ${collaborator} to organization ${this.name}`);
}
}
});
}
deleteUserFromOrganization(client, collaboratorName) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
try {
yield client.users.removeFromOrg(this.name, collaboratorName);
}
catch (error) {
if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
throw commandline_1.failure(commandline_1.ErrorCodes.InvalidParameter, `organization ${this.name} doesn't exist`);
}
else {
debug(`Failed to delete user ${collaboratorName} from organization ${this.name} - ${util_1.inspect(error)}`);
throw commandline_1.failure(commandline_1.ErrorCodes.Exception, `failed to delete user ${collaboratorName} from organization ${this.name}`);
}
}
});
}
changeUsersRole(client, collaborators, userJoinedOrgToRole, role) {
return __awaiter(this, void 0, void 0, function* () {
const limiter = this.getLimiter();
// no need to change role for non-collaborators and collaborators with target role
const filteredCollaboratorsNames = collaborators
.filter((collaborator) => userJoinedOrgToRole.has(collaborator) && userJoinedOrgToRole.get(collaborator).role !== role)
.map((collaborator) => userJoinedOrgToRole.get(collaborator).name);
yield Promise.all(filteredCollaboratorsNames.map((collaboratorName) => limiter(() => this.changeUserRole(client, collaboratorName, role))));
return filteredCollaboratorsNames;
});
}
changeUserRole(client, collaboratorName, role) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
try {
yield client.users.updateOrgRole(this.name, collaboratorName, {
role,
});
}
catch (error) {
if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
throw commandline_1.failure(commandline_1.ErrorCodes.InvalidParameter, `organization ${this.name} doesn't exist`);
}
else {
debug(`Failed to change role of ${collaboratorName} to ${role} - ${util_1.inspect(error)}`);
throw commandline_1.failure(commandline_1.ErrorCodes.Exception, `failed to change role of ${collaboratorName} to ${role}`);
}
}
});
}
toUserEmailMap(users) {
return new Map(users.map((user) => [user.email, user]));
}
};
__decorate([
commandline_1.help("Name of the organization"),
commandline_1.shortName("n"),
commandline_1.longName("name"),
commandline_1.required,
commandline_1.hasArg
], OrgCollaboratorsUpdateCommand.prototype, "name", void 0);
__decorate([
commandline_1.help("List of collaborators to add"),
commandline_1.shortName("c"),
commandline_1.longName("add-collaborators"),
commandline_1.hasArg
], OrgCollaboratorsUpdateCommand.prototype, "collaboratorsToAdd", void 0);
__decorate([
commandline_1.help("Path to the list of collaborators to add"),
commandline_1.shortName("C"),
commandline_1.longName("add-collaborators-file"),
commandline_1.hasArg
], OrgCollaboratorsUpdateCommand.prototype, "collaboratorsToAddFile", void 0);
__decorate([
commandline_1.help("List of collaborators to delete"),
commandline_1.shortName("d"),
commandline_1.longName("delete-collaborators"),
commandline_1.hasArg
], OrgCollaboratorsUpdateCommand.prototype, "collaboratorsToDelete", void 0);
__decorate([
commandline_1.help("Path to the list of collaborators to delete"),
commandline_1.shortName("D"),
commandline_1.longName("delete-collaborators-file"),
commandline_1.hasArg
], OrgCollaboratorsUpdateCommand.prototype, "collaboratorsToDeleteFile", void 0);
__decorate([
commandline_1.help("List of collaborators to make admins"),
commandline_1.shortName("a"),
commandline_1.longName("make-admins"),
commandline_1.hasArg
], OrgCollaboratorsUpdateCommand.prototype, "collaboratorsToMakeAdmins", void 0);
__decorate([
commandline_1.help("Path to the list of collaborators to make admins"),
commandline_1.shortName("A"),
commandline_1.longName("make-admins-file"),
commandline_1.hasArg
], OrgCollaboratorsUpdateCommand.prototype, "collaboratorsToMakeAdminsFile", void 0);
__decorate([
commandline_1.help("List of admins to make collaborators"),
commandline_1.shortName("m"),
commandline_1.longName("make-collaborators"),
commandline_1.hasArg
], OrgCollaboratorsUpdateCommand.prototype, "adminsToMakeCollaborators", void 0);
__decorate([
commandline_1.help("Path to the list of admins to make collaborators"),
commandline_1.shortName("M"),
commandline_1.longName("make-collaborators-file"),
commandline_1.hasArg
], OrgCollaboratorsUpdateCommand.prototype, "adminsToMakeCollaboratorsFile", void 0);
OrgCollaboratorsUpdateCommand = __decorate([
commandline_1.help("Update list of organization collaborators")
], OrgCollaboratorsUpdateCommand);
exports.default = OrgCollaboratorsUpdateCommand;