1'use strict' 2 3const BB = require('bluebird') 4 5const hookApi = require('libnpm/hook') 6const npmConfig = require('./config/figgy-config.js') 7const output = require('./utils/output.js') 8const otplease = require('./utils/otplease.js') 9const pudding = require('figgy-pudding') 10const relativeDate = require('tiny-relative-date') 11const Table = require('cli-table3') 12const validate = require('aproba') 13const npm = require('./npm') 14 15hook.usage = [ 16 'npm hook add <pkg> <url> <secret> [--type=<type>]', 17 'npm hook ls [pkg]', 18 'npm hook rm <id>', 19 'npm hook update <id> <url> <secret>' 20].join('\n') 21 22hook.completion = (opts, cb) => { 23 validate('OF', [opts, cb]) 24 return cb(null, []) // fill in this array with completion values 25} 26 27const HookConfig = pudding({ 28 json: {}, 29 loglevel: {}, 30 parseable: {}, 31 silent: {}, 32 unicode: {} 33}) 34 35function UsageError () { 36 throw Object.assign(new Error(hook.usage), {code: 'EUSAGE'}) 37} 38 39module.exports = (args, cb) => BB.try(() => hook(args)).then( 40 val => cb(null, val), 41 err => err.code === 'EUSAGE' ? cb(err.message) : cb(err) 42) 43function hook (args) { 44 if (args.length === 4) { // secret is passed in the args 45 // we have the user secret in the CLI args, we need to redact it from the referer. 46 redactUserSecret() 47 } 48 return otplease(npmConfig(), opts => { 49 opts = HookConfig(opts) 50 switch (args[0]) { 51 case 'add': 52 return add(args[1], args[2], args[3], opts) 53 case 'ls': 54 return ls(args[1], opts) 55 case 'rm': 56 return rm(args[1], opts) 57 case 'update': 58 case 'up': 59 return update(args[1], args[2], args[3], opts) 60 default: 61 UsageError() 62 } 63 }) 64} 65 66function add (pkg, uri, secret, opts) { 67 return hookApi.add(pkg, uri, secret, opts).then(hook => { 68 if (opts.json) { 69 output(JSON.stringify(hook, null, 2)) 70 } else if (opts.parseable) { 71 output(Object.keys(hook).join('\t')) 72 output(Object.keys(hook).map(k => hook[k]).join('\t')) 73 } else if (!opts.silent && opts.loglevel !== 'silent') { 74 output(`+ ${hookName(hook)} ${ 75 opts.unicode ? ' ➜ ' : ' -> ' 76 } ${hook.endpoint}`) 77 } 78 }) 79} 80 81function ls (pkg, opts) { 82 return hookApi.ls(opts.concat({package: pkg})).then(hooks => { 83 if (opts.json) { 84 output(JSON.stringify(hooks, null, 2)) 85 } else if (opts.parseable) { 86 output(Object.keys(hooks[0]).join('\t')) 87 hooks.forEach(hook => { 88 output(Object.keys(hook).map(k => hook[k]).join('\t')) 89 }) 90 } else if (!hooks.length) { 91 output("You don't have any hooks configured yet.") 92 } else if (!opts.silent && opts.loglevel !== 'silent') { 93 if (hooks.length === 1) { 94 output('You have one hook configured.') 95 } else { 96 output(`You have ${hooks.length} hooks configured.`) 97 } 98 const table = new Table({head: ['id', 'target', 'endpoint']}) 99 hooks.forEach((hook) => { 100 table.push([ 101 {rowSpan: 2, content: hook.id}, 102 hookName(hook), 103 hook.endpoint 104 ]) 105 if (hook.last_delivery) { 106 table.push([ 107 { 108 colSpan: 1, 109 content: `triggered ${relativeDate(hook.last_delivery)}` 110 }, 111 hook.response_code 112 ]) 113 } else { 114 table.push([{colSpan: 2, content: 'never triggered'}]) 115 } 116 }) 117 output(table.toString()) 118 } 119 }) 120} 121 122function rm (id, opts) { 123 return hookApi.rm(id, opts).then(hook => { 124 if (opts.json) { 125 output(JSON.stringify(hook, null, 2)) 126 } else if (opts.parseable) { 127 output(Object.keys(hook).join('\t')) 128 output(Object.keys(hook).map(k => hook[k]).join('\t')) 129 } else if (!opts.silent && opts.loglevel !== 'silent') { 130 output(`- ${hookName(hook)} ${ 131 opts.unicode ? ' ✘ ' : ' X ' 132 } ${hook.endpoint}`) 133 } 134 }) 135} 136 137function update (id, uri, secret, opts) { 138 return hookApi.update(id, uri, secret, opts).then(hook => { 139 if (opts.json) { 140 output(JSON.stringify(hook, null, 2)) 141 } else if (opts.parseable) { 142 output(Object.keys(hook).join('\t')) 143 output(Object.keys(hook).map(k => hook[k]).join('\t')) 144 } else if (!opts.silent && opts.loglevel !== 'silent') { 145 output(`+ ${hookName(hook)} ${ 146 opts.unicode ? ' ➜ ' : ' -> ' 147 } ${hook.endpoint}`) 148 } 149 }) 150} 151 152function hookName (hook) { 153 let target = hook.name 154 if (hook.type === 'scope') { target = '@' + target } 155 if (hook.type === 'owner') { target = '~' + target } 156 return target 157} 158 159function redactUserSecret () { 160 const referer = npm.referer 161 if (!referer) return 162 const splittedReferer = referer.split(' ') 163 splittedReferer[4] = '[REDACTED]' 164 npm.referer = splittedReferer.join(' ') 165} 166