From e723b7e119a788154e31e0cd51308385132331b0 Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 24 Jun 2013 15:54:54 -0700 Subject: [PATCH] Add DoorkeeperObjectRef, DoorkeeperBridge, DoorkeeperBridgeAsana Summary: - `DoorkeeperObjectRef` is a convenience object to keep track of `` 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 --- src/__phutil_library_map__.php | 6 + .../provider/PhabricatorAuthProviderOAuth.php | 8 ++ .../PhabricatorAuthProviderOAuthAsana.php | 12 ++ .../doorkeeper/bridge/DoorkeeperBridge.php | 23 ++++ .../bridge/DoorkeeperBridgeAsana.php | 79 +++++++++++++ .../doorkeeper/engine/DoorkeeperObjectRef.php | 109 ++++++++++++++++++ .../storage/DoorkeeperExternalObject.php | 16 ++- 7 files changed, 244 insertions(+), 9 deletions(-) create mode 100644 src/applications/doorkeeper/bridge/DoorkeeperBridge.php create mode 100644 src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php create mode 100644 src/applications/doorkeeper/engine/DoorkeeperObjectRef.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2514a3e624..b3ac5f457c 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -533,8 +533,11 @@ phutil_register_library_map(array( 'DivinerRenderer' => 'applications/diviner/renderer/DivinerRenderer.php', 'DivinerStaticPublisher' => 'applications/diviner/publisher/DivinerStaticPublisher.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', 'DoorkeeperExternalObject' => 'applications/doorkeeper/storage/DoorkeeperExternalObject.php', + 'DoorkeeperObjectRef' => 'applications/doorkeeper/engine/DoorkeeperObjectRef.php', 'DrydockAllocatorWorker' => 'applications/drydock/worker/DrydockAllocatorWorker.php', 'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php', 'DrydockBlueprint' => 'applications/drydock/blueprint/DrydockBlueprint.php', @@ -2406,12 +2409,15 @@ phutil_register_library_map(array( 'DivinerRemarkupRuleSymbol' => 'PhutilRemarkupRule', 'DivinerStaticPublisher' => 'DivinerPublisher', 'DivinerWorkflow' => 'PhutilArgumentWorkflow', + 'DoorkeeperBridge' => 'Phobject', + 'DoorkeeperBridgeAsana' => 'DoorkeeperBridge', 'DoorkeeperDAO' => 'PhabricatorLiskDAO', 'DoorkeeperExternalObject' => array( 0 => 'DoorkeeperDAO', 1 => 'PhabricatorPolicyInterface', ), + 'DoorkeeperObjectRef' => 'Phobject', 'DrydockAllocatorWorker' => 'PhabricatorWorker', 'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface', 'DrydockCommandInterface' => 'DrydockInterface', diff --git a/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php b/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php index 6ae04d59e9..a945a8760c 100644 --- a/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php +++ b/src/applications/auth/provider/PhabricatorAuthProviderOAuth.php @@ -281,4 +281,12 @@ abstract class PhabricatorAuthProviderOAuth extends PhabricatorAuthProvider { return parent::renderConfigPropertyTransactionTitle($xaction); } + protected function willSaveAccount(PhabricatorExternalAccount $account) { + parent::willSaveAccount($account); + + $oauth_token = $this->getAdapter()->getAccessToken(); + $account->setProperty('oauth.token', $oauth_token); + + } + } diff --git a/src/applications/auth/provider/PhabricatorAuthProviderOAuthAsana.php b/src/applications/auth/provider/PhabricatorAuthProviderOAuthAsana.php index e0c4595066..a929c8f26e 100644 --- a/src/applications/auth/provider/PhabricatorAuthProviderOAuthAsana.php +++ b/src/applications/auth/provider/PhabricatorAuthProviderOAuthAsana.php @@ -35,4 +35,16 @@ final class PhabricatorAuthProviderOAuthAsana return 'Asana'; } + public static function getAsanaProvider() { + $providers = self::getAllEnabledProviders(); + + foreach ($providers as $provider) { + if ($provider instanceof PhabricatorAuthProviderOAuthAsana) { + return $provider; + } + } + + return null; + } + } diff --git a/src/applications/doorkeeper/bridge/DoorkeeperBridge.php b/src/applications/doorkeeper/bridge/DoorkeeperBridge.php new file mode 100644 index 0000000000..5a7459ab5a --- /dev/null +++ b/src/applications/doorkeeper/bridge/DoorkeeperBridge.php @@ -0,0 +1,23 @@ +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); + +} diff --git a/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php b/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php new file mode 100644 index 0000000000..36722ccd91 --- /dev/null +++ b/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php @@ -0,0 +1,79 @@ +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); + } + } + +} diff --git a/src/applications/doorkeeper/engine/DoorkeeperObjectRef.php b/src/applications/doorkeeper/engine/DoorkeeperObjectRef.php new file mode 100644 index 0000000000..f88ea97b33 --- /dev/null +++ b/src/applications/doorkeeper/engine/DoorkeeperObjectRef.php @@ -0,0 +1,109 @@ +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; + } + +} diff --git a/src/applications/doorkeeper/storage/DoorkeeperExternalObject.php b/src/applications/doorkeeper/storage/DoorkeeperExternalObject.php index 3d240bdeee..acdf040029 100644 --- a/src/applications/doorkeeper/storage/DoorkeeperExternalObject.php +++ b/src/applications/doorkeeper/storage/DoorkeeperExternalObject.php @@ -39,15 +39,12 @@ final class DoorkeeperExternalObject extends DoorkeeperDAO public function getObjectKey() { $key = parent::getObjectKey(); if ($key === null) { - $key = PhabricatorHash::digestForIndex( - implode( - ':', - array( - $this->getApplicationType(), - $this->getApplicationDomain(), - $this->getObjectType(), - $this->getObjectID(), - ))); + $key = id(new DoorkeeperObjectRef()) + ->setApplicationType($this->getApplicationType()) + ->setApplicationDomain($this->getApplicationDomain()) + ->setObjectType($this->getObjectType()) + ->setObjectID($this->getObjectID()) + ->getObjectKey(); } return $key; } @@ -56,6 +53,7 @@ final class DoorkeeperExternalObject extends DoorkeeperDAO if (!$this->objectKey) { $this->objectKey = $this->getObjectKey(); } + return parent::save(); }