Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13976205
D15438.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
5 KB
Referenced Files
None
Subscribers
None
D15438.diff
View Options
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->getStatusCode() == 304);
+ }
+
+ private function isRateLimitResponse($status, array $headers) {
+ if ($status->getStatusCode() != 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
Details
Attached
Mime Type
text/plain
Expires
Sat, Oct 19, 1:15 PM (2 w, 2 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6732299
Default Alt Text
D15438.diff (5 KB)
Attached To
Mode
D15438: Add a GitHub API Future
Attached
Detach File
Event Timeline
Log In to Comment