heroku 11.0.0-alpha.12 → 11.0.0-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/lib/commands/addons/index.d.ts +1 -1
  2. package/lib/commands/addons/index.js +4 -4
  3. package/lib/commands/pg/copy.d.ts +6 -6
  4. package/lib/commands/pg/copy.js +16 -16
  5. package/lib/commands/pg/credentials/url.js +3 -5
  6. package/lib/commands/pg/credentials.d.ts +7 -7
  7. package/lib/commands/pg/credentials.js +10 -10
  8. package/lib/commands/webhooks/add.d.ts +16 -0
  9. package/lib/commands/webhooks/add.js +43 -0
  10. package/lib/commands/webhooks/deliveries/index.d.ts +12 -0
  11. package/lib/commands/webhooks/deliveries/index.js +75 -0
  12. package/lib/commands/webhooks/deliveries/info.d.ts +14 -0
  13. package/lib/commands/webhooks/deliveries/info.js +40 -0
  14. package/lib/commands/webhooks/index.d.ts +11 -0
  15. package/lib/commands/webhooks/index.js +42 -0
  16. package/lib/commands/webhooks/info.d.ts +14 -0
  17. package/lib/commands/webhooks/info.js +29 -0
  18. package/lib/commands/webhooks/remove.d.ts +14 -0
  19. package/lib/commands/webhooks/remove.js +24 -0
  20. package/lib/commands/webhooks/update.d.ts +19 -0
  21. package/lib/commands/webhooks/update.js +36 -0
  22. package/lib/lib/addons/util.js +1 -2
  23. package/lib/lib/data/credentialUtils.js +1 -1
  24. package/lib/lib/data/types.d.ts +10 -6
  25. package/lib/lib/pg/types.d.ts +0 -18
  26. package/lib/lib/pg/util.d.ts +2 -2
  27. package/lib/lib/pg/util.js +5 -0
  28. package/lib/lib/run/dyno.d.ts +3 -11
  29. package/lib/lib/run/dyno.js +15 -18
  30. package/lib/lib/utils/multisort.d.ts +4 -7
  31. package/lib/lib/utils/multisort.js +8 -32
  32. package/npm-shrinkwrap.json +1 -1
  33. package/oclif.manifest.json +1468 -1005
  34. package/package.json +2 -2
  35. package/lib/oldCommands/webhooks/add.d.ts +0 -1
  36. package/lib/oldCommands/webhooks/add.js +0 -56
  37. package/lib/oldCommands/webhooks/deliveries/index.d.ts +0 -1
  38. package/lib/oldCommands/webhooks/deliveries/index.js +0 -90
  39. package/lib/oldCommands/webhooks/deliveries/info.d.ts +0 -1
  40. package/lib/oldCommands/webhooks/deliveries/info.js +0 -54
  41. package/lib/oldCommands/webhooks/index.d.ts +0 -1
  42. package/lib/oldCommands/webhooks/index.js +0 -55
  43. package/lib/oldCommands/webhooks/info.d.ts +0 -1
  44. package/lib/oldCommands/webhooks/info.js +0 -41
  45. package/lib/oldCommands/webhooks/remove.d.ts +0 -1
  46. package/lib/oldCommands/webhooks/remove.js +0 -37
  47. package/lib/oldCommands/webhooks/update.d.ts +0 -1
  48. package/lib/oldCommands/webhooks/update.js +0 -50
@@ -1,6 +1,6 @@
1
1
  import { Command } from '@heroku-cli/command';
2
2
  import * as Heroku from '@heroku-cli/schema';
3
- export declare function renderAttachment(attachment: Heroku.AddOnAttachment, app: string, isFirst?: boolean): string;
3
+ export declare function renderAttachment(attachment: Heroku.AddOnAttachment, app: string, isLast?: boolean): string;
4
4
  export default class Addons extends Command {
5
5
  static description: string;
6
6
  static examples: string[];
@@ -136,8 +136,8 @@ function formatAttachment(attachment, showApp = true) {
136
136
  }
137
137
  return output.join(' ');
138
138
  }
