{"action":"created","comment":{"url":"https://api.github.com/repos/operable/cog/pulls/comments/58597136","id":58597136,"diff_hunk":"@@ -0,0 +1,84 @@\n+defmodule Cog.V1.EventHookController do\n+  use Cog.Web, :controller\n+  require Logger\n+\n+  plug Cog.Plug.Authentication\n+  plug Cog.Plug.Authorization, permission: \"#{Cog.embedded_bundle}:manage_hooks\"\n+\n+  alias Cog.Repository.EventHooks\n+\n+  def index(conn, _params),\n+    do: render(conn, \"index.json\", event_hooks: EventHooks.all)\n+\n+  def show(conn, %{\"id\" => id}) do\n+    # hook = EventHooks.hook_definition!(id)\n+    # render(conn, \"show.json\", event_hook: hook)","path":"web/controllers/v1/event_hook_controller.ex","position":15,"original_position":15,"commit_id":"5c51cf94fe00d2e8d829ee40a7a53310e14b7f91","original_commit_id":"5c51cf94fe00d2e8d829ee40a7a53310e14b7f91","user":{"login":"vanstee","id":228734,"avatar_url":"https://avatars.githubusercontent.com/u/228734?v=3","gravatar_id":"","url":"https://api.github.com/users/vanstee","html_url":"https://github.com/vanstee","followers_url":"https://api.github.com/users/vanstee/followers","following_url":"https://api.github.com/users/vanstee/following{/other_user}","gists_url":"https://api.github.com/users/vanstee/gists{/gist_id}","starred_url":"https://api.github.com/users/vanstee/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vanstee/subscriptions","organizations_url":"https://api.github.com/users/vanstee/orgs","repos_url":"https://api.github.com/users/vanstee/repos","events_url":"https://api.github.com/users/vanstee/events{/privacy}","received_events_url":"https://api.github.com/users/vanstee/received_events","type":"User","site_admin":false},"body":"Was this meant to be left here?","created_at":"2016-04-05T19:06:33Z","updated_at":"2016-04-05T19:06:33Z","html_url":"https://github.com/operable/cog/pull/495#discussion_r58597136","pull_request_url":"https://api.github.com/repos/operable/cog/pulls/495","_links":{"self":{"href":"https://api.github.com/repos/operable/cog/pulls/comments/58597136"},"html":{"href":"https://github.com/operable/cog/pull/495#discussion_r58597136"},"pull_request":{"href":"https://api.github.com/repos/operable/cog/pulls/495"}}},"pull_request":{"url":"https://api.github.com/repos/operable/cog/pulls/495","id":65130119,"html_url":"https://github.com/operable/cog/pull/495","diff_url":"https://github.com/operable/cog/pull/495.diff","patch_url":"https://github.com/operable/cog/pull/495.patch","issue_url":"https://api.github.com/repos/operable/cog/issues/495","number":495,"state":"open","locked":false,"title":"Trigger pipelines in response to external events","user":{"login":"christophermaier","id":207178,"avatar_url":"https://avatars.githubusercontent.com/u/207178?v=3","gravatar_id":"","url":"https://api.github.com/users/christophermaier","html_url":"https://github.com/christophermaier","followers_url":"https://api.github.com/users/christophermaier/followers","following_url":"https://api.github.com/users/christophermaier/following{/other_user}","gists_url":"https://api.github.com/users/christophermaier/gists{/gist_id}","starred_url":"https://api.github.com/users/christophermaier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/christophermaier/subscriptions","organizations_url":"https://api.github.com/users/christophermaier/orgs","repos_url":"https://api.github.com/users/christophermaier/repos","events_url":"https://api.github.com/users/christophermaier/events{/privacy}","received_events_url":"https://api.github.com/users/christophermaier/received_events","type":"User","site_admin":false},"body":"Now pipelines may be triggered by HTTP requests instead of just via chat\r\nmessages. This will allow Cog to, e.g., respond to webhooks.\r\n\r\nUsers may define \"event hooks\", which define a pipeline to run in\r\nresponse to an HTTP POST request. The request itself acts as input to\r\nthe pipeline (a COG_ENV for the first command in a pipeline,\r\ntechnically), allowing query parameters, request headers and JSON\r\nrequest bodies to be accessible to the command. Pipelines may run as a\r\ndefined user, or may require token-based authentication for\r\nexecution. Additionally, pipelines may send complete output back to the\r\nrequestor via HTTP response, or direct output to the currently-enabled\r\nCog chat adapter, or both.\r\n\r\nA \"CRUD\" REST API is provided for event hooks and is currently the sole\r\nmeans of manipulating event hooks (i.e., no chat commands for this just\r\nyet). Execution of hooks, while done via HTTP request, is handled by a\r\ndifferent Phoenix Endpoint than the REST API (defaulting to\r\nport 4001, vs. the REST API's default port 4000).\r\n\r\nTo do this, we introduce a new \"HTTP adapter\", which is an\r\nimplementation of our current Adapter behaviour. This adapter is\r\ndifferent than previous ones in a few ways, though.\r\n\r\nFirst, it is a synchronous adapter. All chat adapters thus far have been\r\nasynchronous, in that responding to a Cog request does not need to be\r\ncoupled to the _receiving_ of that request. As we are now responding to\r\nHTTP requests, this is no longer the case; we _must_ send a response\r\nwhen we receive a request! To deal with this, an \"AdapterBridge\" is\r\nintroduced to track which pipeline executions correspond to which\r\ncurrently-processing HTTP request. (If you squint, this \"AdapterBridge\"\r\nperforms the same function for the HTTP adapter as our Slack RTM/API and\r\nHipChat API modules perform for those adapters.)\r\n\r\nSecondly, as a synchronous adapter, the HTTP adapter is the first to\r\nsend output to mulitple places _via multiple adapters_. As mentioned, it\r\nmust _always_ send something back for the HTTP response. However, it\r\nmust _also_ be able to (optionally) send output to chat.\r\n\r\nThird, the HTTP adapter is the first to seed a pipeline with data; a\r\n`COG_ENV` is now presented to the pipeline from the beginning, rather\r\nthan being generated during execution. This allows commands access to\r\nthe data in the HTTP request. To facilitate correlation of HTTP request\r\nprocessing and resultant pipeline execution, the HTTP adapter also\r\npasses the unique ID of the HTTP request to the Executor as the\r\npipeline's unique ID. As a result, the Adapter behaviour contract was\r\nmodified to allow passing these two pieces of data in with a request.\r\n\r\nTo help manage these differences in a general way, adapters now know\r\nwhether they are a chat adapter or not, with the HTTP adapter being the\r\nfirst non-chat adapter. Behavior in the Executor and associated code can\r\nask the adapter which it is in order to do the right thing.\r\n\r\nOther changes of note:\r\n\r\n* The `NOCHAT` behavior has been dropped. If you want to run without a\r\n  chat adapter, choose the `null` adapter.\r\n* A new embedded permission `operable:manage:hooks` has been added; all\r\n  CRUD REST API interaction around event hooks requires this permission.\r\n* The `TemplateCache` has been moved from the top-level supervisor down\r\n  to the `CommandSup` supervisor.\r\n* Pipeline destination processing grew more complicated, now that output\r\n  can be \"full\" or \"status-only\" (the latter for HTTP request that don't\r\n  redirect output to `here`, but still require something for the HTTP\r\n  response), so a new `Cog.Command.Pipeline.Destination` module was\r\n  created to manage destination processing. This removes a fair bit of\r\n  code from the Executor, as well as an entire FSM state.\r\n* Lots of tests\r\n* A new testing utility `Cog.Snoop` is used to eavesdrop on MQTT message\r\n  traffic. Here it is used to verify certain aspects of event hook\r\n  processing, but may be useful in other tests,as well.\r\n\r\nCloses #344\r\nCloses #385\r\nCloses #387","created_at":"2016-04-04T11:24:35Z","updated_at":"2016-04-05T19:06:33Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2b7aef51c2f2120ee3fef2c286c4a76a6f6ba2a9","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/operable/cog/pulls/495/commits","review_comments_url":"https://api.github.com/repos/operable/cog/pulls/495/comments","review_comment_url":"https://api.github.com/repos/operable/cog/pulls/comments{/number}","comments_url":"https://api.github.com/repos/operable/cog/issues/495/comments","statuses_url":"https://api.github.com/repos/operable/cog/statuses/5c51cf94fe00d2e8d829ee40a7a53310e14b7f91","head":{"label":"operable:cm/external-events","ref":"cm/external-events","sha":"5c51cf94fe00d2e8d829ee40a7a53310e14b7f91","user":{"login":"operable","id":6070463,"avatar_url":"https://avatars.githubusercontent.com/u/6070463?v=3","gravatar_id":"","url":"https://api.github.com/users/operable","html_url":"https://github.com/operable","followers_url":"https://api.github.com/users/operable/followers","following_url":"https://api.github.com/users/operable/following{/other_user}","gists_url":"https://api.github.com/users/operable/gists{/gist_id}","starred_url":"https://api.github.com/users/operable/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/operable/subscriptions","organizations_url":"https://api.github.com/users/operable/orgs","repos_url":"https://api.github.com/users/operable/repos","events_url":"https://api.github.com/users/operable/events{/privacy}","received_events_url":"https://api.github.com/users/operable/received_events","type":"Organization","site_admin":false},"repo":{"id":49292670,"name":"cog","full_name":"operable/cog","owner":{"login":"operable","id":6070463,"avatar_url":"https://avatars.githubusercontent.com/u/6070463?v=3","gravatar_id":"","url":"https://api.github.com/users/operable","html_url":"https://github.com/operable","followers_url":"https://api.github.com/users/operable/followers","following_url":"https://api.github.com/users/operable/following{/other_user}","gists_url":"https://api.github.com/users/operable/gists{/gist_id}","starred_url":"https://api.github.com/users/operable/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/operable/subscriptions","organizations_url":"https://api.github.com/users/operable/orgs","repos_url":"https://api.github.com/users/operable/repos","events_url":"https://api.github.com/users/operable/events{/privacy}","received_events_url":"https://api.github.com/users/operable/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/operable/cog","description":"Bringing the power of the command line to chat","fork":false,"url":"https://api.github.com/repos/operable/cog","forks_url":"https://api.github.com/repos/operable/cog/forks","keys_url":"https://api.github.com/repos/operable/cog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/operable/cog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/operable/cog/teams","hooks_url":"https://api.github.com/repos/operable/cog/hooks","issue_events_url":"https://api.github.com/repos/operable/cog/issues/events{/number}","events_url":"https://api.github.com/repos/operable/cog/events","assignees_url":"https://api.github.com/repos/operable/cog/assignees{/user}","branches_url":"https://api.github.com/repos/operable/cog/branches{/branch}","tags_url":"https://api.github.com/repos/operable/cog/tags","blobs_url":"https://api.github.com/repos/operable/cog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/operable/cog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/operable/cog/git/refs{/sha}","trees_url":"https://api.github.com/repos/operable/cog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/operable/cog/statuses/{sha}","languages_url":"https://api.github.com/repos/operable/cog/languages","stargazers_url":"https://api.github.com/repos/operable/cog/stargazers","contributors_url":"https://api.github.com/repos/operable/cog/contributors","subscribers_url":"https://api.github.com/repos/operable/cog/subscribers","subscription_url":"https://api.github.com/repos/operable/cog/subscription","commits_url":"https://api.github.com/repos/operable/cog/commits{/sha}","git_commits_url":"https://api.github.com/repos/operable/cog/git/commits{/sha}","comments_url":"https://api.github.com/repos/operable/cog/comments{/number}","issue_comment_url":"https://api.github.com/repos/operable/cog/issues/comments{/number}","contents_url":"https://api.github.com/repos/operable/cog/contents/{+path}","compare_url":"https://api.github.com/repos/operable/cog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/operable/cog/merges","archive_url":"https://api.github.com/repos/operable/cog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/operable/cog/downloads","issues_url":"https://api.github.com/repos/operable/cog/issues{/number}","pulls_url":"https://api.github.com/repos/operable/cog/pulls{/number}","milestones_url":"https://api.github.com/repos/operable/cog/milestones{/number}","notifications_url":"https://api.github.com/repos/operable/cog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/operable/cog/labels{/name}","releases_url":"https://api.github.com/repos/operable/cog/releases{/id}","deployments_url":"https://api.github.com/repos/operable/cog/deployments","created_at":"2016-01-08T19:45:06Z","updated_at":"2016-04-05T07:07:11Z","pushed_at":"2016-04-05T18:12:45Z","git_url":"git://github.com/operable/cog.git","ssh_url":"git@github.com:operable/cog.git","clone_url":"https://github.com/operable/cog.git","svn_url":"https://github.com/operable/cog","homepage":"http://operable.io","size":2645,"stargazers_count":294,"watchers_count":294,"language":"Elixir","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":19,"mirror_url":null,"open_issues_count":117,"forks":19,"open_issues":117,"watchers":294,"default_branch":"master"}},"base":{"label":"operable:master","ref":"master","sha":"f466ce9b0ca63aa3c62371f856d40e138aec0b61","user":{"login":"operable","id":6070463,"avatar_url":"https://avatars.githubusercontent.com/u/6070463?v=3","gravatar_id":"","url":"https://api.github.com/users/operable","html_url":"https://github.com/operable","followers_url":"https://api.github.com/users/operable/followers","following_url":"https://api.github.com/users/operable/following{/other_user}","gists_url":"https://api.github.com/users/operable/gists{/gist_id}","starred_url":"https://api.github.com/users/operable/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/operable/subscriptions","organizations_url":"https://api.github.com/users/operable/orgs","repos_url":"https://api.github.com/users/operable/repos","events_url":"https://api.github.com/users/operable/events{/privacy}","received_events_url":"https://api.github.com/users/operable/received_events","type":"Organization","site_admin":false},"repo":{"id":49292670,"name":"cog","full_name":"operable/cog","owner":{"login":"operable","id":6070463,"avatar_url":"https://avatars.githubusercontent.com/u/6070463?v=3","gravatar_id":"","url":"https://api.github.com/users/operable","html_url":"https://github.com/operable","followers_url":"https://api.github.com/users/operable/followers","following_url":"https://api.github.com/users/operable/following{/other_user}","gists_url":"https://api.github.com/users/operable/gists{/gist_id}","starred_url":"https://api.github.com/users/operable/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/operable/subscriptions","organizations_url":"https://api.github.com/users/operable/orgs","repos_url":"https://api.github.com/users/operable/repos","events_url":"https://api.github.com/users/operable/events{/privacy}","received_events_url":"https://api.github.com/users/operable/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/operable/cog","description":"Bringing the power of the command line to chat","fork":false,"url":"https://api.github.com/repos/operable/cog","forks_url":"https://api.github.com/repos/operable/cog/forks","keys_url":"https://api.github.com/repos/operable/cog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/operable/cog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/operable/cog/teams","hooks_url":"https://api.github.com/repos/operable/cog/hooks","issue_events_url":"https://api.github.com/repos/operable/cog/issues/events{/number}","events_url":"https://api.github.com/repos/operable/cog/events","assignees_url":"https://api.github.com/repos/operable/cog/assignees{/user}","branches_url":"https://api.github.com/repos/operable/cog/branches{/branch}","tags_url":"https://api.github.com/repos/operable/cog/tags","blobs_url":"https://api.github.com/repos/operable/cog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/operable/cog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/operable/cog/git/refs{/sha}","trees_url":"https://api.github.com/repos/operable/cog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/operable/cog/statuses/{sha}","languages_url":"https://api.github.com/repos/operable/cog/languages","stargazers_url":"https://api.github.com/repos/operable/cog/stargazers","contributors_url":"https://api.github.com/repos/operable/cog/contributors","subscribers_url":"https://api.github.com/repos/operable/cog/subscribers","subscription_url":"https://api.github.com/repos/operable/cog/subscription","commits_url":"https://api.github.com/repos/operable/cog/commits{/sha}","git_commits_url":"https://api.github.com/repos/operable/cog/git/commits{/sha}","comments_url":"https://api.github.com/repos/operable/cog/comments{/number}","issue_comment_url":"https://api.github.com/repos/operable/cog/issues/comments{/number}","contents_url":"https://api.github.com/repos/operable/cog/contents/{+path}","compare_url":"https://api.github.com/repos/operable/cog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/operable/cog/merges","archive_url":"https://api.github.com/repos/operable/cog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/operable/cog/downloads","issues_url":"https://api.github.com/repos/operable/cog/issues{/number}","pulls_url":"https://api.github.com/repos/operable/cog/pulls{/number}","milestones_url":"https://api.github.com/repos/operable/cog/milestones{/number}","notifications_url":"https://api.github.com/repos/operable/cog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/operable/cog/labels{/name}","releases_url":"https://api.github.com/repos/operable/cog/releases{/id}","deployments_url":"https://api.github.com/repos/operable/cog/deployments","created_at":"2016-01-08T19:45:06Z","updated_at":"2016-04-05T07:07:11Z","pushed_at":"2016-04-05T18:12:45Z","git_url":"git://github.com/operable/cog.git","ssh_url":"git@github.com:operable/cog.git","clone_url":"https://github.com/operable/cog.git","svn_url":"https://github.com/operable/cog","homepage":"http://operable.io","size":2645,"stargazers_count":294,"watchers_count":294,"language":"Elixir","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":19,"mirror_url":null,"open_issues_count":117,"forks":19,"open_issues":117,"watchers":294,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/operable/cog/pulls/495"},"html":{"href":"https://github.com/operable/cog/pull/495"},"issue":{"href":"https://api.github.com/repos/operable/cog/issues/495"},"comments":{"href":"https://api.github.com/repos/operable/cog/issues/495/comments"},"review_comments":{"href":"https://api.github.com/repos/operable/cog/pulls/495/comments"},"review_comment":{"href":"https://api.github.com/repos/operable/cog/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/operable/cog/pulls/495/commits"},"statuses":{"href":"https://api.github.com/repos/operable/cog/statuses/5c51cf94fe00d2e8d829ee40a7a53310e14b7f91"}}}}