Changeset View
Changeset View
Standalone View
Standalone View
src/applications/doorkeeper/bridge/DoorkeeperBridgeGitHub.php
- This file was added.
<?php | |||||
final class DoorkeeperBridgeGitHub extends DoorkeeperBridge { | |||||
const APPTYPE_GITHUB = 'github'; | |||||
const OBJTYPE_PULL_REQUEST = 'github:pull-request'; | |||||
public function canPullRef(DoorkeeperObjectRef $ref) { | |||||
if ($ref->getApplicationType() != self::APPTYPE_GITHUB) { | |||||
return false; | |||||
} | |||||
$types = array( | |||||
self::OBJTYPE_PULL_REQUEST => true, | |||||
); | |||||
return isset($types[$ref->getObjectType()]); | |||||
} | |||||
public function pullRefs(array $refs) { | |||||
$id_map = mpull($refs, 'getObjectID', 'getObjectKey'); | |||||
// Object IDs for pull requests are formatted as: | |||||
// | |||||
// user:repo:pullid | |||||
// | |||||
// since we need both the user and repo name to retrieve | |||||
// data about a pull request. | |||||
$data_map = array(); | |||||
foreach ($id_map as $key => $id) { | |||||
$split = explode(':', $id); | |||||
$user = str_replace($split[0], '/', ''); | |||||
$repo = str_replace($split[1], '/', ''); | |||||
$pull_id = (int)$split[2]; | |||||
$data_map[$key] = array( | |||||
'id' => $id, | |||||
'user' => $user, | |||||
'repo' => $repo, | |||||
'pullID' => $pull_id, | |||||
'getURI' => | |||||
'https://api.github.com/repos/'.$user.'/'.$repo.'/pulls/'.$pull_id); | |||||
} | |||||
// Query the GitHub public API, retrieving all of the information. | |||||
$futures = array(); | |||||
foreach ($data_map as $key => $data) { | |||||
$futures[$key] = id(new HTTPSFuture($data['getURI'])) | |||||
->setMethod('GET') | |||||
->setTimeout(30); | |||||
} | |||||
$results = array(); | |||||
$failed = array(); | |||||
foreach (Futures($futures) as $key => $future) { | |||||
try { | |||||
$results[$key] = $future->resolveJSON(); | |||||
} catch (Exception $ex) { | |||||
if (($ex instanceof HTTPFutureResponseStatus) && | |||||
($ex->getStatusCode() == 404)) { | |||||
// This indicates that the object has been deleted (or never existed, | |||||
// or isn't visible to the current user) but it's a successful sync of | |||||
// an object which isn't visible. | |||||
} else { | |||||
// This is something else, so consider it a synchronization failure. | |||||
phlog($ex); | |||||
$failed[$key] = $ex; | |||||
} | |||||
} | |||||
} | |||||
// Set all the associated data. | |||||
foreach ($refs as $ref) { | |||||
$did_fail = idx($failed, $ref->getObjectKey()); | |||||
if ($did_fail) { | |||||
// Set at least the key to identify, normally the | |||||
// name is the name of the PR. | |||||
$ref->setAttribute('name', 'GitHub PR '.$ref->getObjectID()); | |||||
$ref->setSyncFailed(true); | |||||
continue; | |||||
} | |||||
$result = idx($results, $ref->getObjectKey()); | |||||
if (!$result) { | |||||
continue; | |||||
} | |||||
$ref->setIsVisible(true); | |||||
$ref->setAttribute('name', $result['title']); | |||||
$ref->setAttribute('url', $result['url']); | |||||
$ref->setAttribute('diffURL', $result['diff_url']); | |||||
$ref->setAttribute('state', $result['state']); | |||||
$ref->setAttribute('userLogin', $result['user']['login']); | |||||
$ref->setAttribute('body', $result['body']); | |||||
$ref->setAttribute('headGitURL', $result['head']['repo']['git_url']); | |||||
$ref->setAttribute('headGitSHA', $result['head']['sha']); | |||||
$ref->setAttribute('headLabel', $result['head']['label']); | |||||
$ref->setAttribute('baseGitURL', $result['base']['repo']['git_url']); | |||||
hach-que: I'm not sure what the best option is here, both references and objects seem to accept arbitrary… | |||||
$ref->setAttribute('baseGitSHA', $result['base']['sha']); | |||||
$ref->setAttribute('baseLabel', $result['base']['label']); | |||||
$ref->setAttribute('merged', $result['merged']); | |||||
// TODO: Does the data belong on the ref attributes or the | |||||
// object properties?? | |||||
$obj = $ref->getExternalObject(); | |||||
if ($obj->getID()) { | |||||
continue; | |||||
} | |||||
$obj->setObjectURI($result['url']); | |||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); | |||||
$obj->save(); | |||||
unset($unguarded); | |||||
} | |||||
} | |||||
} | |||||
Not Done Inline ActionsSo here we should map the associated GitHub account name back to a Phabricator account with it connected if possible. There's a few problems, namely:
hach-que: So here we should map the associated GitHub account name back to a Phabricator account with it… |
I'm not sure what the best option is here, both references and objects seem to accept arbitrary data.
At the moment it doesn't matter too much (the synchronisation is from GitHub into Phabricator), but when we have a worker pushing Phabricator comments back to the GitHub PR it will be important.