139
- export function renderAttachment(attachment, app, isFirst = false) {
140
- const line = isFirst ? '\u2514\u2500' : '\u251C\u2500';
139
+ export function renderAttachment(attachment, app, isLast = false) {
140
+ const line = isLast ? '\u2514\u2500' : '\u251C\u2500';
141
141
  const attName = formatAttachment(attachment, attachment.app?.name !== app);
142
142
  return printf(' %s %s', color.dim(line), attName);
143
143
  }
@@ -157,8 +157,8 @@ function displayForApp(app, addons) {
157
157
  const atts = _.sortBy(addon.attachments, isForeignApp, 'app.name', 'name');
158
158
  // render each attachment under the add-on
159
159
  const attLines = atts.map((attachment, idx) => {
160
- const isFirst = (idx === addon.attachments.length - 1);
161
- return renderAttachment(attachment, app, isFirst);
160
+ const isLast = (idx === addon.attachments.length - 1);
161
+ return renderAttachment(attachment, app, isLast);
162
162
  });
163
163
  return [addonLine].concat(attLines)
164
164
  .join('\n') + '\n'; // Separate each add-on row by a blank line
@@ -1,18 +1,18 @@
1
1
  import { Command } from '@heroku-cli/command';
2
2
  export default class Copy extends Command {
3
- static description: string;
4
- static help: string;
5
- static topic: string;
6
3
  static args: {
7
4
  source: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
8
5
  target: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
9
6
  };
7
+ static description: string;
10
8
  static flags: {
11
- 'wait-interval': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
- confirm: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
9
  app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ confirm: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
11
  remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ 'wait-interval': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
14
  };
15
+ static help: string;
16
+ static topic: string;
17
17
  run(): Promise<void>;
18
18
  }
@@ -1,6 +1,6 @@
1
+ import { color, utils } from '@heroku/heroku-cli-util';
1
2
  import { Command, flags } from '@heroku-cli/command';
2
3
  import { Args, ux } from '@oclif/core';
3
- import { color, utils } from '@heroku/heroku-cli-util';
4
4
  import ConfirmCommand from '../../lib/confirmCommand.js';
5
5
  import backupsFactory from '../../lib/pg/backups.js';
6
6
  const getAttachmentInfo = async function (heroku, db, app) {
@@ -9,9 +9,9 @@ const getAttachmentInfo = async function (heroku, db, app) {
9
9
  const conn = utils.pg.DatabaseResolver.parsePostgresConnectionString(db);
10
10
  const host = `${conn.host}:${conn.port}`;
11
11
  return {
12
+ confirm: conn.database || conn.host,
12
13
  name: conn.database ? `database ${conn.database} on ${host}` : `database on ${host}`,
13
14
  url: db,
14
- confirm: conn.database || conn.host,
15
15
  };
16
16
  }
17
17
  const attachment = await dbResolver.getAttachment(app, db);
@@ -21,34 +21,34 @@ const getAttachmentInfo = async function (heroku, db, app) {
21
21
  const { body: config } = await heroku.get(`/apps/${attachment.app.name}/config-vars`);
22
22
  const formattedConfig = Object.fromEntries(Object.entries(config).map(([k, v]) => [k.toUpperCase(), v]));
23
23
  return {
24
- name: attachment.name.replace(/^HEROKU_POSTGRESQL_/, '')
25
- .replace(/_URL$/, ''),
26
- url: formattedConfig[attachment.name.toUpperCase() + '_URL'],
27
24
  attachment: {
28
25
  ...attachment,
29
26
  addon,
30
27
  },
31
28
  confirm: app,
29
+ name: attachment.name.replace(/^HEROKU_POSTGRESQL_/, '')
30
+ .replace(/_URL$/, ''),
31
+ url: formattedConfig[attachment.name.toUpperCase() + '_URL'],
32
32
  };
33
33
  };
34
34
  export default class Copy extends Command {
35
- static description = 'copy all data from source db to target';
36
- static help = 'at least one of the databases must be a Heroku PostgreSQL DB';
37
- static topic = 'pg';
38
35
  static args = {
39
- source: Args.string({ required: true, description: 'config var exposed to the owning app containing the source database URL' }),
40
- target: Args.string({ required: true, description: 'config var exposed to the owning app containing the target database URL' }),
36
+ source: Args.string({ description: 'config var exposed to the owning app containing the source database URL', required: true }),
37
+ target: Args.string({ description: 'config var exposed to the owning app containing the target database URL', required: true }),
41
38
  };
39
+ static description = 'copy all data from source db to target';
42
40
  static flags = {
43
- 'wait-interval': flags.string(),
44
- verbose: flags.boolean(),
45
- confirm: flags.string(),
46
41
  app: flags.app({ required: true }),
42
+ confirm: flags.string(),
47
43
  remote: flags.remote(),
44
+ verbose: flags.boolean(),
45
+ 'wait-interval': flags.string(),
48
46
  };
47
+ static help = 'at least one of the databases must be a Heroku PostgreSQL DB';
48
+ static topic = 'pg';
49
49
  async run() {
50
- const { flags, args } = await this.parse(Copy);
51
- const { 'wait-interval': waitInterval, verbose, confirm, app } = flags;
50
+ const { args, flags } = await this.parse(Copy);
51
+ const { app, confirm, verbose, 'wait-interval': waitInterval } = flags;
52
52
  const pgbackups = backupsFactory(app, this.heroku);
53
53
  const interval = Math.max(3, Number.parseInt(waitInterval || '0', 10)) || 3;
54
54
  const [source, target] = await Promise.all([getAttachmentInfo(this.heroku, args.source, app), getAttachmentInfo(this.heroku, args.target, app)]);
@@ -69,10 +69,10 @@ export default class Copy extends Command {
69
69
  ux.action.stop();
70
70
  if (source.attachment) {
71
71
  const { body: credentials } = await this.heroku.get(`/postgres/v0/databases/${source.attachment.addon.name}/credentials`, {
72
- hostname: utils.pg.host(),
73
72
  headers: {
74
73
  Authorization: `Basic ${Buffer.from(`:${this.heroku.auth}`).toString('base64')}`,
75
74
  },
75
+ hostname: utils.pg.host(),
76
76
  });
77
77
  if (credentials.length > 1) {
78
78
  ux.warn('pg:copy will only copy your default credential and the data it has access to. Any additional credentials and data that only they can access will not be copied.');
@@ -39,11 +39,9 @@ export default class Url extends Command {
39
39
  if (!activeCreds) {
40
40
  ux.error(`Could not find any active credentials for ${name}`, { exit: 1 });
41
41
  }
42
- const creds = Object.assign({}, db, {
43
- database: credInfo.database, host: credInfo.host, port: credInfo.port,
44
- }, {
45
- user: activeCreds?.user, password: activeCreds?.password,
46
- });
42
+ const creds = {
43
+ ...db, database: credInfo.database, host: credInfo.host, password: activeCreds?.password, port: credInfo.port, user: activeCreds?.user,
44
+ };
47
45
  const connUrl = new URL(`postgres://${creds.host}/${creds.database}`);
48
46
  connUrl.port = creds.port.toString();
49
47
  if (creds.user && creds.password) {
@@ -1,16 +1,16 @@
1
1
  import { Command } from '@heroku-cli/command';
2
- import type { CredentialInfo, CredentialsInfo } from '../../lib/pg/types.js';
2
+ import type { NonAdvancedCredentialInfo } from '../../lib/data/types.js';
3
3
  export default class Credentials extends Command {
4
- static topic: string;
4
+ static args: {
5
+ database: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
6
+ };
5
7
  static description: string;
6
8
  static flags: {
7
9
  app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
10
  remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
11
  };
10
- static args: {
11
- database: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
12
- };
12
+ static topic: string;
13
+ protected isDefaultCredential(cred: NonAdvancedCredentialInfo): boolean;
13
14
  run(): Promise<void>;
14
- protected sortByDefaultAndName(credentials: CredentialsInfo): CredentialsInfo;
15
- protected isDefaultCredential(cred: CredentialInfo): boolean;
15
+ protected sortByDefaultAndName(credentials: NonAdvancedCredentialInfo[]): NonAdvancedCredentialInfo[];
16
16
  }
@@ -1,29 +1,32 @@
1
+ import { hux, utils } from '@heroku/heroku-cli-util';
1
2
  import { Command, flags } from '@heroku-cli/command';
2
3
  import { Args } from '@oclif/core';
3
- import { hux, utils } from '@heroku/heroku-cli-util';
4
4
  import { presentCredentialAttachments } from '../../lib/pg/util.js';
5
5
  import { nls } from '../../nls.js';
6
6
  export default class Credentials extends Command {
7
- static topic = 'pg';
7
+ static args = {
8
+ database: Args.string({ description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}` }),
9
+ };
8
10
  static description = 'show information on credentials in the database';
9
11
  static flags = {
10
12
  app: flags.app({ required: true }),
11
13
  remote: flags.remote(),
12
14
  };
13
- static args = {
14
- database: Args.string({ description: `${nls('pg:database:arg:description')} ${nls('pg:database:arg:description:default:suffix')}` }),
15
- };
15
+ static topic = 'pg';
16
+ isDefaultCredential(cred) {
17
+ return cred.name === 'default';
18
+ }
16
19
  async run() {
17
- const { flags, args } = await this.parse(Credentials);
20
+ const { args, flags } = await this.parse(Credentials);
18
21
  const { app } = flags;
19
22
  const { database } = args;
20
23
  const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
21
24
  const { addon } = await dbResolver.getAttachment(app, database);
22
25
  const { body: credentials } = await this.heroku.get(`/postgres/v0/databases/${addon.id}/credentials`, {
23
- hostname: utils.pg.host(),
24
26
  headers: {
25
27
  Authorization: `Basic ${Buffer.from(`:${this.heroku.auth}`).toString('base64')}`,
26
28
  },
29
+ hostname: utils.pg.host(),
27
30
  });
28
31
  const sortedCredentials = this.sortByDefaultAndName(credentials);
29
32
  const { body: attachments } = await this.heroku.get(`/addons/${addon.id}/addon-attachments`);
@@ -55,7 +58,4 @@ export default class Credentials extends Command {
55
58
  return isDefaultB < isDefaultA ? -1 : (isDefaultA < isDefaultB ? 1 : a.name.localeCompare(b.name));
56
59
  });
57
60
  }
58
- isDefaultCredential(cred) {
59
- return cred.name === 'default';
60
- }
61
61
  }
@@ -0,0 +1,16 @@
1
+ import BaseCommand from '../../lib/webhooks/base.js';
2
+ export default class WebhooksAdd extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ pipeline: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ include: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ level: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ secret: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ authorization: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ url: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ };
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,43 @@
1
+ import { flags } from '@heroku-cli/command';
2
+ import { hux } from '@heroku/heroku-cli-util';
3
+ import { ux } from '@oclif/core';
4
+ import BaseCommand from '../../lib/webhooks/base.js';
5
+ export default class WebhooksAdd extends BaseCommand {
6
+ static description = 'add a webhook to an app';
7
+ static examples = [
8
+ '$ heroku webhooks:add -i api:dyno -l notify -u https://example.com/hooks',
9
+ ];
10
+ static flags = {
11
+ app: flags.app(),
12
+ remote: flags.remote(),
13
+ pipeline: flags.pipeline({ char: 'p', description: 'pipeline on which to list', hidden: true }),
14
+ include: flags.string({ char: 'i', description: 'comma delimited event types your server will receive ', required: true }),
15
+ level: flags.string({ char: 'l', description: 'notify does not retry, sync will retry until successful or timeout', required: true }),
16
+ secret: flags.string({ char: 's', description: 'value to sign delivery with in Heroku-Webhook-Hmac-SHA256 header' }),
17
+ authorization: flags.string({ char: 't', description: 'authorization header to send with webhooks' }),
18
+ url: flags.string({ char: 'u', description: 'URL for receiver', required: true }),
19
+ };
20
+ async run() {
21
+ const { flags } = await this.parse(WebhooksAdd);
22
+ const { path, display } = this.webhookType(flags);
23
+ ux.action.start(`Adding webhook to ${display}`);
24
+ const response = await this.webhooksClient.post(`${path}/webhooks`, {
25
+ body: {
26
+ include: flags.include.split(',').map((s) => s.trim()),
27
+ level: flags.level,
28
+ secret: flags.secret,
29
+ url: flags.url,
30
+ authorization: flags.authorization,
31
+ },
32
+ });
33
+ ux.action.stop();
34
+ const secret = response.headers && response.headers['heroku-webhook-secret'];
35
+ if (secret) {
36
+ hux.styledHeader('Webhooks Signing Secret');
37
+ ux.stdout(secret);
38
+ }
39
+ else {
40
+ ux.warn('no secret found');
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,12 @@
1
+ import BaseCommand from '../../../lib/webhooks/base.js';
2
+ export default class Deliveries extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ pipeline: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,75 @@
1
+ import { flags } from '@heroku-cli/command';
2
+ import { hux } from '@heroku/heroku-cli-util';
3
+ import BaseCommand from '../../../lib/webhooks/base.js';
4
+ export default class Deliveries extends BaseCommand {
5
+ static description = 'list webhook deliveries on an app';
6
+ static examples = [
7
+ '$ heroku webhooks:deliveries',
8
+ ];
9
+ static flags = {
10
+ app: flags.app(),
11
+ remote: flags.remote(),
12
+ status: flags.string({ char: 's', description: 'filter deliveries by status' }),
13
+ pipeline: flags.pipeline({ char: 'p', description: 'pipeline on which to list', hidden: true }),
14
+ };
15
+ async run() {
16
+ const { flags } = await this.parse(Deliveries);
17
+ const webhookType = this.webhookType(flags);
18
+ let { path } = webhookType;
19
+ const { display } = webhookType;
20
+ const max = 1000;
21
+ path = `${path}/webhook-deliveries`;
22
+ if (flags.status) {
23
+ path += `?eq[status]=${encodeURIComponent(flags.status)}`;
24
+ }
25
+ const { body: deliveries } = await this.webhooksClient.get(path, {
26
+ headers: {
27
+ Range: `seq ..; order=desc,max=${max}`,
28
+ },
29
+ partial: true,
30
+ });
31
+ if (deliveries.length === 0) {
32
+ this.log(`${display} has no deliveries`);
33
+ }
34
+ else {
35
+ const code = (w) => (w.last_attempt && w.last_attempt.code && String(w.last_attempt.code)) || '';
36
+ deliveries.reverse();
37
+ if (deliveries.length === max) {
38
+ this.warn(`Only showing the ${max} most recent deliveries`);
39
+ this.warn('It is possible to filter deliveries by using the --status flag');
40
+ }
41
+ const printLine = (...args) => this.log(...args);
42
+ hux.table(deliveries, {
43
+ id: {
44
+ header: 'Delivery ID',
45
+ },
46
+ created_at: {
47
+ header: 'Created', get: (w) => w.created_at,
48
+ },
49
+ status: {
50
+ header: 'Status', get: (w) => w.status,
51
+ },
52
+ include: {
53
+ header: 'Include', get: (w) => w.event.include,
54
+ },
55
+ level: {
56
+ header: 'Level', get: (w) => w.webhook.level,
57
+ },
58
+ num_attempts: {
59
+ header: 'Attempts', get: (w) => String(w.num_attempts),
60
+ },
61
+ last_code: {
62
+ header: 'Code', get: code,
63
+ },
64
+ last_error: {
65
+ header: 'Error', get: (w) => (w.last_attempt && w.last_attempt.error_class) || '',
66
+ },
67
+ next_attempt_at: {
68
+ header: 'Next Attempt', get: (w) => w.next_attempt_at || '',
69
+ },
70
+ }, {
71
+ printLine,
72
+ });
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,14 @@
1
+ import BaseCommand from '../../../lib/webhooks/base.js';
2
+ export default class DeliveriesInfo extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ pipeline: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static args: {
11
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,40 @@
1
+ import { flags } from '@heroku-cli/command';
2
+ import { Args } from '@oclif/core';
3
+ import { hux } from '@heroku/heroku-cli-util';
4
+ import BaseCommand from '../../../lib/webhooks/base.js';
5
+ export default class DeliveriesInfo extends BaseCommand {
6
+ static description = 'info for a webhook event on an app';
7
+ static examples = [
8
+ '$ heroku webhooks:deliveries:info 99999999-9999-9999-9999-999999999999',
9
+ ];
10
+ static flags = {
11
+ app: flags.app(),
12
+ remote: flags.remote(),
13
+ pipeline: flags.pipeline({ char: 'p', description: 'pipeline on which to list', hidden: true }),
14
+ };
15
+ static args = {
16
+ id: Args.string({ required: true, description: 'ID of the webhook event' }),
17
+ };
18
+ async run() {
19
+ const { flags, args } = await this.parse(DeliveriesInfo);
20
+ const { path } = this.webhookType(flags);
21
+ const { body: delivery } = await this.webhooksClient.get(`${path}/webhook-deliveries/${args.id}`);
22
+ const { body: event } = await this.webhooksClient.get(`${path}/webhook-events/${delivery.event.id}`);
23
+ const obj = {
24
+ Created: delivery.created_at,
25
+ Event: delivery.event.id,
26
+ Webhook: delivery.webhook.id,
27
+ Status: delivery.status,
28
+ Include: delivery.event.include,
29
+ Level: delivery.webhook.level,
30
+ Attempts: delivery.num_attempts,
31
+ Code: delivery.last_attempt && delivery.last_attempt.code,
32
+ Error: delivery.last_attempt && delivery.last_attempt.error_class,
33
+ 'Next Attempt': delivery.next_attempt_at,
34
+ };
35
+ hux.styledHeader(delivery.id);
36
+ hux.styledObject(obj);
37
+ hux.styledHeader('Event Payload');
38
+ hux.styledJSON(event.payload);
39
+ }
40
+ }
@@ -0,0 +1,11 @@
1
+ import BaseCommand from '../../lib/webhooks/base.js';
2
+ export default class Webhooks extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ pipeline: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ run(): Promise<void>;
11
+ }
@@ -0,0 +1,42 @@
1
+ import { color, hux } from '@heroku/heroku-cli-util';
2
+ import { flags } from '@heroku-cli/command';
3
+ import { ux } from '@oclif/core';
4
+ import BaseCommand from '../../lib/webhooks/base.js';
5
+ export default class Webhooks extends BaseCommand {
6
+ static description = 'list webhooks on an app';
7
+ static examples = ['$ heroku webhooks'];
8
+ static flags = {
9
+ app: flags.app(),
10
+ remote: flags.remote(),
11
+ pipeline: flags.pipeline({
12
+ char: 'p',
13
+ description: 'pipeline on which to list',
14
+ hidden: true,
15
+ }),
16
+ };
17
+ async run() {
18
+ const { flags } = await this.parse(Webhooks);
19
+ const { path, display } = this.webhookType(flags);
20
+ const { body: webhooks } = await this.webhooksClient.get(`${path}/webhooks`);
21
+ if (webhooks.length === 0) {
22
+ ux.stdout(`${display} has no webhooks\nUse ${color.command('heroku webhooks:add')} to add one.`);
23
+ return;
24
+ }
25
+ webhooks.sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at));
26
+ hux.table(webhooks, {
27
+ id: {
28
+ header: 'Webhook ID',
29
+ },
30
+ url: {
31
+ header: 'URL',
32
+ },
33
+ include: {
34
+ header: 'Include',
35
+ get: (row) => row.include.join(','),
36
+ },
37
+ level: {
38
+ header: 'Level',
39
+ },
40
+ });
41
+ }
42
+ }
@@ -0,0 +1,14 @@
1
+ import BaseCommand from '../../lib/webhooks/base.js';
2
+ export default class WebhooksInfo extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ pipeline: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static args: {
11
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,29 @@
1
+ import { flags } from '@heroku-cli/command';
2
+ import { Args } from '@oclif/core';
3
+ import { hux } from '@heroku/heroku-cli-util';
4
+ import BaseCommand from '../../lib/webhooks/base.js';
5
+ export default class WebhooksInfo extends BaseCommand {
6
+ static description = 'info for a webhook on an app';
7
+ static examples = ['$ heroku webhooks:info 99999999-9999-9999-9999-999999999999'];
8
+ static flags = {
9
+ app: flags.app(),
10
+ remote: flags.remote(),
11
+ pipeline: flags.pipeline({ char: 'p', description: 'pipeline on which to list', hidden: true }),
12
+ };
13
+ static args = {
14
+ id: Args.string({ required: true, description: 'ID of the webhook' }),
15
+ };
16
+ async run() {
17
+ const { flags, args } = await this.parse(WebhooksInfo);
18
+ const { path } = this.webhookType(flags);
19
+ const { body: webhook } = await this.webhooksClient.get(`${path}/webhooks/${args.id}`);
20
+ const obj = {
21
+ 'Webhook ID': webhook.id,
22
+ URL: webhook.url,
23
+ Include: webhook.include.join(','),
24
+ Level: webhook.level,
25
+ };
26
+ hux.styledHeader(webhook.id);
27
+ hux.styledObject(obj);
28
+ }
29
+ }
@@ -0,0 +1,14 @@
1
+ import BaseCommand from '../../lib/webhooks/base.js';
2
+ export default class WebhooksRemove extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ pipeline: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ static args: {
11
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,24 @@
1
+ import { flags } from '@heroku-cli/command';
2
+ import { Args, ux } from '@oclif/core';
3
+ import BaseCommand from '../../lib/webhooks/base.js';
4
+ export default class WebhooksRemove extends BaseCommand {
5
+ static description = 'removes a webhook from an app';
6
+ static examples = [
7
+ '$ heroku webhooks:remove 99999999-9999-9999-9999-999999999999',
8
+ ];
9
+ static flags = {
10
+ app: flags.app(),
11
+ remote: flags.remote(),
12
+ pipeline: flags.pipeline({ char: 'p', description: 'pipeline on which to list', hidden: true }),
13
+ };
14
+ static args = {
15
+ id: Args.string({ description: 'id of webhook to remove', required: true }),
16
+ };
17
+ async run() {
18
+ const { flags, args } = await this.parse(WebhooksRemove);
19
+ const { path, display } = this.webhookType(flags);
20
+ ux.action.start(`Removing webhook ${args.id} from ${display}`);
21
+ await this.webhooksClient.delete(`${path}/webhooks/${args.id}`);
22
+ ux.action.stop();
23
+ }
24
+ }
@@ -0,0 +1,19 @@
1
+ import BaseCommand from '../../lib/webhooks/base.js';
2
+ export default class WebhooksUpdate extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ pipeline: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ include: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ level: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ secret: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ authorization: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ url: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ };
15
+ static args: {
16
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
17
+ };
18
+ run(): Promise<void>;
19
+ }
@@ -0,0 +1,36 @@
1
+ import { flags } from '@heroku-cli/command';
2
+ import { Args, ux } from '@oclif/core';
3
+ import BaseCommand from '../../lib/webhooks/base.js';
4
+ export default class WebhooksUpdate extends BaseCommand {
5
+ static description = 'updates a webhook in an app';
6
+ static examples = [
7
+ '$ heroku webhooks:update 99999999-9999-9999-9999-999999999999 -i dyno -l notify -s 09928c40bf1b191b645174a19f7053d16a180da37332e719ef0998f4c0a2 -u https://example.com/hooks',
8
+ ];
9
+ static flags = {
10
+ app: flags.app(),
11
+ remote: flags.remote(),
12
+ pipeline: flags.pipeline({ char: 'p', description: 'pipeline on which to list', hidden: true }),
13
+ include: flags.string({ char: 'i', description: 'comma delimited event types your server will receive ', required: true }),
14
+ level: flags.string({ char: 'l', description: 'notify does not retry, sync will retry until successful or timeout', required: true }),
15
+ secret: flags.string({ char: 's', description: 'value to sign delivery with in Heroku-Webhook-Hmac-SHA256 header' }),
16
+ authorization: flags.string({ char: 't', description: 'authorization header to send with webhooks' }),
17
+ url: flags.string({ char: 'u', description: 'URL for receiver', required: true }),
18
+ };
19
+ static args = {
20
+ id: Args.string({ required: true, description: 'ID of the webhook' }),
21
+ };
22
+ async run() {
23
+ const { flags, args } = await this.parse(WebhooksUpdate);
24
+ const { path, display } = this.webhookType(flags);
25
+ ux.action.start(`Updating webhook ${args.id} for ${display}`);
26
+ await this.webhooksClient.patch(`${path}/webhooks/${args.id}`, {
27
+ body: {
28
+ include: flags.include && flags.include.split(',').map((s) => s.trim()),
29
+ level: flags.level,
30
+ secret: flags.secret,
31
+ url: flags.url,
32
+ },
33
+ });
34
+ ux.action.stop();
35
+ }
36
+ }