"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");
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) {
{
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") + [
}
}
- function repoCallback($repo, $links) {
+ function repoCallback($repo) {
$this->app->getView()->addData(compact("repo") + [
"title" => "Github: {$repo->name}"
]);
namespace app\Github;
+use app\Github\API;
use app\Github\Storage;
use app\Github\Exception;
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);
}
}
--- /dev/null
+<?php
+
+namespace app\Github\API;
+
+use app\Github\API;
+use http\QueryString;
+use http\Url;
+use merry\Config;
+
+abstract class Call
+{
+ /**
+ * @var Config
+ */
+ protected $config;
+
+ /**
+ * @var \app\Gituhub\API
+ */
+ protected $api;
+
+ /**
+ * @var array
+ */
+ protected $args = [];
+
+ /**
+ * @var \http\Url
+ */
+ protected $url;
+
+ /**
+ * @var QueryString
+ */
+ protected $query;
+
+ /**
+ * Queue this call to the API client
+ */
+ abstract function enqueue(callable $callback);
+
+ /**
+ * @param API $api
+ * @param array $args
+ */
+ function __construct(API $api, array $args = null) {
+ $this->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);
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API\Hooks;
+
+use app\Github\API\Call;
+use app\Github\Exception\RequestException;
+use http\Client\Request;
+
+class CreateHook extends Call
+{
+ function enqueue(callable $callback) {
+ $url = $this->url->mod("./repos/". $this->args["repo"] ."/hooks");
+ $request = new Request("POST", $url, [
+ "Authorization" => "token " . $this->api->getToken(),
+ "Accept" => $this->config->api->accept,
+ "Content-Type" => "application/json",
+ ]);
+
+ if (!empty($this->args["conf"]["tag"])) {
+ $events[] = "create";
+ }
+ if (!empty($this->args["conf"]["release"])) {
+ $events[] = "release";
+ }
+
+ $request->getBody()->append(json_encode([
+ "name" => "web",
+ "events" => $events,
+ "config" => [
+ "url" => $this->config->hook->url,
+ "content_type" => $this->config->hook->content_type,
+ "insecure_ssl" => $this->config->hook->insecure_ssl,
+ "secret" => $this->config->client->secret, // FIXME: bad idea?
+ ]
+ ]));
+
+ $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;
+ });
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API\Hooks;
+
+use app\Github\API\Call;
+use app\Github\Exception\RequestException;
+use http\Client\Request;
+
+class DeleteHook extends Call
+{
+ function enqueue(callable $callback) {
+ $url = $this->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;
+ });
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API\Hooks;
+
+use app\Github\API\Call;
+use app\Github\Exception\RequestException;
+use app\Github\Links;
+use http\Client\Request;
+
+class ListHooks extends Call
+{
+ function enqueue(callable $callback) {
+ $url = $this->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;
+ });
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API\Releases;
+
+use app\Github\API\Call;
+use app\Github\Exception\RequestException;
+use http\Client\Request;
+
+class CreateRelease extends Call
+{
+ function enqueue(callable $callback) {
+ $url = $this->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;
+ });
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API\Releases;
+
+use app\Github\API\Call;
+use app\Github\Exception\RequestException;
+use http\Client\Request;
+use http\Message\Body;
+
+class CreateReleaseAsset extends Call
+{
+ function enqueue(callable $callback) {
+ $body = new Body(fopen($this->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;
+ });
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API\Releases;
+
+class ListReleaseAssets extends \app\Github\API\Call
+{
+ function enqueue(callable $callback) {
+ $url = $this->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;
+ });
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API\Releases;
+
+use app\Github\API\Call;
+use app\Github\Exception\RequestException;
+use app\Github\Links;
+use http\Client\Request;
+
+class ListReleases extends Call
+{
+ function enqueue(callable $callback) {
+ $url = $this->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;
+ });
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API\Repos;
+
+use app\Github\API\Call;
+use app\Github\Exception\RequestException;
+use app\Github\Links;
+use http\Client\Request;
+
+class ListRepos extends Call
+{
+ function enqueue(callable $callback) {
+ $url = $this->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
--- /dev/null
+<?php
+
+namespace app\Github\API\Repos;
+
+use app\Github\API\Call;
+use app\Github\Exception\RequestException;
+use http\Client\Request;
+
+class ReadContents extends Call
+{
+ function enqueue(callable $callback) {
+ $url = $this->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;
+ });
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API\Repos;
+
+use app\Github\API\Call;
+use app\Github\Exception\RequestException;
+use http\Client\Request;
+
+class ReadRepo extends Call
+{
+ function enqueue(callable $callback) {
+ $url = $this->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;
+ });
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API\Tags;
+
+use app\Github\API\Call;
+use app\Github\Exception\RequestException;
+use app\Github\Links;
+use http\Client\Request;
+
+class ListTags extends Call
+{
+ function enqueue(callable $callback) {
+ $url = $this->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;
+ });
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API\Users;
+
+use app\Github\API\Call;
+use app\Github\Exception\RequestException;
+use http\Client\Request;
+use http\QueryString;
+
+class ReadAuthToken extends Call
+{
+ function enqueue(callable $callback) {
+ $request = new Request("POST", "https://github.com/login/oauth/access_token", [
+ "Accept" => "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;
+ });
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API\Users;
+
+use app\Github\API\Call;
+use app\Github\Exception\RequestException;
+use http\Client\Request;
+
+class ReadAuthUser extends Call
+{
+ function enqueue(callable $callback) {
+ $url = $this->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;
+ });
+ }
+}
+++ /dev/null
-<?php
-
-namespace app\Github;
-
-use http\Header;
-use http\Url;
-
-abstract class Create
-{
- /**
- * @var \app\Github\API
- */
- protected $api;
-
- /**
- * @var \merry\Config
- */
- protected $config;
-
- /**
- * @var array
- */
- protected $args;
-
- /**
- * @var \http\Url
- */
- protected $url;
-
- function __construct(API $api, array $args = []) {
- $this->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
+++ /dev/null
-<?php
-
-namespace app\Github\Create;
-
-use app\Github\Create;
-use app\Github\Exception\ReleaseCreateFailed;
-use http\Client\Request;
-
-class Release extends Create
-{
- function getRequest() {
- $url = $this->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);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Create;
-
-use app\Github\Create;
-use app\Github\Exception\ReleaseAssetCreateFailed;
-use http\Client\Request;
-
-class ReleaseAsset extends Create
-{
- function getRequest() {
- $body = new \http\Message\Body(fopen($this->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);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Create;
-
-use app\Github\Create;
-use app\Github\Exception\WebhookCreateFailed;
-use http\Client\Request;
-
-class Webhook extends Create
-{
- function getRequest() {
- $url = $this->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(),
- ]);
-
- if (!empty($this->args["conf"]["tag"])) {
- $events[] = "create";
- }
- if (!empty($this->args["conf"]["release"])) {
- $events[] = "release";
- }
-
- $request->getBody()->append(json_encode([
- "name" => "web",
- "events" => $events,
- "config" => [
- "url" => $this->config->hook->url,
- "content_type" => $this->config->hook->content_type,
- "insecure_ssl" => $this->config->hook->insecure_ssl,
- "secret" => $this->config->client->secret, // FIXME: bad idea?
- ]
- ]));
- return $request;
- }
-
- function getException($message, $code, $previous = null) {
- return new WebhookCreateFailed($message, $code, $previous);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github;
-
-use http\Header;
-use http\Url;
-
-abstract class Delete
-{
- /**
- * @var \app\Github\API
- */
- protected $api;
-
- /**
- * @var \merry\Config
- */
- protected $config;
-
- /**
- * @var array
- */
- protected $args;
-
- /**
- * @var \http\Url
- */
- protected $url;
-
- function __construct(API $api, array $args = []) {
- $this->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
+++ /dev/null
-<?php
-
-namespace app\Github\Delete;
-
-use app\Github\Delete;
-use app\Github\Exception\WebhookDeleteFailed;
-use http\Client\Request;
-
-class Webhook extends Delete
-{
- function getRequest() {
- $url = $this->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);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Exception;
-
-use app\Github\Exception\RequestException;
-
-class ContentsFetchFailed extends \Exception implements RequestException
-{
- function __construct($message, $code, $previous = null) {
- parent::__construct($message ?: "Contents fetch request failed", $code, $previous);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Exception;
-
-use app\Github\Exception\RequestException;
-
-class HooksFetchFailed extends \Exception implements RequestException
-{
- function __construct($message, $code, $previous = null) {
- parent::__construct($message ?: "Hooks fetch request failed", $code, $previous);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Exception;
-
-use app\Github\Exception\RequestException;
-
-class ReleaseAssetCreateFailed extends \Exception implements RequestException
-{
- function __construct($message, $code, $previous = null)
- {
- parent::__construct($message ?: "ReleaseAsset create request failed", $code, $previous);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Exception;
-
-use app\Github\Exception\RequestException;
-
-class ReleaseCreateFailed extends \Exception implements RequestException
-{
- function __construct($message, $code, $previous = null)
- {
- parent::__construct($message ?: "Release create request failed", $code, $previous);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Exception;
-
-use app\Github\Exception\RequestException;
-
-class ReleasesFetchFailed extends \Exception implements RequestException
-{
- function __construct($message, $code, $previous = null) {
- parent::__construct($message ?: "Releases fetch request failed", $code, $previous);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Exception;
-
-use app\Github\Exception\RequestException;
-
-class ReposFetchFailed extends \Exception implements RequestException
-{
- function __construct($message, $code, $previous = null) {
- parent::__construct($message ?: "Repos fetch request failed", $code, $previous);
- }
-}
namespace app\Github\Exception;
use app\Github\Exception;
+use http\Header;
+use http\Client\Response;
-interface RequestException extends Exception
+class RequestException extends \Exception implements Exception
{
+ protected $errors = [];
+
+ function __construct(Response $response) {
+ if (($h = $response->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();
+ }
}
+++ /dev/null
-<?php
-
-namespace app\Github\Exception;
-
-use app\Github\Exception\TokenException;
-use app\Github\Exception\RequestException;
-
-class TokenFetchFailed extends \Exception implements TokenException, RequestException
-{
- function __construct($message, $code, $previous = null) {
- parent::__construct($message ?: "Token fetch request failed", $code, $previous);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Exeption;
-
-class UserFetchFailed extends \Exception implements app\Github\Exception\RequestException
-{
- function __construct($message, $code, $previous = null) {
- parent::__construct($message ?: "User fetch request failed", $code, $previous);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Exception;
-
-class WebhookCreateFailed extends \Exception implements \app\Github\Exception\RequestException
-{
- function __construct($message, $code, $previous = null) {
- parent::__construct($message ?: "Webhook create request failed", $code, $previous);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Exception;
-
-class WebhookDeleteFailed extends \Exception implements \app\Github\Exception\RequestException
-{
- function __construct($message, $code, $previous = null) {
- parent::__construct($message ?: "Webhook delete request failed", $code, $previous);
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github;
-
-use http\Header;
-use http\Params;
-use http\Url;
-
-abstract class Fetch
-{
- /**
- * @var \app\Github\API
- */
- protected $api;
-
- /**
- * @var \merry\Config
- */
- protected $config;
-
- /**
- * @var array
- */
- protected $args;
-
- /**
- * @var int
- */
- protected $page = 1;
-
- /**
- * @var \http\Url
- */
- protected $url;
-
- function __construct(API $api, array $args = []) {
- $this->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
+++ /dev/null
-<?php
-
-namespace app\Github\Fetch;
-
-use app\Github\Exception\ContentsFetchFailed;
-use app\Github\Fetch;
-use http\Client\Request;
-
-class Contents extends Fetch
-{
- function getRequest() {
- $url = $this->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"]));
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Fetch;
-
-use app\Github\Exception\HooksFetchFailed;
-use app\Github\Fetch;
-
-use http\Client\Request;
-
-class Hooks extends Fetch
-{
- function getRequest() {
- $url = $this->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"]));
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Fetch;
-
-use app\Github\Exception\ReleasesFetchFailed;
-use app\Github\Fetch;
-
-use http\Client\Request;
-use http\QueryString;
-
-class Releases extends Fetch
-{
- function getRequest() {
- $url = $this->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
+++ /dev/null
-<?php
-
-namespace app\Github\Fetch;
-
-use app\Github\Exception\ReposFetchFailed;
-use app\Github\Fetch;
-
-use http\Client\Request;
-
-class Repo extends Fetch
-{
- function getRequest() {
- $url = $this->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"]));
- }
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Fetch;
-
-use app\Github\Exception\ReposFetchFailed;
-use app\Github\Fetch;
-
-use http\Client\Request;
-use http\QueryString;
-
-class Repos extends Fetch
-{
- function getRequest() {
- $url = $this->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;
- });
- }
-
-}
+++ /dev/null
-<?php
-
-namespace app\Github\Fetch;
-
-use app\Github\Exception\TagsFetchFailed;
-use app\Github\Fetch;
-
-use http\Client\Request;
-use http\QueryString;
-
-class Tags extends Fetch
-{
- function getRequest() {
- $url = $this->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
+++ /dev/null
-<?php
-
-namespace app\Github\Fetch;
-
-use app\Github\Exception\TokenFetchFailed;
-use app\Github\Fetch;
-
-use http\Client\Request;
-use http\QueryString;
-
-class Token extends Fetch
-{
- function getRequest() {
- $request = new Request("POST", "https://github.com/login/oauth/access_token", [
- "Accept" => "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
+++ /dev/null
-<?php
-
-namespace app\Github\Fetch;
-
-use app\Github\Exeption\UserFetchFailed;
-use app\Github\Fetch;
-use http\Client\Request;
-
-class User extends Fetch
-{
- function getRequest() {
- $url = $this->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");
- }
-}
--- /dev/null
+<?php
+
+namespace app\Github;
+
+use http\Params;
+use http\QueryString;
+use http\Url;
+
+class Links
+{
+ /**
+ * @var \http\Params
+ */
+ private $params;
+
+ /**
+ * @var array
+ */
+ private $relations = [];
+
+ function __construct($header_value) {
+ $this->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;
+ }
+}
<div class="alert alert-danger" role="alert">
- <strong><?= nl2br($this->e($exception->getMessage())) ?></strong><br>
+ <strong><?= nl2br($this->e($exception->getMessage())) ?></strong>
+ (<?= $exception->getCode() ?>)<br>
<?php if ($exception instanceof \app\Github\Exception\TokenException) : ?>
You might want to try to <a href="<?= $baseUrl->mod("./github/signin") ?>">renew your token</a>!
<?php endif; ?>
<?php if (APP_ENVIRONMENT != "production") : ?>
<h3>Stack Trace</h3>
<pre><?= $this->e($exception->getTraceAsString()) ?></pre>
+<h3>Full Dump</h3>
+<pre><?= $this->e($exception) ?></pre>
<?php endif; ?>
<?php $this->layout("layout") ?>
-<?php if (isset($exception)) : ?>
- <?= $this->fetch("alert") ?>
-<?php endif; ?>
</h1>
</div>
-<?php if (isset($exception)) : ?>
- <?= $this->fetch("alert") ?>
-<?php elseif (isset($repos)) : ?>
-
<table class="table table-hover">
<thead>
<tr>
<nav>
<ul class="pager">
- <?php
- $first= $this->link("first");
- $prev = $this->link("prev");
- $next = $this->link("next");
- $last = $this->link("last");
- ?>
- <li class="<?= $first ?"":"disabled" ?>"><a href="?page=<?= $first ?>" title="First Page"><span class="glyphicon glyphicon-fast-backward"></span></a></li>
- <li class="<?= $prev ?"":"disabled" ?>"><a href="?page=<?= $prev ?>" rel="prev" title="Previous Page"><span class="glyphicon glyphicon-chevron-left"></span> Previous</a></li>
- <li class="<?= $next ?"":"disabled" ?>"><a href="?page=<?= $next ?>" rel="next" title="Next Page">Next <span class="glyphicon glyphicon-chevron-right"></span></a></li>
- <li class="<?= $last ?"":"disabled" ?>"><a href="?page=<?= $last ?>" title="Last Page"><span class="glyphicon glyphicon-fast-forward"></span></a></li>
+ <li class="<?= $links->getFirst() ?"":"disabled" ?>"><a href="?page=<?= $links->getPage("first") ?>" title="First Page"><span class="glyphicon glyphicon-fast-backward"></span></a></li>
+ <li class="<?= $links->getPrev() ?"":"disabled" ?>"><a href="?page=<?= $links->getPage("prev") ?>" rel="prev" title="Previous Page"><span class="glyphicon glyphicon-chevron-left"></span> Previous</a></li>
+ <li class="<?= $links->getNext() ?"":"disabled" ?>"><a href="?page=<?= $links->getPage("next") ?>" rel="next" title="Next Page">Next <span class="glyphicon glyphicon-chevron-right"></span></a></li>
+ <li class="<?= $links->getLast() ?"":"disabled" ?>"><a href="?page=<?= $links->getPage("last") ?>" title="Last Page"><span class="glyphicon glyphicon-fast-forward"></span></a></li>
</ul>
</nav>
-<?php endif; ?>
</h1>
</div>
-<?php if (isset($exception)) : ?>
- <?= $this->fetch("alert") ?>
-<?php elseif (isset($repo)) : ?>
-
<div class="row">
<div class="col-md-6">
<div class="well">
</div>
<?php endforeach; ?>
-<?php endif; ?>
[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