diff --git a/CHANGELOG.md b/CHANGELOG.md
index 62ab8e3a21..672fa607c4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [7.29.0](https://github.com/heroku/cli/compare/v7.28.0...v7.29.0) (2019-08-21)
+
+
+### Features
+
+* **webhooks:** add oclif version of webhooks plugin ([#1253](https://github.com/heroku/cli/issues/1253)) ([110c516](https://github.com/heroku/cli/commit/110c516))
+
+
+
+
+
# [7.28.0](https://github.com/heroku/cli/compare/v7.27.1...v7.28.0) (2019-08-19)
diff --git a/docs/local.md b/docs/local.md
index 5137911403..cb08688f6a 100644
--- a/docs/local.md
+++ b/docs/local.md
@@ -33,7 +33,7 @@ EXAMPLE
$ heroku local web=1,worker=2
```
-_See code: [@heroku-cli/plugin-local](https://github.com/heroku/cli/blob/v7.26.2/src/commands/local/index.ts)_
+_See code: [@heroku-cli/plugin-local](https://github.com/heroku/cli/blob/v7.28.0/src/commands/local/index.ts)_
## `heroku local:run`
@@ -51,7 +51,7 @@ EXAMPLE
$ heroku local:run bin/migrate
```
-_See code: [@heroku-cli/plugin-local](https://github.com/heroku/cli/blob/v7.26.2/src/commands/local/run.ts)_
+_See code: [@heroku-cli/plugin-local](https://github.com/heroku/cli/blob/v7.28.0/src/commands/local/run.ts)_
## `heroku local:version`
@@ -62,4 +62,4 @@ USAGE
$ heroku local:version
```
-_See code: [@heroku-cli/plugin-local](https://github.com/heroku/cli/blob/v7.26.2/src/commands/local/version.ts)_
+_See code: [@heroku-cli/plugin-local](https://github.com/heroku/cli/blob/v7.28.0/src/commands/local/version.ts)_
diff --git a/docs/reviewapps.md b/docs/reviewapps.md
index 1db56d98ee..c151a0643f 100644
--- a/docs/reviewapps.md
+++ b/docs/reviewapps.md
@@ -25,7 +25,7 @@ EXAMPLE
$ heroku reviewapps:disable -p mypipeline -a myapp --autodeploy
```
-_See code: [@heroku-cli/plugin-pipelines](https://github.com/heroku/heroku-cli-plugin-pipelines/blob/v7.27.0/src/commands/reviewapps/disable.ts)_
+_See code: [@heroku-cli/plugin-pipelines](https://github.com/heroku/heroku-cli-plugin-pipelines/blob/v7.28.0/src/commands/reviewapps/disable.ts)_
## `heroku reviewapps:enable`
@@ -45,4 +45,4 @@ EXAMPLE
$ heroku reviewapps:enable -p mypipeline -a myapp --autodeploy --autodestroy
```
-_See code: [@heroku-cli/plugin-pipelines](https://github.com/heroku/heroku-cli-plugin-pipelines/blob/v7.27.0/src/commands/reviewapps/enable.ts)_
+_See code: [@heroku-cli/plugin-pipelines](https://github.com/heroku/heroku-cli-plugin-pipelines/blob/v7.28.0/src/commands/reviewapps/enable.ts)_
diff --git a/lerna.json b/lerna.json
index 0a33f105d4..3e4ed1280c 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "7.28.0",
+ "version": "7.29.0",
"lerna": "2.11.0",
"useWorkspaces": true,
"npmClient": "yarn",
diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md
index 62ab8e3a21..672fa607c4 100644
--- a/packages/cli/CHANGELOG.md
+++ b/packages/cli/CHANGELOG.md
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [7.29.0](https://github.com/heroku/cli/compare/v7.28.0...v7.29.0) (2019-08-21)
+
+
+### Features
+
+* **webhooks:** add oclif version of webhooks plugin ([#1253](https://github.com/heroku/cli/issues/1253)) ([110c516](https://github.com/heroku/cli/commit/110c516))
+
+
+
+
+
# [7.28.0](https://github.com/heroku/cli/compare/v7.27.1...v7.28.0) (2019-08-19)
diff --git a/packages/cli/README.md b/packages/cli/README.md
index 3c610b4239..54f0fd0a7e 100644
--- a/packages/cli/README.md
+++ b/packages/cli/README.md
@@ -77,7 +77,6 @@ For other issues, [submit a support ticket](https://help.heroku.com/).
* [`heroku status`](docs/status.md) - status of the Heroku platform
* [`heroku teams`](docs/teams.md) - manage teams
* [`heroku update`](docs/update.md) - update the Heroku CLI
-* [`heroku webhooks`](docs/webhooks.md) - setup HTTP notifications of app activity
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 21e461ad74..3115f7386a 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,7 +1,7 @@
{
"name": "heroku",
"description": "CLI to interact with Heroku",
- "version": "7.28.0",
+ "version": "7.29.0",
"author": "Jeff Dickey @jdxcode",
"bin": {
"heroku": "./bin/run"
@@ -34,7 +34,7 @@
"@heroku-cli/plugin-run-v5": "^7.24.0",
"@heroku-cli/plugin-spaces": "^7.27.0",
"@heroku-cli/plugin-status": "^7.24.0",
- "@heroku-cli/plugin-webhooks-v5": "^7.24.0",
+ "@heroku-cli/plugin-webhooks": "^7.29.0",
"@oclif/command": "1.5.18",
"@oclif/config": "1.13.2",
"@oclif/errors": "1.2.2",
@@ -130,7 +130,7 @@
"@heroku-cli/plugin-run-v5",
"@heroku-cli/plugin-spaces",
"@heroku-cli/plugin-status",
- "@heroku-cli/plugin-webhooks-v5",
+ "@heroku-cli/plugin-webhooks",
"@oclif/plugin-commands",
"@oclif/plugin-help",
"@oclif/plugin-not-found",
diff --git a/packages/webhooks-v5/.gitignore b/packages/webhooks-v5/.gitignore
deleted file mode 100644
index 16d4f51201..0000000000
--- a/packages/webhooks-v5/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-.nyc_output
-node_modules
diff --git a/packages/webhooks-v5/CHANGELOG.md b/packages/webhooks-v5/CHANGELOG.md
deleted file mode 100644
index dc369b15d5..0000000000
--- a/packages/webhooks-v5/CHANGELOG.md
+++ /dev/null
@@ -1,290 +0,0 @@
-# Change Log
-
-All notable changes to this project will be documented in this file.
-See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
-
-# [7.24.0](https://github.com/heroku/cli/compare/v7.23.0...v7.24.0) (2019-04-25)
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-
-
-
-## [7.22.7](https://github.com/heroku/cli/compare/v7.22.6...v7.22.7) (2019-03-14)
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-
-
-
-## [7.22.6](https://github.com/heroku/cli/compare/v7.22.5...v7.22.6) (2019-03-14)
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-
-
-
-## [7.22.5](https://github.com/heroku/cli/compare/v7.22.4...v7.22.5) (2019-03-13)
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-
-
-
-## [7.22.4](https://github.com/heroku/cli/compare/v7.22.3...v7.22.4) (2019-03-06)
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-
-
-
-# [7.21.0](https://github.com/heroku/cli/compare/v7.20.1...v7.21.0) (2019-02-11)
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-
-
-
-# [7.20.0](https://github.com/heroku/cli/compare/v7.19.4...v7.20.0) (2019-02-05)
-
-
-### Bug Fixes
-
-* increase mocha timeouts ([7d31bc1](https://github.com/heroku/cli/commit/7d31bc1))
-
-
-
-
-
-## [7.19.4](https://github.com/heroku/cli/compare/v7.19.3...v7.19.4) (2018-12-19)
-
-
-### Bug Fixes
-
-* upgrade dependencies ([f1b3886](https://github.com/heroku/cli/commit/f1b3886))
-
-
-
-
-
-## [7.19.3](https://github.com/heroku/cli/compare/v7.19.2...v7.19.3) (2018-12-04)
-
-
-### Bug Fixes
-
-* remove login warnings ([39d8c47](https://github.com/heroku/cli/commit/39d8c47))
-
-
-
-
-
-# [7.19.0](https://github.com/heroku/cli/compare/v7.18.10...v7.19.0) (2018-12-03)
-
-
-### Bug Fixes
-
-* updated command ([1c49051](https://github.com/heroku/cli/commit/1c49051))
-
-
-
-
-
-## [7.18.10](https://github.com/heroku/cli/compare/v7.18.9...v7.18.10) (2018-11-26)
-
-
-### Bug Fixes
-
-* updated deps ([576c85a](https://github.com/heroku/cli/commit/576c85a))
-
-
-
-
-
-## [7.18.9](https://github.com/heroku/cli/compare/v7.18.8...v7.18.9) (2018-11-15)
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-
-
-
-
-## [7.18.8](https://github.com/heroku/cli/compare/v7.18.7...v7.18.8) (2018-11-14)
-
-
-### Bug Fixes
-
-* **webhooks-v5:** add remote flag ([#1136](https://github.com/heroku/cli/issues/1136)) ([eede7a9](https://github.com/heroku/cli/commit/eede7a9))
-
-
-
-
-
-## [7.16.2](https://github.com/heroku/cli/compare/v7.16.1...v7.16.2) (2018-10-02)
-
-
-### Bug Fixes
-
-* updated deps ([482dc85](https://github.com/heroku/cli/commit/482dc85))
-
-
-
-
-
-
-# [7.16.0](https://github.com/heroku/cli/compare/v7.15.2...v7.16.0) (2018-09-14)
-
-
-### Bug Fixes
-
-* updated deps ([6d3be5a](https://github.com/heroku/cli/commit/6d3be5a))
-* updated dev-cli ([022396b](https://github.com/heroku/cli/commit/022396b))
-
-
-
-
-
-
-# [7.15.0](https://github.com/heroku/cli/compare/v7.14.4...v7.15.0) (2018-09-10)
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-
-
-
-
-## [7.12.6](https://github.com/heroku/cli/compare/v7.12.5...v7.12.6) (2018-08-30)
-
-
-### Bug Fixes
-
-* windows test failures ([5b4d171](https://github.com/heroku/cli/commit/5b4d171))
-
-
-
-
-
-
-## [7.12.4](https://github.com/heroku/cli/compare/v7.12.3...v7.12.4) (2018-08-29)
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-
-
-
-
-## [7.9.3](https://github.com/heroku/cli/compare/v7.9.2...v7.9.3) (2018-08-18)
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-
-
-
-
-## [7.9.2](https://github.com/heroku/cli/compare/v7.9.1...v7.9.2) (2018-08-18)
-
-
-### Bug Fixes
-
-* typescript 3.0 ([268c0af](https://github.com/heroku/cli/commit/268c0af))
-
-
-
-
-
-
-## [7.9.1](https://github.com/heroku/cli/compare/v7.9.0...v7.9.1) (2018-08-17)
-
-
-### Bug Fixes
-
-* updated some dependencies ([0306f88](https://github.com/heroku/cli/commit/0306f88))
-
-
-
-
-
-## [7.5.6](https://github.com/heroku/cli/compare/v7.5.5...v7.5.6) (2018-06-29)
-
-
-### Bug Fixes
-
-* bump legacy and color ([a3fa970](https://github.com/heroku/cli/commit/a3fa970))
-
-
-
-
-
-## [7.5.5](https://github.com/heroku/cli/compare/v7.5.4...v7.5.5) (2018-06-29)
-
-
-
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-## [7.5.1](https://github.com/heroku/cli/compare/v7.5.0...v7.5.1) (2018-06-26)
-
-
-### Bug Fixes
-
-* bump dev-cli ([fb3e41a](https://github.com/heroku/cli/commit/fb3e41a))
-
-
-
-
-
-# [7.5.0](https://github.com/heroku/cli/compare/v7.4.11...v7.5.0) (2018-06-26)
-
-
-
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-## [7.4.8](https://github.com/heroku/cli/compare/v7.4.7...v7.4.8) (2018-06-21)
-
-
-
-
-**Note:** Version bump only for package @heroku-cli/plugin-webhooks-v5
-
-
-## [7.4.6](https://github.com/heroku/cli/compare/v7.4.5...v7.4.6) (2018-06-20)
-
-
-### Bug Fixes
-
-* update dev-cli readme generation ([42a77bc](https://github.com/heroku/cli/commit/42a77bc))
-
-
-
-
-
-## [7.4.5](https://github.com/heroku/cli/compare/v7.4.4...v7.4.5) (2018-06-20)
-
-
-### Bug Fixes
-
-* updated monorepo documentation urls ([4bb6fe0](https://github.com/heroku/cli/commit/4bb6fe0))
-
-
-
-
-
-# [7.4.0](https://github.com/heroku/cli/compare/v7.3.0...v7.4.0) (2018-06-19)
-
-
-### Bug Fixes
-
-* **webhooks:** rename webhook-type to fix lint issue ([52ecf1a](https://github.com/heroku/cli/commit/52ecf1a))
-* repo name ([c306c78](https://github.com/heroku/cli/commit/c306c78))
diff --git a/packages/webhooks-v5/README.md b/packages/webhooks-v5/README.md
deleted file mode 100644
index 0e5611714d..0000000000
--- a/packages/webhooks-v5/README.md
+++ /dev/null
@@ -1,190 +0,0 @@
-# heroku-webhooks
-
-
-* [`@heroku-cli/plugin-webhooks-v5 webhooks`](#heroku-cliplugin-webhooks-v5-webhooks)
-* [`@heroku-cli/plugin-webhooks-v5 webhooks:add`](#heroku-cliplugin-webhooks-v5-webhooksadd)
-* [`@heroku-cli/plugin-webhooks-v5 webhooks:deliveries`](#heroku-cliplugin-webhooks-v5-webhooksdeliveries)
-* [`@heroku-cli/plugin-webhooks-v5 webhooks:deliveries:info [ID]`](#heroku-cliplugin-webhooks-v5-webhooksdeliveriesinfo-id)
-* [`@heroku-cli/plugin-webhooks-v5 webhooks:events`](#heroku-cliplugin-webhooks-v5-webhooksevents)
-* [`@heroku-cli/plugin-webhooks-v5 webhooks:events:info [ID]`](#heroku-cliplugin-webhooks-v5-webhookseventsinfo-id)
-* [`@heroku-cli/plugin-webhooks-v5 webhooks:info [ID]`](#heroku-cliplugin-webhooks-v5-webhooksinfo-id)
-* [`@heroku-cli/plugin-webhooks-v5 webhooks:remove [ID]`](#heroku-cliplugin-webhooks-v5-webhooksremove-id)
-* [`@heroku-cli/plugin-webhooks-v5 webhooks:update [ID]`](#heroku-cliplugin-webhooks-v5-webhooksupdate-id)
-
-## `@heroku-cli/plugin-webhooks-v5 webhooks`
-
-list webhooks on an app
-
-```
-USAGE
- $ @heroku-cli/plugin-webhooks-v5 webhooks
-
-OPTIONS
- -a, --app=app app to run command against
- -r, --remote=remote git remote of app to use
-
-EXAMPLE
- $ heroku webhooks
-```
-
-_See code: [commands/webhooks/index.js](https://github.com/heroku/cli/blob/v7.24.0/packages/webhooks-v5/commands/webhooks/index.js)_
-
-## `@heroku-cli/plugin-webhooks-v5 webhooks:add`
-
-add a webhook to an app
-
-```
-USAGE
- $ @heroku-cli/plugin-webhooks-v5 webhooks:add
-
-OPTIONS
- -a, --app=app app to run command against
- -i, --include=include (required) comma delimited event types your server will receive
- -l, --level=level (required) notify does not retry, sync will retry until successful or timeout
- -r, --remote=remote git remote of app to use
- -s, --secret=secret value to sign delivery with in Heroku-Webhook-Hmac-SHA256 header
- -t, --authorization=authorization authoriation header to send with webhooks
- -u, --url=url (required) URL for receiver
-
-EXAMPLE
- $ heroku webhooks:add -i api:dyno -l notify -u https://example.com/hooks
-```
-
-_See code: [commands/webhooks/add.js](https://github.com/heroku/cli/blob/v7.24.0/packages/webhooks-v5/commands/webhooks/add.js)_
-
-## `@heroku-cli/plugin-webhooks-v5 webhooks:deliveries`
-
-list webhook deliveries on an app
-
-```
-USAGE
- $ @heroku-cli/plugin-webhooks-v5 webhooks:deliveries
-
-OPTIONS
- -a, --app=app app to run command against
- -r, --remote=remote git remote of app to use
- -s, --status=status filter deliveries by status
-
-EXAMPLE
- $ heroku webhooks:deliveries
-```
-
-_See code: [commands/webhooks/deliveries/index.js](https://github.com/heroku/cli/blob/v7.24.0/packages/webhooks-v5/commands/webhooks/deliveries/index.js)_
-
-## `@heroku-cli/plugin-webhooks-v5 webhooks:deliveries:info [ID]`
-
-info for a webhook event on an app
-
-```
-USAGE
- $ @heroku-cli/plugin-webhooks-v5 webhooks:deliveries:info [ID]
-
-OPTIONS
- -a, --app=app app to run command against
- -r, --remote=remote git remote of app to use
-
-EXAMPLE
- $ heroku webhooks:deliveries:info 99999999-9999-9999-9999-999999999999
-```
-
-_See code: [commands/webhooks/deliveries/info.js](https://github.com/heroku/cli/blob/v7.24.0/packages/webhooks-v5/commands/webhooks/deliveries/info.js)_
-
-## `@heroku-cli/plugin-webhooks-v5 webhooks:events`
-
-list webhook events on an app
-
-```
-USAGE
- $ @heroku-cli/plugin-webhooks-v5 webhooks:events
-
-OPTIONS
- -a, --app=app app to run command against
- -r, --remote=remote git remote of app to use
-
-EXAMPLE
- $ heroku webhooks:events
-```
-
-_See code: [commands/webhooks/events/index.js](https://github.com/heroku/cli/blob/v7.24.0/packages/webhooks-v5/commands/webhooks/events/index.js)_
-
-## `@heroku-cli/plugin-webhooks-v5 webhooks:events:info [ID]`
-
-info for a webhook event on an app
-
-```
-USAGE
- $ @heroku-cli/plugin-webhooks-v5 webhooks:events:info [ID]
-
-OPTIONS
- -a, --app=app app to run command against
- -r, --remote=remote git remote of app to use
-
-EXAMPLE
- $ heroku webhooks:events:info 99999999-9999-9999-9999-999999999999
-```
-
-_See code: [commands/webhooks/events/info.js](https://github.com/heroku/cli/blob/v7.24.0/packages/webhooks-v5/commands/webhooks/events/info.js)_
-
-## `@heroku-cli/plugin-webhooks-v5 webhooks:info [ID]`
-
-info for a webhook on an app
-
-```
-USAGE
- $ @heroku-cli/plugin-webhooks-v5 webhooks:info [ID]
-
-OPTIONS
- -a, --app=app app to run command against
- -r, --remote=remote git remote of app to use
-
-EXAMPLE
- $ heroku webhooks:info 99999999-9999-9999-9999-999999999999
-```
-
-_See code: [commands/webhooks/info.js](https://github.com/heroku/cli/blob/v7.24.0/packages/webhooks-v5/commands/webhooks/info.js)_
-
-## `@heroku-cli/plugin-webhooks-v5 webhooks:remove [ID]`
-
-removes a webhook from an app
-
-```
-USAGE
- $ @heroku-cli/plugin-webhooks-v5 webhooks:remove [ID]
-
-ARGUMENTS
- ID id of webhook to remove
-
-OPTIONS
- -a, --app=app app to run command against
- -r, --remote=remote git remote of app to use
-
-EXAMPLE
- $ heroku webhooks:remove 99999999-9999-9999-9999-999999999999
-```
-
-_See code: [commands/webhooks/remove.js](https://github.com/heroku/cli/blob/v7.24.0/packages/webhooks-v5/commands/webhooks/remove.js)_
-
-## `@heroku-cli/plugin-webhooks-v5 webhooks:update [ID]`
-
-updates a webhook in an app
-
-```
-USAGE
- $ @heroku-cli/plugin-webhooks-v5 webhooks:update [ID]
-
-OPTIONS
- -a, --app=app app to run command against
- -i, --include=include (required) comma delimited event types your server will receive
- -l, --level=level (required) notify does not retry, sync will retry until successful or timeout
- -r, --remote=remote git remote of app to use
- -s, --secret=secret value to sign delivery with in Heroku-Webhook-Hmac-SHA256 header
- -t, --authorization=authorization authoriation header to send with webhooks
- -u, --url=url (required) URL for receiver
-
-EXAMPLE
- $ heroku webhooks:update 99999999-9999-9999-9999-999999999999 -i dyno -l notify -s
- 09928c40bf1b191b645174a19f7053d16a180da37332e719ef0998f4c0a2 -u https://example.com/hooks
-```
-
-_See code: [commands/webhooks/update.js](https://github.com/heroku/cli/blob/v7.24.0/packages/webhooks-v5/commands/webhooks/update.js)_
-
diff --git a/packages/webhooks-v5/circle.yml b/packages/webhooks-v5/circle.yml
deleted file mode 100644
index cc29b0c09a..0000000000
--- a/packages/webhooks-v5/circle.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-machine:
- node:
- version: 7.10.0
diff --git a/packages/webhooks-v5/commands/webhooks/add.js b/packages/webhooks-v5/commands/webhooks/add.js
deleted file mode 100644
index 21177ae840..0000000000
--- a/packages/webhooks-v5/commands/webhooks/add.js
+++ /dev/null
@@ -1,50 +0,0 @@
-const { Command, flags } = require('@heroku-cli/command')
-const cli = require('heroku-cli-util')
-const webhookType = require('../../lib/webhook-type.js')
-
-class Add extends Command {
- async run () {
- const { flags } = this.parse(Add)
- let { path, display } = webhookType(flags)
-
- let secret = await cli.action(`Adding webhook to ${display}`, new Promise(async resolve => {
- let { response } = await this.heroku.post(`${path}/webhooks`, {
- headers: { Accept: 'application/vnd.heroku+json; version=3.webhooks' },
- body: {
- include: flags.include.split(',').map(s => s.trim()),
- level: flags.level,
- secret: flags.secret,
- url: flags.url,
- authorization: flags.authorization
- }
- })
- let secret
- if (response.headers) secret = response.headers['heroku-webhook-secret']
- resolve(secret)
- }))
-
- if (secret) {
- cli.styledHeader('Webhooks Signing Secret')
- cli.log(secret)
- }
- }
-}
-
-Add.description = 'add a webhook to an app'
-
-Add.examples = [
- '$ heroku webhooks:add -i api:dyno -l notify -u https://example.com/hooks'
-]
-
-Add.flags = {
- app: flags.app(),
- remote: flags.remote(),
- pipeline: flags.string({ char: 'p', description: 'pipeline on which to list', hidden: true }),
- include: flags.string({ char: 'i', description: 'comma delimited event types your server will receive ', required: true }),
- level: flags.string({ char: 'l', description: 'notify does not retry, sync will retry until successful or timeout', required: true }),
- secret: flags.string({ char: 's', description: 'value to sign delivery with in Heroku-Webhook-Hmac-SHA256 header' }),
- authorization: flags.string({ char: 't', description: 'authoriation header to send with webhooks' }),
- url: flags.string({ char: 'u', description: 'URL for receiver', required: true })
-}
-
-module.exports = Add
diff --git a/packages/webhooks-v5/commands/webhooks/deliveries/index.js b/packages/webhooks-v5/commands/webhooks/deliveries/index.js
deleted file mode 100644
index 6f6a6b67b9..0000000000
--- a/packages/webhooks-v5/commands/webhooks/deliveries/index.js
+++ /dev/null
@@ -1,67 +0,0 @@
-const { Command, flags } = require('@heroku-cli/command')
-const cli = require('heroku-cli-util')
-const webhookType = require('../../../lib/webhook-type.js')
-
-class Deliveries extends Command {
- async run () {
- const { flags } = this.parse(Deliveries)
- let { path, display } = webhookType(flags)
- let max = 1000
-
- path = `${path}/webhook-deliveries`
- if (flags.status) {
- path += `?eq[status]=${encodeURIComponent(flags.status)}`
- }
-
- let { body } = await this.heroku.get(path, {
- headers: {
- Accept: 'application/vnd.heroku+json; version=3.webhooks',
- Range: `seq ..; order=desc,max=${max}`
- },
- partial: true
- })
- let deliveries = body
-
- if (deliveries.length === 0) {
- cli.log(`${display} has no deliveries`)
- } else {
- let code = w => {
- return (w.last_attempt && w.last_attempt.code && String(w.last_attempt.code)) || ''
- }
-
- deliveries.reverse()
-
- if (deliveries.length === max) {
- cli.error(`Only showing the ${max} most recent deliveries`)
- cli.error('It is possible to filter deliveries by using the --status flag')
- }
-
- cli.table(deliveries, { columns: [
- { key: 'id', label: 'Delivery ID' },
- { key: 'created_at', label: 'Created', get: w => w.created_at },
- { key: 'status', label: 'Status', get: w => w.status },
- { key: 'include', label: 'Include', get: w => w.event.include },
- { key: 'level', label: 'Level', get: w => w.webhook.level },
- { key: 'num_attempts', label: 'Attempts', get: w => String(w.num_attempts) },
- { key: 'last_code', label: 'Code', get: code },
- { key: 'last_error', label: 'Error', get: w => (w.last_attempt && w.last_attempt.error_class) || '' },
- { key: 'next_attempt_at', label: 'Next Attempt', get: w => w.next_attempt_at || '' }
- ] })
- }
- }
-}
-
-Deliveries.description = 'list webhook deliveries on an app'
-
-Deliveries.examples = [
- '$ heroku webhooks:deliveries'
-]
-
-Deliveries.flags = {
- app: flags.app(),
- remote: flags.remote(),
- status: flags.string({ char: 's', description: 'filter deliveries by status' }),
- pipeline: flags.string({ char: 'p', description: 'pipeline on which to list', hidden: true })
-}
-
-module.exports = Deliveries
diff --git a/packages/webhooks-v5/commands/webhooks/deliveries/info.js b/packages/webhooks-v5/commands/webhooks/deliveries/info.js
deleted file mode 100644
index d0ae7d159a..0000000000
--- a/packages/webhooks-v5/commands/webhooks/deliveries/info.js
+++ /dev/null
@@ -1,57 +0,0 @@
-const { Command, flags } = require('@heroku-cli/command')
-const cli = require('heroku-cli-util')
-const webhookType = require('../../../lib/webhook-type.js')
-
-class Info extends Command {
- async run () {
- const { flags, args } = this.parse(Info)
- let { path } = webhookType(flags)
-
- let { body } = await this.heroku.get(`${path}/webhook-deliveries/${args.id}`, {
- headers: { Accept: 'application/vnd.heroku+json; version=3.webhooks' }
- })
- let delivery = body
-
- let res = await this.heroku.get(`${path}/webhook-events/${delivery.event.id}`, {
- headers: { Accept: 'application/vnd.heroku+json; version=3.webhooks' }
- })
- let event = res.body
-
- let obj = {
- Created: delivery.created_at,
- Event: delivery.event.id,
- Webhook: delivery.webhook.id,
- Status: delivery.status,
- Include: delivery.event.include,
- Level: delivery.webhook.level,
- Attempts: delivery.num_attempts,
- Code: delivery.last_attempt && delivery.last_attempt.code,
- Error: delivery.last_attempt && delivery.last_attempt.error_class,
- 'Next Attempt': delivery.next_attempt_at
- }
-
- cli.styledHeader(delivery.id)
- cli.styledObject(obj)
-
- cli.styledHeader('Event Payload')
- cli.styledJSON(event.payload)
- }
-}
-
-Info.description = 'info for a webhook event on an app'
-
-Info.examples = [
- '$ heroku webhooks:deliveries:info 99999999-9999-9999-9999-999999999999'
-]
-
-Info.args = [
- { name: 'id' }
-]
-
-Info.flags = {
- app: flags.app(),
- remote: flags.remote(),
- pipeline: flags.string({ char: 'p', description: 'pipeline on which to list', hidden: true })
-}
-
-module.exports = Info
diff --git a/packages/webhooks-v5/commands/webhooks/events/index.js b/packages/webhooks-v5/commands/webhooks/events/index.js
deleted file mode 100644
index eb9f8c1ae6..0000000000
--- a/packages/webhooks-v5/commands/webhooks/events/index.js
+++ /dev/null
@@ -1,43 +0,0 @@
-const { Command, flags } = require('@heroku-cli/command')
-const cli = require('heroku-cli-util')
-const webhookType = require('../../../lib/webhook-type.js')
-
-class Events extends Command {
- async run () {
- const { flags } = this.parse(Events)
- cli.warn('heroku webhooks:event is deprecated, please use heroku webhooks:deliveries')
- let { path, display } = webhookType(flags)
-
- let { body } = await this.heroku.get(`${path}/webhook-events`, {
- headers: { Accept: 'application/vnd.heroku+json; version=3.webhooks' }
- })
- let events = body
-
- if (events.length === 0) {
- cli.log(`${display} has no events`)
- } else {
- events.sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at))
-
- cli.table(events, { columns: [
- { key: 'id', label: 'Event ID' },
- { key: 'resource', label: 'Resource', get: w => w.payload.resource },
- { key: 'action', label: 'Action', get: w => w.payload.action },
- { key: 'published_at', label: 'Published At', get: w => w.payload.published_at }
- ] })
- }
- }
-}
-
-Events.description = 'list webhook events on an app'
-
-Events.examples = [
- '$ heroku webhooks:events'
-]
-
-Events.flags = {
- app: flags.app(),
- remote: flags.remote(),
- pipeline: flags.string({ char: 'p', description: 'pipeline on which to list', hidden: true })
-}
-
-module.exports = Events
diff --git a/packages/webhooks-v5/commands/webhooks/events/info.js b/packages/webhooks-v5/commands/webhooks/events/info.js
deleted file mode 100644
index e8df122f6c..0000000000
--- a/packages/webhooks-v5/commands/webhooks/events/info.js
+++ /dev/null
@@ -1,41 +0,0 @@
-const { Command, flags } = require('@heroku-cli/command')
-const cli = require('heroku-cli-util')
-const webhookType = require('../../../lib/webhook-type.js')
-
-class Info extends Command {
- async run () {
- const { flags, args } = this.parse(Info)
- cli.warn('heroku webhooks:event:info is deprecated, please use heroku webhooks:deliveries:info')
- let { path } = webhookType(flags)
-
- let { body } = await this.heroku.get(`${path}/webhook-events/${args.id}`, {
- headers: { Accept: 'application/vnd.heroku+json; version=3.webhooks' }
- })
- let webhookEvent = body
-
- let obj = {
- payload: JSON.stringify(webhookEvent.payload, null, 2)
- }
-
- cli.styledHeader(webhookEvent.id)
- cli.styledObject(obj)
- }
-}
-
-Info.description = 'info for a webhook event on an app'
-
-Info.examples = [
- '$ heroku webhooks:events:info 99999999-9999-9999-9999-999999999999'
-]
-
-Info.flags = {
- app: flags.app(),
- remote: flags.remote(),
- pipeline: flags.string({ char: 'p', description: 'pipeline on which to list', hidden: true })
-}
-
-Info.args = [
- { name: 'id' }
-]
-
-module.exports = Info
diff --git a/packages/webhooks-v5/commands/webhooks/index.js b/packages/webhooks-v5/commands/webhooks/index.js
deleted file mode 100644
index 455e279199..0000000000
--- a/packages/webhooks-v5/commands/webhooks/index.js
+++ /dev/null
@@ -1,43 +0,0 @@
-const { Command, flags } = require('@heroku-cli/command')
-const cli = require('heroku-cli-util')
-const webhookType = require('../../lib/webhook-type.js')
-
-class Webhooks extends Command {
- async run () {
- const { flags } = this.parse(Webhooks)
- let { path, display } = webhookType(flags)
-
- let { body } = await this.heroku.get(`${path}/webhooks`, {
- headers: { Accept: 'application/vnd.heroku+json; version=3.webhooks' }
- })
- let webhooks = body
-
- if (webhooks.length === 0) {
- cli.log(`${display} has no webhooks\nUse ${cli.color.cmd('heroku webhooks:add')} to add one.`)
- return
- }
-
- webhooks.sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at))
-
- cli.table(webhooks, { columns: [
- { key: 'id', label: 'Webhook ID' },
- { key: 'url', label: 'URL' },
- { key: 'include', label: 'Include' },
- { key: 'level', label: 'Level' }
- ] })
- }
-}
-
-Webhooks.description = 'list webhooks on an app'
-
-Webhooks.examples = [
- '$ heroku webhooks'
-]
-
-Webhooks.flags = {
- app: flags.app(),
- remote: flags.remote(),
- pipeline: flags.string({ char: 'p', description: 'pipeline on which to list', hidden: true })
-}
-
-module.exports = Webhooks
diff --git a/packages/webhooks-v5/commands/webhooks/info.js b/packages/webhooks-v5/commands/webhooks/info.js
deleted file mode 100644
index 041f351ada..0000000000
--- a/packages/webhooks-v5/commands/webhooks/info.js
+++ /dev/null
@@ -1,43 +0,0 @@
-const { Command, flags } = require('@heroku-cli/command')
-const cli = require('heroku-cli-util')
-const webhookType = require('../../lib/webhook-type.js')
-
-class Info extends Command {
- async run () {
- const { flags, args } = this.parse(Info)
- let { path } = webhookType(flags)
-
- let { body } = await this.heroku.get(`${path}/webhooks/${args.id}`, {
- headers: { Accept: 'application/vnd.heroku+json; version=3.webhooks' }
- })
- let webhook = body
-
- let obj = {
- 'Webhook ID': webhook.id,
- URL: webhook.url,
- Include: webhook.include.join(','),
- Level: webhook.level
- }
-
- cli.styledHeader(webhook.id)
- cli.styledObject(obj)
- }
-}
-
-Info.description = 'info for a webhook on an app'
-
-Info.examples = [
- '$ heroku webhooks:info 99999999-9999-9999-9999-999999999999'
-]
-
-Info.flags = {
- app: flags.app(),
- remote: flags.remote(),
- pipeline: flags.string({ char: 'p', description: 'pipeline on which to list', hidden: true })
-}
-
-Info.args = [
- { name: 'id' }
-]
-
-module.exports = Info
diff --git a/packages/webhooks-v5/commands/webhooks/remove.js b/packages/webhooks-v5/commands/webhooks/remove.js
deleted file mode 100644
index 815c462ff1..0000000000
--- a/packages/webhooks-v5/commands/webhooks/remove.js
+++ /dev/null
@@ -1,33 +0,0 @@
-const { Command, flags } = require('@heroku-cli/command')
-const cli = require('heroku-cli-util')
-const webhookType = require('../../lib/webhook-type.js')
-
-class Remove extends Command {
- async run () {
- const { flags, args } = this.parse(Remove)
- let { path, display } = webhookType(flags)
- await cli.action(`Removing webhook ${args.id} from ${display}`, {},
- this.heroku.delete(`${path}/webhooks/${args.id}`, {
- headers: { Accept: 'application/vnd.heroku+json; version=3.webhooks' }
- }
- ))
- }
-}
-
-Remove.description = 'removes a webhook from an app'
-
-Remove.examples = [
- '$ heroku webhooks:remove 99999999-9999-9999-9999-999999999999'
-]
-
-Remove.flags = {
- app: flags.app(),
- remote: flags.remote(),
- pipeline: flags.string({ char: 'p', description: 'pipeline on which to list', hidden: true })
-}
-
-Remove.args = [
- { name: 'id', description: 'id of webhook to remove' }
-]
-
-module.exports = Remove
diff --git a/packages/webhooks-v5/commands/webhooks/update.js b/packages/webhooks-v5/commands/webhooks/update.js
deleted file mode 100644
index 1a329970f3..0000000000
--- a/packages/webhooks-v5/commands/webhooks/update.js
+++ /dev/null
@@ -1,89 +0,0 @@
-const { Command, flags } = require('@heroku-cli/command')
-const cli = require('heroku-cli-util')
-const webhookType = require('../../lib/webhook-type.js')
-
-class Update extends Command {
- async run () {
- const { flags, args } = this.parse(Update)
- let { path, display } = webhookType(flags)
- await cli.action(`Updating webhook ${args.id} for ${display}`, {},
- this.heroku.patch(`${path}/webhooks/${args.id}`, {
- headers: { Accept: 'application/vnd.heroku+json; version=3.webhooks' },
- body: {
- include: flags.include && flags.include.split(',').map(s => s.trim()),
- level: flags.level,
- secret: flags.secret,
- url: flags.url
- }
- })
- )
- }
-}
-
-Update.description = 'updates a webhook in an app'
-
-Update.examples = [
- '$ heroku webhooks:update 99999999-9999-9999-9999-999999999999 -i dyno -l notify -s 09928c40bf1b191b645174a19f7053d16a180da37332e719ef0998f4c0a2 -u https://example.com/hooks'
-]
-
-Update.flags = {
- app: flags.app(),
- remote: flags.remote(),
- pipeline: flags.string({ char: 'p', description: 'pipeline on which to list', hidden: true }),
- include: flags.string({ char: 'i', description: 'comma delimited event types your server will receive ', required: true }),
- level: flags.string({ char: 'l', description: 'notify does not retry, sync will retry until successful or timeout', required: true }),
- secret: flags.string({ char: 's', description: 'value to sign delivery with in Heroku-Webhook-Hmac-SHA256 header' }),
- authorization: flags.string({ char: 't', description: 'authoriation header to send with webhooks' }),
- url: flags.string({ char: 'u', description: 'URL for receiver', required: true })
-}
-
-Update.args = [
- { name: 'id' }
-]
-
-module.exports = Update
-
-//
-// 'use strict'
-//
-// let co = require('co')
-// let cli = require('heroku-cli-util')
-// let webhookType = require('../../lib/webhook-type.js')
-//
-// function * run(context, heroku) {
-// let {path, display} = webhookType(context)
-// yield cli.action(`Updating webhook ${context.args.id} for ${display}`, {},
-// heroku.patch(`${path}/webhooks/${context.args.id}`, {
-// headers: {Accept: 'application/vnd.heroku+json; version=3.webhooks'},
-// body: {
-// include: context.flags.include && context.flags.include.split(',').map(s => s.trim()),
-// level: context.flags.level,
-// secret: context.flags.secret,
-// url: context.flags.url,
-// },
-// }
-// ))
-// }
-//
-// module.exports = {
-// topic: 'webhooks',
-// command: 'update',
-// args: [
-// {name: 'id', description: 'id of webhook to remove'},
-// ],
-// flags: [
-// {name: 'include', char: 'i', description: 'comma delimited webhook types', hasValue: true},
-// {name: 'level', char: 'l', description: 'webhook notification level', hasValue: true},
-// {name: 'secret', char: 's', description: 'comma delimited hook types', hasValue: true},
-// {name: 'url', char: 'u', description: 'url to send webhook to', hasValue: true},
-// {name: 'pipeline', char: 'p', hasValue: true, description: 'pipeline on which to remove', hidden: true},
-// ],
-// description: 'updates a webhook in an app',
-// help: `Example:
-//
-// $ heroku webhooks:update 99999999-9999-9999-9999-999999999999 -i dyno -l notify -s 09928c40bf1b191b645174a19f7053d16a180da37332e719ef0998f4c0a2 -u https://example.com/hooks
-// `,
-// wantsApp: true,
-// needsAuth: true,
-// run: cli.command(co.wrap(run)),
-// }
diff --git a/packages/webhooks-v5/lib/webhook-type.js b/packages/webhooks-v5/lib/webhook-type.js
deleted file mode 100644
index b0b8b12ba6..0000000000
--- a/packages/webhooks-v5/lib/webhook-type.js
+++ /dev/null
@@ -1,17 +0,0 @@
-let cli = require('heroku-cli-util')
-
-module.exports = function (context) {
- if (context.pipeline) {
- return {
- path: `/pipelines/${context.pipeline}`,
- display: context.pipeline
- }
- }
- if (context.app) {
- return {
- path: `/apps/${context.app}`,
- display: cli.color.app(context.app)
- }
- }
- throw new Error('No app specified')
-}
diff --git a/packages/webhooks-v5/package.json b/packages/webhooks-v5/package.json
deleted file mode 100644
index aa66164566..0000000000
--- a/packages/webhooks-v5/package.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
- "name": "@heroku-cli/plugin-webhooks-v5",
- "description": "Heroku CLI plugin to manage webhooks.",
- "version": "7.24.0",
- "author": "Ransom Briggs (@ransombriggs)",
- "bugs": "https://github.com/heroku/cli/issues",
- "dependencies": {
- "@heroku-cli/command": "^8.2.10",
- "heroku-cli-util": "^8.0.11"
- },
- "devDependencies": {
- "@oclif/dev-cli": "^1.21.3",
- "@oclif/plugin-legacy": "^1.1.4",
- "chai": "^4.2.0",
- "cross-env": "^5.2.0",
- "mocha": "^5.2.0",
- "mocha-junit-reporter": "1.18.0",
- "nock": "10.0.6",
- "nyc": "13.2.0"
- },
- "engines": {
- "node": ">=8.0.0"
- },
- "files": [
- "oclif.manifest.json",
- "index.js",
- "commands",
- "lib"
- ],
- "homepage": "https://github.com/heroku/cli/tree/master/packages/webhooks-v5",
- "keywords": [
- "heroku-plugin"
- ],
- "license": "ISC",
- "oclif": {
- "commands": "./commands",
- "topics": {
- "webhooks": {
- "description": "setup HTTP notifications of app activity"
- }
- },
- "repositoryPrefix": "<%- repo %>/blob/v<%- version %>/packages/webhooks-v5/<%- commandPath %>"
- },
- "repository": "heroku/cli",
- "scripts": {
- "postpublish": "rm oclif.manifest.json",
- "prepack": "oclif-dev manifest",
- "test": "cross-env TZ=utc nyc mocha",
- "version": "oclif-dev readme && git add README.md"
- }
-}
diff --git a/packages/webhooks-v5/test/commands/webhooks/add.js b/packages/webhooks-v5/test/commands/webhooks/add.js
deleted file mode 100644
index 71ef968ecc..0000000000
--- a/packages/webhooks-v5/test/commands/webhooks/add.js
+++ /dev/null
@@ -1,80 +0,0 @@
-'use strict'
-/* globals describe it beforeEach cli */
-
-let expect = require('chai').expect
-let nock = require('nock')
-let certs = require('../../../commands/webhooks/add')
-
-describe('heroku webhooks:add', function () {
- beforeEach(function () {
- cli.mockConsole()
- nock.cleanAll()
- })
-
- it('# adds an webhook', function () {
- let mock = nock('https://api.heroku.com')
- .post('/apps/example/webhooks', {
- include: ['foo', 'bar'],
- level: 'notify',
- secret: '1234',
- url: 'http://foobar.com'
- })
- .reply(200, {})
-
- return certs.run([
- '--app', 'example',
- '--include', 'foo,bar',
- '--secret', '1234',
- '--level', 'notify',
- '--url', 'http://foobar.com'
- ]).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('Adding webhook to example... done\n')
- expect(cli.stdout).to.equal('')
- })
- })
-
- it('# adds an webhook with secret', function () {
- let mock = nock('https://api.heroku.com')
- .post('/apps/example/webhooks', {
- include: ['foo', 'bar'],
- level: 'notify',
- url: 'http://foobar.com'
- })
- .reply(200, {}, { 'Heroku-Webhook-Secret': '1234' })
-
- return certs.run([
- '--app', 'example',
- '--include', 'foo,bar',
- '--level', 'notify',
- '--url', 'http://foobar.com'
- ]).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('Adding webhook to example... done\n')
- expect(cli.stdout).to.equal('=== Webhooks Signing Secret\n1234\n')
- })
- })
-
- it('# adds a pipeline webhook', function () {
- let mock = nock('https://api.heroku.com')
- .post('/pipelines/example/webhooks', {
- include: ['foo', 'bar'],
- level: 'notify',
- secret: '1234',
- url: 'http://foobar.com'
- })
- .reply(200, {})
-
- return certs.run([
- '--pipeline', 'example',
- '--include', 'foo,bar',
- '--secret', '1234',
- '--level', 'notify',
- '--url', 'http://foobar.com'
- ]).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('Adding webhook to example... done\n')
- expect(cli.stdout).to.equal('')
- })
- })
-})
diff --git a/packages/webhooks-v5/test/commands/webhooks/deliveries/index.js b/packages/webhooks-v5/test/commands/webhooks/deliveries/index.js
deleted file mode 100644
index dccf495be8..0000000000
--- a/packages/webhooks-v5/test/commands/webhooks/deliveries/index.js
+++ /dev/null
@@ -1,177 +0,0 @@
-/* eslint-disable camelcase */
-'use strict'
-/* globals describe it beforeEach cli */
-
-let expect = require('chai').expect
-let nock = require('nock')
-let certs = require('../../../../commands/webhooks/deliveries')
-const unwrap = require('../../../unwrap')
-
-describe('heroku webhooks:deliveries', function () {
- beforeEach(function () {
- cli.mockConsole()
- nock.cleanAll()
- })
-
- it('# lists deliveries', function () {
- let mock = nock('https://api.heroku.com', {
- reqheaders: { range: 'seq ..; order=desc,max=1000' }
- })
- .get('/apps/example/webhook-deliveries')
- .reply(206, [{
- id: '66666666-6666-6666-6666-666666666666',
- event: {
- id: '55555555-5555-5555-5555-555555555555',
- include: 'api:build'
- },
- webhook: {
- id: '44444444-4444-4444-4444-444444444444',
- level: 'notify'
- },
- status: 'pending',
- num_attempts: 4,
- created_at: '2017-08-17T20:22:38Z'
- }, {
- id: '99999999-9999-9999-9999-999999999999',
- event: {
- id: '88888888-8888-8888-8888-888888888888',
- include: 'api:build'
- },
- webhook: {
- id: '77777777-7777-7777-7777-777777777777',
- level: 'notify'
- },
- last_attempt: {
- code: 401,
- error_class: 'Foobar'
- },
- status: 'retrying',
- num_attempts: 4,
- created_at: '2017-08-17T20:22:37Z',
- next_attempt_at: '2017-08-17T20:22:39Z'
- }], { 'next-range': 'id 99999999-9999-9999-9999-999999999999' })
-
- return certs.run(['--app', 'example']).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal(
- `Delivery ID Created Status Include Level Attempts Code Error Next Attempt
-──────────────────────────────────── ──────────────────── ──────── ───────── ────── ──────── ──── ────── ────────────────────
-99999999-9999-9999-9999-999999999999 2017-08-17T20:22:37Z retrying api:build notify 4 401 Foobar 2017-08-17T20:22:39Z
-66666666-6666-6666-6666-666666666666 2017-08-17T20:22:38Z pending api:build notify 4
-`)
- })
- })
-
- it('# lists deliveries by state', function () {
- let mock = nock('https://api.heroku.com', {
- reqheaders: { range: 'seq ..; order=desc,max=1000' }
- })
- .get('/apps/example/webhook-deliveries?eq[status]=pending')
- .reply(206, [{
- id: '66666666-6666-6666-6666-666666666666',
- event: {
- id: '55555555-5555-5555-5555-555555555555',
- include: 'api:build'
- },
- webhook: {
- id: '44444444-4444-4444-4444-444444444444',
- level: 'notify'
- },
- status: 'pending',
- num_attempts: 4,
- created_at: '2017-08-17T20:22:38Z'
- }])
-
- return certs.run(['--app', 'example', '--status', 'pending']).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal(
- `Delivery ID Created Status Include Level Attempts Code Error Next Attempt
-──────────────────────────────────── ──────────────────── ─────── ───────── ────── ──────── ──── ───── ────────────
-66666666-6666-6666-6666-666666666666 2017-08-17T20:22:38Z pending api:build notify 4
-`)
- })
- })
-
- it('# lists 1000 deliveries', function () {
- let delivery = {
- id: '66666666-6666-6666-6666-666666666666',
- event: {
- id: '55555555-5555-5555-5555-555555555555',
- include: 'api:build'
- },
- webhook: {
- id: '44444444-4444-4444-4444-444444444444',
- level: 'notify'
- },
- status: 'pending',
- num_attempts: 4,
- created_at: '2017-08-17T20:22:38Z'
- }
-
- let mock = nock('https://api.heroku.com', {
- reqheaders: { range: 'seq ..; order=desc,max=1000' }
- })
- .get('/apps/example/webhook-deliveries')
- .reply(206, new Array(1000).fill().map(() => delivery))
-
- return certs.run(['--app', 'example']).then(function () {
- mock.done()
- expect(unwrap(cli.stderr)).to.equal('Only showing the 1000 most recent deliveries It is possible to filter deliveries by using the --status flag\n')
- })
- })
-
- it('# lists empty deliveries', function () {
- let mock = nock('https://api.heroku.com')
- .get('/apps/example/webhook-deliveries')
- .reply(200, [])
-
- return certs.run(['--app', 'example']).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal('example has no deliveries\n')
- })
- })
-
- it('# lists deliveries (piplines)', function () {
- let mock = nock('https://api.heroku.com')
- .get('/pipelines/example/webhook-deliveries')
- .reply(200, [{
- id: '66666666-6666-6666-6666-666666666666',
- event: {
- id: '55555555-5555-5555-5555-555555555555',
- include: 'api:build'
- },
- webhook: {
- id: '44444444-4444-4444-4444-444444444444',
- level: 'notify'
- },
- status: 'pending',
- num_attempts: 4,
- created_at: '2017-08-17T20:22:38Z'
- }])
-
- return certs.run(['--pipeline', 'example']).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal(
- `Delivery ID Created Status Include Level Attempts Code Error Next Attempt
-──────────────────────────────────── ──────────────────── ─────── ───────── ────── ──────── ──── ───── ────────────
-66666666-6666-6666-6666-666666666666 2017-08-17T20:22:38Z pending api:build notify 4
-`)
- })
- })
-
- it('# lists empty deliveries (pipelines)', function () {
- let mock = nock('https://api.heroku.com')
- .get('/pipelines/example/webhook-deliveries')
- .reply(200, [])
-
- return certs.run(['--pipeline', 'example']).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal('example has no deliveries\n')
- })
- })
-})
diff --git a/packages/webhooks-v5/test/commands/webhooks/events/index.js b/packages/webhooks-v5/test/commands/webhooks/events/index.js
deleted file mode 100644
index ca3082164e..0000000000
--- a/packages/webhooks-v5/test/commands/webhooks/events/index.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/* eslint-disable camelcase */
-'use strict'
-/* globals describe it beforeEach cli */
-
-let expect = require('chai').expect
-let nock = require('nock')
-let certs = require('../../../../commands/webhooks/events')
-const unwrap = require('../../../unwrap')
-
-describe('heroku webhooks:events', function () {
- beforeEach(function () {
- cli.mockConsole()
- nock.cleanAll()
- })
-
- it('# lists events', function () {
- let mock = nock('https://api.heroku.com')
- .get('/apps/example/webhook-events')
- .reply(200, [{
- id: '99999999-9999-9999-9999-999999999999',
- payload: {
- published_at: '2016-08-31T21:55:06Z',
- resource: 'api:release',
- action: 'create'
- }
-
- }])
-
- return certs.run(['--app', 'example']).then(function () {
- mock.done()
- expect(unwrap(cli.stderr)).to.equal('heroku webhooks:event is deprecated, please use heroku webhooks:deliveries\n')
- expect(cli.stdout).to.equal(
- `Event ID Resource Action Published At
-──────────────────────────────────── ─────────── ────── ────────────────────
-99999999-9999-9999-9999-999999999999 api:release create 2016-08-31T21:55:06Z
-`)
- })
- })
-
- it('# lists events (pipelines)', function () {
- let mock = nock('https://api.heroku.com')
- .get('/pipelines/example/webhook-events')
- .reply(200, [{
- id: '99999999-9999-9999-9999-999999999999',
- payload: {
- published_at: '2016-08-31T21:55:06Z',
- resource: 'api:release',
- action: 'create'
- }
-
- }])
-
- return certs.run(['--pipeline', 'example']).then(function () {
- mock.done()
- expect(unwrap(cli.stderr)).to.equal('heroku webhooks:event is deprecated, please use heroku webhooks:deliveries\n')
- expect(cli.stdout).to.equal(
- `Event ID Resource Action Published At
-──────────────────────────────────── ─────────── ────── ────────────────────
-99999999-9999-9999-9999-999999999999 api:release create 2016-08-31T21:55:06Z
-`)
- })
- })
-
- it('# lists empty events', function () {
- let mock = nock('https://api.heroku.com')
- .get('/apps/example/webhook-events')
- .reply(200, [])
-
- return certs.run(['--app', 'example']).then(function () {
- mock.done()
- expect(unwrap(cli.stderr)).to.equal('heroku webhooks:event is deprecated, please use heroku webhooks:deliveries\n')
- expect(cli.stdout).to.equal('example has no events\n')
- })
- })
-
- it('# lists empty events (pipelines)', function () {
- let mock = nock('https://api.heroku.com')
- .get('/pipelines/example/webhook-events')
- .reply(200, [])
-
- return certs.run(['--pipeline', 'example']).then(function () {
- mock.done()
- expect(unwrap(cli.stderr)).to.equal('heroku webhooks:event is deprecated, please use heroku webhooks:deliveries\n')
- expect(cli.stdout).to.equal('example has no events\n')
- })
- })
-})
diff --git a/packages/webhooks-v5/test/commands/webhooks/events/info.js b/packages/webhooks-v5/test/commands/webhooks/events/info.js
deleted file mode 100644
index f7217c14ba..0000000000
--- a/packages/webhooks-v5/test/commands/webhooks/events/info.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/* eslint-disable camelcase */
-'use strict'
-/* globals describe it beforeEach cli */
-
-let expect = require('chai').expect
-let nock = require('nock')
-let certs = require('../../../../commands/webhooks/events/info')
-const unwrap = require('../../../unwrap')
-
-describe('heroku webhooks:events:info', function () {
- beforeEach(function () {
- cli.mockConsole()
- nock.cleanAll()
- })
-
- it('# shows an event', function () {
- let mock = nock('https://api.heroku.com')
- .get('/apps/example/webhook-events/99999999-9999-9999-9999-999999999999')
- .reply(200, {
- id: '99999999-9999-9999-9999-999999999999',
- payload: {
- published_at: '2016-08-31T21:55:06Z',
- resource: 'api:release',
- action: 'create',
- data: {
- foo: 'bar'
- }
- }
- })
-
- return certs.run(['--app', 'example', '99999999-9999-9999-9999-999999999999']).then(function () {
- mock.done()
- expect(unwrap(cli.stderr)).to.equal('heroku webhooks:event:info is deprecated, please use heroku webhooks:deliveries:info\n')
- expect(cli.stdout).to.equal(
- `=== 99999999-9999-9999-9999-999999999999
-payload: {
- "published_at": "2016-08-31T21:55:06Z",
- "resource": "api:release",
- "action": "create",
- "data": {
- "foo": "bar"
- }
-}
-`)
- })
- })
-
- it('# shows an event (pipelines)', function () {
- let mock = nock('https://api.heroku.com')
- .get('/pipelines/example/webhook-events/99999999-9999-9999-9999-999999999999')
- .reply(200, {
- id: '99999999-9999-9999-9999-999999999999',
- payload: {
- published_at: '2016-08-31T21:55:06Z',
- resource: 'api:release',
- action: 'create',
- data: {
- foo: 'bar'
- }
- }
- })
-
- return certs.run(['--pipeline', 'example', '99999999-9999-9999-9999-999999999999']).then(function () {
- mock.done()
- expect(unwrap(cli.stderr)).to.equal('heroku webhooks:event:info is deprecated, please use heroku webhooks:deliveries:info\n')
- expect(cli.stdout).to.equal(
- `=== 99999999-9999-9999-9999-999999999999
-payload: {
- "published_at": "2016-08-31T21:55:06Z",
- "resource": "api:release",
- "action": "create",
- "data": {
- "foo": "bar"
- }
-}
-`)
- })
- })
-})
diff --git a/packages/webhooks-v5/test/commands/webhooks/index.js b/packages/webhooks-v5/test/commands/webhooks/index.js
deleted file mode 100644
index 952451ab9d..0000000000
--- a/packages/webhooks-v5/test/commands/webhooks/index.js
+++ /dev/null
@@ -1,79 +0,0 @@
-'use strict'
-/* globals describe it beforeEach cli */
-
-let expect = require('chai').expect
-let nock = require('nock')
-let certs = require('../../../commands/webhooks')
-
-describe('heroku webhooks:add', function () {
- beforeEach(function () {
- cli.mockConsole()
- nock.cleanAll()
- })
-
- it('# lists webhooks', function () {
- let mock = nock('https://api.heroku.com')
- .get('/apps/example/webhooks')
- .reply(200, [{
- id: '99999999-9999-9999-9999-999999999999',
- include: ['foo', 'bar'],
- level: 'notify',
- url: 'http://foobar.com'
- }])
-
- return certs.run(['--app', 'example']).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal(
- `Webhook ID URL Include Level
-──────────────────────────────────── ───────────────── ─────── ──────
-99999999-9999-9999-9999-999999999999 http://foobar.com foo,bar notify
-`)
- })
- })
-
- it('# lists empty webhooks', function () {
- let mock = nock('https://api.heroku.com')
- .get('/apps/example/webhooks')
- .reply(200, [])
-
- return certs.run(['--app', 'example']).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal('example has no webhooks\nUse heroku webhooks:add to add one.\n')
- })
- })
-
- it('# lists pipeline webhooks', function () {
- let mock = nock('https://api.heroku.com')
- .get('/pipelines/example/webhooks')
- .reply(200, [{
- id: '99999999-9999-9999-9999-999999999999',
- include: ['foo', 'bar'],
- level: 'notify',
- url: 'http://foobar.com'
- }])
-
- return certs.run(['--pipeline', 'example']).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal(
- `Webhook ID URL Include Level
-──────────────────────────────────── ───────────────── ─────── ──────
-99999999-9999-9999-9999-999999999999 http://foobar.com foo,bar notify
-`)
- })
- })
-
- it('# lists empty pipeline webhooks', function () {
- let mock = nock('https://api.heroku.com')
- .get('/pipelines/example/webhooks')
- .reply(200, [])
-
- return certs.run(['--pipeline', 'example']).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal('example has no webhooks\nUse heroku webhooks:add to add one.\n')
- })
- })
-})
diff --git a/packages/webhooks-v5/test/commands/webhooks/info.js b/packages/webhooks-v5/test/commands/webhooks/info.js
deleted file mode 100644
index b871eede06..0000000000
--- a/packages/webhooks-v5/test/commands/webhooks/info.js
+++ /dev/null
@@ -1,59 +0,0 @@
-'use strict'
-/* globals describe it beforeEach cli */
-
-let expect = require('chai').expect
-let nock = require('nock')
-let info = require('../../../commands/webhooks/info')
-
-describe('heroku webhooks:deliveries:info', function () {
- beforeEach(function () {
- cli.mockConsole()
- nock.cleanAll()
- })
-
- it('# shows a webhook', function () {
- let mock = nock('https://api.heroku.com')
- .get('/apps/example/webhooks/99999999-9999-9999-9999-999999999999')
- .reply(200, {
- id: '99999999-9999-9999-9999-999999999999',
- include: ['foo', 'bar'],
- level: 'notify',
- url: 'http://foobar.com'
- })
-
- return info.run(['99999999-9999-9999-9999-999999999999', '--app', 'example']).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal(
- `=== 99999999-9999-9999-9999-999999999999
-Include: foo,bar
-Level: notify
-URL: http://foobar.com
-Webhook ID: 99999999-9999-9999-9999-999999999999
-`)
- })
- })
-
- it('# shows a webhook (pipelines)', function () {
- let mock = nock('https://api.heroku.com')
- .get('/pipelines/example/webhooks/99999999-9999-9999-9999-999999999999')
- .reply(200, {
- id: '99999999-9999-9999-9999-999999999999',
- include: ['foo', 'bar'],
- level: 'notify',
- url: 'http://foobar.com'
- })
-
- return info.run(['99999999-9999-9999-9999-999999999999', '--pipeline', 'example']).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal(
- `=== 99999999-9999-9999-9999-999999999999
-Include: foo,bar
-Level: notify
-URL: http://foobar.com
-Webhook ID: 99999999-9999-9999-9999-999999999999
-`)
- })
- })
-})
diff --git a/packages/webhooks-v5/test/commands/webhooks/remove.js b/packages/webhooks-v5/test/commands/webhooks/remove.js
deleted file mode 100644
index 93cb6a1c51..0000000000
--- a/packages/webhooks-v5/test/commands/webhooks/remove.js
+++ /dev/null
@@ -1,37 +0,0 @@
-'use strict'
-/* globals describe it beforeEach cli */
-
-let expect = require('chai').expect
-let nock = require('nock')
-let certs = require('../../../commands/webhooks/remove')
-
-describe('heroku webhooks:add', function () {
- beforeEach(function () {
- cli.mockConsole()
- nock.cleanAll()
- })
-
- it('# removes webhooks', function () {
- let mock = nock('https://api.heroku.com')
- .delete('/apps/example/webhooks/99999999-9999-9999-9999-999999999999')
- .reply(200, {})
-
- return certs.run(['99999999-9999-9999-9999-999999999999', '--app', 'example']).then(function () {
- mock.done()
- expect(cli.stdout).to.equal('')
- expect(cli.stderr).to.equal('Removing webhook 99999999-9999-9999-9999-999999999999 from example... done\n')
- })
- })
-
- it('# removes webhooks (pipelines)', function () {
- let mock = nock('https://api.heroku.com')
- .delete('/pipelines/example/webhooks/99999999-9999-9999-9999-999999999999')
- .reply(200, {})
-
- return certs.run(['99999999-9999-9999-9999-999999999999', '--pipeline', 'example']).then(function () {
- mock.done()
- expect(cli.stdout).to.equal('')
- expect(cli.stderr).to.equal('Removing webhook 99999999-9999-9999-9999-999999999999 from example... done\n')
- })
- })
-})
diff --git a/packages/webhooks-v5/test/commands/webhooks/update.js b/packages/webhooks-v5/test/commands/webhooks/update.js
deleted file mode 100644
index 4fb0a62624..0000000000
--- a/packages/webhooks-v5/test/commands/webhooks/update.js
+++ /dev/null
@@ -1,61 +0,0 @@
-'use strict'
-/* globals describe it beforeEach cli */
-
-let expect = require('chai').expect
-let nock = require('nock')
-let certs = require('../../../commands/webhooks/update')
-
-describe('heroku webhooks:update', function () {
- beforeEach(function () {
- cli.mockConsole()
- nock.cleanAll()
- })
-
- it('# updates a webhook', function () {
- let mock = nock('https://api.heroku.com')
- .patch('/apps/example/webhooks/99999999-9999-9999-9999-999999999999', {
- include: ['foo', 'bar'],
- level: 'notify',
- secret: '1234',
- url: 'http://foobar.com'
- })
- .reply(200, {})
-
- return certs.run([
- '99999999-9999-9999-9999-999999999999',
- '--app', 'example',
- '--include', 'foo,bar',
- '--secret', '1234',
- '--level', 'notify',
- '--url', 'http://foobar.com'
- ]).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('Updating webhook 99999999-9999-9999-9999-999999999999 for example... done\n')
- expect(cli.stdout).to.equal('')
- })
- })
-
- it('# updates a webhook', function () {
- let mock = nock('https://api.heroku.com')
- .patch('/pipelines/example/webhooks/99999999-9999-9999-9999-999999999999', {
- include: ['foo', 'bar'],
- level: 'notify',
- secret: '1234',
- url: 'http://foobar.com'
- })
- .reply(200, {})
-
- return certs.run([
- '99999999-9999-9999-9999-999999999999',
- '--pipeline', 'example',
- '--include', 'foo,bar',
- '--secret', '1234',
- '--level', 'notify',
- '--url', 'http://foobar.com'
- ]).then(function () {
- mock.done()
- expect(cli.stderr).to.equal('Updating webhook 99999999-9999-9999-9999-999999999999 for example... done\n')
- expect(cli.stdout).to.equal('')
- })
- })
-})
diff --git a/packages/webhooks-v5/test/helpers.js b/packages/webhooks-v5/test/helpers.js
deleted file mode 100644
index 3933dd488f..0000000000
--- a/packages/webhooks-v5/test/helpers.js
+++ /dev/null
@@ -1,9 +0,0 @@
-'use strict'
-/* globals cli */
-
-global.cli = require('heroku-cli-util')
-cli.raiseErrors = true
-cli.color.enabled = false
-
-let nock = require('nock')
-nock.disableNetConnect()
diff --git a/packages/webhooks-v5/test/mocha.opts b/packages/webhooks-v5/test/mocha.opts
deleted file mode 100644
index 2e333ba791..0000000000
--- a/packages/webhooks-v5/test/mocha.opts
+++ /dev/null
@@ -1,4 +0,0 @@
---require ./test/helpers.js
---reporter list
---recursive
---timeout 50000
diff --git a/packages/webhooks-v5/test/unwrap.js b/packages/webhooks-v5/test/unwrap.js
deleted file mode 100644
index e8ca37ef19..0000000000
--- a/packages/webhooks-v5/test/unwrap.js
+++ /dev/null
@@ -1,10 +0,0 @@
-'use strict'
-
-function unwrap (str) {
- let sanitize = str.replace(/\n ([▸!]) {3}/g, '')
- sanitize = sanitize.replace(/ ([▸!]) {4}/g, '')
-
- return sanitize
-}
-
-module.exports = unwrap
diff --git a/packages/webhooks/.circleci/config.yml b/packages/webhooks/.circleci/config.yml
new file mode 100644
index 0000000000..dd07079240
--- /dev/null
+++ b/packages/webhooks/.circleci/config.yml
@@ -0,0 +1,60 @@
+---
+version: 2
+jobs:
+ node-latest: &test
+ docker:
+ - image: node:latest
+ working_directory: ~/cli
+ steps:
+ - checkout
+ - restore_cache: &restore_cache
+ keys:
+ - v1-npm-{{checksum ".circleci/config.yml"}}-{{checksum "yarn.lock"}}
+ - v1-npm-{{checksum ".circleci/config.yml"}}
+ - run:
+ name: Install dependencies
+ command: yarn
+ - run: ./bin/run --help
+ - run:
+ name: Testing
+ command: yarn test
+ - run:
+ name: Submitting code coverage to codecov
+ command: |
+ ./node_modules/.bin/nyc report --reporter text-lcov > coverage.lcov
+ curl -s https://codecov.io/bash | bash
+ node-8:
+ <<: *test
+ docker:
+ - image: node:8
+ node-10:
+ <<: *test
+ docker:
+ - image: node:10
+ cache:
+ <<: *test
+ steps:
+ - checkout
+ - run:
+ name: Install dependencies
+ command: yarn
+ - save_cache:
+ key: v1-npm-{{checksum ".circleci/config.yml"}}-{{checksum "yarn.lock"}}
+ paths:
+ - ~/cli/node_modules
+ - /usr/local/share/.cache/yarn
+ - /usr/local/share/.config/yarn
+
+workflows:
+ version: 2
+ "webhooks":
+ jobs:
+ - node-latest
+ - node-8
+ - node-10
+ - cache:
+ filters:
+ tags:
+ only: /^v.*/
+ branches:
+ ignore: /.*/
diff --git a/packages/webhooks/.editorconfig b/packages/webhooks/.editorconfig
new file mode 100644
index 0000000000..beffa3084e
--- /dev/null
+++ b/packages/webhooks/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/packages/webhooks/.gitignore b/packages/webhooks/.gitignore
new file mode 100644
index 0000000000..9d6ea2ca58
--- /dev/null
+++ b/packages/webhooks/.gitignore
@@ -0,0 +1,8 @@
+*-debug.log
+*-error.log
+/.nyc_output
+/dist
+/lib
+/package-lock.json
+/tmp
+node_modules
diff --git a/packages/webhooks/CHANGELOG.md b/packages/webhooks/CHANGELOG.md
new file mode 100644
index 0000000000..1eac94bcd7
--- /dev/null
+++ b/packages/webhooks/CHANGELOG.md
@@ -0,0 +1,11 @@
+# Change Log
+
+All notable changes to this project will be documented in this file.
+See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+
+# [7.29.0](https://github.com/heroku/cli/compare/v7.28.0...v7.29.0) (2019-08-21)
+
+
+### Features
+
+* **webhooks:** add oclif version of webhooks plugin ([#1253](https://github.com/heroku/cli/issues/1253)) ([110c516](https://github.com/heroku/cli/commit/110c516))
diff --git a/packages/webhooks/README.md b/packages/webhooks/README.md
new file mode 100644
index 0000000000..6588f3d0ef
--- /dev/null
+++ b/packages/webhooks/README.md
@@ -0,0 +1,230 @@
+webhooks
+========
+
+
+
+[![oclif](https://img.shields.io/badge/cli-oclif-brightgreen.svg)](https://oclif.io)
+[![Version](https://img.shields.io/npm/v/webhooks.svg)](https://npmjs.org/package/webhooks)
+[![CircleCI](https://circleci.com/gh/chadian/webhooks/tree/master.svg?style=shield)](https://circleci.com/gh/chadian/webhooks/tree/master)
+[![Appveyor CI](https://ci.appveyor.com/api/projects/status/github/chadian/webhooks?branch=master&svg=true)](https://ci.appveyor.com/project/chadian/webhooks/branch/master)
+[![Codecov](https://codecov.io/gh/chadian/webhooks/branch/master/graph/badge.svg)](https://codecov.io/gh/chadian/webhooks)
+[![Downloads/week](https://img.shields.io/npm/dw/webhooks.svg)](https://npmjs.org/package/webhooks)
+[![License](https://img.shields.io/npm/l/webhooks.svg)](https://github.com/chadian/webhooks/blob/master/package.json)
+
+
+* [Usage](#usage)
+* [Commands](#commands)
+
+# Usage
+
+```sh-session
+$ npm install -g @heroku-cli/plugin-webhooks
+$ heroku COMMAND
+running command...
+$ heroku (-v|--version|version)
+@heroku-cli/plugin-webhooks/7.29.0 darwin-x64 node-v10.2.1
+$ heroku --help [COMMAND]
+USAGE
+ $ heroku COMMAND
+...
+```
+
+# Commands
+
+* [`heroku base`](#heroku-base)
+* [`heroku webhooks`](#heroku-webhooks)
+* [`heroku webhooks:add`](#heroku-webhooksadd)
+* [`heroku webhooks:deliveries`](#heroku-webhooksdeliveries)
+* [`heroku webhooks:deliveries:info ID`](#heroku-webhooksdeliveriesinfo-id)
+* [`heroku webhooks:events`](#heroku-webhooksevents)
+* [`heroku webhooks:events:info ID`](#heroku-webhookseventsinfo-id)
+* [`heroku webhooks:info ID`](#heroku-webhooksinfo-id)
+* [`heroku webhooks:remove ID`](#heroku-webhooksremove-id)
+* [`heroku webhooks:update ID`](#heroku-webhooksupdate-id)
+
+## `heroku base`
+
+```
+USAGE
+ $ heroku base
+```
+
+_See code: [src/commands/base.ts](https://github.com/heroku/cli/blob/v7.29.0/src/commands/base.ts)_
+
+## `heroku webhooks`
+
+list webhooks on an app
+
+```
+USAGE
+ $ heroku webhooks
+
+OPTIONS
+ -a, --app=app app to run command against
+ -r, --remote=remote git remote of app to use
+
+EXAMPLE
+ $ heroku webhooks
+```
+
+_See code: [src/commands/webhooks/index.ts](https://github.com/heroku/cli/blob/v7.29.0/src/commands/webhooks/index.ts)_
+
+## `heroku webhooks:add`
+
+add a webhook to an app
+
+```
+USAGE
+ $ heroku webhooks:add
+
+OPTIONS
+ -a, --app=app app to run command against
+ -i, --include=include (required) comma delimited event types your server will receive
+ -l, --level=level (required) notify does not retry, sync will retry until successful or timeout
+ -r, --remote=remote git remote of app to use
+ -s, --secret=secret value to sign delivery with in Heroku-Webhook-Hmac-SHA256 header
+ -t, --authorization=authorization authoriation header to send with webhooks
+ -u, --url=url (required) URL for receiver
+
+EXAMPLE
+ $ heroku webhooks:add -i api:dyno -l notify -u https://example.com/hooks
+```
+
+_See code: [src/commands/webhooks/add.ts](https://github.com/heroku/cli/blob/v7.29.0/src/commands/webhooks/add.ts)_
+
+## `heroku webhooks:deliveries`
+
+list webhook deliveries on an app
+
+```
+USAGE
+ $ heroku webhooks:deliveries
+
+OPTIONS
+ -a, --app=app app to run command against
+ -r, --remote=remote git remote of app to use
+ -s, --status=status filter deliveries by status
+
+EXAMPLE
+ $ heroku webhooks:deliveries
+```
+
+_See code: [src/commands/webhooks/deliveries/index.ts](https://github.com/heroku/cli/blob/v7.29.0/src/commands/webhooks/deliveries/index.ts)_
+
+## `heroku webhooks:deliveries:info ID`
+
+info for a webhook event on an app
+
+```
+USAGE
+ $ heroku webhooks:deliveries:info ID
+
+OPTIONS
+ -a, --app=app app to run command against
+ -r, --remote=remote git remote of app to use
+
+EXAMPLE
+ $ heroku webhooks:deliveries:info 99999999-9999-9999-9999-999999999999
+```
+
+_See code: [src/commands/webhooks/deliveries/info.ts](https://github.com/heroku/cli/blob/v7.29.0/src/commands/webhooks/deliveries/info.ts)_
+
+## `heroku webhooks:events`
+
+list webhook events on an app
+
+```
+USAGE
+ $ heroku webhooks:events
+
+OPTIONS
+ -a, --app=app app to run command against
+ -r, --remote=remote git remote of app to use
+
+EXAMPLE
+ $ heroku webhooks:events
+```
+
+_See code: [src/commands/webhooks/events/index.ts](https://github.com/heroku/cli/blob/v7.29.0/src/commands/webhooks/events/index.ts)_
+
+## `heroku webhooks:events:info ID`
+
+info for a webhook event on an app
+
+```
+USAGE
+ $ heroku webhooks:events:info ID
+
+OPTIONS
+ -a, --app=app app to run command against
+ -r, --remote=remote git remote of app to use
+
+EXAMPLE
+ $ heroku webhooks:events:info 99999999-9999-9999-9999-999999999999
+```
+
+_See code: [src/commands/webhooks/events/info.ts](https://github.com/heroku/cli/blob/v7.29.0/src/commands/webhooks/events/info.ts)_
+
+## `heroku webhooks:info ID`
+
+info for a webhook on an app
+
+```
+USAGE
+ $ heroku webhooks:info ID
+
+OPTIONS
+ -a, --app=app app to run command against
+ -r, --remote=remote git remote of app to use
+
+EXAMPLE
+ $ heroku webhooks:info 99999999-9999-9999-9999-999999999999
+```
+
+_See code: [src/commands/webhooks/info.ts](https://github.com/heroku/cli/blob/v7.29.0/src/commands/webhooks/info.ts)_
+
+## `heroku webhooks:remove ID`
+
+removes a webhook from an app
+
+```
+USAGE
+ $ heroku webhooks:remove ID
+
+ARGUMENTS
+ ID id of webhook to remove
+
+OPTIONS
+ -a, --app=app app to run command against
+ -r, --remote=remote git remote of app to use
+
+EXAMPLE
+ $ heroku webhooks:remove 99999999-9999-9999-9999-999999999999
+```
+
+_See code: [src/commands/webhooks/remove.ts](https://github.com/heroku/cli/blob/v7.29.0/src/commands/webhooks/remove.ts)_
+
+## `heroku webhooks:update ID`
+
+updates a webhook in an app
+
+```
+USAGE
+ $ heroku webhooks:update ID
+
+OPTIONS
+ -a, --app=app app to run command against
+ -i, --include=include (required) comma delimited event types your server will receive
+ -l, --level=level (required) notify does not retry, sync will retry until successful or timeout
+ -r, --remote=remote git remote of app to use
+ -s, --secret=secret value to sign delivery with in Heroku-Webhook-Hmac-SHA256 header
+ -t, --authorization=authorization authoriation header to send with webhooks
+ -u, --url=url (required) URL for receiver
+
+EXAMPLE
+ $ heroku webhooks:update 99999999-9999-9999-9999-999999999999 -i dyno -l notify -s
+ 09928c40bf1b191b645174a19f7053d16a180da37332e719ef0998f4c0a2 -u https://example.com/hooks
+```
+
+_See code: [src/commands/webhooks/update.ts](https://github.com/heroku/cli/blob/v7.29.0/src/commands/webhooks/update.ts)_
+
diff --git a/packages/webhooks/appveyor.yml b/packages/webhooks/appveyor.yml
new file mode 100644
index 0000000000..b2537719ab
--- /dev/null
+++ b/packages/webhooks/appveyor.yml
@@ -0,0 +1,23 @@
+environment:
+ nodejs_version: "10"
+cache:
+ - '%LOCALAPPDATA%\Yarn -> appveyor.yml'
+ - node_modules -> yarn.lock
+
+install:
+ - ps: Install-Product node $env:nodejs_version x64
+ - yarn
+test_script:
+ - .\bin\run --help
+ - yarn test
+
+after_test:
+ - .\node_modules\.bin\nyc report --reporter text-lcov > coverage.lcov
+ - ps: |
+ $env:PATH = 'C:\msys64\usr\bin;' + $env:PATH
+ Invoke-WebRequest -Uri 'https://codecov.io/bash' -OutFile codecov.sh
+ bash codecov.sh
+
+
+build: off
+
diff --git a/packages/webhooks/bin/run b/packages/webhooks/bin/run
new file mode 100755
index 0000000000..3c4ae3ac07
--- /dev/null
+++ b/packages/webhooks/bin/run
@@ -0,0 +1,4 @@
+#!/usr/bin/env node
+
+require('@oclif/command').run()
+.catch(require('@oclif/errors/handle'))
diff --git a/packages/webhooks/bin/run.cmd b/packages/webhooks/bin/run.cmd
new file mode 100644
index 0000000000..968fc30758
--- /dev/null
+++ b/packages/webhooks/bin/run.cmd
@@ -0,0 +1,3 @@
+@echo off
+
+node "%~dp0\run" %*
diff --git a/packages/webhooks/package.json b/packages/webhooks/package.json
new file mode 100644
index 0000000000..07b13bb733
--- /dev/null
+++ b/packages/webhooks/package.json
@@ -0,0 +1,60 @@
+{
+ "name": "@heroku-cli/plugin-webhooks",
+ "description": "Heroku CLI plugin to manage webhooks",
+ "version": "7.29.0",
+ "author": "Chad Carbert @chadian",
+ "bugs": "https://github.com/heroku/cli/issues",
+ "dependencies": {
+ "@heroku-cli/color": "^1.1.14",
+ "@heroku-cli/command": "^8.2.11",
+ "@oclif/command": "^1",
+ "@oclif/config": "^1",
+ "cli-ux": "^5.2.1",
+ "tslib": "^1"
+ },
+ "devDependencies": {
+ "@oclif/dev-cli": "^1",
+ "@oclif/plugin-help": "^2",
+ "@oclif/test": "^1",
+ "@oclif/tslint": "^3",
+ "@types/chai": "^4",
+ "@types/mocha": "^5",
+ "@types/node": "^10",
+ "chai": "^4",
+ "date-fns": "^1.30.1",
+ "globby": "^8",
+ "mocha": "^5",
+ "nyc": "^13",
+ "ts-node": "^8",
+ "tslint": "^5",
+ "typescript": "^3.3"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ },
+ "files": [
+ "/lib",
+ "/oclif.manifest.json",
+ "/yarn.lock"
+ ],
+ "homepage": "https://github.com/heroku/cli/tree/master/packages/webhooks",
+ "keywords": [
+ "heroku-plugin"
+ ],
+ "license": "MIT",
+ "oclif": {
+ "commands": "./lib/commands",
+ "bin": "heroku",
+ "devPlugins": [
+ "@oclif/plugin-help"
+ ]
+ },
+ "repository": "heroku/cli",
+ "scripts": {
+ "postpack": "rm -f oclif.manifest.json",
+ "posttest": "tslint -p test -t stylish",
+ "prepack": "rm -rf lib && tsc -b && oclif-dev manifest && oclif-dev readme",
+ "test": "nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"",
+ "version": "oclif-dev readme && git add README.md"
+ }
+}
diff --git a/packages/webhooks/src/commands/base.ts b/packages/webhooks/src/commands/base.ts
new file mode 100644
index 0000000000..f16316c3c9
--- /dev/null
+++ b/packages/webhooks/src/commands/base.ts
@@ -0,0 +1,36 @@
+import color from '@heroku-cli/color'
+import {APIClient, Command} from '@heroku-cli/command'
+
+import {IConfig} from '@oclif/config'
+
+export default abstract class extends Command {
+ webhooksClient: APIClient
+
+ protected constructor(argv: string[], config: IConfig) {
+ super(argv, config)
+
+ const client = new APIClient(this.config, {})
+ client.defaults.headers = {
+ ...this.heroku.defaults.headers,
+ Accept: 'application/vnd.heroku+json; version=3.webhooks',
+ authorization: `Basic ${Buffer.from(':' + this.heroku.auth).toString('base64')}`
+ }
+ this.webhooksClient = client
+ }
+
+ webhookType(context: {pipeline?: string, app?: string}): {path: string, display: string} {
+ if (context.pipeline) {
+ return {
+ path: `/pipelines/${context.pipeline}`,
+ display: context.pipeline
+ }
+ }
+ if (context.app) {
+ return {
+ path: `/apps/${context.app}`,
+ display: color.app(context.app)
+ }
+ }
+ return this.error('No app specified')
+ }
+}
diff --git a/packages/webhooks/src/commands/webhooks/add.ts b/packages/webhooks/src/commands/webhooks/add.ts
new file mode 100644
index 0000000000..a4c3e750c0
--- /dev/null
+++ b/packages/webhooks/src/commands/webhooks/add.ts
@@ -0,0 +1,51 @@
+import {flags} from '@heroku-cli/command'
+import {cli} from 'cli-ux'
+
+import BaseCommand from '../base'
+
+export default class WebhooksAdd extends BaseCommand {
+ static description = 'add a webhook to an app'
+
+ static examples = [
+ '$ heroku webhooks:add -i api:dyno -l notify -u https://example.com/hooks'
+ ]
+
+ static flags = {
+ app: flags.app(),
+ remote: flags.remote(),
+ pipeline: flags.pipeline({char: 'p', description: 'pipeline on which to list', hidden: true}),
+ include: flags.string({char: 'i', description: 'comma delimited event types your server will receive ', required: true}),
+ level: flags.string({char: 'l', description: 'notify does not retry, sync will retry until successful or timeout', required: true}),
+ secret: flags.string({char: 's', description: 'value to sign delivery with in Heroku-Webhook-Hmac-SHA256 header'}),
+ authorization: flags.string({char: 't', description: 'authoriation header to send with webhooks'}),
+ url: flags.string({char: 'u', description: 'URL for receiver', required: true})
+ }
+
+ async run() {
+ const {flags} = this.parse(WebhooksAdd)
+ const {path, display} = this.webhookType(flags)
+
+ cli.action.start(`Adding webhook to ${display}`)
+
+ const response = await this.webhooksClient.post(`${path}/webhooks`, {
+ body: {
+ include: flags.include.split(',').map(s => s.trim()),
+ level: flags.level,
+ secret: flags.secret,
+ url: flags.url,
+ authorization: flags.authorization
+ }
+ })
+
+ const secret = response.headers && response.headers['heroku-webhook-secret'] as string
+
+ cli.action.stop()
+
+ if (secret) {
+ cli.styledHeader('Webhooks Signing Secret')
+ this.log(secret)
+ } else {
+ cli.warn('no secret found')
+ }
+ }
+}
diff --git a/packages/webhooks/src/commands/webhooks/deliveries/index.ts b/packages/webhooks/src/commands/webhooks/deliveries/index.ts
new file mode 100644
index 0000000000..39974b03f4
--- /dev/null
+++ b/packages/webhooks/src/commands/webhooks/deliveries/index.ts
@@ -0,0 +1,84 @@
+import {flags} from '@heroku-cli/command'
+import {cli} from 'cli-ux'
+
+import BaseCommand from '../../base'
+
+export default class Deliveries extends BaseCommand {
+ static description = 'list webhook deliveries on an app'
+
+ static examples = [
+ '$ heroku webhooks:deliveries'
+ ]
+
+ static flags = {
+ app: flags.app(),
+ remote: flags.remote(),
+ status: flags.string({char: 's', description: 'filter deliveries by status'}),
+ pipeline: flags.pipeline({char: 'p', description: 'pipeline on which to list', hidden: true})
+ }
+
+ async run() {
+ const {flags} = this.parse(Deliveries)
+ let {path, display} = this.webhookType(flags)
+ const max = 1000
+
+ path = `${path}/webhook-deliveries`
+ if (flags.status) {
+ path += `?eq[status]=${encodeURIComponent(flags.status)}`
+ }
+
+ const {body: deliveries} = await this.webhooksClient.get(path, {
+ headers: {
+ Range: `seq ..; order=desc,max=${max}`
+ },
+ partial: true
+ })
+
+ if (deliveries.length === 0) {
+ this.log(`${display} has no deliveries`)
+ } else {
+ let code = (w: any) => {
+ return (w.last_attempt && w.last_attempt.code && String(w.last_attempt.code)) || ''
+ }
+
+ deliveries.reverse()
+
+ if (deliveries.length === max) {
+ this.warn(`Only showing the ${max} most recent deliveries`)
+ this.warn('It is possible to filter deliveries by using the --status flag')
+ }
+
+ cli.table(deliveries, {
+ id: {
+ header: 'Delivery ID'
+ },
+ created_at: {
+ header: 'Created', get: (w: any) => w.created_at
+ },
+ status: {
+ get: (w: any) => w.status
+ },
+ include: {
+ get: (w: any) => w.event.include
+ },
+ level: {
+ get: (w: any) => w.webhook.level
+ },
+ num_attempts: {
+ header: 'Attempts', get: (w: any) => String(w.num_attempts)
+ },
+ last_code: {
+ header: 'Code', get: code
+ },
+ last_error: {
+ header: 'Error', get: (w: any) => (w.last_attempt && w.last_attempt.error_class) || ''
+ },
+ next_attempt_at: {
+ header: 'Next Attempt', get: (w: any) => w.next_attempt_at || ''
+ },
+ }, {
+ printLine: this.log
+ })
+ }
+ }
+}
diff --git a/packages/webhooks/src/commands/webhooks/deliveries/info.ts b/packages/webhooks/src/commands/webhooks/deliveries/info.ts
new file mode 100644
index 0000000000..70f6e06bb4
--- /dev/null
+++ b/packages/webhooks/src/commands/webhooks/deliveries/info.ts
@@ -0,0 +1,50 @@
+import {flags} from '@heroku-cli/command'
+import {cli} from 'cli-ux'
+
+import BaseCommand from '../../base'
+
+export default class DeliveriesInfo extends BaseCommand {
+ static description = 'info for a webhook event on an app'
+
+ static examples = [
+ '$ heroku webhooks:deliveries:info 99999999-9999-9999-9999-999999999999'
+ ]
+
+ static flags = {
+ app: flags.app(),
+ remote: flags.remote(),
+ pipeline: flags.pipeline({char: 'p', description: 'pipeline on which to list', hidden: true})
+ }
+
+ static args = [
+ {name: 'id', required: true}
+ ]
+
+ async run() {
+ const {flags, args} = this.parse(DeliveriesInfo)
+ const {path} = this.webhookType(flags)
+
+ const {body: delivery} = await this.webhooksClient.get(`${path}/webhook-deliveries/${args.id}`)
+
+ const {body: event} = await this.webhooksClient.get(`${path}/webhook-events/${delivery.event.id}`)
+
+ const obj = {
+ Created: delivery.created_at,
+ Event: delivery.event.id,
+ Webhook: delivery.webhook.id,
+ Status: delivery.status,
+ Include: delivery.event.include,
+ Level: delivery.webhook.level,
+ Attempts: delivery.num_attempts,
+ Code: delivery.last_attempt && delivery.last_attempt.code,
+ Error: delivery.last_attempt && delivery.last_attempt.error_class,
+ 'Next Attempt': delivery.next_attempt_at
+ }
+
+ cli.styledHeader(delivery.id)
+ cli.styledObject(obj)
+
+ cli.styledHeader('Event Payload')
+ cli.styledJSON(event.payload)
+ }
+}
diff --git a/packages/webhooks/src/commands/webhooks/events/index.ts b/packages/webhooks/src/commands/webhooks/events/index.ts
new file mode 100644
index 0000000000..ebfeb24cc9
--- /dev/null
+++ b/packages/webhooks/src/commands/webhooks/events/index.ts
@@ -0,0 +1,50 @@
+import {flags} from '@heroku-cli/command'
+import {cli} from 'cli-ux'
+
+import BaseCommand from '../../base'
+
+export default class EventsIndex extends BaseCommand {
+ static description = 'list webhook events on an app'
+
+ static examples = [
+ '$ heroku webhooks:events'
+ ]
+
+ static flags = {
+ app: flags.app(),
+ remote: flags.remote(),
+ pipeline: flags.pipeline({char: 'p', description: 'pipeline on which to list', hidden: true})
+ }
+
+ async run() {
+ const {flags} = this.parse(EventsIndex)
+ const {path, display} = this.webhookType(flags)
+
+ cli.warn('heroku webhooks:event is deprecated, please use heroku webhooks:deliveries')
+
+ const {body: events} = await this.webhooksClient.get(`${path}/webhook-events`)
+
+ if (events.length === 0) {
+ this.log(`${display} has no events`)
+ } else {
+ events.sort((a: any, b: any) => Date.parse(a.created_at) - Date.parse(b.created_at))
+
+ cli.table(events, {
+ id: {
+ header: 'Event ID'
+ },
+ resource: {
+ get: (w: any) => w.payload.resource
+ },
+ action: {
+ get: (w: any) => w.payload.action
+ },
+ published_at: {
+ header: 'Published At', get: (w: any) => w.payload.published_at
+ }
+ }, {
+ printLine: this.log
+ })
+ }
+ }
+}
diff --git a/packages/webhooks/src/commands/webhooks/events/info.ts b/packages/webhooks/src/commands/webhooks/events/info.ts
new file mode 100644
index 0000000000..40cd6c77c1
--- /dev/null
+++ b/packages/webhooks/src/commands/webhooks/events/info.ts
@@ -0,0 +1,38 @@
+import {flags} from '@heroku-cli/command'
+import {cli} from 'cli-ux'
+
+import BaseCommand from '../../base'
+
+export default class Info extends BaseCommand {
+ static description = 'info for a webhook event on an app'
+
+ static examples = [
+ '$ heroku webhooks:events:info 99999999-9999-9999-9999-999999999999'
+ ]
+
+ static flags = {
+ app: flags.app(),
+ remote: flags.remote(),
+ pipeline: flags.pipeline({char: 'p', description: 'pipeline on which to list', hidden: true})
+ }
+
+ static args = [
+ {name: 'id', required: true}
+ ]
+
+ async run() {
+ const {flags, args} = this.parse(Info)
+ const {path} = this.webhookType(flags)
+
+ cli.warn('heroku webhooks:event:info is deprecated, please use heroku webhooks:deliveries:info')
+
+ const {body: webhookEvent} = await this.webhooksClient.get(`${path}/webhook-events/${args.id}`)
+
+ const obj = {
+ payload: JSON.stringify(webhookEvent.payload, null, 2)
+ }
+
+ cli.styledHeader(webhookEvent.id)
+ cli.styledObject(obj)
+ }
+}
diff --git a/packages/webhooks/src/commands/webhooks/index.ts b/packages/webhooks/src/commands/webhooks/index.ts
new file mode 100644
index 0000000000..1629824c12
--- /dev/null
+++ b/packages/webhooks/src/commands/webhooks/index.ts
@@ -0,0 +1,51 @@
+import color from '@heroku-cli/color'
+import {flags} from '@heroku-cli/command'
+import {cli} from 'cli-ux'
+
+import BaseCommand from '../base'
+
+export default class Webhooks extends BaseCommand {
+ static description = 'list webhooks on an app'
+
+ static examples = ['$ heroku webhooks']
+
+ static flags = {
+ app: flags.app(),
+ remote: flags.remote(),
+ pipeline: flags.pipeline({
+ char: 'p',
+ description:
+ 'pipeline on which to list',
+ hidden: true
+ })
+ }
+
+ async run() {
+ const {flags} = this.parse(Webhooks)
+ const {path, display} = this.webhookType(flags)
+
+ const {body: webhooks} = await this.webhooksClient.get(`${path}/webhooks`)
+
+ if (webhooks.length === 0) {
+ this.log(`${display} has no webhooks\nUse ${color.cmd('heroku webhooks:add')} to add one.`)
+ return
+ }
+
+ webhooks.sort((a: any, b: any) => Date.parse(a.created_at) - Date.parse(b.created_at))
+
+ cli.table(webhooks, {
+ id: {
+ header: 'Webhook ID'
+ },
+ url: {
+ header: 'URL'
+ },
+ include: {
+ get: (row: any) => row.include.join(',')
+ },
+ level: {}
+ }, {
+ printLine: this.log
+ })
+ }
+}
diff --git a/packages/webhooks/src/commands/webhooks/info.ts b/packages/webhooks/src/commands/webhooks/info.ts
new file mode 100644
index 0000000000..96add04610
--- /dev/null
+++ b/packages/webhooks/src/commands/webhooks/info.ts
@@ -0,0 +1,35 @@
+import {flags} from '@heroku-cli/command'
+import {cli} from 'cli-ux'
+
+import BaseCommand from '../base'
+
+export default class WebhooksInfo extends BaseCommand {
+ static description = 'info for a webhook on an app'
+
+ static example = ['$ heroku webhooks:info 99999999-9999-9999-9999-999999999999']
+
+ static flags = {
+ app: flags.app(),
+ remote: flags.remote(),
+ pipeline: flags.pipeline({char: 'p', description: 'pipeline on which to list', hidden: true})
+ }
+
+ static args = [{name: 'id', required: true}]
+
+ async run() {
+ const {flags, args} = this.parse(WebhooksInfo)
+ const {path} = this.webhookType(flags)
+
+ const {body: webhook} = await this.webhooksClient.get(`${path}/webhooks/${args.id}`)
+
+ const obj = {
+ 'Webhook ID': webhook.id,
+ URL: webhook.url,
+ Include: webhook.include.join(','),
+ Level: webhook.level
+ }
+
+ cli.styledHeader(webhook.id)
+ cli.styledObject(obj)
+ }
+}
diff --git a/packages/webhooks/src/commands/webhooks/remove.ts b/packages/webhooks/src/commands/webhooks/remove.ts
new file mode 100644
index 0000000000..6d71af7ab4
--- /dev/null
+++ b/packages/webhooks/src/commands/webhooks/remove.ts
@@ -0,0 +1,32 @@
+import {flags} from '@heroku-cli/command'
+import {cli} from 'cli-ux'
+
+import BaseCommand from '../base'
+export default class WebhooksRemove extends BaseCommand {
+ static description = 'removes a webhook from an app'
+
+ static examples = [
+ '$ heroku webhooks:remove 99999999-9999-9999-9999-999999999999'
+ ]
+
+ static flags = {
+ app: flags.app(),
+ remote: flags.remote(),
+ pipeline: flags.pipeline({char: 'p', description: 'pipeline on which to list', hidden: true})
+ }
+
+ static args = [
+ {name: 'id', description: 'id of webhook to remove', required: true}
+ ]
+
+ async run() {
+ const {flags, args} = this.parse(WebhooksRemove)
+ const {path, display} = this.webhookType(flags)
+
+ cli.action.start(`Removing webhook ${args.id} from ${display}`)
+
+ await this.webhooksClient.delete(`${path}/webhooks/${args.id}`)
+
+ cli.action.stop()
+ }
+}
diff --git a/packages/webhooks/src/commands/webhooks/update.ts b/packages/webhooks/src/commands/webhooks/update.ts
new file mode 100644
index 0000000000..2958ec6aed
--- /dev/null
+++ b/packages/webhooks/src/commands/webhooks/update.ts
@@ -0,0 +1,45 @@
+import {flags} from '@heroku-cli/command'
+import {cli} from 'cli-ux'
+
+import BaseCommand from '../base'
+
+export default class WebhooksUpdate extends BaseCommand {
+ static description = 'updates a webhook in an app'
+
+ static examples = [
+ '$ heroku webhooks:update 99999999-9999-9999-9999-999999999999 -i dyno -l notify -s 09928c40bf1b191b645174a19f7053d16a180da37332e719ef0998f4c0a2 -u https://example.com/hooks'
+ ]
+
+ static flags = {
+ app: flags.app(),
+ remote: flags.remote(),
+ pipeline: flags.pipeline({char: 'p', description: 'pipeline on which to list', hidden: true}),
+ include: flags.string({char: 'i', description: 'comma delimited event types your server will receive ', required: true}),
+ level: flags.string({char: 'l', description: 'notify does not retry, sync will retry until successful or timeout', required: true}),
+ secret: flags.string({char: 's', description: 'value to sign delivery with in Heroku-Webhook-Hmac-SHA256 header'}),
+ authorization: flags.string({char: 't', description: 'authoriation header to send with webhooks'}),
+ url: flags.string({char: 'u', description: 'URL for receiver', required: true})
+ }
+
+ static args = [
+ {name: 'id', required: true}
+ ]
+
+ async run() {
+ const {flags, args} = this.parse(WebhooksUpdate)
+ const {path, display} = this.webhookType(flags)
+
+ cli.action.start(`Updating webhook ${args.id} for ${display}`)
+
+ await this.webhooksClient.patch(`${path}/webhooks/${args.id}`, {
+ body: {
+ include: flags.include && flags.include.split(',').map(s => s.trim()),
+ level: flags.level,
+ secret: flags.secret,
+ url: flags.url
+ }
+ })
+
+ cli.action.stop()
+ }
+}
diff --git a/packages/webhooks/src/index.ts b/packages/webhooks/src/index.ts
new file mode 100644
index 0000000000..b1c6ea436a
--- /dev/null
+++ b/packages/webhooks/src/index.ts
@@ -0,0 +1 @@
+export default {}
diff --git a/packages/webhooks/test/commands/base.test.ts b/packages/webhooks/test/commands/base.test.ts
new file mode 100644
index 0000000000..58def7e622
--- /dev/null
+++ b/packages/webhooks/test/commands/base.test.ts
@@ -0,0 +1,41 @@
+import {Config, IConfig} from '@oclif/config'
+import {expect, test} from '@oclif/test'
+import * as path from 'path'
+
+import webhooksAbstractClass from '../../src/commands/base'
+
+class Webhooks extends webhooksAbstractClass {
+ constructor(argv: string[], config: IConfig) {
+ super(argv, config)
+ }
+ async run() {}
+}
+const root = path.resolve(__dirname, '../package.json')
+const config = new Config({root})
+const webhookObject = new Webhooks([], config)
+
+describe('webhooks type', () => {
+ test
+ .stdout()
+ .do(function () {
+ const webhookInfo = webhookObject.webhookType({pipeline: 'randomPipeline', app: ''})
+ expect(webhookInfo).to.deep.equal({path: '/pipelines/randomPipeline', display: 'randomPipeline'})
+ })
+ .it('returns correct pipeline path and display info')
+
+ test
+ .stdout()
+ .do(function () {
+ const webhookInfo = webhookObject.webhookType({pipeline: '', app: 'randomApp'})
+ expect(webhookInfo).to.deep.equal({path: '/apps/randomApp', display: 'randomApp'})
+ })
+ .it('returns correct app path and display info')
+
+ test
+ .stdout()
+ .do(function () {
+ webhookObject.webhookType({pipeline: '', app: ''})
+ })
+ .catch(e => expect(e.message).to.equal('No app specified'))
+ .it('returns error if no arguments are given')
+})
diff --git a/packages/webhooks/test/commands/webhooks/add.test.ts b/packages/webhooks/test/commands/webhooks/add.test.ts
new file mode 100644
index 0000000000..3cdbdb2a05
--- /dev/null
+++ b/packages/webhooks/test/commands/webhooks/add.test.ts
@@ -0,0 +1,76 @@
+import {expect, test} from '@oclif/test'
+
+describe('webhooks:add', () => {
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .post('/apps/example-app/webhooks', {
+ include: ['foo', 'bar'],
+ level: 'notify',
+ secret: '1234',
+ url: 'http://foobar.com'
+ })
+ .reply(200, {})
+ )
+ .command([
+ 'webhooks:add',
+ '--app', 'example-app',
+ '--include', 'foo,bar',
+ '--secret', '1234',
+ '--level', 'notify',
+ '--url', 'http://foobar.com'
+ ])
+ .it('adds a specific app webhook', ctx => {
+ expect(ctx.stdout).to.equal('')
+ expect(ctx.stderr).to.contain('Adding webhook to example-app... done\n')
+ })
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .post('/pipelines/example-pipeline/webhooks', {
+ include: ['foo', 'bar'],
+ level: 'notify',
+ secret: '1234',
+ url: 'http://foobar.com'
+ })
+ .reply(200, {})
+ )
+ .command([
+ 'webhooks:add',
+ '--pipeline', 'example-pipeline',
+ '--include', 'foo,bar',
+ '--secret', '1234',
+ '--level', 'notify',
+ '--url', 'http://foobar.com'
+ ])
+ .it('adds a specific pipeline webhook', ctx => {
+ expect(ctx.stdout).to.equal('')
+ expect(ctx.stderr).to.contain('Adding webhook to example-pipeline... done\n')
+ })
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .post('/pipelines/example-pipeline/webhooks', {
+ include: ['foo', 'bar'],
+ level: 'notify',
+ url: 'http://foobar.com'
+ })
+ .reply(200, {}, {'heroku-webhook-secret': '1234'})
+ )
+ .command([
+ 'webhooks:add',
+ '--pipeline', 'example-pipeline',
+ '--include', 'foo,bar',
+ '--level', 'notify',
+ '--url', 'http://foobar.com'
+ ])
+ .it('adds a specific pipeline webhook', ctx => {
+ expect(ctx.stdout).to.equal('=== Webhooks Signing Secret\n1234\n')
+ expect(ctx.stderr).to.contain('Adding webhook to example-pipeline... done\n')
+ })
+})
diff --git a/packages/webhooks/test/commands/webhooks/deliveries/index.test.ts b/packages/webhooks/test/commands/webhooks/deliveries/index.test.ts
new file mode 100644
index 0000000000..83a2a42c20
--- /dev/null
+++ b/packages/webhooks/test/commands/webhooks/deliveries/index.test.ts
@@ -0,0 +1,217 @@
+import {expect, test} from '@oclif/test'
+
+describe('webhooks:deliveries', () => {
+ describe('app webhooks', () => {
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', {
+ reqheaders: {range: 'seq ..; order=desc,max=1000'}
+ }, api => api
+ .get('/apps/example-app/webhook-deliveries')
+ .reply(206, [
+ {
+ id: '66666666-6666-6666-6666-666666666666',
+ event: {
+ id: '55555555-5555-5555-5555-555555555555',
+ include: 'api:build'
+ },
+ webhook: {
+ id: '44444444-4444-4444-4444-444444444444',
+ level: 'notify'
+ },
+ status: 'pending',
+ num_attempts: 4,
+ created_at: '2017-08-17T20:22:38Z'
+ },
+ {
+ id: '99999999-9999-9999-9999-999999999999',
+ event: {
+ id: '88888888-8888-8888-8888-888888888888',
+ include: 'api:build'
+ },
+ webhook: {
+ id: '77777777-7777-7777-7777-777777777777',
+ level: 'notify'
+ },
+ last_attempt: {
+ code: 401,
+ error_class: 'Foobar'
+ },
+ status: 'retrying',
+ num_attempts: 4,
+ created_at: '2017-08-17T20:22:37Z',
+ next_attempt_at: '2017-08-17T20:22:39Z'
+ }
+ ])
+ )
+ .command(['webhooks:deliveries', '--app', 'example-app'])
+ .it('lists webhooks deliveries for app webhooks', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout.trim()).to.equal(
+ `Delivery ID Created Status Include Level Attempts Code Error Next Attempt
+99999999-9999-9999-9999-999999999999 2017-08-17T20:22:37Z retrying api:build notify 4 401 Foobar 2017-08-17T20:22:39Z
+66666666-6666-6666-6666-666666666666 2017-08-17T20:22:38Z pending api:build notify 4`)
+ })
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', {
+ reqheaders: {range: 'seq ..; order=desc,max=1000'}
+ }, api => api
+ .get('/apps/example-app/webhook-deliveries?eq[status]=pending')
+ .reply(206, [
+ {
+ id: '66666666-6666-6666-6666-666666666666',
+ event: {
+ id: '55555555-5555-5555-5555-555555555555',
+ include: 'api:build'
+ },
+ webhook: {
+ id: '44444444-4444-4444-4444-444444444444',
+ level: 'notify'
+ },
+ status: 'pending',
+ num_attempts: 4,
+ created_at: '2017-08-17T20:22:38Z'
+ }
+ ])
+ )
+ .command(['webhooks:deliveries', '--app', 'example-app', '--status', 'pending'])
+ .it('lists webhook deliveries for app webhooks filtered by status', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout.trim()).to.equal(
+ `Delivery ID Created Status Include Level Attempts Code Error Next Attempt
+66666666-6666-6666-6666-666666666666 2017-08-17T20:22:38Z pending api:build notify 4`)
+ })
+
+ test
+ .stderr()
+ .stdout()
+ .nock('https://api.heroku.com', {
+ reqheaders: {range: 'seq ..; order=desc,max=1000'}
+ }, api => api
+ .get('/apps/example-app/webhook-deliveries')
+ .reply(206, () => {
+ let delivery = {
+ id: '66666666-6666-6666-6666-666666666666',
+ event: {
+ id: '55555555-5555-5555-5555-555555555555',
+ include: 'api:build'
+ },
+ webhook: {
+ id: '44444444-4444-4444-4444-444444444444',
+ level: 'notify'
+ },
+ status: 'pending',
+ num_attempts: 4,
+ created_at: '2017-08-17T20:22:38Z'
+ }
+
+ return new Array(1000).fill(delivery)
+ })
+ )
+ .command(['webhooks:deliveries', '--app', 'example-app'])
+ .it('only shows 1000 webhook deliveries', ctx => {
+ let expectedHeader = 'Delivery ID Created Status Include Level Attempts Code Error Next Attempt'
+ let expectedRow = '66666666-6666-6666-6666-666666666666 2017-08-17T20:22:38Z pending api:build notify 4'
+ let angleBrackets = process.platform === 'win32' ? '»' : '›'
+ const rows = ctx.stdout.split('\n')
+
+ const headerRowCount = 1
+ const dataRowsCount = 1000
+ const finalTrailingRowCount = 1
+ expect(rows.length).to.equal(headerRowCount + dataRowsCount + finalTrailingRowCount)
+
+ expect(rows[0].trim()).to.equal(expectedHeader)
+ expect(rows[1].trim()).to.equal(expectedRow)
+
+ expect(ctx.stderr).to.include(` ${angleBrackets} Warning: Only showing the 1000 most recent deliveries\n ${angleBrackets} Warning: It is possible to filter deliveries by using the --status flag\n`)
+ })
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', {
+ reqheaders: {range: 'seq ..; order=desc,max=1000'}
+ }, api => api
+ .get('/apps/example-app/webhook-deliveries')
+ .reply(200, [])
+ )
+ .command(['webhooks:deliveries', '--app', 'example-app'])
+ .it('lists empty deliveries', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout.trim()).to.equal('example-app has no deliveries')
+ })
+ })
+
+ describe('pipeline webhooks', () => {
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', {
+ reqheaders: {range: 'seq ..; order=desc,max=1000'}
+ }, api => api
+ .get('/pipelines/example-pipeline/webhook-deliveries')
+ .reply(206, [
+ {
+ id: '66666666-6666-6666-6666-666666666666',
+ event: {
+ id: '55555555-5555-5555-5555-555555555555',
+ include: 'api:build'
+ },
+ webhook: {
+ id: '44444444-4444-4444-4444-444444444444',
+ level: 'notify'
+ },
+ status: 'pending',
+ num_attempts: 4,
+ created_at: '2017-08-17T20:22:38Z'
+ },
+ {
+ id: '99999999-9999-9999-9999-999999999999',
+ event: {
+ id: '88888888-8888-8888-8888-888888888888',
+ include: 'api:build'
+ },
+ webhook: {
+ id: '77777777-7777-7777-7777-777777777777',
+ level: 'notify'
+ },
+ last_attempt: {
+ code: 401,
+ error_class: 'Foobar'
+ },
+ status: 'retrying',
+ num_attempts: 4,
+ created_at: '2017-08-17T20:22:37Z',
+ next_attempt_at: '2017-08-17T20:22:39Z'
+ }
+ ])
+ )
+ .command(['webhooks:deliveries', '--pipeline', 'example-pipeline'])
+ .it('lists webhooks deliveries for pipeline webhooks', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout.trim()).to.equal(
+ `Delivery ID Created Status Include Level Attempts Code Error Next Attempt
+99999999-9999-9999-9999-999999999999 2017-08-17T20:22:37Z retrying api:build notify 4 401 Foobar 2017-08-17T20:22:39Z
+66666666-6666-6666-6666-666666666666 2017-08-17T20:22:38Z pending api:build notify 4`)
+ })
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', {
+ reqheaders: {range: 'seq ..; order=desc,max=1000'}
+ }, api => api
+ .get('/pipelines/example-pipeline/webhook-deliveries')
+ .reply(200, [])
+ )
+ .command(['webhooks:deliveries', '--pipeline', 'example-pipeline'])
+ .it('lists empty webhooks deliveries for pipeline webhooks', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout.trim()).to.equal('example-pipeline has no deliveries')
+ })
+ })
+})
diff --git a/packages/webhooks-v5/test/commands/webhooks/deliveries/info.js b/packages/webhooks/test/commands/webhooks/deliveries/info.test.ts
similarity index 54%
rename from packages/webhooks-v5/test/commands/webhooks/deliveries/info.js
rename to packages/webhooks/test/commands/webhooks/deliveries/info.test.ts
index dc7c63fb7c..981e8c052b 100644
--- a/packages/webhooks-v5/test/commands/webhooks/deliveries/info.js
+++ b/packages/webhooks/test/commands/webhooks/deliveries/info.test.ts
@@ -1,20 +1,11 @@
-/* eslint-disable camelcase */
-'use strict'
-/* globals describe it beforeEach cli */
+import { expect, test } from '@oclif/test'
-let expect = require('chai').expect
-let nock = require('nock')
-let certs = require('../../../../commands/webhooks/deliveries/info')
-
-describe('heroku webhooks:deliveries:info', function () {
- beforeEach(function () {
- cli.mockConsole()
- nock.cleanAll()
- })
-
- it('# shows a delivery', function () {
- let mockDelivery = nock('https://api.heroku.com')
- .get('/apps/example/webhook-deliveries/99999999-9999-9999-9999-999999999999')
+describe('webhooks:deliveries:info', () => {
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get('/apps/example-app/webhook-deliveries/99999999-9999-9999-9999-999999999999')
.reply(200, {
id: '99999999-9999-9999-9999-999999999999',
event: {
@@ -25,9 +16,9 @@ describe('heroku webhooks:deliveries:info', function () {
},
status: 'pending'
})
-
- let mockEvent = nock('https://api.heroku.com')
- .get('/apps/example/webhook-events/88888888-8888-8888-8888-888888888888')
+ )
+ .nock('https://api.heroku.com', api => api
+ .get('/apps/example-app/webhook-events/88888888-8888-8888-8888-888888888888')
.reply(200, {
id: '88888888-8888-8888-8888-888888888888',
payload: {
@@ -39,12 +30,15 @@ describe('heroku webhooks:deliveries:info', function () {
}
}
})
-
- return certs.run(['99999999-9999-9999-9999-999999999999', '--app', 'example']).then(function () {
- mockDelivery.done()
- mockEvent.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal(
+ )
+ .command([
+ 'webhooks:deliveries:info',
+ '--app', 'example-app',
+ '99999999-9999-9999-9999-999999999999'
+ ])
+ .it('shows an app webhook delivery', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout).to.equal(
`=== 99999999-9999-9999-9999-999999999999
Event: 88888888-8888-8888-8888-888888888888
Status: pending
@@ -60,11 +54,12 @@ Webhook: 77777777-7777-7777-7777-777777777777
}
`)
})
- })
- it('# shows a delivery (piplines)', function () {
- let mockDelivery = nock('https://api.heroku.com')
- .get('/pipelines/example/webhook-deliveries/99999999-9999-9999-9999-999999999999')
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get('/pipelines/example-pipeline/webhook-deliveries/99999999-9999-9999-9999-999999999999')
.reply(200, {
id: '99999999-9999-9999-9999-999999999999',
event: {
@@ -75,9 +70,9 @@ Webhook: 77777777-7777-7777-7777-777777777777
},
status: 'pending'
})
-
- let mockEvent = nock('https://api.heroku.com')
- .get('/pipelines/example/webhook-events/88888888-8888-8888-8888-888888888888')
+ )
+ .nock('https://api.heroku.com', api => api
+ .get('/pipelines/example-pipeline/webhook-events/88888888-8888-8888-8888-888888888888')
.reply(200, {
id: '88888888-8888-8888-8888-888888888888',
payload: {
@@ -89,12 +84,15 @@ Webhook: 77777777-7777-7777-7777-777777777777
}
}
})
-
- return certs.run(['99999999-9999-9999-9999-999999999999', '--pipeline', 'example']).then(function () {
- mockDelivery.done()
- mockEvent.done()
- expect(cli.stderr).to.equal('')
- expect(cli.stdout).to.equal(
+ )
+ .command([
+ 'webhooks:deliveries:info',
+ '--pipeline', 'example-pipeline',
+ '99999999-9999-9999-9999-999999999999'
+ ])
+ .it('shows a pipeline webhook delivery ', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout).to.equal(
`=== 99999999-9999-9999-9999-999999999999
Event: 88888888-8888-8888-8888-888888888888
Status: pending
@@ -110,5 +108,4 @@ Webhook: 77777777-7777-7777-7777-777777777777
}
`)
})
- })
})
diff --git a/packages/webhooks/test/commands/webhooks/events/index.test.ts b/packages/webhooks/test/commands/webhooks/events/index.test.ts
new file mode 100644
index 0000000000..43289134e3
--- /dev/null
+++ b/packages/webhooks/test/commands/webhooks/events/index.test.ts
@@ -0,0 +1,147 @@
+import {expect, test} from '@oclif/test'
+import { addDays, parse } from 'date-fns'
+
+describe('webhooks:events', () => {
+ const deprecationWarning = 'Warning: heroku webhooks:event is deprecated, please use heroku webhooks:deliveries\n'
+
+ describe('app webhooks', () => {
+ const appWebhookEventsPath = '/apps/example-app/webhook-events'
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get(appWebhookEventsPath)
+ .reply(200, [{
+ id: '99999999-9999-9999-9999-999999999999',
+ payload: {
+ published_at: '2016-08-31T21:55:06Z',
+ resource: 'api:release',
+ action: 'create'
+ }
+ }])
+ )
+ .command(['webhooks:events', '--app', 'example-app'])
+ .it('lists app webhook events', ctx => {
+ expect(ctx.stderr).to.include(deprecationWarning)
+ expect(ctx.stdout).to.equal(`Event ID Resource Action Published At
+99999999-9999-9999-9999-999999999999 api:release create 2016-08-31T21:55:06Z
+`)
+ })
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get(appWebhookEventsPath)
+ .reply(200, [])
+ )
+ .command(['webhooks:events', '--app', 'example-app'])
+ .it('displays an empty events message', ctx => {
+ expect(ctx.stderr).to.include(deprecationWarning)
+ expect(ctx.stdout).to.equal('example-app has no events\n')
+ })
+ })
+
+ describe('pipeline webhooks', () => {
+ const pipelineWebhookEventsPath = '/pipelines/example-pipeline/webhook-events'
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get(pipelineWebhookEventsPath)
+ .reply(200, [{
+ id: '99999999-9999-9999-9999-999999999999',
+ payload: {
+ published_at: '2016-08-31T21:55:06Z',
+ resource: 'api:release',
+ action: 'create'
+ }
+ }])
+ )
+ .command(['webhooks:events', '--pipeline', 'example-pipeline'])
+ .it('lists pipeline webhook events', ctx => {
+ expect(ctx.stderr).to.include(deprecationWarning)
+ expect(ctx.stdout).to.equal(`Event ID Resource Action Published At
+99999999-9999-9999-9999-999999999999 api:release create 2016-08-31T21:55:06Z
+`)
+ })
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get(pipelineWebhookEventsPath)
+ .reply(200, [])
+ )
+ .command(['webhooks:events', '--pipeline', 'example-pipeline'])
+ .it('displays an empty events message', ctx => {
+ expect(ctx.stderr).to.include(deprecationWarning)
+ expect(ctx.stdout).to.equal('example-pipeline has no events\n')
+ })
+ })
+
+ describe('by default the table is sorted by `created_at`', () => {
+ const firstDate = parse('2019-06-11T14:20:42Z')
+ const secondDate = addDays(parse(firstDate), 1)
+ const thirdDate = addDays(parse(firstDate), 2)
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get('/apps/example-app/webhook-events')
+ .reply(200, [
+ // the returned ordered from the api is not ordered by
+ // `created_at` but the results displayed by the cli
+ // in thae table *are* ordered by `created_at`
+
+ // first date
+ {
+ id: '00000000-0000-0000-0000-000000000000',
+ created_at: firstDate.toISOString(),
+ payload: {
+ published_at: '2019-06-15T14:20:42Z',
+ resource: 'api:release',
+ action: 'create'
+ }
+ },
+
+ // third date
+ {
+ id: '11111111-1111-1111-1111-111111111111',
+ created_at: thirdDate.toISOString(),
+ payload: {
+ published_at: '2019-06-15T14:20:42Z',
+ resource: 'api:release',
+ action: 'create'
+ }
+ },
+
+ // second date
+ {
+ id: '22222222-2222-2222-2222-222222222222',
+ created_at: secondDate.toISOString(),
+ payload: {
+ published_at: '2019-06-15T14:20:42Z',
+ resource: 'api:release',
+ action: 'create'
+ }
+ }
+ ])
+ )
+ .command(['webhooks:events', '--app', 'example-app'])
+ .it('displays webhooks sorted by `created_at`', ctx => {
+ expect(ctx.stderr).to.include(deprecationWarning)
+
+ // Note: The table is sorted by `created_at` date even though
+ // it is not displayed in the table
+ expect(ctx.stdout).to.equal(`Event ID Resource Action Published At
+00000000-0000-0000-0000-000000000000 api:release create 2019-06-15T14:20:42Z
+22222222-2222-2222-2222-222222222222 api:release create 2019-06-15T14:20:42Z
+11111111-1111-1111-1111-111111111111 api:release create 2019-06-15T14:20:42Z
+`)
+ })
+ })
+})
diff --git a/packages/webhooks/test/commands/webhooks/events/info.test.ts b/packages/webhooks/test/commands/webhooks/events/info.test.ts
new file mode 100644
index 0000000000..2a6c1b685d
--- /dev/null
+++ b/packages/webhooks/test/commands/webhooks/events/info.test.ts
@@ -0,0 +1,77 @@
+import {expect, test} from '@oclif/test'
+
+describe('webhooks:events:info', () => {
+ const deprecationWarning = 'Warning: heroku webhooks:event:info is deprecated, please use heroku webhooks:deliveries:info\n'
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get('/apps/example-app/webhook-events/99999999-9999-9999-9999-999999999999')
+ .reply(200, {
+ id: '99999999-9999-9999-9999-999999999999',
+ payload: {
+ published_at: '2016-08-31T21:55:06Z',
+ resource: 'api:release',
+ action: 'create',
+ data: {
+ foo: 'bar'
+ }
+ }
+ })
+ )
+ .command([
+ 'webhooks:events:info',
+ '--app', 'example-app',
+ '99999999-9999-9999-9999-999999999999'
+ ])
+ .it('lists webhooks events info for app webhooks', ctx => {
+ expect(ctx.stderr).to.include(deprecationWarning)
+ expect(ctx.stdout).to.equal(`=== 99999999-9999-9999-9999-999999999999
+payload: {
+ "published_at": "2016-08-31T21:55:06Z",
+ "resource": "api:release",
+ "action": "create",
+ "data": {
+ "foo": "bar"
+ }
+}
+`)
+ })
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get('/pipelines/example-pipeline/webhook-events/99999999-9999-9999-9999-999999999999')
+ .reply(200, {
+ id: '99999999-9999-9999-9999-999999999999',
+ payload: {
+ published_at: '2016-08-31T21:55:06Z',
+ resource: 'api:release',
+ action: 'create',
+ data: {
+ foo: 'bar'
+ }
+ }
+ })
+ )
+ .command([
+ 'webhooks:events:info',
+ '--pipeline', 'example-pipeline',
+ '99999999-9999-9999-9999-999999999999'
+ ])
+ .it('lists webhooks events info for pipeline webhooks', ctx => {
+ expect(ctx.stderr).to.include(deprecationWarning)
+ expect(ctx.stdout).to.equal(`=== 99999999-9999-9999-9999-999999999999
+payload: {
+ "published_at": "2016-08-31T21:55:06Z",
+ "resource": "api:release",
+ "action": "create",
+ "data": {
+ "foo": "bar"
+ }
+}
+`)
+ })
+})
diff --git a/packages/webhooks/test/commands/webhooks/index.test.ts b/packages/webhooks/test/commands/webhooks/index.test.ts
new file mode 100644
index 0000000000..8d9d07e9cf
--- /dev/null
+++ b/packages/webhooks/test/commands/webhooks/index.test.ts
@@ -0,0 +1,132 @@
+import { expect, test } from '@oclif/test'
+import { addDays, parse } from 'date-fns'
+
+describe('webhooks:index', () => {
+ describe('app webhooks', () => {
+ let appWebhooksUrl = '/apps/example/webhooks'
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get(appWebhooksUrl)
+ .reply(200, [{
+ id: '99999999-9999-9999-9999-999999999999',
+ include: ['foo', 'bar'],
+ level: 'notify',
+ url: 'http://foobar.com'
+ }])
+ )
+ .command(['webhooks', '--app', 'example'])
+ .it('lists webhooks', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout).to.equal(`Webhook ID URL Include Level
+99999999-9999-9999-9999-999999999999 http://foobar.com foo,bar notify
+`)
+ })
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get(appWebhooksUrl)
+ .reply(200, [])
+ )
+ .command(['webhooks', '--app', 'example'])
+ .it('displays a "no webhooks" message', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout).to.equal('example has no webhooks\nUse heroku webhooks:add to add one.\n')
+ })
+ })
+
+ describe('pipeline webhooks', () => {
+ let pipelinesWebhooksUrl = '/pipelines/example/webhooks'
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get(pipelinesWebhooksUrl)
+ .reply(200, [{
+ id: '99999999-9999-9999-9999-999999999999',
+ include: ['foo', 'bar'],
+ level: 'notify',
+ url: 'http://foobar.com'
+ }])
+ )
+ .command(['webhooks', '--pipeline', 'example'])
+ .it('lists webhooks', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout).to.equal(`Webhook ID URL Include Level
+99999999-9999-9999-9999-999999999999 http://foobar.com foo,bar notify
+`)
+ })
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get(pipelinesWebhooksUrl)
+ .reply(200, [])
+ )
+ .command(['webhooks', '--pipeline', 'example'])
+ .it('displays a "no webhooks" message', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout).to.equal('example has no webhooks\nUse heroku webhooks:add to add one.\n')
+ })
+ })
+
+ describe('by default the table is sorted by `created_at`', () => {
+ let firstDate = parse('2019-06-11T14:20:42Z')
+ let secondDate = addDays(parse(firstDate), 1)
+ let thirdDate = addDays(parse(firstDate), 2)
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get('/apps/example/webhooks')
+ .reply(200, [
+ // the returned ordered from the api is not ordered by
+ // `created_at` but the results displayed by the cli
+ // in thae table *are* ordered by `created_at`
+
+ // first date
+ {
+ created_at: firstDate.toISOString(),
+ id: '00000000-0000-0000-0000-000000000000',
+ include: ['api:release'],
+ level: 'sync',
+ url: 'https://test.com/hook'
+ },
+
+ // third date
+ {
+ created_at: thirdDate.toISOString(),
+ id: '11111111-1111-1111-1111-111111111111',
+ include: ['api:release'],
+ level: 'sync',
+ url: 'https://test.com/hook'
+ },
+
+ // second date
+ {
+ created_at: secondDate.toISOString(),
+ id: '22222222-2222-2222-2222-222222222222',
+ include: ['api:release'],
+ level: 'sync',
+ url: 'https://test.com/hook'
+ }
+ ])
+ )
+ .command(['webhooks', '--app', 'example'])
+ .it('displays webhooks sorted by `created_at`', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout).to.equal(`Webhook ID URL Include Level
+00000000-0000-0000-0000-000000000000 https://test.com/hook api:release sync
+22222222-2222-2222-2222-222222222222 https://test.com/hook api:release sync
+11111111-1111-1111-1111-111111111111 https://test.com/hook api:release sync
+`)
+ })
+ })
+})
diff --git a/packages/webhooks/test/commands/webhooks/info.test.ts b/packages/webhooks/test/commands/webhooks/info.test.ts
new file mode 100644
index 0000000000..34c91171f8
--- /dev/null
+++ b/packages/webhooks/test/commands/webhooks/info.test.ts
@@ -0,0 +1,49 @@
+import {expect, test} from '@oclif/test'
+
+describe('webhooks:info', () => {
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get('/apps/example-app/webhooks/99999999-9999-9999-9999-999999999999')
+ .reply(200, {
+ id: '99999999-9999-9999-9999-999999999999',
+ include: ['foo', 'bar'],
+ level: 'notify',
+ url: 'http://foobar.com'
+ })
+ )
+ .command(['webhooks:info', '--app', 'example-app', '99999999-9999-9999-9999-999999999999'])
+ .it('displays info for a given app webhook', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout).to.equal(`=== 99999999-9999-9999-9999-999999999999
+Include: foo,bar
+Level: notify
+URL: http://foobar.com
+Webhook ID: 99999999-9999-9999-9999-999999999999
+`)
+ })
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .get('/pipelines/example-pipeline/webhooks/99999999-9999-9999-9999-999999999999')
+ .reply(200, {
+ id: '99999999-9999-9999-9999-999999999999',
+ include: ['foo', 'bar'],
+ level: 'notify',
+ url: 'http://foobar.com'
+ })
+ )
+ .command(['webhooks:info', '--pipeline', 'example-pipeline', '99999999-9999-9999-9999-999999999999'])
+ .it('displays info for a given pipeline webhook', ctx => {
+ expect(ctx.stderr).to.equal('')
+ expect(ctx.stdout).to.equal(`=== 99999999-9999-9999-9999-999999999999
+Include: foo,bar
+Level: notify
+URL: http://foobar.com
+Webhook ID: 99999999-9999-9999-9999-999999999999
+`)
+ })
+})
diff --git a/packages/webhooks/test/commands/webhooks/remove.test.ts b/packages/webhooks/test/commands/webhooks/remove.test.ts
new file mode 100644
index 0000000000..43e75e27dd
--- /dev/null
+++ b/packages/webhooks/test/commands/webhooks/remove.test.ts
@@ -0,0 +1,29 @@
+import {expect, test} from '@oclif/test'
+
+describe('webhooks:remove', () => {
+ test
+ .stderr()
+ .stdout()
+ .nock('https://api.heroku.com', api => api
+ .delete('/apps/example-app/webhooks/99999999-9999-9999-9999-999999999999')
+ .reply(200, {})
+ )
+ .command(['webhooks:remove', '--app', 'example-app', '99999999-9999-9999-9999-999999999999'])
+ .it('removes the specified app webhook', ctx => {
+ expect(ctx.stdout).to.equal('')
+ expect(ctx.stderr).to.contain('Removing webhook 99999999-9999-9999-9999-999999999999 from example-app... done\n')
+ })
+
+ test
+ .stderr()
+ .stdout()
+ .nock('https://api.heroku.com', api => api
+ .delete('/pipelines/example-pipeline/webhooks/99999999-9999-9999-9999-999999999999')
+ .reply(200, {})
+ )
+ .command(['webhooks:remove', '--pipeline', 'example-pipeline', '99999999-9999-9999-9999-999999999999'])
+ .it('removes the specified pipeline webhook', ctx => {
+ expect(ctx.stdout).to.equal('')
+ expect(ctx.stderr).to.contain('Removing webhook 99999999-9999-9999-9999-999999999999 from example-pipeline... done\n')
+ })
+})
diff --git a/packages/webhooks/test/commands/webhooks/update.test.ts b/packages/webhooks/test/commands/webhooks/update.test.ts
new file mode 100644
index 0000000000..a8df30b6cc
--- /dev/null
+++ b/packages/webhooks/test/commands/webhooks/update.test.ts
@@ -0,0 +1,55 @@
+import {expect, test} from '@oclif/test'
+
+describe('webhooks:update', () => {
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .patch('/apps/example-app/webhooks/99999999-9999-9999-9999-999999999999', {
+ include: ['foo', 'bar'],
+ level: 'notify',
+ secret: '1234',
+ url: 'http://foobar.com'
+ })
+ .reply(200, {})
+ )
+ .command([
+ 'webhooks:update',
+ '--app', 'example-app',
+ '--include', 'foo,bar',
+ '--secret', '1234',
+ '--level', 'notify',
+ '--url', 'http://foobar.com',
+ '99999999-9999-9999-9999-999999999999'
+ ])
+ .it('updates app webhooks', ctx => {
+ expect(ctx.stdout).to.equal('')
+ expect(ctx.stderr).to.contain('Updating webhook 99999999-9999-9999-9999-999999999999 for example-app... done\n')
+ })
+
+ test
+ .stdout()
+ .stderr()
+ .nock('https://api.heroku.com', api => api
+ .patch('/pipelines/example-pipeline/webhooks/99999999-9999-9999-9999-999999999999', {
+ include: ['foo', 'bar'],
+ level: 'notify',
+ secret: '1234',
+ url: 'http://foobar.com'
+ })
+ .reply(200, {})
+ )
+ .command([
+ 'webhooks:update',
+ '--pipeline', 'example-pipeline',
+ '--include', 'foo,bar',
+ '--secret', '1234',
+ '--level', 'notify',
+ '--url', 'http://foobar.com',
+ '99999999-9999-9999-9999-999999999999'
+ ])
+ .it('updates pipelines webhooks', ctx => {
+ expect(ctx.stdout).to.equal('')
+ expect(ctx.stderr).to.contain('Updating webhook 99999999-9999-9999-9999-999999999999 for example-pipeline... done\n')
+ })
+})
diff --git a/packages/webhooks/test/helpers/init.js b/packages/webhooks/test/helpers/init.js
new file mode 100644
index 0000000000..e37b3fac3e
--- /dev/null
+++ b/packages/webhooks/test/helpers/init.js
@@ -0,0 +1,10 @@
+// hardcodes `columns` terminal width for tests which is
+// important for things like tables and other places where
+// dynamic wrapping or truncating is an issue.
+// see: https://github.com/oclif/screen
+
+global.columns = 140
+
+// disable color for tests
+const { color } = require('@heroku-cli/color')
+color.enabled = false
diff --git a/packages/webhooks/test/mocha.opts b/packages/webhooks/test/mocha.opts
new file mode 100644
index 0000000000..1e74adb03d
--- /dev/null
+++ b/packages/webhooks/test/mocha.opts
@@ -0,0 +1,6 @@
+--require test/helpers/init.js
+--require ts-node/register
+--watch-extensions ts
+--recursive
+--reporter spec
+--timeout 10000
diff --git a/packages/webhooks/test/tsconfig.json b/packages/webhooks/test/tsconfig.json
new file mode 100644
index 0000000000..95898fcedf
--- /dev/null
+++ b/packages/webhooks/test/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../tsconfig",
+ "compilerOptions": {
+ "noEmit": true
+ },
+ "references": [
+ {"path": ".."}
+ ]
+}
diff --git a/packages/webhooks/tsconfig.json b/packages/webhooks/tsconfig.json
new file mode 100644
index 0000000000..f20eee7215
--- /dev/null
+++ b/packages/webhooks/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "declaration": true,
+ "importHelpers": true,
+ "module": "commonjs",
+ "outDir": "lib",
+ "rootDir": "src",
+ "strict": true,
+ "target": "es2017",
+ },
+ "include": [
+ "src/**/*"
+ ]
+}
diff --git a/packages/webhooks/tslint.json b/packages/webhooks/tslint.json
new file mode 100644
index 0000000000..f5703540c9
--- /dev/null
+++ b/packages/webhooks/tslint.json
@@ -0,0 +1,3 @@
+{
+ "extends": "@oclif/tslint"
+}
diff --git a/yarn.lock b/yarn.lock
index 815596175c..16c9c679d7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2925,7 +2925,7 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
-date-fns@^1.29.0:
+date-fns@^1.29.0, date-fns@^1.30.1:
version "1.30.1"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==