From 07b87ac26f62bc3c069bb16983fe7500272e19b4 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Mon, 11 May 2015 10:47:48 +0200 Subject: [PATCH] refactor guthub api --- app/Controller/Github/Callback.php | 32 ++--- app/Controller/Github/Index.php | 17 +-- app/Controller/Github/Repo.php | 15 +- app/Github/API.php | 62 +++++---- app/Github/API/Call.php | 119 ++++++++++++++++ .../Webhook.php => API/Hooks/CreateHook.php} | 27 ++-- app/Github/API/Hooks/DeleteHook.php | 25 ++++ app/Github/API/Hooks/ListHooks.php | 28 ++++ app/Github/API/Releases/CreateRelease.php | 29 ++++ .../API/Releases/CreateReleaseAsset.php | 27 ++++ app/Github/API/Releases/ListReleaseAssets.php | 23 +++ app/Github/API/Releases/ListReleases.php | 28 ++++ app/Github/API/Repos/ListRepos.php | 28 ++++ app/Github/API/Repos/ReadContents.php | 26 ++++ app/Github/API/Repos/ReadRepo.php | 26 ++++ app/Github/API/Tags/ListTags.php | 28 ++++ app/Github/API/Users/ReadAuthToken.php | 25 ++++ app/Github/API/Users/ReadAuthUser.php | 26 ++++ app/Github/Create.php | 80 ----------- app/Github/Create/Release.php | 27 ---- app/Github/Create/ReleaseAsset.php | 24 ---- app/Github/Delete.php | 74 ---------- app/Github/Delete/Webhook.php | 23 --- app/Github/Exception/ContentsFetchFailed.php | 12 -- app/Github/Exception/HooksFetchFailed.php | 12 -- .../Exception/ReleaseAssetCreateFailed.php | 13 -- app/Github/Exception/ReleaseCreateFailed.php | 13 -- app/Github/Exception/ReleasesFetchFailed.php | 12 -- app/Github/Exception/ReposFetchFailed.php | 12 -- app/Github/Exception/RequestException.php | 50 ++++++- app/Github/Exception/TokenFetchFailed.php | 13 -- app/Github/Exception/UserFetchFailed.php | 10 -- app/Github/Exception/WebhookCreateFailed.php | 10 -- app/Github/Exception/WebhookDeleteFailed.php | 10 -- app/Github/Fetch.php | 131 ------------------ app/Github/Fetch/Contents.php | 28 ---- app/Github/Fetch/Hooks.php | 29 ---- app/Github/Fetch/Releases.php | 33 ----- app/Github/Fetch/Repo.php | 27 ---- app/Github/Fetch/Repos.php | 49 ------- app/Github/Fetch/Tags.php | 33 ----- app/Github/Fetch/Token.php | 39 ------ app/Github/Fetch/User.php | 26 ---- app/Github/Links.php | 77 ++++++++++ app/views/alert.phtml | 5 +- app/views/github/callback.phtml | 3 - app/views/github/index.phtml | 19 +-- app/views/github/repo.phtml | 5 - config/app.ini | 9 +- 49 files changed, 650 insertions(+), 849 deletions(-) create mode 100644 app/Github/API/Call.php rename app/Github/{Create/Webhook.php => API/Hooks/CreateHook.php} (60%) create mode 100644 app/Github/API/Hooks/DeleteHook.php create mode 100644 app/Github/API/Hooks/ListHooks.php create mode 100644 app/Github/API/Releases/CreateRelease.php create mode 100644 app/Github/API/Releases/CreateReleaseAsset.php create mode 100644 app/Github/API/Releases/ListReleaseAssets.php create mode 100644 app/Github/API/Releases/ListReleases.php create mode 100644 app/Github/API/Repos/ListRepos.php create mode 100644 app/Github/API/Repos/ReadContents.php create mode 100644 app/Github/API/Repos/ReadRepo.php create mode 100644 app/Github/API/Tags/ListTags.php create mode 100644 app/Github/API/Users/ReadAuthToken.php create mode 100644 app/Github/API/Users/ReadAuthUser.php delete mode 100644 app/Github/Create.php delete mode 100644 app/Github/Create/Release.php delete mode 100644 app/Github/Create/ReleaseAsset.php delete mode 100644 app/Github/Delete.php delete mode 100644 app/Github/Delete/Webhook.php delete mode 100644 app/Github/Exception/ContentsFetchFailed.php delete mode 100644 app/Github/Exception/HooksFetchFailed.php delete mode 100644 app/Github/Exception/ReleaseAssetCreateFailed.php delete mode 100644 app/Github/Exception/ReleaseCreateFailed.php delete mode 100644 app/Github/Exception/ReleasesFetchFailed.php delete mode 100644 app/Github/Exception/ReposFetchFailed.php delete mode 100644 app/Github/Exception/TokenFetchFailed.php delete mode 100644 app/Github/Exception/UserFetchFailed.php delete mode 100644 app/Github/Exception/WebhookCreateFailed.php delete mode 100644 app/Github/Exception/WebhookDeleteFailed.php delete mode 100644 app/Github/Fetch.php delete mode 100644 app/Github/Fetch/Contents.php delete mode 100644 app/Github/Fetch/Hooks.php delete mode 100644 app/Github/Fetch/Releases.php delete mode 100644 app/Github/Fetch/Repo.php delete mode 100644 app/Github/Fetch/Repos.php delete mode 100644 app/Github/Fetch/Tags.php delete mode 100644 app/Github/Fetch/Token.php delete mode 100644 app/Github/Fetch/User.php create mode 100644 app/Github/Links.php diff --git a/app/Controller/Github/Callback.php b/app/Controller/Github/Callback.php index d6d1192..496baf3 100644 --- a/app/Controller/Github/Callback.php +++ b/app/Controller/Github/Callback.php @@ -28,24 +28,20 @@ class Callback extends Github "error" => $this->app->getRequest()->getQuery("error_description") ]); } else { - try { - $this->github->fetchToken( - $this->app->getRequest()->getQuery("code"), - $this->app->getRequest()->getQuery("state"), - function($token) { - $this->github->setToken($token->access_token); - $this->github->fetchUser($this->createUserCallback($token)); - })->send(); - if (isset($this->session->returnto)) { - $returnto = $this->session->returnto; - unset($this->session->returnto); - $this->app->redirect($returnto); - } else { - $this->app->redirect( - $this->app->getBaseUrl()->mod("./github")); - } - } catch (Exception $exception) { - $this->app->getView()->addData(compact("exception")); + $this->github->fetchToken( + $this->app->getRequest()->getQuery("code"), + $this->app->getRequest()->getQuery("state"), + function($token) { + $this->github->setToken($token->access_token); + $this->github->fetchUser($this->createUserCallback($token)); + })->send(); + if (isset($this->session->returnto)) { + $returnto = $this->session->returnto; + unset($this->session->returnto); + $this->app->redirect($returnto); + } else { + $this->app->redirect( + $this->app->getBaseUrl()->mod("./github")); } } $this->app->display("github/callback"); diff --git a/app/Controller/Github/Index.php b/app/Controller/Github/Index.php index f55eb0c..935f249 100644 --- a/app/Controller/Github/Index.php +++ b/app/Controller/Github/Index.php @@ -4,27 +4,20 @@ namespace app\Controller\Github; use app\Controller\Github; -use http\QueryString; - class Index extends Github { function __invoke(array $args = null) { if ($this->checkToken()) { - try { - $this->github->fetchRepos( - $this->app->getRequest()->getQuery("page"), - [$this, "reposCallback"] - )->send(); - } catch (\app\Github\Exception $exception) { - $this->view->addData(compact("exception")); - } + $this->github->fetchRepos( + $this->app->getRequest()->getQuery("page"), + [$this, "reposCallback"] + )->send(); $this->app->display("github/index"); } } function reposCallback($repos, $links) { - $this->app->getView()->addData(compact("repos")); - $this->app->getView()->registerFunction("link", $this->createLinkGenerator($links)); + $this->app->getView()->addData(compact("repos", "links")); foreach ($repos as $repo) { $this->github->fetchHooks($repo->full_name, function($hooks) use($repo) { diff --git a/app/Controller/Github/Repo.php b/app/Controller/Github/Repo.php index f194d8e..8caba2a 100644 --- a/app/Controller/Github/Repo.php +++ b/app/Controller/Github/Repo.php @@ -8,15 +8,12 @@ class Repo extends Github { function __invoke(array $args = null) { extract($args); + $this->app->getView()->addData(compact("owner", "name")); if ($this->checkToken()) { - try { - $this->github->fetchRepo( - "$owner/$name", - [$this, "repoCallback"] - )->send(); - } catch (\app\Github\Exception $exception) { - $this->app->getView()->addData(compact("exception", "owner", "name")); - } + $this->github->fetchRepo( + "$owner/$name", + [$this, "repoCallback"] + )->send(); if (($modal = $this->app->getRequest()->getQuery("modal"))) { $this->app->getView()->addData(compact("modal") + [ @@ -28,7 +25,7 @@ class Repo extends Github } } - function repoCallback($repo, $links) { + function repoCallback($repo) { $this->app->getView()->addData(compact("repo") + [ "title" => "Github: {$repo->name}" ]); diff --git a/app/Github/API.php b/app/Github/API.php index 8737c16..1023769 100644 --- a/app/Github/API.php +++ b/app/Github/API.php @@ -2,6 +2,7 @@ namespace app\Github; +use app\Github\API; use app\Github\Storage; use app\Github\Exception; @@ -107,71 +108,72 @@ class API if ($state !== $orig_state) { throw new Exception\StateMismatch($orig_state, $state); } - - $fetch = new Fetch\Token($this, compact("code") + $this->config->client->toArray()); - return $fetch($callback); + + $call = new API\Users\ReadAuthToken($this, [ + "code" => $code, + "client_id" => $this->config->client->id, + "client_secret" => $this->config->client->secret, + ]); + return $call($callback); } function fetchUser(callable $callback) { - $fetch = new Fetch\User($this); - return $fetch($callback); + $call = new API\Users\ReadAuthUser($this); + return $call($callback); } function fetchRepos($page, callable $callback) { - $fetch = new Fetch\Repos($this); - $fetch->setPage($page); - return $fetch($callback); + $call = new API\Repos\ListRepos($this, compact("page")); + return $call($callback); } function fetchRepo($repo, callable $callback) { - $fetch = new Fetch\Repo($this, compact("repo")); - return $fetch($callback); + $call = new API\Repos\ReadRepo($this, compact("repo")); + return $call($callback); } function fetchHooks($repo, callable $callback) { - $fetch = new Fetch\Hooks($this, compact("repo")); - return $fetch($callback); + $call = new API\Hooks\ListHooks($this, compact("repo")); + return $call($callback); } function fetchReleases($repo, $page, callable $callback) { - $fetch = new Fetch\Releases($this, compact("repo")); - $fetch->setPage($page); - return $fetch($callback); + $call = new API\Releases\ListReleases($this, compact("repo", "page")); + return $call($callback); } function fetchTags($repo, $page, callable $callback) { - $fetch = new Fetch\Tags($this, compact("repo")); - $fetch->setPage($page); - return $fetch($callback); + $call = new API\Tags\ListTags($this, compact("repo", "page")); + return $call($callback); } function fetchContents($repo, $path, callable $callback) { - $fetch = new Fetch\Contents($this, compact("repo", "path")); - return $fetch($callback); + $call = new API\Repos\ReadContents($this, compact("repo", "path")); + return $call($callback); } function createRepoHook($repo, $conf, callable $callback) { - $create = new Create\Webhook($this, compact("repo", "conf")); - return $create($callback); + $call = new API\Hooks\CreateHook($this, compact("repo", "conf")); + return $call($callback); } function updateRepoHook($repo, $id, $conf, callable $callback) { - $update = new Update\Webhook($this, compact("repo", "id", "conf")); - return $update($callback); + $call = new API\Hooks\UpdateHook($this, compact("repo", "id", "conf")); + return $call($callback); } function deleteRepoHook($repo, $id, callable $callback) { - $delete = new Delete\Webhook($this, compact("repo", "id")); - return $delete($callback); + $call = new API\Hooks\DeleteHook($this, compact("repo", "id")); + return $call($callback); } function createRelease($repo, $tag, callable $callback) { - $create = new Create\Release($this, compact("repo", "tag")); - return $create($callback); + $call = new API\Releases\CreateRelease($this, compact("repo", "tag")); + return $call($callback); } function createReleaseAsset($url, $asset, $type, callable $callback) { - $create = new Create\ReleaseAsset($this, compact("url", "asset", "type")); - return $create($callback); + $call = new API\Releases\CreateReleaseAsset($this, compact("url", "asset", "type")); + return $call($callback); } } diff --git a/app/Github/API/Call.php b/app/Github/API/Call.php new file mode 100644 index 0000000..f40e486 --- /dev/null +++ b/app/Github/API/Call.php @@ -0,0 +1,119 @@ +api = $api; + $this->config = $this->api->getConfig(); + $this->url = new Url($this->config->api->url, null, 0); + + if ($args) { + $this->args = $args; + } + if (isset($this->config->api->call->{$this}->args)) { + $this->args += $this->config->api->call->{$this}->args->toArray(); + } + } + + function __invoke(callable $callback) { + if (empty($this->args["fresh"]) && ($cache = $this->api->getCacheStorage())) { + $key = $this->getCacheKey(); + + if ($cache->get($key, $cached)) { + call_user_func_array($callback, $cached); + return $this->api->getClient(); + } + } + + $this->enqueue($callback); + return $this->api->getClient(); + } + + /** + * Get type of call + * @return string + */ + function __toString() { + $parts = explode("\\", get_class($this)); + return strtolower(end($parts)); + } + + /** + * Get associated cache storage + * @param int $ttl out param of configure ttl + * @return Storage + */ + function getCache(&$ttl = null) { + if (isset($this->config->storage->cache->{$this}->ttl)) { + $ttl = $this->config->storage->cache->{$this}->ttl; + } + return $this->api->getCacheStorage(); + } + + function getCacheKey() { + return sprintf("github:%s:%s:%s", $this->api->getToken(), $this, + new QueryString($this->args)); + } + + function readFromCache(&$cached = null, &$ttl = null) { + if (empty($this->args["fresh"]) && ($cache = $this->api->getCacheStorage())) { + $key = $this->getCacheKey(); + return $cache->get($key, $cached, $ttl); + } + return false; + } + + function saveToCache($fresh) { + if (($cache = $this->api->getCacheStorage())) { + if (isset($this->config->storage->cache->{$this}->ttl)) { + $ttl = $this->config->storage->cache->{$this}->ttl; + } else { + $ttl = 0; + } + + $key = $this->getCacheKey(); + $cache->set($key, $fresh, $ttl); + } + } +} diff --git a/app/Github/Create/Webhook.php b/app/Github/API/Hooks/CreateHook.php similarity index 60% rename from app/Github/Create/Webhook.php rename to app/Github/API/Hooks/CreateHook.php index 72ff2d7..325dca7 100644 --- a/app/Github/Create/Webhook.php +++ b/app/Github/API/Hooks/CreateHook.php @@ -1,19 +1,19 @@ 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(), + "Accept" => $this->config->api->accept, + "Content-Type" => "application/json", ]); if (!empty($this->args["conf"]["tag"])) { @@ -33,10 +33,13 @@ class Webhook extends Create "secret" => $this->config->client->secret, // FIXME: bad idea? ] ])); - return $request; - } - - function getException($message, $code, $previous = null) { - return new WebhookCreateFailed($message, $code, $previous); + + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getReesponseCode() != 400 || null === ($json = json_decode($response->getBody()))) { + throw new RequestException($response); + } + $callback($json); + return true; + }); } } diff --git a/app/Github/API/Hooks/DeleteHook.php b/app/Github/API/Hooks/DeleteHook.php new file mode 100644 index 0000000..311e64d --- /dev/null +++ b/app/Github/API/Hooks/DeleteHook.php @@ -0,0 +1,25 @@ +url->mod(uri_template("./repos/{+repo}/hooks{/id}", $this->args)); + $request = new Request("DELETE", $url, [ + "Authorization" => "token " . $this->api->getToken(), + "Accept" => $this->config->api->accept, + ]); + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getResponseCode() >= 400) { + throw new RequestException($response); + } + $callback($json); + return true; + }); + } +} diff --git a/app/Github/API/Hooks/ListHooks.php b/app/Github/API/Hooks/ListHooks.php new file mode 100644 index 0000000..6cd784e --- /dev/null +++ b/app/Github/API/Hooks/ListHooks.php @@ -0,0 +1,28 @@ +url->mod(uri_template("./repos/{+repo}/hooks{?page,per_page}", $this->args)); + $request = new Request("GET", $url, [ + "Authorization" => "token ". $this->api->getToken(), + "Accept" => $this->config->api->accept, + ]); + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getResponseCode() >= 400 || null === ($json = json_decode($response->getBody()))) { + throw new RequestException($response); + } + $links = new Links($response->getHeader("Link")); + $this->saveToCache([$json, $links]); + $callback($json, $links); + return true; + }); + } +} diff --git a/app/Github/API/Releases/CreateRelease.php b/app/Github/API/Releases/CreateRelease.php new file mode 100644 index 0000000..21f849f --- /dev/null +++ b/app/Github/API/Releases/CreateRelease.php @@ -0,0 +1,29 @@ +url->mod("/repos/". $this->args["repo"] ."/releases"); + $request = new Request("POST", $url, [ + "Authorization" => "token ". $this->api->getToken(), + "Accept" => $this->config->api->accept, + "Content-Type" => "application/json", + ]); + $request->getBody()->append(json_encode([ + "tag_name" => $this->args["tag"] + ])); + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getResponseCode() >= 400 || null === ($json = json_decode($response->getBody()))) { + throw new RequestException($response); + } + $callback($json); + return true; + }); + } +} diff --git a/app/Github/API/Releases/CreateReleaseAsset.php b/app/Github/API/Releases/CreateReleaseAsset.php new file mode 100644 index 0000000..c69d805 --- /dev/null +++ b/app/Github/API/Releases/CreateReleaseAsset.php @@ -0,0 +1,27 @@ +args["asset"], "rb")); + $request = new Request("POST", $this->args["url"], [ + "Authorization" => "token ". $this->api->getToken(), + "Accept" => $this->config->api->accept, + "Content-Type" => $this->args["type"], + ], $body); + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getResponseCode() >= 400 || null === ($json = json_decode($response->getBody()))) { + throw new RequestException($response); + } + $callback($json); + return true; + }); + } +} diff --git a/app/Github/API/Releases/ListReleaseAssets.php b/app/Github/API/Releases/ListReleaseAssets.php new file mode 100644 index 0000000..b365183 --- /dev/null +++ b/app/Github/API/Releases/ListReleaseAssets.php @@ -0,0 +1,23 @@ +url->mod(uri_template("./repos/{+repo}/releases{/release}/assets", $this->args)); + $request = new \http\Client\Request("GET", $url, [ + "Authorization" => "token ". $this->api->getToken(), + "Accept" => $this->config->api->accept, + ]); + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getResponseCode() >= 400 || null === ($json = json_decode($response->getBody()))) { + throw new \app\Github\Exception\RequestException($response); + } + $links = new Links($response->getHeader("Link")); + $this->saveToCache([$json, $links]); + $callback($json, $links); + return true; + }); + } +} diff --git a/app/Github/API/Releases/ListReleases.php b/app/Github/API/Releases/ListReleases.php new file mode 100644 index 0000000..c1760b9 --- /dev/null +++ b/app/Github/API/Releases/ListReleases.php @@ -0,0 +1,28 @@ +url->mod(uri_template("./repos/{+repo}/releases{?page,per_page}", $this->args)); + $request = new Request("GET", $url, [ + "Authorization" => "token ". $this->api->getToken(), + "Accept" => $this->config->api->accept, + ]); + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getResponseCode() >= 400 || null === ($json = json_decode($response->getBody()))) { + throw new RequestException($response); + } + $links = new Links($response->getHeader("Link")); + $this->saveToCache([$json, $links]); + $callback($json, $links); + return true; + }); + } +} diff --git a/app/Github/API/Repos/ListRepos.php b/app/Github/API/Repos/ListRepos.php new file mode 100644 index 0000000..c842196 --- /dev/null +++ b/app/Github/API/Repos/ListRepos.php @@ -0,0 +1,28 @@ +url->mod(uri_template("./user/repos{?page,per_page}", $this->args)); + $request = new Request("GET", $url, [ + "Authorization" => "token ". $this->api->getToken(), + "Accept" => $this->config->api->accept, + ]); + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getResponseCode() >= 400 || null === ($json = json_decode($response->getBody()))) { + throw new RequestException($response); + } + $links = new Links($response->getHeader("Link")); + $this->saveToCache([$json, $links]); + $callback($json, $links); + return true; + }); + } +} \ No newline at end of file diff --git a/app/Github/API/Repos/ReadContents.php b/app/Github/API/Repos/ReadContents.php new file mode 100644 index 0000000..02b28fb --- /dev/null +++ b/app/Github/API/Repos/ReadContents.php @@ -0,0 +1,26 @@ +url->mod(uri_template("./repos/{+repo}/contents/{+path}", $this->args)); + $request = new Request("GET", $url, [ + "Authorization" => "token ". $this->api->getToken(), + "Accept" => $this->config->api->accept, + ]); + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getResponseCode() >= 400 || null === ($json = json_decode($response->getBody()))) { + throw new RequestException($response); + } + $this->saveToCache($json); + $callback($json); + return true; + }); + } +} diff --git a/app/Github/API/Repos/ReadRepo.php b/app/Github/API/Repos/ReadRepo.php new file mode 100644 index 0000000..e432d2a --- /dev/null +++ b/app/Github/API/Repos/ReadRepo.php @@ -0,0 +1,26 @@ +url->mod(uri_template("./repos/{+repo}", $this->args)); + $request = new Request("GET", $url, [ + "Authorization" => "token " . $this->api->getToken(), + "Accept" => $this->config->api->accept, + ]); + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getResponseCode() >= 400 || null === ($json = json_decode($response->getBody()))) { + throw new RequestException($response); + } + $this->saveToCache($json); + $callback($json); + return true; + }); + } +} diff --git a/app/Github/API/Tags/ListTags.php b/app/Github/API/Tags/ListTags.php new file mode 100644 index 0000000..b7a1d4c --- /dev/null +++ b/app/Github/API/Tags/ListTags.php @@ -0,0 +1,28 @@ +url->mod(uri_template("./repos/{+repo}/tags{?page,per_page}", $this->args)); + $request = new Request("GET", $url, [ + "Authorization" => "token ". $this->api->getToken(), + "Accept" => $this->config->api->accept, + ]); + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getResponseCode() >= 400 || null === ($json = json_decode($response->getBody()))) { + throw new RequestException($response); + } + $links = new Links($response->getHeader("Link")); + $this->saveToCache([$json, $links]); + $callback($json, $links); + return true; + }); + } +} diff --git a/app/Github/API/Users/ReadAuthToken.php b/app/Github/API/Users/ReadAuthToken.php new file mode 100644 index 0000000..4430b45 --- /dev/null +++ b/app/Github/API/Users/ReadAuthToken.php @@ -0,0 +1,25 @@ + "application/json", + ]); + $request->getBody()->append(new QueryString($this->args)); + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getResponseCode() >= 400 || null === ($json = json_decode($response->getBody()))) { + throw new RequestException($response); + } + $callback($json); + return true; + }); + } +} diff --git a/app/Github/API/Users/ReadAuthUser.php b/app/Github/API/Users/ReadAuthUser.php new file mode 100644 index 0000000..5feb7c8 --- /dev/null +++ b/app/Github/API/Users/ReadAuthUser.php @@ -0,0 +1,26 @@ +url->mod("./user"); + $request = new Request("GET", $url, [ + "Authorization" => "token ". $this->api->getToken(), + "Accept" => $this->config->api->accept, + ]); + $this->api->getClient()->enqueue($request, function($response) use($callback) { + if ($response->getResponseCode() >= 400 || null === ($json = json_decode($response->getBody()))) { + throw new RequestException($response); + } + $this->saveToCache($json); + $callback($json); + return true; + }); + } +} diff --git a/app/Github/Create.php b/app/Github/Create.php deleted file mode 100644 index 0f3f31a..0000000 --- a/app/Github/Create.php +++ /dev/null @@ -1,80 +0,0 @@ -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/Release.php b/app/Github/Create/Release.php deleted file mode 100644 index 3f9f710..0000000 --- a/app/Github/Create/Release.php +++ /dev/null @@ -1,27 +0,0 @@ -url->mod("/repos/". $this->args["repo"] ."/releases"); - $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([ - "tag_name" => $this->args["tag"] - ])); - return $request; - } - - function getException($message, $code, $previous = null) { - return new ReleaseCreateFailed($message, $code, $previous); - } -} diff --git a/app/Github/Create/ReleaseAsset.php b/app/Github/Create/ReleaseAsset.php deleted file mode 100644 index 538273d..0000000 --- a/app/Github/Create/ReleaseAsset.php +++ /dev/null @@ -1,24 +0,0 @@ -args["asset"], "rb")); - $request = new Request("POST", $this->args["url"], [ - "Accept" => "application/vnd.github.v3+json", - "Content-Type" => $this->args["type"], - "Authorization" => "token ". $this->api->getToken() - ], $body); - return $request; - } - - function getException($message, $code, $previous = null) { - return new ReleaseAssetCreateFailed($message, $code, $previous); - } -} diff --git a/app/Github/Delete.php b/app/Github/Delete.php deleted file mode 100644 index 4e859a6..0000000 --- a/app/Github/Delete.php +++ /dev/null @@ -1,74 +0,0 @@ -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 deleted file mode 100644 index 5493872..0000000 --- a/app/Github/Delete/Webhook.php +++ /dev/null @@ -1,23 +0,0 @@ -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/ContentsFetchFailed.php b/app/Github/Exception/ContentsFetchFailed.php deleted file mode 100644 index cd2abd7..0000000 --- a/app/Github/Exception/ContentsFetchFailed.php +++ /dev/null @@ -1,12 +0,0 @@ -getHeader("Content-Type", Header::class)) + && $h->match("application/json", Header::MATCH_WORD) + && $failure = json_decode($response->getBody())) { + $message = $failure->message; + if (isset($failure->errors)) { + $this->errors = (array) $failure->errors; + } + } else { + $message = $response->getBody()->toString(); + } + + if (!strlen($message)) { + $message = $response->getTransferInfo("error"); + } + + parent::__construct($message, $response->getResponseCode(), null); + } + + function getErrors() { + return $this->errors; + } + + function getErrorsAsString() { + static $reasons = [ + "missing" => "The resource %1\$s does not exist\n", + "missing_field" => "Missing field %2\$s of resource %1\$s\n", + "invalid" => "Invalid formatting of field %2\$s of resource %1\$s\n", + "already_exists" => "A resource %1\$s with the same value of field %2\$s already exists\n", + ]; + + if (!$this->errors) { + return ""; + } + + $errors = "JSON errors:\n"; + foreach ($this->errors as $error) { + $errors .= sprintf($reasons[$error->code], $error->resource, $error->field); + } + return $errors; + } + function __toString() { + return parent::__toString() . "\n". $this->getErrorsAsString(); + } } diff --git a/app/Github/Exception/TokenFetchFailed.php b/app/Github/Exception/TokenFetchFailed.php deleted file mode 100644 index 128330f..0000000 --- a/app/Github/Exception/TokenFetchFailed.php +++ /dev/null @@ -1,13 +0,0 @@ -api = $api; - $this->config = $api->getConfig(); - $this->args = $args; - $this->url = new Url("https://api.github.com/", null, 0); - if (isset($this->config->fetch->{$this}->per_page)) { - $this->url->query = "per_page=" . $this->config->fetch->{$this}->per_page; - } - } - - function setPage($page) { - $this->page = $page; - return $this; - } - - function getPage() { - return $this->page; - } - - function __toString() { - $parts = explode("\\", get_class($this)); - return strtolower(end($parts)); - } - - function getStorage(&$ttl = null) { - if (isset($this->config->storage->cache->{$this}->ttl)) { - $ttl = $this->config->storage->cache->{$this}->ttl; - } - return $this->api->getCacheStorage(); - } - - abstract function getRequest(); - abstract function getException($message, $code, $previous = null); - abstract function getCacheKey(); - - function parseLinks(Header $header) { - $params = new Params($header->value, ",", ";", "=", - Params::PARSE_RFC5988 | Params::PARSE_ESCAPED); - $links = []; - foreach ($params->params as $link => $param) { - // strip enclosing brackets - $links[$param["arguments"]["rel"]] = $link; - } - return $links; - } - - function __invoke(callable $callback) { - $ttl = -1; - $key = $this->getCacheKey(); - if (($cache = $this->api->getCacheStorage()) - && $cache->get($key, $cached, $ttl)) { - call_user_func_array($callback, $cached); - } else { - $this->enqueue($callback); - } - return $this->api->getClient(); - } - - protected function wrap(callable $callback) { - return function($response) use($callback) { - $rc = $response->getResponseCode(); - - if ($rc !== 200) { - 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 (($link = $response->getHeader("Link", Header::class))) { - $links = $this->parseLinks($link); - } else { - $links = []; - } - - if (isset($json->error)) { - throw $this->getException($json->error_description, $rc); - } elseif (($cache = $this->getStorage($ttl))) { - $cache->set($this->getCacheKey(), [$json, $links], $ttl); - } - - $callback($json, $links); - - 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/Fetch/Contents.php b/app/Github/Fetch/Contents.php deleted file mode 100644 index 955cb3c..0000000 --- a/app/Github/Fetch/Contents.php +++ /dev/null @@ -1,28 +0,0 @@ -url->mod(sprintf("/repos/%s/contents/%s", - $this->args["repo"], $this->args["path"])); - return new Request("GET", $url, [ - "Accept" => "application/vnd.github.v3+json", - "Authorization" => "token " . $this->api->getToken() - ]); - } - - function getException($message, $code, $previous = null) { - return new ContentsFetchFailed($message, $code, $previous); - } - - function getCacheKey() { - return $this->api->getCacheKey(sprintf("contents:%s:%s", - $this->args["repo"], $this->args["path"])); - } -} diff --git a/app/Github/Fetch/Hooks.php b/app/Github/Fetch/Hooks.php deleted file mode 100644 index c2730e4..0000000 --- a/app/Github/Fetch/Hooks.php +++ /dev/null @@ -1,29 +0,0 @@ -url->mod([ - "path" => "/repos/" . $this->args["repo"] . "/hooks", - ]); - return new Request("GET", $url, [ - "Accept" => "application/vnd.github.v3+json", - "Authorization" => "token " . $this->api->getToken(), - ]); - } - - function getException($message, $code, $previous = null) { - return new HooksFetchFailed($message, $code, $previous); - } - - function getCacheKey() { - return $this->api->getCacheKey(sprintf("hooks:%s", $this->args["repo"])); - } -} diff --git a/app/Github/Fetch/Releases.php b/app/Github/Fetch/Releases.php deleted file mode 100644 index bd9f8f9..0000000 --- a/app/Github/Fetch/Releases.php +++ /dev/null @@ -1,33 +0,0 @@ -url->mod([ - "path" => "/repos/" . $this->args["repo"] . "/releases", - "query" => new QueryString([ - "page" => $this->getPage(), - ]) - ], 0); - return new Request("GET", $url, [ - "Accept" => "application/vnd.github.v3+json", - "Authorization" => "token " . $this->api->getToken(), - ]); - } - - function getException($message, $code, $previous = null) { - return new ReleasesFetchFailed($message, $code, $previous); - } - - function getCacheKey() { - return $this->api->getCacheKey(sprintf("releases:%s", $this->args["repo"])); - } -} \ No newline at end of file diff --git a/app/Github/Fetch/Repo.php b/app/Github/Fetch/Repo.php deleted file mode 100644 index a0889d6..0000000 --- a/app/Github/Fetch/Repo.php +++ /dev/null @@ -1,27 +0,0 @@ -url->mod("/repos/" . $this->args["repo"]); - return new Request("GET", $url, [ - "Accept" => "application/vnd.github.v3+json", - "Authorization" => "token " . $this->api->getToken(), - ]); - } - - function getException($message, $code, $previous = null) { - return new ReposFetchFailed($message, $code, $previous); - } - - function getCacheKey() { - return $this->api->getCacheKey(sprintf("repo:%s", $this->args["repo"])); - } -} diff --git a/app/Github/Fetch/Repos.php b/app/Github/Fetch/Repos.php deleted file mode 100644 index 30b3c4c..0000000 --- a/app/Github/Fetch/Repos.php +++ /dev/null @@ -1,49 +0,0 @@ -url->mod([ - "path" => "/user/repos", - "query" => new QueryString([ - "page" => $this->getPage() - ]) - ]); - return new Request("GET", $url, [ - "Accept" => "application/vnd.github.v3+json", - "Authorization" => "token " . $this->api->getToken(), - ]); - } - - function getException($message, $code, $previous = null) { - return new ReposFetchFailed($message, $code, $previous); - } - - function getCacheKey() { - return $this->api->getCacheKey("repos", $this->page); - } - - protected function wrap(callable $callback) { - return parent::wrap(function($json, $links) use($callback) { - if (($cache = $this->getStorage($ttl))) { - foreach ($json as $repo) { - $key = $this->api->getCacheKey(sprintf("repo:%s", $repo->full_name)); - $cache->set($key, [$repo, $links], $ttl); - } - } - - $callback($json, $links); - - return true; - }); - } - -} diff --git a/app/Github/Fetch/Tags.php b/app/Github/Fetch/Tags.php deleted file mode 100644 index 9ddd606..0000000 --- a/app/Github/Fetch/Tags.php +++ /dev/null @@ -1,33 +0,0 @@ -url->mod([ - "path" => "/repos/" . $this->args["repo"] . "/tags", - "query" => new QueryString([ - "page" => $this->getPage(), - ]) - ], 0); - return new Request("GET", $url, [ - "Accept" => "application/vnd.github.v3+json", - "Authorization" => "token " . $this->api->getToken(), - ]); - } - - function getException($message, $code, $previous = null) { - return new TagsFetchFailed($message, $code, $previous); - } - - function getCacheKey() { - return $this->api->getCacheKey(sprintf("tags:%s", $this->args["repo"])); - } -} \ No newline at end of file diff --git a/app/Github/Fetch/Token.php b/app/Github/Fetch/Token.php deleted file mode 100644 index 16a270a..0000000 --- a/app/Github/Fetch/Token.php +++ /dev/null @@ -1,39 +0,0 @@ - "application/json", - ]); - $request->getBody()->append( - new QueryString([ - "client_id" => $this->args["id"], - "client_secret" => $this->args["secret"], - "code" => $this->args["code"] - ]) - ); - return $request; - } - - function getException($message, $code, $previous = null) { - return new TokenFetchFailed($message, $code, $previous); - } - - function getCacheKey() { - return "access_token"; - } - - function getStorage(&$ttl = null) { - /* do not cache externally */ - return null; - } -} \ No newline at end of file diff --git a/app/Github/Fetch/User.php b/app/Github/Fetch/User.php deleted file mode 100644 index 010fb5e..0000000 --- a/app/Github/Fetch/User.php +++ /dev/null @@ -1,26 +0,0 @@ -url->mod("/user"); - return new Request("GET", $url, [ - "Accept" => "application/vnd.github.v3+json", - "Authorization" => "token " . $this->api->getToken() - ]); - } - - function getException($message, $code, $previous = null) { - return new UserFetchFailed($message, $code, $previous); - } - - function getCacheKey() { - return $this->api->getCacheKey("user"); - } -} diff --git a/app/Github/Links.php b/app/Github/Links.php new file mode 100644 index 0000000..7a498b2 --- /dev/null +++ b/app/Github/Links.php @@ -0,0 +1,77 @@ +params = new Params($header_value, ",", ";", "=", + Params::PARSE_RFC5988 | Params::PARSE_ESCAPED); + if ($this->params->params) { + foreach ($this->params->params as $link => $param) { + $this->relations[$param["arguments"]["rel"]] = $link; + } + } + } + + function getRelations() { + return $this->relations; + } + + function getNext() { + if (isset($this->relations["next"])) { + return $this->relations["next"]; + } + if (isset($this->relations["last"])) { + return $this->relations["last"]; + } + return null; + } + + function getPrev() { + if (isset($this->relations["prev"])) { + return $this->relations["prev"]; + } + if (isset($this->relations["first"])) { + return $this->relations["first"]; + } + return null; + } + + function getLast() { + if (isset($this->relations["last"])) { + return $this->relations["last"]; + } + return null; + } + + function getFirst() { + if (isset($this->relations["first"])) { + return $this->relations["first"]; + } + return null; + } + + function getPage($which) { + if (($link = $this->{"get$which"}())) { + $url = new Url($link, null, 0); + $qry = new QueryString($url->query); + return $qry->getInt("page", 1); + } + return 1; + } +} diff --git a/app/views/alert.phtml b/app/views/alert.phtml index 5d0a516..2a12f16 100644 --- a/app/views/alert.phtml +++ b/app/views/alert.phtml @@ -1,6 +1,7 @@ - - fetch("alert") ?> - - @@ -57,16 +53,9 @@ - diff --git a/app/views/github/repo.phtml b/app/views/github/repo.phtml index d321830..1b7a640 100644 --- a/app/views/github/repo.phtml +++ b/app/views/github/repo.phtml @@ -20,10 +20,6 @@ - - fetch("alert") ?> - -
@@ -214,4 +210,3 @@
- diff --git a/config/app.ini b/config/app.ini index 1cdc655..ec01dbf 100644 --- a/config/app.ini +++ b/config/app.ini @@ -1,12 +1,15 @@ [production] +github.api.url = https://api.github.com/ +github.api.accept = application/vnd.github.v3+json + +github.api.call.listrepos.args.per_page = 10 +github.api.call.listhooks.args.per_page = 100 + github.hook.url = https://pharext.org/github/hook github.hook.content_type = json github.hook.insecure_ssl = 0 -github.fetch.repos.per_page = 10 -github.fetch.hooks.per_page = 100 - github.storage.token.ttl = 3600 github.storage.cache.repo.ttl = 3600 github.storage.cache.repos.ttl = 3600 -- 2.30.2