Changeset View
Changeset View
Standalone View
Standalone View
src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHubUser.php
- This file was added.
| <?php | |||||
| final class DoorkeeperBridgeGitHubUser | |||||
| extends DoorkeeperBridgeGitHub { | |||||
| const OBJTYPE_GITHUB_USER = 'github.user'; | |||||
| public function canPullRef(DoorkeeperObjectRef $ref) { | |||||
| if (!parent::canPullRef($ref)) { | |||||
| return false; | |||||
| } | |||||
| if ($ref->getObjectType() !== self::OBJTYPE_GITHUB_USER) { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| public function pullRefs(array $refs) { | |||||
| $token = $this->getGitHubAccessToken(); | |||||
| if (!strlen($token)) { | |||||
| return null; | |||||
| } | |||||
| $template = id(new PhutilGitHubFuture()) | |||||
| ->setAccessToken($token); | |||||
| $futures = array(); | |||||
| $id_map = mpull($refs, 'getObjectID', 'getObjectKey'); | |||||
| foreach ($id_map as $key => $id) { | |||||
| // GitHub doesn't provide a way to query for users by ID directly, but we | |||||
| // can list all users, ordered by ID, starting at some particular ID, | |||||
| // with a page size of one, which will achieve the desired effect. | |||||
| $one_less = ($id - 1); | |||||
| $uri = "/users?since={$one_less}&per_page=1"; | |||||
| $data = array(); | |||||
| $futures[$key] = id(clone $template) | |||||
| ->setRawGitHubQuery($uri, $data); | |||||
| } | |||||
| $results = array(); | |||||
| $failed = array(); | |||||
| foreach (new FutureIterator($futures) as $key => $future) { | |||||
| try { | |||||
| $results[$key] = $future->resolve(); | |||||
| } catch (Exception $ex) { | |||||
| if (($ex instanceof HTTPFutureResponseStatus) && | |||||
| ($ex->getStatusCode() == 404)) { | |||||
| // TODO: Do we end up here for deleted objects and invisible | |||||
| // objects? | |||||
| } else { | |||||
| phlog($ex); | |||||
| $failed[$key] = $ex; | |||||
| } | |||||
| } | |||||
| } | |||||
| $viewer = $this->getViewer(); | |||||
| foreach ($refs as $ref) { | |||||
| $ref->setAttribute('name', pht('GitHub User %s', $ref->getObjectID())); | |||||
| $did_fail = idx($failed, $ref->getObjectKey()); | |||||
| if ($did_fail) { | |||||
| $ref->setSyncFailed(true); | |||||
| continue; | |||||
| } | |||||
| $result = idx($results, $ref->getObjectKey()); | |||||
| if (!$result) { | |||||
| continue; | |||||
| } | |||||
| $body = $result->getBody(); | |||||
| if (!is_array($body) || !count($body)) { | |||||
| $ref->setSyncFailed(true); | |||||
| continue; | |||||
| } | |||||
| $spec = head($body); | |||||
| if (!is_array($spec)) { | |||||
| $ref->setSyncFailed(true); | |||||
| continue; | |||||
| } | |||||
| // Because we're using a paging query to load each user, if a user (say, | |||||
| // user ID 123) does not exist for some reason, we might get the next | |||||
| // user (say, user ID 124) back. Make sure the user we got back is really | |||||
| // the user we expect. | |||||
| $id = idx($spec, 'id'); | |||||
| if ($id !== $ref->getObjectID()) { | |||||
| $ref->setSyncFailed(true); | |||||
| continue; | |||||
| } | |||||
| $ref->setIsVisible(true); | |||||
| $ref->setAttribute('api.raw', $spec); | |||||
| $ref->setAttribute('name', $spec['login']); | |||||
| $obj = $ref->getExternalObject(); | |||||
| $this->fillObjectFromData($obj, $spec); | |||||
| $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); | |||||
| $obj->save(); | |||||
| unset($unguarded); | |||||
| } | |||||
| } | |||||
| public function fillObjectFromData(DoorkeeperExternalObject $obj, $spec) { | |||||
| $uri = $spec['html_url']; | |||||
| $obj->setObjectURI($uri); | |||||
| $login = $spec['login']; | |||||
| $obj->setDisplayName(pht('%s <%s>', $login, pht('GitHub'))); | |||||
| } | |||||
| } | |||||