Changeset View
Changeset View
Standalone View
Standalone View
src/applications/nuance/item/NuanceGitHubEventItemType.php
| <?php | <?php | ||||
| final class NuanceGitHubEventItemType | final class NuanceGitHubEventItemType | ||||
| extends NuanceItemType { | extends NuanceItemType { | ||||
| const ITEMTYPE = 'github.event'; | const ITEMTYPE = 'github.event'; | ||||
| private $externalObject; | |||||
| public function getItemTypeDisplayName() { | public function getItemTypeDisplayName() { | ||||
| return pht('GitHub Event'); | return pht('GitHub Event'); | ||||
| } | } | ||||
| public function getItemTypeDisplayIcon() { | public function getItemTypeDisplayIcon() { | ||||
| return 'fa-github'; | return 'fa-github'; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | final class NuanceGitHubEventItemType | ||||
| } | } | ||||
| protected function updateItemFromSource(NuanceItem $item) { | protected function updateItemFromSource(NuanceItem $item) { | ||||
| $viewer = $this->getViewer(); | $viewer = $this->getViewer(); | ||||
| $is_dirty = false; | $is_dirty = false; | ||||
| // TODO: Link up the requestor, etc. | // TODO: Link up the requestor, etc. | ||||
| $source = $item->getSource(); | $is_dirty = false; | ||||
| $token = $source->getSourceProperty('github.token'); | |||||
| $token = new PhutilOpaqueEnvelope($token); | |||||
| $ref = $this->getDoorkeeperRef($item); | |||||
| if ($ref) { | |||||
| $ref = id(new DoorkeeperImportEngine()) | |||||
| ->setViewer($viewer) | |||||
| ->setRefs(array($ref)) | |||||
| ->setThrowOnMissingLink(true) | |||||
| ->setContextProperty('github.token', $token) | |||||
| ->executeOne(); | |||||
| if ($ref->getSyncFailed()) { | $xobj = $this->reloadExternalObject($item); | ||||
| $xobj = null; | |||||
| } else { | |||||
| $xobj = $ref->getExternalObject(); | |||||
| } | |||||
| if ($xobj) { | if ($xobj) { | ||||
| $item->setItemProperty('doorkeeper.xobj.phid', $xobj->getPHID()); | $item->setItemProperty('doorkeeper.xobj.phid', $xobj->getPHID()); | ||||
| $is_dirty = true; | $is_dirty = true; | ||||
| } | } | ||||
| } | |||||
| if ($item->getStatus() == NuanceItem::STATUS_IMPORTING) { | if ($item->getStatus() == NuanceItem::STATUS_IMPORTING) { | ||||
| $item->setStatus(NuanceItem::STATUS_ROUTING); | $item->setStatus(NuanceItem::STATUS_ROUTING); | ||||
| $is_dirty = true; | $is_dirty = true; | ||||
| } | } | ||||
| if ($is_dirty) { | if ($is_dirty) { | ||||
| $item->save(); | $item->save(); | ||||
| Show All 18 Lines | private function getDoorkeeperRef(NuanceItem $item) { | ||||
| return id(new DoorkeeperObjectRef()) | return id(new DoorkeeperObjectRef()) | ||||
| ->setApplicationType(DoorkeeperBridgeGitHub::APPTYPE_GITHUB) | ->setApplicationType(DoorkeeperBridgeGitHub::APPTYPE_GITHUB) | ||||
| ->setApplicationDomain(DoorkeeperBridgeGitHub::APPDOMAIN_GITHUB) | ->setApplicationDomain(DoorkeeperBridgeGitHub::APPDOMAIN_GITHUB) | ||||
| ->setObjectType($ref_type) | ->setObjectType($ref_type) | ||||
| ->setObjectID($full_ref); | ->setObjectID($full_ref); | ||||
| } | } | ||||
| private function reloadExternalObject(NuanceItem $item, $local = false) { | |||||
| $ref = $this->getDoorkeeperRef($item); | |||||
| if (!$ref) { | |||||
| return null; | |||||
| } | |||||
| $source = $item->getSource(); | |||||
| $token = $source->getSourceProperty('github.token'); | |||||
| $token = new PhutilOpaqueEnvelope($token); | |||||
| $viewer = $this->getViewer(); | |||||
| $ref = id(new DoorkeeperImportEngine()) | |||||
| ->setViewer($viewer) | |||||
| ->setRefs(array($ref)) | |||||
| ->setThrowOnMissingLink(true) | |||||
| ->setContextProperty('github.token', $token) | |||||
| ->needLocalOnly($local) | |||||
| ->executeOne(); | |||||
| if ($ref->getSyncFailed()) { | |||||
| $xobj = null; | |||||
| } else { | |||||
| $xobj = $ref->getExternalObject(); | |||||
| } | |||||
| if ($xobj) { | |||||
| $this->externalObject = $xobj; | |||||
| } | |||||
| return $xobj; | |||||
| } | |||||
| private function getExternalObject(NuanceItem $item) { | |||||
| if ($this->externalObject === null) { | |||||
| $xobj = $this->reloadExternalObject($item, $local = true); | |||||
| if ($xobj) { | |||||
| $this->externalObject = $xobj; | |||||
| } else { | |||||
| $this->externalObject = false; | |||||
| } | |||||
| } | |||||
| if ($this->externalObject) { | |||||
| return $this->externalObject; | |||||
| } | |||||
| return null; | |||||
| } | |||||
| private function newRawEvent(NuanceItem $item) { | private function newRawEvent(NuanceItem $item) { | ||||
| $type = $item->getItemProperty('api.type'); | $type = $item->getItemProperty('api.type'); | ||||
| $raw = $item->getItemProperty('api.raw', array()); | $raw = $item->getItemProperty('api.raw', array()); | ||||
| return NuanceGitHubRawEvent::newEvent($type, $raw); | return NuanceGitHubRawEvent::newEvent($type, $raw); | ||||
| } | } | ||||
| public function getItemActions(NuanceItem $item) { | public function getItemActions(NuanceItem $item) { | ||||
| $actions = array(); | $actions = array(); | ||||
| $xobj = $this->getExternalObject($item); | |||||
| if ($xobj) { | |||||
| $actions[] = $this->newItemAction($item, 'reload') | |||||
| ->setName(pht('Reload from GitHub')) | |||||
| ->setIcon('fa-refresh') | |||||
| ->setWorkflow(true) | |||||
| ->setRenderAsForm(true); | |||||
| } | |||||
| $actions[] = $this->newItemAction($item, 'sync') | $actions[] = $this->newItemAction($item, 'sync') | ||||
| ->setName(pht('Import to Maniphest')) | ->setName(pht('Import to Maniphest')) | ||||
| ->setIcon('fa-anchor') | ->setIcon('fa-anchor') | ||||
| ->setWorkflow(true) | ->setWorkflow(true) | ||||
| ->setRenderAsForm(true); | ->setRenderAsForm(true); | ||||
| $actions[] = $this->newItemAction($item, 'raw') | $actions[] = $this->newItemAction($item, 'raw') | ||||
| ->setName(pht('View Raw Event')) | ->setName(pht('View Raw Event')) | ||||
| Show All 26 Lines | switch ($action) { | ||||
| ->appendChild($raw_box); | ->appendChild($raw_box); | ||||
| return $controller->newDialog() | return $controller->newDialog() | ||||
| ->setWidth(AphrontDialogView::WIDTH_FULL) | ->setWidth(AphrontDialogView::WIDTH_FULL) | ||||
| ->setTitle(pht('GitHub Raw Event')) | ->setTitle(pht('GitHub Raw Event')) | ||||
| ->appendForm($form) | ->appendForm($form) | ||||
| ->addCancelButton($item->getURI(), pht('Done')); | ->addCancelButton($item->getURI(), pht('Done')); | ||||
| case 'sync': | case 'sync': | ||||
| case 'reload': | |||||
| $item->issueCommand($viewer->getPHID(), $action); | $item->issueCommand($viewer->getPHID(), $action); | ||||
| return id(new AphrontRedirectResponse())->setURI($item->getURI()); | return id(new AphrontRedirectResponse())->setURI($item->getURI()); | ||||
| } | } | ||||
| return null; | return null; | ||||
| } | } | ||||
| protected function newItemView(NuanceItem $item) { | protected function newItemView(NuanceItem $item) { | ||||
| Show All 33 Lines | private function newGitHubEventItemPropertyBox($item) { | ||||
| } | } | ||||
| return id(new PHUIObjectBoxView()) | return id(new PHUIObjectBoxView()) | ||||
| ->setHeaderText(pht('Event Properties')) | ->setHeaderText(pht('Event Properties')) | ||||
| ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) | ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) | ||||
| ->appendChild($property_list); | ->appendChild($property_list); | ||||
| } | } | ||||
| protected function handleCommand(NuanceItem $item, $action) { | protected function handleCommand( | ||||
| NuanceItem $item, | |||||
| NuanceItemCommand $command) { | |||||
| $action = $command->getCommand(); | |||||
| switch ($action) { | |||||
| case 'sync': | |||||
| return $this->syncItem($item, $command); | |||||
| case 'reload': | |||||
| $this->reloadExternalObject($item); | |||||
| return true; | |||||
| } | |||||
| return null; | return null; | ||||
| } | } | ||||
| private function syncItem( | |||||
| NuanceItem $item, | |||||
| NuanceItemCommand $command) { | |||||
| $xobj_phid = $item->getItemProperty('doorkeeper.xobj.phid'); | |||||
| if (!$xobj_phid) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Unable to sync: no external object PHID.')); | |||||
| } | |||||
| // TODO: Write some kind of marker to prevent double-synchronization. | |||||
| $viewer = $this->getViewer(); | |||||
| $xobj = id(new DoorkeeperExternalObjectQuery()) | |||||
| ->setViewer($viewer) | |||||
| ->withPHIDs(array($xobj_phid)) | |||||
| ->executeOne(); | |||||
| if (!$xobj) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Unable to sync: failed to load object "%s".', | |||||
| $xobj_phid)); | |||||
| } | |||||
| $nuance_phid = id(new PhabricatorNuanceApplication())->getPHID(); | |||||
| $xactions = array(); | |||||
| $task = id(new ManiphestTaskQuery()) | |||||
| ->setViewer($viewer) | |||||
| ->withBridgedObjectPHIDs(array($xobj_phid)) | |||||
| ->executeOne(); | |||||
| if (!$task) { | |||||
| $task = ManiphestTask::initializeNewTask($viewer) | |||||
| ->setAuthorPHID($nuance_phid) | |||||
| ->setBridgedObjectPHID($xobj_phid); | |||||
| $title = $xobj->getProperty('task.title'); | |||||
| if (!strlen($title)) { | |||||
| $title = pht('Nuance Item %d Task', $item->getID()); | |||||
| } | |||||
| $description = $xobj->getProperty('task.description'); | |||||
| $created = $xobj->getProperty('task.created'); | |||||
| $state = $xobj->getProperty('task.state'); | |||||
| $xactions[] = id(new ManiphestTransaction()) | |||||
| ->setTransactionType(ManiphestTransaction::TYPE_TITLE) | |||||
| ->setNewValue($title) | |||||
| ->setDateCreated($created); | |||||
| $xactions[] = id(new ManiphestTransaction()) | |||||
| ->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION) | |||||
| ->setNewValue($description) | |||||
| ->setDateCreated($created); | |||||
| $task->setDateCreated($created); | |||||
| // TODO: Synchronize state. | |||||
| } | |||||
| $event = $this->newRawEvent($item); | |||||
| $comment = $event->getComment(); | |||||
| if (strlen($comment)) { | |||||
| $xactions[] = id(new ManiphestTransaction()) | |||||
| ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) | |||||
| ->attachComment( | |||||
| id(new ManiphestTransactionComment()) | |||||
| ->setContent($comment)); | |||||
| } | |||||
| // TODO: Preserve the item's original source. | |||||
| $source = PhabricatorContentSource::newForSource( | |||||
| PhabricatorContentSource::SOURCE_DAEMON, | |||||
| array()); | |||||
| // TOOD: This should really be the external source. | |||||
| $acting_phid = $nuance_phid; | |||||
| $editor = id(new ManiphestTransactionEditor()) | |||||
| ->setActor($viewer) | |||||
| ->setActingAsPHID($acting_phid) | |||||
| ->setContentSource($source) | |||||
| ->setContinueOnNoEffect(true) | |||||
| ->setContinueOnMissingFields(true); | |||||
| $xactions = $editor->applyTransactions($task, $xactions); | |||||
| return array( | |||||
| 'objectPHID' => $task->getPHID(), | |||||
| 'xactionPHIDs' => mpull($xactions, 'getPHID'), | |||||
| ); | |||||
| } | |||||
| } | } | ||||