1const columns = require('cli-columns') 2const libteam = require('libnpmteam') 3 4const otplease = require('../utils/otplease.js') 5 6const BaseCommand = require('../base-command.js') 7class Team extends BaseCommand { 8 static description = 'Manage organization teams and team memberships' 9 static name = 'team' 10 static usage = [ 11 'create <scope:team> [--otp <otpcode>]', 12 'destroy <scope:team> [--otp <otpcode>]', 13 'add <scope:team> <user> [--otp <otpcode>]', 14 'rm <scope:team> <user> [--otp <otpcode>]', 15 'ls <scope>|<scope:team>', 16 ] 17 18 static params = [ 19 'registry', 20 'otp', 21 'parseable', 22 'json', 23 ] 24 25 static ignoreImplicitWorkspace = false 26 27 static async completion (opts) { 28 const { conf: { argv: { remain: argv } } } = opts 29 const subcommands = ['create', 'destroy', 'add', 'rm', 'ls'] 30 31 if (argv.length === 2) { 32 return subcommands 33 } 34 35 if (subcommands.includes(argv[2])) { 36 return [] 37 } 38 39 throw new Error(argv[2] + ' not recognized') 40 } 41 42 async exec ([cmd, entity = '', user = '']) { 43 // Entities are in the format <scope>:<team> 44 // XXX: "description" option to libnpmteam is used as a description of the 45 // team, but in npm's options, this is a boolean meaning "show the 46 // description in npm search output". Hence its being set to null here. 47 await otplease(this.npm, { ...this.npm.flatOptions }, opts => { 48 entity = entity.replace(/^@/, '') 49 switch (cmd) { 50 case 'create': return this.create(entity, opts) 51 case 'destroy': return this.destroy(entity, opts) 52 case 'add': return this.add(entity, user, opts) 53 case 'rm': return this.rm(entity, user, opts) 54 case 'ls': { 55 const match = entity.match(/[^:]+:.+/) 56 if (match) { 57 return this.listUsers(entity, opts) 58 } else { 59 return this.listTeams(entity, opts) 60 } 61 } 62 default: 63 throw this.usageError() 64 } 65 }) 66 } 67 68 async create (entity, opts) { 69 await libteam.create(entity, opts) 70 if (opts.json) { 71 this.npm.output(JSON.stringify({ 72 created: true, 73 team: entity, 74 })) 75 } else if (opts.parseable) { 76 this.npm.output(`${entity}\tcreated`) 77 } else if (!this.npm.silent) { 78 this.npm.output(`+@${entity}`) 79 } 80 } 81 82 async destroy (entity, opts) { 83 await libteam.destroy(entity, opts) 84 if (opts.json) { 85 this.npm.output(JSON.stringify({ 86 deleted: true, 87 team: entity, 88 })) 89 } else if (opts.parseable) { 90 this.npm.output(`${entity}\tdeleted`) 91 } else if (!this.npm.silent) { 92 this.npm.output(`-@${entity}`) 93 } 94 } 95 96 async add (entity, user, opts) { 97 await libteam.add(user, entity, opts) 98 if (opts.json) { 99 this.npm.output(JSON.stringify({ 100 added: true, 101 team: entity, 102 user, 103 })) 104 } else if (opts.parseable) { 105 this.npm.output(`${user}\t${entity}\tadded`) 106 } else if (!this.npm.silent) { 107 this.npm.output(`${user} added to @${entity}`) 108 } 109 } 110 111 async rm (entity, user, opts) { 112 await libteam.rm(user, entity, opts) 113 if (opts.json) { 114 this.npm.output(JSON.stringify({ 115 removed: true, 116 team: entity, 117 user, 118 })) 119 } else if (opts.parseable) { 120 this.npm.output(`${user}\t${entity}\tremoved`) 121 } else if (!this.npm.silent) { 122 this.npm.output(`${user} removed from @${entity}`) 123 } 124 } 125 126 async listUsers (entity, opts) { 127 const users = (await libteam.lsUsers(entity, opts)).sort() 128 if (opts.json) { 129 this.npm.output(JSON.stringify(users, null, 2)) 130 } else if (opts.parseable) { 131 this.npm.output(users.join('\n')) 132 } else if (!this.npm.silent) { 133 const plural = users.length === 1 ? '' : 's' 134 const more = users.length === 0 ? '' : ':\n' 135 this.npm.output(`\n@${entity} has ${users.length} user${plural}${more}`) 136 this.npm.output(columns(users, { padding: 1 })) 137 } 138 } 139 140 async listTeams (entity, opts) { 141 const teams = (await libteam.lsTeams(entity, opts)).sort() 142 if (opts.json) { 143 this.npm.output(JSON.stringify(teams, null, 2)) 144 } else if (opts.parseable) { 145 this.npm.output(teams.join('\n')) 146 } else if (!this.npm.silent) { 147 const plural = teams.length === 1 ? '' : 's' 148 const more = teams.length === 0 ? '' : ':\n' 149 this.npm.output(`\n@${entity} has ${teams.length} team${plural}${more}`) 150 this.npm.output(columns(teams.map(t => `@${t}`), { padding: 1 })) 151 } 152 } 153} 154module.exports = Team 155