1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-03 20:22:46 +01:00
phorge-phorge/src/applications/maniphest/conduit/ManiphestConduitAPIMethod.php

326 lines
10 KiB
PHP
Raw Normal View History

<?php
abstract class ManiphestConduitAPIMethod extends ConduitAPIMethod {
final public function getApplication() {
return PhabricatorApplication::getByClass(
'PhabricatorManiphestApplication');
}
protected function defineErrorTypes() {
return array(
'ERR-INVALID-PARAMETER' => pht('Missing or malformed parameter.'),
);
}
protected function buildTaskInfoDictionary(ManiphestTask $task) {
$results = $this->buildTaskInfoDictionaries(array($task));
return idx($results, $task->getPHID());
}
protected function getTaskFields($is_new) {
$fields = array();
if (!$is_new) {
$fields += array(
'id' => 'optional int',
'phid' => 'optional int',
);
}
$fields += array(
'title' => $is_new ? 'required string' : 'optional string',
'description' => 'optional string',
'ownerPHID' => 'optional phid',
'viewPolicy' => 'optional phid or policy string',
'editPolicy' => 'optional phid or policy string',
'ccPHIDs' => 'optional list<phid>',
'priority' => 'optional int',
'projectPHIDs' => 'optional list<phid>',
'auxiliary' => 'optional dict',
);
if (!$is_new) {
$fields += array(
'status' => 'optional string',
'comments' => 'optional string',
);
}
return $fields;
}
protected function applyRequest(
ManiphestTask $task,
ConduitAPIRequest $request,
$is_new) {
$changes = array();
if ($is_new) {
$task->setTitle((string)$request->getValue('title'));
$task->setDescription((string)$request->getValue('description'));
$changes[ManiphestTransaction::TYPE_STATUS] =
ManiphestTaskStatus::getDefaultStatus();
$changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] =
array('+' => array($request->getUser()->getPHID()));
} else {
$comments = $request->getValue('comments');
if (!$is_new && $comments !== null) {
Migrate all Maniphest transaction data to new storage Summary: Ref T2217. This is the risky, hard part; everything after this should be smooth sailing. This is //mostly// clean, except: - The old format would opportunistically combine a comment with some other transaction type if it could. We no longer do that, so: - When migrating, "edit" + "comment" transactions need to be split in two. - When editing now, we should no longer combine these transaction types. - These changes are pretty straightforward and low-impact. - This migration promotes "auxiliary field" data to the new CustomField/StandardField format, so that's not a straight migration either. The formats are very similar, though. Broadly, this takes the same attack that the auth migration did: proxy all the code through to the new storage. `ManiphestTransaction` is now just an API on top of `ManiphestTransactionPro`, which is the new storage format. The two formats are very similar, so this was mostly a straight copy from one table to the other. Test Plan: - Without performing the migration, made a bunch of edits and comments on tasks and verified the new code works correctly. - Droped the test data and performed the migration. - Looked at the resulting data for obvious discrepancies. - Looked at a bunch of tasks and their transaction history. - Used Conduit to pull transaction data. - Edited task description and clicked "View Details" on transaction. - Used batch editor. - Made a bunch more edits. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2217 Differential Revision: https://secure.phabricator.com/D7068
2013-09-23 23:25:28 +02:00
$changes[PhabricatorTransactions::TYPE_COMMENT] = null;
}
$title = $request->getValue('title');
if ($title !== null) {
$changes[ManiphestTransaction::TYPE_TITLE] = $title;
}
$desc = $request->getValue('description');
if ($desc !== null) {
$changes[ManiphestTransaction::TYPE_DESCRIPTION] = $desc;
}
$status = $request->getValue('status');
if ($status !== null) {
$valid_statuses = ManiphestTaskStatus::getTaskStatusMap();
if (!isset($valid_statuses[$status])) {
throw id(new ConduitException('ERR-INVALID-PARAMETER'))
->setErrorDescription(pht('Status set to invalid value.'));
}
$changes[ManiphestTransaction::TYPE_STATUS] = $status;
}
}
$priority = $request->getValue('priority');
if ($priority !== null) {
$valid_priorities = ManiphestTaskPriority::getTaskPriorityMap();
if (!isset($valid_priorities[$priority])) {
throw id(new ConduitException('ERR-INVALID-PARAMETER'))
->setErrorDescription(pht('Priority set to invalid value.'));
}
$changes[ManiphestTransaction::TYPE_PRIORITY] = $priority;
}
$owner_phid = $request->getValue('ownerPHID');
if ($owner_phid !== null) {
$this->validatePHIDList(
array($owner_phid),
PhabricatorPeopleUserPHIDType::TYPECONST,
'ownerPHID');
$changes[ManiphestTransaction::TYPE_OWNER] = $owner_phid;
}
$ccs = $request->getValue('ccPHIDs');
if ($ccs !== null) {
$changes[PhabricatorTransactions::TYPE_SUBSCRIBERS] =
array('=' => array_fuse($ccs));
}
$transactions = array();
$view_policy = $request->getValue('viewPolicy');
if ($view_policy !== null) {
$transactions[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
->setNewValue($view_policy);
}
$edit_policy = $request->getValue('editPolicy');
if ($edit_policy !== null) {
$transactions[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($edit_policy);
}
$project_phids = $request->getValue('projectPHIDs');
if ($project_phids !== null) {
$this->validatePHIDList(
$project_phids,
PhabricatorProjectProjectPHIDType::TYPECONST,
'projectPHIDS');
$project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
$transactions[] = id(new ManiphestTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $project_type)
->setNewValue(
array(
'=' => array_fuse($project_phids),
));
}
$template = new ManiphestTransaction();
foreach ($changes as $type => $value) {
$transaction = clone $template;
$transaction->setTransactionType($type);
Migrate all Maniphest transaction data to new storage Summary: Ref T2217. This is the risky, hard part; everything after this should be smooth sailing. This is //mostly// clean, except: - The old format would opportunistically combine a comment with some other transaction type if it could. We no longer do that, so: - When migrating, "edit" + "comment" transactions need to be split in two. - When editing now, we should no longer combine these transaction types. - These changes are pretty straightforward and low-impact. - This migration promotes "auxiliary field" data to the new CustomField/StandardField format, so that's not a straight migration either. The formats are very similar, though. Broadly, this takes the same attack that the auth migration did: proxy all the code through to the new storage. `ManiphestTransaction` is now just an API on top of `ManiphestTransactionPro`, which is the new storage format. The two formats are very similar, so this was mostly a straight copy from one table to the other. Test Plan: - Without performing the migration, made a bunch of edits and comments on tasks and verified the new code works correctly. - Droped the test data and performed the migration. - Looked at the resulting data for obvious discrepancies. - Looked at a bunch of tasks and their transaction history. - Used Conduit to pull transaction data. - Edited task description and clicked "View Details" on transaction. - Used batch editor. - Made a bunch more edits. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2217 Differential Revision: https://secure.phabricator.com/D7068
2013-09-23 23:25:28 +02:00
if ($type == PhabricatorTransactions::TYPE_COMMENT) {
$transaction->attachComment(
id(new ManiphestTransactionComment())
->setContent($comments));
} else {
$transaction->setNewValue($value);
}
$transactions[] = $transaction;
}
$field_list = PhabricatorCustomField::getObjectFields(
$task,
PhabricatorCustomField::ROLE_EDIT);
$field_list->readFieldsFromStorage($task);
$auxiliary = $request->getValue('auxiliary');
if ($auxiliary) {
foreach ($field_list->getFields() as $key => $field) {
if (!array_key_exists($key, $auxiliary)) {
continue;
}
$transaction = clone $template;
$transaction->setTransactionType(
Migrate all Maniphest transaction data to new storage Summary: Ref T2217. This is the risky, hard part; everything after this should be smooth sailing. This is //mostly// clean, except: - The old format would opportunistically combine a comment with some other transaction type if it could. We no longer do that, so: - When migrating, "edit" + "comment" transactions need to be split in two. - When editing now, we should no longer combine these transaction types. - These changes are pretty straightforward and low-impact. - This migration promotes "auxiliary field" data to the new CustomField/StandardField format, so that's not a straight migration either. The formats are very similar, though. Broadly, this takes the same attack that the auth migration did: proxy all the code through to the new storage. `ManiphestTransaction` is now just an API on top of `ManiphestTransactionPro`, which is the new storage format. The two formats are very similar, so this was mostly a straight copy from one table to the other. Test Plan: - Without performing the migration, made a bunch of edits and comments on tasks and verified the new code works correctly. - Droped the test data and performed the migration. - Looked at the resulting data for obvious discrepancies. - Looked at a bunch of tasks and their transaction history. - Used Conduit to pull transaction data. - Edited task description and clicked "View Details" on transaction. - Used batch editor. - Made a bunch more edits. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2217 Differential Revision: https://secure.phabricator.com/D7068
2013-09-23 23:25:28 +02:00
PhabricatorTransactions::TYPE_CUSTOMFIELD);
$transaction->setMetadataValue('customfield:key', $key);
$transaction->setOldValue(
$field->getOldValueForApplicationTransactions());
$transaction->setNewValue($auxiliary[$key]);
$transactions[] = $transaction;
}
}
if (!$transactions) {
return;
}
$event = new PhabricatorEvent(
PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK,
array(
'task' => $task,
'new' => $is_new,
'transactions' => $transactions,
));
$event->setUser($request->getUser());
$event->setConduitRequest($request);
PhutilEventEngine::dispatchEvent($event);
$task = $event->getValue('task');
$transactions = $event->getValue('transactions');
$content_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_CONDUIT,
array());
$editor = id(new ManiphestTransactionEditor())
->setActor($request->getUser())
->setContentSource($content_source)
->setContinueOnNoEffect(true);
if (!$is_new) {
$editor->setContinueOnMissingFields(true);
}
$editor->applyTransactions($task, $transactions);
$event = new PhabricatorEvent(
PhabricatorEventType::TYPE_MANIPHEST_DIDEDITTASK,
array(
'task' => $task,
'new' => $is_new,
'transactions' => $transactions,
));
$event->setUser($request->getUser());
$event->setConduitRequest($request);
PhutilEventEngine::dispatchEvent($event);
// reload the task now that we've done all the fun stuff
return id(new ManiphestTaskQuery())
->setViewer($request->getUser())
->withPHIDs(array($task->getPHID()))
->needSubscriberPHIDs(true)
Maniphest - introduce needProjectPHIDs Summary: Ref T5245. This is some of the associated cleanup there. Test Plan: foreach ManiphestTaskQuery site, I made the change (or not) and tested as follows: === Call sites where added needProjectPHIDs === - PhabricatorHomeMainController - loaded the home page - ManiphestBatchEditController - batch edited some tasks (added a project) - ManiphestConduitAPIMethod - tested implicitly when tested ManiphestUpdateConduitAPIMethod - ManiphestInfoConduitAPIMethod - used the method via conduit console with input id : 1 - ManiphestQueryConduitAPIMethod - used the method via conduit console with input ids : [1, 2] - ManiphestUpdateConduitAPIMethod - used the method via conduit with input id : 1 and comment : “asdasds" - ManiphestReportController - viewed “By User” and “By Project” - ManiphestSubpriorityController - changed the priority of a task via a drag on manphest home - ManiphestTaskMailReceiver - updated Task 1 via bin/mail receive-test with a comment that is the README - ManiphestTaskSearchEngine - loaded Manifest home page - ManiphestTaskEditController - edited a task - ManiphestTransactionEditor - closed a blocking task - ManiphestTransactionSaveController - commented on a task - PhabricatorProjectProfileController - viewed project with id of 1 that has a few tasks in it - PhabricatorSearchAttachController - merged tasks together - DifferentialTransactionEditor - submit a diff that references a task; commit the diff (thus closing the diff) and the task gets updated - PhabricatorRepositoryCommitMessageParserWorker - submit a diff that references a task; commit the diff (thus closing the diff) and the task gets updated === Calls sites where *did not* add needProjectPHIDs (they do not appear in this revision) === - PhabricatorManiphestApplication - loaded the home page - ManiphestGetTaskTransactionsConduitAPIMethod - used the method via conduit console with input ids : [1, 2] ManiphestTaskDetailController - viewed a task with and without associated projects; finished workflow creating a task with a parent - ManiphestTransactionPreviewController - verified transaction preview showed up properly - PhabricatorProjectBoardViewController - viewed a board - PhabricatorProjectMoveController - moved a task around - ManiphestRemarkupRule - made a task reference like {T123} - ManiphestTaskQuery - executed a custom query for all tasks with page size of 2 and paginated through some tasks - ManiphestTaskPHIDType - nothing random seems broken? =D === Call sites where had to do something funky === - ManiphestHovercardEventListener - loaded hover cards from task mentions Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin, epriestley Maniphest Tasks: T5245 Differential Revision: https://secure.phabricator.com/D11004
2014-12-18 22:53:45 +01:00
->needProjectPHIDs(true)
->executeOne();
}
protected function buildTaskInfoDictionaries(array $tasks) {
assert_instances_of($tasks, 'ManiphestTask');
if (!$tasks) {
return array();
}
$task_phids = mpull($tasks, 'getPHID');
$all_deps = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($task_phids)
->withEdgeTypes(array(ManiphestTaskDependsOnTaskEdgeType::EDGECONST));
$all_deps->execute();
$result = array();
foreach ($tasks as $task) {
// TODO: Batch this get as CustomField gets cleaned up.
$field_list = PhabricatorCustomField::getObjectFields(
$task,
PhabricatorCustomField::ROLE_EDIT);
$field_list->readFieldsFromStorage($task);
$auxiliary = mpull(
$field_list->getFields(),
'getValueForStorage',
'getFieldKey');
$task_deps = $all_deps->getDestinationPHIDs(
array($task->getPHID()),
array(ManiphestTaskDependsOnTaskEdgeType::EDGECONST));
$result[$task->getPHID()] = array(
'id' => $task->getID(),
'phid' => $task->getPHID(),
'authorPHID' => $task->getAuthorPHID(),
'ownerPHID' => $task->getOwnerPHID(),
'ccPHIDs' => $task->getSubscriberPHIDs(),
'status' => $task->getStatus(),
'statusName' => ManiphestTaskStatus::getTaskStatusName(
$task->getStatus()),
'isClosed' => $task->isClosed(),
'priority' => ManiphestTaskPriority::getTaskPriorityName(
$task->getPriority()),
'priorityColor' => ManiphestTaskPriority::getTaskPriorityColor(
$task->getPriority()),
'title' => $task->getTitle(),
'description' => $task->getDescription(),
'projectPHIDs' => $task->getProjectPHIDs(),
'uri' => PhabricatorEnv::getProductionURI('/T'.$task->getID()),
'auxiliary' => $auxiliary,
'objectName' => 'T'.$task->getID(),
'dateCreated' => $task->getDateCreated(),
'dateModified' => $task->getDateModified(),
'dependsOnTaskPHIDs' => $task_deps,
);
}
return $result;
}
/**
* NOTE: This is a temporary stop gap since its easy to make malformed tasks.
* Long-term, the values set in @{method:defineParamTypes} will be used to
* validate data implicitly within the larger Conduit application.
*
* TODO: Remove this in favor of generalized Conduit hotness.
*/
private function validatePHIDList(array $phid_list, $phid_type, $field) {
$phid_groups = phid_group_by_type($phid_list);
unset($phid_groups[$phid_type]);
if (!empty($phid_groups)) {
throw id(new ConduitException('ERR-INVALID-PARAMETER'))
->setErrorDescription(
pht(
'One or more PHIDs were invalid for %s.',
$field));
}
return true;
}
}