diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1420,6 +1420,7 @@ 'NuanceConsoleController' => 'applications/nuance/controller/NuanceConsoleController.php', 'NuanceController' => 'applications/nuance/controller/NuanceController.php', 'NuanceDAO' => 'applications/nuance/storage/NuanceDAO.php', + 'NuanceGitHubEventItemType' => 'applications/nuance/item/NuanceGitHubEventItemType.php', 'NuanceGitHubRepositoryImportCursor' => 'applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php', 'NuanceGitHubRepositorySourceDefinition' => 'applications/nuance/source/NuanceGitHubRepositorySourceDefinition.php', 'NuanceImportCursor' => 'applications/nuance/cursor/NuanceImportCursor.php', @@ -1437,8 +1438,11 @@ 'NuanceItemTransaction' => 'applications/nuance/storage/NuanceItemTransaction.php', 'NuanceItemTransactionComment' => 'applications/nuance/storage/NuanceItemTransactionComment.php', 'NuanceItemTransactionQuery' => 'applications/nuance/query/NuanceItemTransactionQuery.php', + 'NuanceItemType' => 'applications/nuance/item/NuanceItemType.php', + 'NuanceItemUpdateWorker' => 'applications/nuance/worker/NuanceItemUpdateWorker.php', 'NuanceItemViewController' => 'applications/nuance/controller/NuanceItemViewController.php', 'NuanceManagementImportWorkflow' => 'applications/nuance/management/NuanceManagementImportWorkflow.php', + 'NuanceManagementUpdateWorkflow' => 'applications/nuance/management/NuanceManagementUpdateWorkflow.php', 'NuanceManagementWorkflow' => 'applications/nuance/management/NuanceManagementWorkflow.php', 'NuancePhabricatorFormSourceDefinition' => 'applications/nuance/source/NuancePhabricatorFormSourceDefinition.php', 'NuanceQuery' => 'applications/nuance/query/NuanceQuery.php', @@ -1488,6 +1492,7 @@ 'NuanceSourceTransactionQuery' => 'applications/nuance/query/NuanceSourceTransactionQuery.php', 'NuanceSourceViewController' => 'applications/nuance/controller/NuanceSourceViewController.php', 'NuanceTransaction' => 'applications/nuance/storage/NuanceTransaction.php', + 'NuanceWorker' => 'applications/nuance/worker/NuanceWorker.php', 'OwnersConduitAPIMethod' => 'applications/owners/conduit/OwnersConduitAPIMethod.php', 'OwnersEditConduitAPIMethod' => 'applications/owners/conduit/OwnersEditConduitAPIMethod.php', 'OwnersPackageReplyHandler' => 'applications/owners/mail/OwnersPackageReplyHandler.php', @@ -5671,6 +5676,7 @@ 'NuanceConsoleController' => 'NuanceController', 'NuanceController' => 'PhabricatorController', 'NuanceDAO' => 'PhabricatorLiskDAO', + 'NuanceGitHubEventItemType' => 'NuanceItemType', 'NuanceGitHubRepositoryImportCursor' => 'NuanceImportCursor', 'NuanceGitHubRepositorySourceDefinition' => 'NuanceSourceDefinition', 'NuanceImportCursor' => 'Phobject', @@ -5695,8 +5701,11 @@ 'NuanceItemTransaction' => 'NuanceTransaction', 'NuanceItemTransactionComment' => 'PhabricatorApplicationTransactionComment', 'NuanceItemTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'NuanceItemType' => 'Phobject', + 'NuanceItemUpdateWorker' => 'NuanceWorker', 'NuanceItemViewController' => 'NuanceController', 'NuanceManagementImportWorkflow' => 'NuanceManagementWorkflow', + 'NuanceManagementUpdateWorkflow' => 'NuanceManagementWorkflow', 'NuanceManagementWorkflow' => 'PhabricatorManagementWorkflow', 'NuancePhabricatorFormSourceDefinition' => 'NuanceSourceDefinition', 'NuanceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', @@ -5759,6 +5768,7 @@ 'NuanceSourceTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'NuanceSourceViewController' => 'NuanceSourceController', 'NuanceTransaction' => 'PhabricatorApplicationTransaction', + 'NuanceWorker' => 'PhabricatorWorker', 'OwnersConduitAPIMethod' => 'ConduitAPIMethod', 'OwnersEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'OwnersPackageReplyHandler' => 'PhabricatorMailReplyHandler', diff --git a/src/applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php b/src/applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php --- a/src/applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php +++ b/src/applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php @@ -166,14 +166,7 @@ $source->saveTransaction(); foreach ($new_items as $new_item) { - PhabricatorWorker::scheduleTask( - 'NuanceImportWorker', - array( - 'itemPHID' => $new_item->getPHID(), - ), - array( - 'objectPHID' => $new_item->getPHID(), - )); + $new_item->scheduleUpdate(); } return false; @@ -256,7 +249,7 @@ return NuanceItem::initializeNewItem() ->setStatus(NuanceItem::STATUS_IMPORTING) ->setSourcePHID($source->getPHID()) - ->setItemType('github.event') + ->setItemType(NuanceGitHubEventItemType::ITEMTYPE) ->setItemKey($item_key) ->setItemContainerKey($container_key) ->setItemProperty('api.raw', $record); diff --git a/src/applications/nuance/item/NuanceGitHubEventItemType.php b/src/applications/nuance/item/NuanceGitHubEventItemType.php new file mode 100644 --- /dev/null +++ b/src/applications/nuance/item/NuanceGitHubEventItemType.php @@ -0,0 +1,22 @@ +getStatus() == NuanceItem::STATUS_IMPORTING) { + $item + ->setStatus(NuanceItem::STATUS_ROUTING) + ->save(); + } + } + +} diff --git a/src/applications/nuance/item/NuanceItemType.php b/src/applications/nuance/item/NuanceItemType.php new file mode 100644 --- /dev/null +++ b/src/applications/nuance/item/NuanceItemType.php @@ -0,0 +1,37 @@ +canUpdateItems()) { + throw new Exception( + pht( + 'This item type ("%s", of class "%s") can not update items.', + $this->getItemTypeConstant(), + get_class($this))); + } + + $this->updateItemFromSource($item); + } + + protected function updateItemFromSource(NuanceItem $item) { + throw new PhutilMethodNotImplementedException(); + } + + final public function getItemTypeConstant() { + return $this->getPhobjectClassConstant('ITEMTYPE', 64); + } + + final public static function getAllItemTypes() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getItemTypeConstant') + ->execute(); + } + +} diff --git a/src/applications/nuance/management/NuanceManagementImportWorkflow.php b/src/applications/nuance/management/NuanceManagementImportWorkflow.php --- a/src/applications/nuance/management/NuanceManagementImportWorkflow.php +++ b/src/applications/nuance/management/NuanceManagementImportWorkflow.php @@ -6,7 +6,7 @@ protected function didConstruct() { $this ->setName('import') - ->setExamples('**import** [__options__]') + ->setExamples('**import** --source __source__ [__options__]') ->setSynopsis(pht('Import data from a source.')) ->setArguments( array( diff --git a/src/applications/nuance/management/NuanceManagementUpdateWorkflow.php b/src/applications/nuance/management/NuanceManagementUpdateWorkflow.php new file mode 100644 --- /dev/null +++ b/src/applications/nuance/management/NuanceManagementUpdateWorkflow.php @@ -0,0 +1,30 @@ +setName('update') + ->setExamples('**update** --item __item__ [__options__]') + ->setSynopsis(pht('Update or route an item.')) + ->setArguments( + array( + array( + 'name' => 'item', + 'param' => 'item', + 'help' => pht('Choose which item to route.'), + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $item = $this->loadItem($args, 'item'); + + PhabricatorWorker::setRunAllTasksInProcess(true); + $item->scheduleUpdate(); + + return 0; + } + +} diff --git a/src/applications/nuance/management/NuanceManagementWorkflow.php b/src/applications/nuance/management/NuanceManagementWorkflow.php --- a/src/applications/nuance/management/NuanceManagementWorkflow.php +++ b/src/applications/nuance/management/NuanceManagementWorkflow.php @@ -64,4 +64,54 @@ return head($sources); } + protected function loadITem(PhutilArgumentParser $argv, $key) { + $item = $argv->getArg($key); + if (!strlen($item)) { + throw new PhutilArgumentUsageException( + pht( + 'Specify a item with %s.', + '--'.$key)); + } + + $query = id(new NuanceItemQuery()) + ->setViewer($this->getViewer()) + ->setRaisePolicyExceptions(true); + + $type_unknown = PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN; + + if (ctype_digit($item)) { + $kind = 'id'; + $query->withIDs(array($item)); + } else if (phid_get_type($item) !== $type_unknown) { + $kind = 'phid'; + $query->withPHIDs($item); + } else { + throw new PhutilArgumentUsageException( + pht( + 'Specify the ID or PHID of an item to update. Parameter "%s" '. + 'is not an ID or PHID.', + $item)); + } + + $items = $query->execute(); + + if (!$items) { + switch ($kind) { + case 'id': + $message = pht( + 'No item exists with ID "%s".', + $item); + break; + case 'phid': + $message = pht( + 'No item exists with PHID "%s".', + $item); + break; + } + + throw new PhutilArgumentUsageException($message); + } + + return head($items); + } } diff --git a/src/applications/nuance/query/NuanceItemQuery.php b/src/applications/nuance/query/NuanceItemQuery.php --- a/src/applications/nuance/query/NuanceItemQuery.php +++ b/src/applications/nuance/query/NuanceItemQuery.php @@ -70,6 +70,17 @@ $item->attachSource($source); } + $type_map = NuanceItemType::getAllItemTypes(); + foreach ($items as $key => $item) { + $type = idx($type_map, $item->getItemType()); + if (!$type) { + $this->didRejectResult($items[$key]); + unset($items[$key]); + continue; + } + $item->attachImplementation($type); + } + return $items; } diff --git a/src/applications/nuance/storage/NuanceItem.php b/src/applications/nuance/storage/NuanceItem.php --- a/src/applications/nuance/storage/NuanceItem.php +++ b/src/applications/nuance/storage/NuanceItem.php @@ -7,6 +7,7 @@ PhabricatorApplicationTransactionInterface { const STATUS_IMPORTING = 'importing'; + const STATUS_ROUTING = 'routing'; const STATUS_OPEN = 'open'; const STATUS_ASSIGNED = 'assigned'; const STATUS_CLOSED = 'closed'; @@ -23,6 +24,7 @@ protected $mailKey; private $source = self::ATTACHABLE; + private $implementation = self::ATTACHABLE; public static function initializeNewItem() { return id(new NuanceItem()) @@ -145,6 +147,26 @@ return pht('An Item'); } + public function scheduleUpdate() { + PhabricatorWorker::scheduleTask( + 'NuanceItemUpdateWorker', + array( + 'itemPHID' => $this->getPHID(), + ), + array( + 'objectPHID' => $this->getPHID(), + )); + } + + public function getImplementation() { + return $this->assertAttached($this->implementation); + } + + public function attachImplementation(NuanceItemType $type) { + $this->implementation = $type; + return $this; + } + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ diff --git a/src/applications/nuance/worker/NuanceItemUpdateWorker.php b/src/applications/nuance/worker/NuanceItemUpdateWorker.php new file mode 100644 --- /dev/null +++ b/src/applications/nuance/worker/NuanceItemUpdateWorker.php @@ -0,0 +1,49 @@ +getTaskDataValue('itemPHID'); + + $hash = PhabricatorHash::digestForIndex($item_phid); + $lock_key = "nuance.item.{$hash}"; + $lock = PhabricatorGlobalLock::newLock($lock_key); + + $lock->lock(1); + try { + $item = $this->loadItem($item_phid); + $this->updateItem($item); + $this->routeItem($item); + } catch (Exception $ex) { + $lock->unlock(); + throw $ex; + } + + $lock->unlock(); + } + + private function updateItem(NuanceItem $item) { + $impl = $item->getImplementation(); + if ($impl->canUpdateItems()) { + $impl->updateItem($item); + } + } + + private function routeItem(NuanceItem $item) { + $status = $item->getStatus(); + if ($status != NuanceItem::STATUS_ROUTING) { + return; + } + + $source = $item->getSource(); + + // For now, always route items into the source's default queue. + + $item + ->setQueuePHID($source->getDefaultQueuePHID()) + ->setStatus(NuanceItem::STATUS_OPEN) + ->save(); + } + +} diff --git a/src/applications/nuance/worker/NuanceWorker.php b/src/applications/nuance/worker/NuanceWorker.php new file mode 100644 --- /dev/null +++ b/src/applications/nuance/worker/NuanceWorker.php @@ -0,0 +1,25 @@ +setViewer($this->getViewer()) + ->withPHIDs(array($item_phid)) + ->executeOne(); + + if (!$item) { + throw new PhabricatorWorkerPermanentFailureException( + pht( + 'There is no Nuance item with PHID "%s".', + $item_phid)); + } + + return $item; + } + +}