UNPKG

4.21 kBJavaScriptView Raw
1'use strict'
2
3const cli = require('heroku-cli-util')
4const co = require('co')
5
6let context
7let heroku
8let app
9let name
10let plan
11let addon
12let service
13
14function noPlanError () {
15 throw new Error(`Error: No plan specified.
16You need to specify a plan to move ${cli.color.yellow(name)} to.
17For example: ${cli.color.blue('heroku addons:upgrade heroku-redis:premium-0')}
18
19${cli.color.cyan('https://devcenter.heroku.com/articles/managing-add-ons')}`)
20}
21
22function handlePlanChangeAPIError (err) {
23 const sortBy = require('lodash.sortby')
24
25 if (err.statusCode === 422 && err.body.message && err.body.message.startsWith("Couldn't find either the add-on")) {
26 return heroku.get(`/addon-services/${service}/plans`)
27 .then((plans) => {
28 plans = sortBy(plans, 'price.cents').map((plans) => plans.name)
29 throw new Error(`${err.body.message}
30
31Here are the available plans for ${cli.color.yellow(service)}:
32${plans.join('\n')}
33
34See more plan information with ${cli.color.blue('heroku addons:plans ' + service)}
35
36${cli.color.cyan('https://devcenter.heroku.com/articles/managing-add-ons')}`)
37 })
38 }
39 throw err
40}
41
42function handleAPIError (err) {
43 if (err.statusCode === 422 && err.body.id === 'multiple_matches') {
44 let example = err.body.message.split(', ')[2] || 'redis-triangular-1234'
45 throw new Error(`${err.body.message}
46
47Multiple add-ons match ${cli.color.yellow(name)}${app ? ' on ' + app : ''}
48It is not clear which add-on's plan you are trying to change.
49
50Specify the add-on name instead of the name of the add-on service.
51For example, instead of: ${cli.color.blue('heroku addons:upgrade ' + context.args.addon + ' ' + (context.args.plan || ''))}
52Run this: ${cli.color.blue('heroku addons:upgrade ' + example + ' ' + name + ':' + plan)}
53${!app ? 'Alternatively, specify an app to filter by with ' + cli.color.blue('--app') : ''}
54${cli.color.cyan('https://devcenter.heroku.com/articles/managing-add-ons')}`)
55 }
56 throw err
57}
58
59function * run (c, h) {
60 const resolver = require('../../lib/resolve')
61 const util = require('../../lib/util')
62
63 context = c
64 heroku = h
65 app = context.app
66 name = context.args.addon
67 plan = context.args.plan
68
69 // called with just one argument in the form of `heroku addons:upgrade heroku-redis:hobby`
70 if (!plan && name.indexOf(':') !== -1) {
71 let s = name.split(':')
72 name = s[0]
73 plan = s[1]
74 }
75
76 if (!plan) noPlanError()
77 // ignore the service part of the plan since we can infer the service based on the add-on
78 if (plan.indexOf(':') !== -1) plan = plan.split(':')[1]
79
80 // find the add-on to be changed
81 addon = yield resolver.addon(h, app, name).catch((e) => handleAPIError(e))
82
83 service = addon.addon_service.name
84 app = addon.app.name
85 plan = `${service}:${plan}`
86 yield cli.action(`Changing ${cli.color.magenta(addon.name)} on ${cli.color.cyan(app)} from ${cli.color.blue(addon.plan.name)} to ${cli.color.blue(plan)}`, {success: false}, co(function * () {
87 addon = yield heroku.request({
88 path: `/apps/${app}/addons/${addon.name}`,
89 method: 'PATCH',
90 body: {plan: {name: plan}},
91 headers: {
92 'Accept-Expansion': 'plan',
93 'X-Heroku-Legacy-Provider-Messages': 'true'
94 }
95 }).catch((e) => handlePlanChangeAPIError(e))
96 cli.action.done(`done, ${cli.color.green(util.formatPrice(addon.plan.price))}`)
97 if (addon.provision_message) cli.log(addon.provision_message)
98 }))
99}
100
101let cmd = {
102 topic: 'addons',
103 description: 'change add-on plan',
104 help: `
105See available plans with \`heroku addons:plans SERVICE\`.
106
107Note that \`heroku addons:upgrade\` and \`heroku addons:downgrade\` are the same.
108Either one can be used to change an add-on plan up or down.
109
110https://devcenter.heroku.com/articles/managing-add-ons
111
112Examples:
113
114 Upgrade an add-on by service name:
115
116 $ heroku addons:upgrade heroku-redis:premium-2
117
118 Upgrade a specific add-on:
119
120 $ heroku addons:upgrade swimming-briskly-123 heroku-redis:premium-2
121 `,
122 needsAuth: true,
123 wantsApp: true,
124 args: [{name: 'addon'}, {name: 'plan', optional: true}],
125 run: cli.command({preauth: true}, co.wrap(run))
126}
127
128module.exports = [
129 Object.assign({command: 'upgrade'}, cmd),
130 Object.assign({command: 'downgrade'}, cmd)
131]