From 728020f863c8562d9c42e926b08ba1c7a390e53d Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Mon, 4 May 2015 12:22:37 +0200 Subject: [PATCH] webhook create&delete --- app/Controller/Github.php | 9 ++- app/Controller/Github/Repo/Hook.php | 53 +++++++++++++ app/Github/API.php | 10 +++ app/Github/Create.php | 80 ++++++++++++++++++++ app/Github/Create/Webhook.php | 33 ++++++++ app/Github/Delete.php | 74 ++++++++++++++++++ app/Github/Delete/Webhook.php | 23 ++++++ app/Github/Exception/UserFetchFailed.php | 2 +- app/Github/Exception/WebhookCreateFailed.php | 10 +++ app/Github/Exception/WebhookDeleteFailed.php | 10 +++ app/Web.php | 2 +- app/routes.ini | 3 + app/views/github/index.phtml | 23 ++++-- app/views/github/repo.phtml | 8 +- 14 files changed, 327 insertions(+), 13 deletions(-) create mode 100644 app/Controller/Github/Repo/Hook.php create mode 100644 app/Github/Create.php create mode 100644 app/Github/Create/Webhook.php create mode 100644 app/Github/Delete.php create mode 100644 app/Github/Delete/Webhook.php create mode 100644 app/Github/Exception/WebhookCreateFailed.php create mode 100644 app/Github/Exception/WebhookDeleteFailed.php diff --git a/app/Controller/Github.php b/app/Controller/Github.php index 0dfc5b0..6432c86 100644 --- a/app/Controller/Github.php +++ b/app/Controller/Github.php @@ -49,15 +49,20 @@ abstract class Github implements Controller return false; } + /** + * Check if the pharext webhook is set for the repo and return its id + * @param object $repo + * @return int hook id + */ function checkRepoHook($repo) { if ($repo->hooks) { foreach ($repo->hooks as $hook) { if ($hook->name === "web" && $hook->config->url === $this->github->getConfig()->hook->url) { - return true; + return $hook->id; } } } - return false; + return null; } function createLinkGenerator($links) { diff --git a/app/Controller/Github/Repo/Hook.php b/app/Controller/Github/Repo/Hook.php new file mode 100644 index 0000000..7bfb545 --- /dev/null +++ b/app/Controller/Github/Repo/Hook.php @@ -0,0 +1,53 @@ +addHook($args["owner"], $args["name"]); + break; + + case "del": + $this->delHook($args["owner"], $args["name"]); + break; + } + } + + function addHook($owner, $repo) { + $this->github->createRepoHook("$owner/$repo", function($hook) use($owner, $repo) { + if (($cache = $this->github->getCacheStorage())) { + $cache->del($this->github->getCacheKey("hooks:$owner/$repo")); + } + if (($back = $this->app->getRequest()->getForm("returnback")) && isset($this->session->previous)) { + $this->app->redirect($this->app->getBaseUrl()->mod($this->session->previous)); + } else { + $this->app->redirect($this->app->getBaseUrl()->mod("./github/repo/$owner/$repo")); + } + })->send(); + } + + function delHook($owner, $repo) { + $this->github->fetchRepo("$owner/$repo", function($repo) { + $this->github->fetchHooks($repo->full_name, function($hooks) use($repo) { + $repo->hooks = $hooks; + if (($id = $this->checkRepoHook($repo))) { + $this->github->deleteRepoHook($repo->full_name, $id, function() use($repo) { + if (($cache = $this->github->getCacheStorage())) { + $cache->del($this->github->getCacheKey("hooks:" . $repo->full_name)); + } + if (($back = $this->app->getRequest()->getForm("returnback")) && isset($this->session->previous)) { + $this->app->redirect($this->app->getBaseUrl()->mod($this->session->previous)); + } else { + $this->app->redirect($this->app->getBaseUrl()->mod("./github/repo/" . $repo->full_name)); + } + }); + } + }); + })->send(); + } +} diff --git a/app/Github/API.php b/app/Github/API.php index 10cf2c0..f62bf06 100644 --- a/app/Github/API.php +++ b/app/Github/API.php @@ -149,4 +149,14 @@ class API $fetch = new Fetch\Contents($this, compact("repo", "path")); return $fetch($callback); } + + function createRepoHook($repo, callable $callback) { + $create = new Create\Webhook($this, compact("repo")); + return $create($callback); + } + + function deleteRepoHook($repo, $id, callable $callback) { + $delete = new Delete\Webhook($this, compact("repo", "id")); + return $delete($callback); + } } diff --git a/app/Github/Create.php b/app/Github/Create.php new file mode 100644 index 0000000..0f3f31a --- /dev/null +++ b/app/Github/Create.php @@ -0,0 +1,80 @@ +api = $api; + $this->config = $api->getConfig(); + $this->args = $args; + $this->url = new Url("https://api.github.com/", null, 0); + } + + function __toString() { + $parts = explode("\\", get_class($this)); + return strtolower(end($parts)); + } + + abstract function getRequest(); + abstract function getException($message, $code, $previous = null); + + function __invoke(callable $callback) { + $this->enqueue($callback); + return $this->api->getClient(); + } + + protected function wrap(callable $callback) { + return function($response) use($callback) { + $rc = $response->getResponseCode(); + + if ($rc !== 201) { + if ($response->getHeader("Content-Type", Header::class)->match("application/json", Header::MATCH_WORD)) { + $message = json_decode($response->getBody())->message; + } else { + $message = $response->getBody(); + } + throw $this->getException($message, $rc); + } + + $json = json_decode($response->getBody()); + + if (isset($json->error)) { + throw $this->getException($json->error_description, $rc); + } + + $callback($json); + + return true; + }; + } + + function enqueue(callable $callback) { + $request = $this->getRequest(); + $wrapper = $this->wrap($callback); + return $this->api->getClient()->enqueue($request, $wrapper); + } +} \ No newline at end of file diff --git a/app/Github/Create/Webhook.php b/app/Github/Create/Webhook.php new file mode 100644 index 0000000..6e583fc --- /dev/null +++ b/app/Github/Create/Webhook.php @@ -0,0 +1,33 @@ +url->mod("./repos/". $this->args["repo"] ."/hooks"); + $request = new Request("POST", $url, [ + "Accept" => "application/vnd.github.v3+json", + "Content-Type" => "application/json", + "Authorization" => "token " . $this->api->getToken(), + ]); + $request->getBody()->append(json_encode([ + "name" => "web", + "config" => [ + "url" => $this->config->hook->url, + "content_type" => $this->config->hook->content_type, + "secret" => $this->config->client->secret, // FIXME: bad idea? + "insecure_ssl" => false, + ] + ])); + return $request; + } + + function getException($message, $code, $previous = null) { + return new WebhookCreateFailed($message, $code, $previous); + } +} diff --git a/app/Github/Delete.php b/app/Github/Delete.php new file mode 100644 index 0000000..4e859a6 --- /dev/null +++ b/app/Github/Delete.php @@ -0,0 +1,74 @@ +api = $api; + $this->config = $api->getConfig(); + $this->args = $args; + $this->url = new Url("https://api.github.com/", null, 0); + } + + function __toString() { + $parts = explode("\\", get_class($this)); + return strtolower(end($parts)); + } + + abstract function getRequest(); + abstract function getException($message, $code, $previous = null); + + function __invoke(callable $callback) { + $this->enqueue($callback); + return $this->api->getClient(); + } + + protected function wrap(callable $callback) { + return function($response) use($callback) { + $rc = $response->getResponseCode(); + + if ($rc !== 204) { + if ($response->getHeader("Content-Type", Header::class)->match("application/json", Header::MATCH_WORD)) { + $message = json_decode($response->getBody())->message; + } else { + $message = $response->getBody(); + } + throw $this->getException($message, $rc); + } + + $callback(); + + return true; + }; + } + + function enqueue(callable $callback) { + $request = $this->getRequest(); + $wrapper = $this->wrap($callback); + return $this->api->getClient()->enqueue($request, $wrapper); + } +} \ No newline at end of file diff --git a/app/Github/Delete/Webhook.php b/app/Github/Delete/Webhook.php new file mode 100644 index 0000000..5493872 --- /dev/null +++ b/app/Github/Delete/Webhook.php @@ -0,0 +1,23 @@ +url->mod("./repos/". $this->args["repo"] ."/hooks/". $this->args["id"]); + $request = new Request("DELETE", $url, [ + "Accept" => "application/vnd.github.v3+json", + "Authorization" => "token " . $this->api->getToken(), + ]); + return $request; + } + + function getException($message, $code, $previous = null) { + return new WebhookDeleteFailed($message, $code, $previous); + } +} diff --git a/app/Github/Exception/UserFetchFailed.php b/app/Github/Exception/UserFetchFailed.php index 578e106..b43f884 100644 --- a/app/Github/Exception/UserFetchFailed.php +++ b/app/Github/Exception/UserFetchFailed.php @@ -2,7 +2,7 @@ namespace app\Github\Exeption; -class UserFetchFailed extends Exception implements app\Github\Exception\RequestException +class UserFetchFailed extends \Exception implements app\Github\Exception\RequestException { function __construct($message, $code, $previous = null) { parent::__construct($message ?: "User fetch request failed", $code, $previous); diff --git a/app/Github/Exception/WebhookCreateFailed.php b/app/Github/Exception/WebhookCreateFailed.php new file mode 100644 index 0000000..2b047e2 --- /dev/null +++ b/app/Github/Exception/WebhookCreateFailed.php @@ -0,0 +1,10 @@ +baseUrl = $baseUrl; $this->request = $request; $this->response = $response; - $this->view = $view; + $this->view = $view->addData(["location" => null]); ob_start($response); } diff --git a/app/routes.ini b/app/routes.ini index f9954d8..40782bf 100644 --- a/app/routes.ini +++ b/app/routes.ini @@ -14,6 +14,9 @@ GET[] = /github GET[] = /github/repo/{owner}/{name} GET[] = /github/repo/{owner}/{name}/{page} +[Github\Repo\Hook] +POST[] = /github/repo/{owner}/{name}/hook/{action} + [Github\Signin] GET[] = /github/signin diff --git a/app/views/github/index.phtml b/app/views/github/index.phtml index 987ea8e..bba0e7b 100644 --- a/app/views/github/index.phtml +++ b/app/views/github/index.phtml @@ -17,7 +17,7 @@ Repo Fork Last Pushed - Hook + Hook @@ -35,13 +35,20 @@ - - " - href="mod("./github/repo/".$repo->full_name) ?>"> - - " - href="mod(["path" => "github/repo", "query" => "hook=del&repo=". urlencode($repo->full_name)]) ?>"> - + +
full_name ."/hook/add") ?>"> + + +
+
full_name ."/hook/del") ?>"> + + +
+ diff --git a/app/views/github/repo.phtml b/app/views/github/repo.phtml index 58721bb..372de5d 100644 --- a/app/views/github/repo.phtml +++ b/app/views/github/repo.phtml @@ -119,14 +119,20 @@
+
- +
full_name ."/hook/add") ?>"> + +
+
full_name ."/hook/del") ?>"> +
+
-- 2.30.2