mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-24 13:38:19 +01:00
Add DoorkeeperObjectRef, DoorkeeperBridge, DoorkeeperBridgeAsana
Summary: - `DoorkeeperObjectRef` is a convenience object to keep track of `<applicationType, applicationDomain, objectType, objectID>` tuples. - `DoorkeeperBridge` provides pull/push between Phabricator and external systems. - `DoorkeeperBridgeAsana` is a bridge to Asana. Test Plan: Ran this snippet and got a task from Asana: {P871} Reviewers: btrahan Reviewed By: btrahan CC: aran Differential Revision: https://secure.phabricator.com/D6273
This commit is contained in:
parent
f54a5d8087
commit
e723b7e119
7 changed files with 244 additions and 9 deletions
|
@ -533,8 +533,11 @@ phutil_register_library_map(array(
|
||||||
'DivinerRenderer' => 'applications/diviner/renderer/DivinerRenderer.php',
|
'DivinerRenderer' => 'applications/diviner/renderer/DivinerRenderer.php',
|
||||||
'DivinerStaticPublisher' => 'applications/diviner/publisher/DivinerStaticPublisher.php',
|
'DivinerStaticPublisher' => 'applications/diviner/publisher/DivinerStaticPublisher.php',
|
||||||
'DivinerWorkflow' => 'applications/diviner/workflow/DivinerWorkflow.php',
|
'DivinerWorkflow' => 'applications/diviner/workflow/DivinerWorkflow.php',
|
||||||
|
'DoorkeeperBridge' => 'applications/doorkeeper/bridge/DoorkeeperBridge.php',
|
||||||
|
'DoorkeeperBridgeAsana' => 'applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php',
|
||||||
'DoorkeeperDAO' => 'applications/doorkeeper/storage/DoorkeeperDAO.php',
|
'DoorkeeperDAO' => 'applications/doorkeeper/storage/DoorkeeperDAO.php',
|
||||||
'DoorkeeperExternalObject' => 'applications/doorkeeper/storage/DoorkeeperExternalObject.php',
|
'DoorkeeperExternalObject' => 'applications/doorkeeper/storage/DoorkeeperExternalObject.php',
|
||||||
|
'DoorkeeperObjectRef' => 'applications/doorkeeper/engine/DoorkeeperObjectRef.php',
|
||||||
'DrydockAllocatorWorker' => 'applications/drydock/worker/DrydockAllocatorWorker.php',
|
'DrydockAllocatorWorker' => 'applications/drydock/worker/DrydockAllocatorWorker.php',
|
||||||
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php',
|
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php',
|
||||||
'DrydockBlueprint' => 'applications/drydock/blueprint/DrydockBlueprint.php',
|
'DrydockBlueprint' => 'applications/drydock/blueprint/DrydockBlueprint.php',
|
||||||
|
@ -2406,12 +2409,15 @@ phutil_register_library_map(array(
|
||||||
'DivinerRemarkupRuleSymbol' => 'PhutilRemarkupRule',
|
'DivinerRemarkupRuleSymbol' => 'PhutilRemarkupRule',
|
||||||
'DivinerStaticPublisher' => 'DivinerPublisher',
|
'DivinerStaticPublisher' => 'DivinerPublisher',
|
||||||
'DivinerWorkflow' => 'PhutilArgumentWorkflow',
|
'DivinerWorkflow' => 'PhutilArgumentWorkflow',
|
||||||
|
'DoorkeeperBridge' => 'Phobject',
|
||||||
|
'DoorkeeperBridgeAsana' => 'DoorkeeperBridge',
|
||||||
'DoorkeeperDAO' => 'PhabricatorLiskDAO',
|
'DoorkeeperDAO' => 'PhabricatorLiskDAO',
|
||||||
'DoorkeeperExternalObject' =>
|
'DoorkeeperExternalObject' =>
|
||||||
array(
|
array(
|
||||||
0 => 'DoorkeeperDAO',
|
0 => 'DoorkeeperDAO',
|
||||||
1 => 'PhabricatorPolicyInterface',
|
1 => 'PhabricatorPolicyInterface',
|
||||||
),
|
),
|
||||||
|
'DoorkeeperObjectRef' => 'Phobject',
|
||||||
'DrydockAllocatorWorker' => 'PhabricatorWorker',
|
'DrydockAllocatorWorker' => 'PhabricatorWorker',
|
||||||
'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
|
'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
|
||||||
'DrydockCommandInterface' => 'DrydockInterface',
|
'DrydockCommandInterface' => 'DrydockInterface',
|
||||||
|
|
|
@ -281,4 +281,12 @@ abstract class PhabricatorAuthProviderOAuth extends PhabricatorAuthProvider {
|
||||||
return parent::renderConfigPropertyTransactionTitle($xaction);
|
return parent::renderConfigPropertyTransactionTitle($xaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function willSaveAccount(PhabricatorExternalAccount $account) {
|
||||||
|
parent::willSaveAccount($account);
|
||||||
|
|
||||||
|
$oauth_token = $this->getAdapter()->getAccessToken();
|
||||||
|
$account->setProperty('oauth.token', $oauth_token);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,4 +35,16 @@ final class PhabricatorAuthProviderOAuthAsana
|
||||||
return 'Asana';
|
return 'Asana';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getAsanaProvider() {
|
||||||
|
$providers = self::getAllEnabledProviders();
|
||||||
|
|
||||||
|
foreach ($providers as $provider) {
|
||||||
|
if ($provider instanceof PhabricatorAuthProviderOAuthAsana) {
|
||||||
|
return $provider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
23
src/applications/doorkeeper/bridge/DoorkeeperBridge.php
Normal file
23
src/applications/doorkeeper/bridge/DoorkeeperBridge.php
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class DoorkeeperBridge extends Phobject {
|
||||||
|
|
||||||
|
private $viewer;
|
||||||
|
|
||||||
|
final public function setViewer($viewer) {
|
||||||
|
$this->viewer = $viewer;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getViewer() {
|
||||||
|
return $this->viewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function canPullRef(DoorkeeperObjectRef $ref);
|
||||||
|
abstract public function pullRefs(array $refs);
|
||||||
|
|
||||||
|
}
|
79
src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php
Normal file
79
src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DoorkeeperBridgeAsana extends DoorkeeperBridge {
|
||||||
|
|
||||||
|
public function canPullRef(DoorkeeperObjectRef $ref) {
|
||||||
|
return ($ref->getApplicationType() == 'asana') &&
|
||||||
|
($ref->getApplicationDomain() == 'asana.com') &&
|
||||||
|
($ref->getObjectType() == 'asana:task');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pullRefs(array $refs) {
|
||||||
|
|
||||||
|
$id_map = mpull($refs, 'getObjectID', 'getObjectKey');
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$provider = PhabricatorAuthProviderOAuthAsana::getAsanaProvider();
|
||||||
|
if (!$provider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$accounts = id(new PhabricatorExternalAccountQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withUserPHIDs(array($viewer->getPHID()))
|
||||||
|
->withAccountTypes(array($provider->getProviderType()))
|
||||||
|
->withAccountDomains(array($provider->getProviderDomain()))
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
// TODO: If the user has several linked Asana accounts, we just pick the
|
||||||
|
// first one arbitrarily. We might want to try using all of them or do
|
||||||
|
// something with more finesse. There's no UI way to link multiple accounts
|
||||||
|
// right now so this is currently moot.
|
||||||
|
$account = head($accounts);
|
||||||
|
|
||||||
|
$token = $account->getProperty('oauth.token');
|
||||||
|
if (!$token) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$template = id(new PhutilAsanaFuture())
|
||||||
|
->setAccessToken($token);
|
||||||
|
|
||||||
|
$futures = array();
|
||||||
|
foreach ($id_map as $key => $id) {
|
||||||
|
$futures[$key] = id(clone $template)
|
||||||
|
->setRawAsanaQuery("tasks/{$id}");
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
foreach (Futures($futures) as $key => $future) {
|
||||||
|
$results[$key] = $future->resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($refs as $ref) {
|
||||||
|
$result = idx($results, $ref->getObjectKey());
|
||||||
|
if (!$result) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ref->setIsVisible(true);
|
||||||
|
$ref->setAttribute('asana.data', $result);
|
||||||
|
$ref->setAttribute('name', $result['name']);
|
||||||
|
$ref->setAttribute('description', $result['notes']);
|
||||||
|
|
||||||
|
$obj = $ref->getExternalObject();
|
||||||
|
if ($obj->getID()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $result['id'];
|
||||||
|
$uri = "https://app.asana.com/0/{$id}/{$id}";
|
||||||
|
$obj->setObjectURI($uri);
|
||||||
|
|
||||||
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||||
|
$obj->save();
|
||||||
|
unset($unguarded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
109
src/applications/doorkeeper/engine/DoorkeeperObjectRef.php
Normal file
109
src/applications/doorkeeper/engine/DoorkeeperObjectRef.php
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DoorkeeperObjectRef extends Phobject {
|
||||||
|
|
||||||
|
private $objectKey;
|
||||||
|
private $applicationType;
|
||||||
|
private $applicationDomain;
|
||||||
|
private $objectType;
|
||||||
|
private $objectID;
|
||||||
|
private $attributes = array();
|
||||||
|
private $isVisible;
|
||||||
|
private $externalObject;
|
||||||
|
|
||||||
|
public function newExternalObject() {
|
||||||
|
return id(new DoorkeeperExternalObject())
|
||||||
|
->setApplicationType($this->getApplicationType())
|
||||||
|
->setApplicationDomain($this->getApplicationDomain())
|
||||||
|
->setObjectType($this->getObjectType())
|
||||||
|
->setObjectID($this->getObjectID())
|
||||||
|
->setViewPolicy(PhabricatorPolicies::POLICY_USER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachExternalObject(
|
||||||
|
DoorkeeperExternalObject $external_object) {
|
||||||
|
$this->externalObject = $external_object;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExternalObject() {
|
||||||
|
if (!$this->externalObject) {
|
||||||
|
throw new Exception(
|
||||||
|
"Call attachExternalObject() before getExternalObject()!");
|
||||||
|
}
|
||||||
|
return $this->externalObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsVisible($is_visible) {
|
||||||
|
$this->isVisible = $is_visible;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsVisible() {
|
||||||
|
return $this->isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAttribute($key, $default = null) {
|
||||||
|
return idx($this->attribute, $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAttribute($key, $value) {
|
||||||
|
$this->attributes[$key] = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setObjectID($object_id) {
|
||||||
|
$this->objectID = $object_id;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getObjectID() {
|
||||||
|
return $this->objectID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function setObjectType($object_type) {
|
||||||
|
$this->objectType = $object_type;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getObjectType() {
|
||||||
|
return $this->objectType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function setApplicationDomain($application_domain) {
|
||||||
|
$this->applicationDomain = $application_domain;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationDomain() {
|
||||||
|
return $this->applicationDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function setApplicationType($application_type) {
|
||||||
|
$this->applicationType = $application_type;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationType() {
|
||||||
|
return $this->applicationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getObjectKey() {
|
||||||
|
if (!$this->objectKey) {
|
||||||
|
$this->objectKey = PhabricatorHash::digestForIndex(
|
||||||
|
implode(
|
||||||
|
':',
|
||||||
|
array(
|
||||||
|
$this->getApplicationType(),
|
||||||
|
$this->getApplicationDomain(),
|
||||||
|
$this->getObjectType(),
|
||||||
|
$this->getObjectID(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
return $this->objectKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -39,15 +39,12 @@ final class DoorkeeperExternalObject extends DoorkeeperDAO
|
||||||
public function getObjectKey() {
|
public function getObjectKey() {
|
||||||
$key = parent::getObjectKey();
|
$key = parent::getObjectKey();
|
||||||
if ($key === null) {
|
if ($key === null) {
|
||||||
$key = PhabricatorHash::digestForIndex(
|
$key = id(new DoorkeeperObjectRef())
|
||||||
implode(
|
->setApplicationType($this->getApplicationType())
|
||||||
':',
|
->setApplicationDomain($this->getApplicationDomain())
|
||||||
array(
|
->setObjectType($this->getObjectType())
|
||||||
$this->getApplicationType(),
|
->setObjectID($this->getObjectID())
|
||||||
$this->getApplicationDomain(),
|
->getObjectKey();
|
||||||
$this->getObjectType(),
|
|
||||||
$this->getObjectID(),
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
return $key;
|
return $key;
|
||||||
}
|
}
|
||||||
|
@ -56,6 +53,7 @@ final class DoorkeeperExternalObject extends DoorkeeperDAO
|
||||||
if (!$this->objectKey) {
|
if (!$this->objectKey) {
|
||||||
$this->objectKey = $this->getObjectKey();
|
$this->objectKey = $this->getObjectKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::save();
|
return parent::save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue