Page MenuHomePhabricator

D15438.id37204.diff
No OneTemporary

D15438.id37204.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -199,6 +199,8 @@
'PhutilFileLockTestCase' => 'filesystem/__tests__/PhutilFileLockTestCase.php',
'PhutilFileTree' => 'filesystem/PhutilFileTree.php',
'PhutilGitHubAuthAdapter' => 'auth/PhutilGitHubAuthAdapter.php',
+ 'PhutilGitHubFuture' => 'future/github/PhutilGitHubFuture.php',
+ 'PhutilGitHubResponse' => 'future/github/PhutilGitHubResponse.php',
'PhutilGitURI' => 'parser/PhutilGitURI.php',
'PhutilGitURITestCase' => 'parser/__tests__/PhutilGitURITestCase.php',
'PhutilGoogleAuthAdapter' => 'auth/PhutilGoogleAuthAdapter.php',
@@ -736,6 +738,8 @@
'PhutilFileLockTestCase' => 'PhutilTestCase',
'PhutilFileTree' => 'Phobject',
'PhutilGitHubAuthAdapter' => 'PhutilOAuthAuthAdapter',
+ 'PhutilGitHubFuture' => 'FutureProxy',
+ 'PhutilGitHubResponse' => 'Phobject',
'PhutilGitURI' => 'Phobject',
'PhutilGitURITestCase' => 'PhutilTestCase',
'PhutilGoogleAuthAdapter' => 'PhutilOAuthAuthAdapter',
diff --git a/src/future/github/PhutilGitHubFuture.php b/src/future/github/PhutilGitHubFuture.php
new file mode 100644
--- /dev/null
+++ b/src/future/github/PhutilGitHubFuture.php
@@ -0,0 +1,130 @@
+<?php
+
+final class PhutilGitHubFuture extends FutureProxy {
+
+ private $future;
+ private $accessToken;
+ private $action;
+ private $params;
+ private $method = 'GET';
+ private $headers = array();
+
+ public function __construct() {
+ parent::__construct(null);
+ }
+
+ public function setAccessToken($token) {
+ $this->accessToken = $token;
+ return $this;
+ }
+
+ public function setRawGitHubQuery($action, array $params = array()) {
+ $this->action = $action;
+ $this->params = $params;
+ return $this;
+ }
+
+ public function setMethod($method) {
+ $this->method = $method;
+ return $this;
+ }
+
+ public function addHeader($key, $value) {
+ $this->headers[] = array($key, $value);
+ return $this;
+ }
+
+ protected function getProxiedFuture() {
+ if (!$this->future) {
+ $params = $this->params;
+
+ if (!$this->action) {
+ throw new Exception(
+ pht(
+ 'You must %s!',
+ 'setRawGitHubQuery()'));
+ }
+
+ if (!$this->accessToken) {
+ throw new Exception(
+ pht(
+ 'You must %s!',
+ 'setAccessToken()'));
+ }
+
+ $uri = new PhutilURI('https://api.github.com/');
+ $uri->setPath('/'.ltrim($this->action, '/'));
+
+ $future = new HTTPSFuture($uri);
+ $future->setData($this->params);
+ $future->addHeader('Authorization', 'token '.$this->accessToken);
+ // NOTE: GitHub requires a 'User-Agent' header.
+ $future->addHeader('User-Agent', __CLASS__);
+ $future->setMethod($this->method);
+
+ foreach ($this->headers as $header) {
+ list($key, $value) = $header;
+ $future->addHeader($key, $value);
+ }
+
+ $this->future = $future;
+ }
+
+ return $this->future;
+ }
+
+ protected function didReceiveResult($result) {
+ list($status, $body, $headers) = $result;
+
+ if ($status->isError()) {
+ if ($this->isRateLimitResponse($status, $headers)) {
+ // Do nothing, this is a rate limit.
+ } else if ($this->isNotModifiedResponse($status)) {
+ // Do nothing, this is a "Not Modified" response.
+ } else {
+ // This is an error condition we do not expect.
+ throw $status;
+ }
+ }
+
+ try {
+ if (strlen($body)) {
+ $data = phutil_json_decode($body);
+ } else {
+ // This can happen for 304 responses.
+ $data = array();
+ }
+ } catch (PhutilJSONParserException $ex) {
+ throw new PhutilProxyException(
+ pht('Expected JSON response from GitHub.'),
+ $ex);
+ }
+
+ return id(new PhutilGitHubResponse())
+ ->setStatus($status)
+ ->setHeaders($headers)
+ ->setBody($data);
+ }
+
+ private function isNotModifiedResponse($status) {
+ return ($status->getCode() == 304);
+ }
+
+ private function isRateLimitResponse($status, array $headers) {
+ if ($status->getCode() != 403) {
+ return false;
+ }
+
+ foreach ($headers as $header) {
+ list($key, $value) = $header;
+ if (phutil_utf8_strtolower($key) === 'x-ratelimit-remaining') {
+ if (!(int)$value) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/src/future/github/PhutilGitHubResponse.php b/src/future/github/PhutilGitHubResponse.php
new file mode 100644
--- /dev/null
+++ b/src/future/github/PhutilGitHubResponse.php
@@ -0,0 +1,49 @@
+<?php
+
+final class PhutilGitHubResponse extends Phobject {
+
+ private $status;
+ private $headers;
+ private $body;
+
+ public function setStatus($status) {
+ $this->status = $status;
+ return $this;
+ }
+
+ public function getStatus() {
+ return $this->status;
+ }
+
+ public function setBody(array $body) {
+ $this->body = $body;
+ return $this;
+ }
+
+ public function getBody() {
+ return $this->body;
+ }
+
+ public function setHeaders(array $headers) {
+ $this->headers = $headers;
+ return $this;
+ }
+
+ public function getHeaders() {
+ return $this->headers;
+ }
+
+ public function getHeaderValue($key, $default = null) {
+ $key = phutil_utf8_strtolower($key);
+
+ foreach ($this->headers as $header) {
+ list($hkey, $value) = $header;
+ if (phutil_utf8_strtolower($hkey) === $key) {
+ return $value;
+ }
+ }
+
+ return $default;
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Mon, May 20, 6:51 AM (3 w, 5 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6297363
Default Alt Text
D15438.id37204.diff (5 KB)

Event Timeline