X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;f=app%2FGithub%2FAPI.php;h=8cff984c4707bb2645c7ecc5bcf06069a83f9442;hb=303950054385d4c55cfef9a8828ee79420b7366e;hp=10cf2c0c33640f90f9dfbbc360b69366f4d0c000;hpb=4c462ab66e6c65c15df7bd5fe6d2673884c639a0;p=pharext%2Fpharext.org diff --git a/app/Github/API.php b/app/Github/API.php index 10cf2c0..8cff984 100644 --- a/app/Github/API.php +++ b/app/Github/API.php @@ -2,8 +2,10 @@ namespace app\Github; +use app\Github\API; use app\Github\Storage; use app\Github\Exception; +use app\Pharext; use merry\Config; @@ -11,6 +13,8 @@ use http\Client; use http\QueryString; use http\Url; +use Psr\Log\LoggerInterface; + class API { /** @@ -32,13 +36,49 @@ class API * @var merry\Config */ private $config; + + /** + * @var int + */ + private $maxAge; + + /** + * @var \Psr\Log\LoggerInterface; + */ + private $logger; + + /** + * Queued promises + * @var array + */ + private $queue; - function __construct(Config $config, Storage $tokens = null, Storage $cache = null) { + function __construct(Config $config, LoggerInterface $logger, Storage $tokens = null, Storage $cache = null) { + $this->logger = $logger; $this->config = $config; - $this->client = new Client; + $this->client = new Client("curl", "github"); + $this->client->configure($config->http->configure->toArray()); + $this->client->attach(new ClientObserver($logger)); $this->tokens = $tokens ?: new Storage\Session; $this->cache = $cache; } + + /** + * Set maximum acceptable age of cache items + * @param int $seconds + */ + function setMaxAge($seconds) { + $this->maxAge = $seconds; + return $this; + } + + function getMaxAge() { + return $this->maxAge; + } + + function getLogger() { + return $this->logger; + } function getConfig() { return $this->config; @@ -61,20 +101,30 @@ class API } function hasToken() { - return $this->tokens->get("access_token"); + if ($this->tokens->get("access_token", $token)) { + $access_token = $token->getValue(); + if (isset($access_token)) { + return true; + } + $this->dropToken(); + } + return false; } function setToken($token) { - $this->tokens->set("access_token", $token, - $this->config->storage->token->ttl); + $this->tokens->set("access_token", new Storage\Item( + $token, + $this->config->storage->token->ttl + )); } function getToken() { - if ($this->tokens->get("access_token", $token, $ttl, true)) { - return $token; + if ($this->tokens->get("access_token", $token, true)) { + return $token->getValue(); } - if (isset($ttl)) { - throw new Exception\TokenExpired($ttl); + if (isset($token)) { + $this->logger->notice("Token expired", $token); + throw new Exception\TokenExpired($token->getLTL()); } throw new Exception\TokenNotSet; } @@ -85,68 +135,159 @@ class API function getAuthUrl($callback_url) { $state = base64_encode(openssl_random_pseudo_bytes(24)); - $this->tokens->set("state", $state, 5*60); + $this->tokens->set("state", new Storage\Item($state, 5*60)); $param = [ "state" => $state, "client_id" => $this->config->client->id, "scope" => $this->config->client->scope, - "redirect_uri" => $callback_url, + "redirect_uri" => (string) $callback_url, ]; return new Url("https://github.com/login/oauth/authorize", [ "query" => new QueryString($param) ], 0); } - function fetchToken($code, $state, callable $callback) { - if (!$this->tokens->get("state", $orig_state, $ttl, true)) { - if (isset($ttl)) { - throw new Exception\StateExpired($ttl); + function fetchToken($code, $state) { + if (!$this->tokens->get("state", $orig_state, true)) { + if (isset($orig_state)) { + $this->logger->notice("State expired", compact("state", "orig_state")); + throw new Exception\StateExpired($orig_state->getLTL()); } throw new Exception\StateNotSet; } - if ($state !== $orig_state) { - throw new Exception\StateMismatch($orig_state, $state); + if ($state !== $orig_state->getValue()) { + $this->logger->warning("State mismatch", compact("state", "orig_state")); + throw new Exception\StateMismatch($orig_state->getValue(), $state); } - - $fetch = new Fetch\Token($this, compact("code") + $this->config->client->toArray()); - return $fetch($callback); + + return $this->queue(new API\Users\ReadAuthToken($this, [ + "code" => $code, + "client_id" => $this->config->client->id, + "client_secret" => $this->config->client->secret, + ])); } - function fetchUser(callable $callback) { - $fetch = new Fetch\User($this); - return $fetch($callback); + function queue(API\Call $call) { + return $this->queue[] = $call(); + } + + function drain() { + $queue = $this->queue; + $this->queue = array(); + $this->client->send(); + return $queue; + } + + function readAuthUser() { + return $this->queue(new API\Users\ReadAuthUser($this)); } - function fetchRepos($page, callable $callback) { - $fetch = new Fetch\Repos($this); - $fetch->setPage($page); - return $fetch($callback); + function listRepos($page) { + return $this->queue(new API\Repos\ListRepos($this, compact("page"))); } - function fetchRepo($repo, callable $callback) { - $fetch = new Fetch\Repo($this, compact("repo")); - return $fetch($callback); + function readRepo($repo) { + return $this->queue(new API\Repos\ReadRepo($this, compact("repo"))); } - function fetchHooks($repo, callable $callback) { - $fetch = new Fetch\Hooks($this, compact("repo")); - return $fetch($callback); + /** + * Check if the pharext webhook is set and return it + * @param array $hooks + * @return stdClass hook + */ + function checkHook($hooks) { + if (!empty($hooks)) { + foreach ($hooks as $hook) { + if ($hook->name === "web" && $hook->config->url === $this->config->hook->url) { + return $hook; + } + } + } + return null; + } + + /** + * Check if the pharext webhook is set for the repo and return it + * @param object $repo + * @return stdClass hook + */ + function checkRepoHook($repo) { + if (!empty($repo->hooks)) { + return $this->checkHook($repo->hooks); + } + return null; + } + + function listHooks($repo) { + return $this->queue(new API\Hooks\ListHooks($this, compact("repo"))); + } + + function listReleases($repo, $page) { + return $this->queue(new API\Releases\ListReleases($this, compact("repo", "page"))); + } + + function listTags($repo, $page) { + return $this->queue(new API\Tags\ListTags($this, compact("repo", "page"))); + } + + function readContents($repo, $path = null) { + return $this->queue(new API\Repos\ReadContents($this, compact("repo", "path"))); + } + + function createRepoHook($repo, $conf) { + return $this->queue(new API\Hooks\CreateHook($this, compact("repo", "conf"))); + } + + function updateRepoHook($repo, $id, $conf) { + return $this->queue(new API\Hooks\UpdateHook($this, compact("repo", "id", "conf"))); + } + + function deleteRepoHook($repo, $id) { + return $this->queue(new API\Hooks\DeleteHook($this, compact("repo", "id"))); + } + + function createRelease($repo, $tag) { + return $this->queue(new API\Releases\CreateRelease($this, compact("repo", "tag"))); } - function fetchReleases($repo, $page, callable $callback) { - $fetch = new Fetch\Releases($this, compact("repo")); - $fetch->setPage($page); - return $fetch($callback); + function publishRelease($repo, $id, $tag) { + return $this->queue(new API\Releases\PublishRelease($this, compact("repo", "id", "tag"))); } - function fetchTags($repo, $page, callable $callback) { - $fetch = new Fetch\Tags($this, compact("repo")); - $fetch->setPage($page); - return $fetch($callback); + function createReleaseAsset($url, $asset, $type) { + return $this->queue(new API\Releases\CreateReleaseAsset($this, compact("url", "asset", "type"))); } - function fetchContents($repo, $path, callable $callback) { - $fetch = new Fetch\Contents($this, compact("repo", "path")); - return $fetch($callback); + function listReleaseAssets($repo, $id) { + return $this->queue(new API\Releases\ListReleaseAssets($this, compact("repo", "id"))); + } + + function uploadAssetForRelease($repo, $release, $config = null) { + return $this->listHooks($repo->full_name)->then(function($result) use($release, $repo, $config) { + list($repo->hooks) = $result; + $phar = new Pharext\Package( + $repo->clone_url, + $release->tag_name, + $repo->name, + $config ?: (array) $this->checkRepoHook($repo)->config + ); + $name = $phar->build(); + $url = uri_template($release->upload_url, compact("name")); + $promise = $this->createReleaseAsset($url, $phar, "application/phar"); + if ($release->draft) { + return $promise->then(function($result) use($release, $repo) { + return $this->publishRelease($repo->full_name, $release->id, $release->tag_name); + }); + } + return $promise; + }); } + + function createReleaseFromTag($repo, $tag_name, $config = null) { + return $this->createRelease($repo->full_name, $tag_name)->then(function($result) use($repo, $config) { + list($release) = $result; + return $this->uploadAssetForRelease($repo, $release, $config); + }); + } + }