Differential D21698 Diff 51708 src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/harbormaster/conduit/HarbormasterSendMessageConduitAPIMethod.php
<?php | <?php | ||||
final class HarbormasterSendMessageConduitAPIMethod | final class HarbormasterSendMessageConduitAPIMethod | ||||
extends HarbormasterConduitAPIMethod { | extends HarbormasterConduitAPIMethod { | ||||
public function getAPIMethodName() { | public function getAPIMethodName() { | ||||
return 'harbormaster.sendmessage'; | return 'harbormaster.sendmessage'; | ||||
} | } | ||||
public function getMethodSummary() { | public function getMethodSummary() { | ||||
return pht( | return pht( | ||||
'Send a message about the status of a build target to Harbormaster, '. | 'Modify running builds, and report build results.'); | ||||
'notifying the application of build results in an external system.'); | |||||
} | } | ||||
public function getMethodDescription() { | public function getMethodDescription() { | ||||
return pht(<<<EOREMARKUP | |||||
Pause, abort, restart, and report results for builds. | |||||
EOREMARKUP | |||||
); | |||||
} | |||||
protected function newDocumentationPages(PhabricatorUser $viewer) { | |||||
$pages = array(); | |||||
$pages[] = $this->newSendingDocumentationBoxPage($viewer); | |||||
$pages[] = $this->newBuildsDocumentationBoxPage($viewer); | |||||
$pages[] = $this->newCommandsDocumentationBoxPage($viewer); | |||||
$pages[] = $this->newTargetsDocumentationBoxPage($viewer); | |||||
$pages[] = $this->newUnitDocumentationBoxPage($viewer); | |||||
$pages[] = $this->newLintDocumentationBoxPage($viewer); | |||||
return $pages; | |||||
} | |||||
private function newSendingDocumentationBoxPage(PhabricatorUser $viewer) { | |||||
$title = pht('Sending Messages'); | |||||
$content = pht(<<<EOREMARKUP | |||||
Harbormaster build objects work somewhat differently from objects in many other | |||||
applications. Most application objects can be edited directly using synchronous | |||||
APIs (like `maniphest.edit`, `differential.revision.edit`, and so on). | |||||
However, builds require long-running background processing and Habormaster | |||||
objects have a more complex lifecycle than most other application objects and | |||||
may spend significant periods of time locked by daemon processes during build | |||||
execition. A synchronous edit might need to wait an arbitrarily long amount of | |||||
time for this lock to become available so the edit could be applied. | |||||
Additionally, some edits may also require an arbitrarily long amount of time to | |||||
//complete//. For example, aborting a build may execute cleanup steps which | |||||
take minutes (or even hours) to complete. | |||||
Since a synchronous API could not guarantee it could return results to the | |||||
caller in a reasonable amount of time, the edit API for Harbormaster build | |||||
objects is asynchronous: to update a Harbormaster build or build target, use | |||||
this API (`harbormaster.sendmessage`) to send it a message describing an edit | |||||
you would like to effect or additional information you want to provide. | |||||
The message will be processed by the daemons once the build or target reaches | |||||
a suitable state to receive messages. | |||||
Select an object to send a message to using the `receiver` parameter. This | |||||
API method can send messages to multiple types of objects: | |||||
<table> | |||||
<tr> | |||||
<th>Object Type</th> | |||||
<th>PHID Example</th> | |||||
<th>Description</th> | |||||
</tr> | |||||
<tr> | |||||
<td>Harbormaster Buildable</td> | |||||
<td>`PHID-HMBB-...`</td> | |||||
<td>%s</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Harbormaster Build</td> | |||||
<td>`PHID-HMBD-...`</td> | |||||
<td>%s</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Harbormaster Build Target</td> | |||||
<td>`PHID-HMBT-...`</td> | |||||
<td>%s</td> | |||||
</tr> | |||||
</table> | |||||
See below for specifics on sending messages to different object types. | |||||
EOREMARKUP | |||||
, | |||||
pht( | |||||
'Buildables may receive control commands like "abort" and "restart". '. | |||||
'Sending a control command to a Buildable is the same as sending it '. | |||||
'to each Build for the Buildable.'), | |||||
pht( | |||||
'Builds may receive control commands like "pause", "resume", "abort", '. | |||||
'and "restart".'), | |||||
pht( | |||||
'Build Targets may receive build status and result messages, like '. | |||||
'"pass" or "fail".')); | |||||
$content = $this->newRemarkupDocumentationView($content); | |||||
return $this->newDocumentationBoxPage($viewer, $title, $content) | |||||
->setAnchor('sending') | |||||
->setIconIcon('fa-envelope-o'); | |||||
} | |||||
private function newBuildsDocumentationBoxPage(PhabricatorUser $viewer) { | |||||
$title = pht('Updating Builds'); | |||||
$content = pht(<<<EOREMARKUP | |||||
You can use this method (`harbormaster.sendmessage`) to send control commands | |||||
to Buildables and Builds. | |||||
Specify the Build or Buildable to receive the control command by providing its | |||||
PHID in the `receiver` parameter. | |||||
Sending a control command to a Buildable has the same effect as sending it to | |||||
each Build for the Buildable. For example, sending a "Pause" message to a | |||||
Buildable will pause all builds for the Buildable (or at least attempt to). | |||||
When sending control commands, the `unit` and `lint` parameters of this API | |||||
method must be omitted. You can not report lint or unit results directly to | |||||
a Build or Buildable, and can not report them alongside a control command. | |||||
More broadly, you can not report build results directly to a Build or | |||||
Buildable. Instead, report results to a Build Target. | |||||
See below for a list of control commands. | |||||
EOREMARKUP | |||||
); | |||||
$content = $this->newRemarkupDocumentationView($content); | |||||
return $this->newDocumentationBoxPage($viewer, $title, $content) | |||||
->setAnchor('builds') | |||||
->setIconIcon('fa-cubes'); | |||||
} | |||||
private function newCommandsDocumentationBoxPage(PhabricatorUser $viewer) { | |||||
$messages = HarbormasterBuildMessageTransaction::getAllMessages(); | |||||
$rows = array(); | |||||
$rows[] = '<tr>'; | |||||
$rows[] = '<th>'.pht('Message Type').'</th>'; | |||||
$rows[] = '<th>'.pht('Description').'</th>'; | |||||
$rows[] = '</tr>'; | |||||
foreach ($messages as $message) { | |||||
$row = array(); | |||||
$row[] = sprintf( | |||||
'<td>`%s`</td>', | |||||
$message->getHarbormasterBuildMessageType()); | |||||
$row[] = sprintf( | |||||
'<td>%s</td>', | |||||
$message->getHarbormasterBuildMessageDescription()); | |||||
$rows[] = sprintf( | |||||
'<tr>%s</tr>', | |||||
implode("\n", $row)); | |||||
} | |||||
$message_table = sprintf( | |||||
'<table>%s</table>', | |||||
implode("\n", $rows)); | |||||
$title = pht('Control Commands'); | |||||
$content = pht(<<<EOREMARKUP | |||||
You can use this method to send control commands to Buildables and Builds. | |||||
This table summarizes which object types may receive control commands: | |||||
<table> | |||||
<tr> | |||||
<th>Object Type</th> | |||||
<th>PHID Example</th> | |||||
<th /> | |||||
<th>Description</th> | |||||
</tr> | |||||
<tr> | |||||
<td>Harbormaster Buildable</td> | |||||
<td>`PHID-HMBB-...`</td> | |||||
<td>{icon check color=green}</td> | |||||
<td>Buildables may receive control commands.</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Harbormaster Build</td> | |||||
<td>`PHID-HMBD-...`</td> | |||||
<td>{icon check color=green}</td> | |||||
<td>Builds may receive control commands.</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Harbormaster Build Target</td> | |||||
<td>`PHID-HMBT-...`</td> | |||||
<td>{icon times color=red}</td> | |||||
<td>You may **NOT** send control commands to build targets.</td> | |||||
</tr> | |||||
</table> | |||||
You can send these commands: | |||||
%s | |||||
To send a command message, specify the PHID of the object you would like to | |||||
receive the message using the `receiver` parameter, and specify the message | |||||
type using the `type` parameter. | |||||
EOREMARKUP | |||||
, | |||||
$message_table); | |||||
$content = $this->newRemarkupDocumentationView($content); | |||||
return $this->newDocumentationBoxPage($viewer, $title, $content) | |||||
->setAnchor('commands') | |||||
->setIconIcon('fa-exclamation-triangle'); | |||||
} | |||||
private function newTargetsDocumentationBoxPage(PhabricatorUser $viewer) { | |||||
$messages = HarbormasterMessageType::getAllMessages(); | $messages = HarbormasterMessageType::getAllMessages(); | ||||
$head_type = pht('Constant'); | |||||
$head_desc = pht('Description'); | |||||
$head_key = pht('Key'); | |||||
$head_type = pht('Type'); | $head_type = pht('Type'); | ||||
$head_name = pht('Name'); | $head_desc = pht('Description'); | ||||
$rows = array(); | $rows = array(); | ||||
$rows[] = "| {$head_type} | {$head_desc} |"; | $rows[] = "| {$head_type} | {$head_desc} |"; | ||||
$rows[] = '|--------------|--------------|'; | $rows[] = '|--------------|--------------|'; | ||||
foreach ($messages as $message) { | foreach ($messages as $message) { | ||||
$description = HarbormasterMessageType::getMessageDescription($message); | $description = HarbormasterMessageType::getMessageDescription($message); | ||||
$rows[] = "| `{$message}` | {$description} |"; | $rows[] = "| `{$message}` | {$description} |"; | ||||
} | } | ||||
$message_table = implode("\n", $rows); | $message_table = implode("\n", $rows); | ||||
$content = pht(<<<EOREMARKUP | |||||
If you run external builds, you can use this method to publish build results | |||||
back into Harbormaster after the external system finishes work (or as it makes | |||||
progress). | |||||
To report build status or results, you must send a message to the appropriate | |||||
Build Target. This table summarizes which object types may receive build status | |||||
and result messages: | |||||
<table> | |||||
<tr> | |||||
<th>Object Type</th> | |||||
<th>PHID Example</th> | |||||
<th /> | |||||
<th>Description</th> | |||||
</tr> | |||||
<tr> | |||||
<td>Harbormaster Buildable</td> | |||||
<td>`PHID-HMBB-...`</td> | |||||
<td>{icon times color=red}</td> | |||||
<td>Buildables may **NOT** receive status or result messages.</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Harbormaster Build</td> | |||||
<td>`PHID-HMBD-...`</td> | |||||
<td>{icon times color=red}</td> | |||||
<td>Builds may **NOT** receive status or result messages.</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Harbormaster Build Target</td> | |||||
<td>`PHID-HMBT-...`</td> | |||||
<td>{icon check color=green}</td> | |||||
<td>Report build status and results to Build Targets.</td> | |||||
</tr> | |||||
</table> | |||||
The simplest way to use this method to report build results is to call it once | |||||
after the build finishes with a `pass` or `fail` message. This will record the | |||||
build result, and continue the next step in the build if the build was waiting | |||||
for a result. | |||||
When you send a status message about a build target, you can optionally include | |||||
detailed `lint` or `unit` results alongside the message. See below for details. | |||||
If you want to report intermediate results but a build hasn't completed yet, | |||||
you can use the `work` message. This message doesn't have any direct effects, | |||||
but allows you to send additional data to update the progress of the build | |||||
target. The target will continue waiting for a completion message, but the UI | |||||
will update to show the progress which has been made. | |||||
When sending a message to a build target to report the status or results of | |||||
a build, your message must include a `type` which describes the overall state | |||||
of the build. For example, use `pass` to tell Harbormaster that a build target | |||||
completed successfully. | |||||
Supported message types are: | |||||
%s | |||||
EOREMARKUP | |||||
, | |||||
$message_table); | |||||
$title = pht('Updating Build Targets'); | |||||
$content = $this->newRemarkupDocumentationView($content); | |||||
return $this->newDocumentationBoxPage($viewer, $title, $content) | |||||
->setAnchor('targets') | |||||
->setIconIcon('fa-bullseye'); | |||||
} | |||||
private function newUnitDocumentationBoxPage(PhabricatorUser $viewer) { | |||||
$head_key = pht('Key'); | |||||
$head_desc = pht('Description'); | |||||
$head_name = pht('Name'); | |||||
$head_type = pht('Type'); | |||||
$rows = array(); | $rows = array(); | ||||
$rows[] = "| {$head_key} | {$head_type} | {$head_desc} |"; | $rows[] = "| {$head_key} | {$head_type} | {$head_desc} |"; | ||||
$rows[] = '|-------------|--------------|--------------|'; | $rows[] = '|-------------|--------------|--------------|'; | ||||
$unit_spec = HarbormasterBuildUnitMessage::getParameterSpec(); | $unit_spec = HarbormasterBuildUnitMessage::getParameterSpec(); | ||||
foreach ($unit_spec as $key => $parameter) { | foreach ($unit_spec as $key => $parameter) { | ||||
$type = idx($parameter, 'type'); | $type = idx($parameter, 'type'); | ||||
$type = str_replace('|', ' '.pht('or').' ', $type); | $type = str_replace('|', ' '.pht('or').' ', $type); | ||||
$description = idx($parameter, 'description'); | $description = idx($parameter, 'description'); | ||||
$rows[] = "| `{$key}` | //{$type}// | {$description} |"; | $rows[] = "| `{$key}` | //{$type}// | {$description} |"; | ||||
} | } | ||||
$unit_table = implode("\n", $rows); | $unit_table = implode("\n", $rows); | ||||
$rows = array(); | $rows = array(); | ||||
$rows[] = "| {$head_key} | {$head_name} | {$head_desc} |"; | $rows[] = "| {$head_key} | {$head_name} | {$head_desc} |"; | ||||
$rows[] = '|-------------|--------------|--------------|'; | $rows[] = '|-------------|--------------|--------------|'; | ||||
$results = ArcanistUnitTestResult::getAllResultCodes(); | $results = ArcanistUnitTestResult::getAllResultCodes(); | ||||
foreach ($results as $result_code) { | foreach ($results as $result_code) { | ||||
$name = ArcanistUnitTestResult::getResultCodeName($result_code); | $name = ArcanistUnitTestResult::getResultCodeName($result_code); | ||||
$description = ArcanistUnitTestResult::getResultCodeDescription( | $description = ArcanistUnitTestResult::getResultCodeDescription( | ||||
$result_code); | $result_code); | ||||
$rows[] = "| `{$result_code}` | **{$name}** | {$description} |"; | $rows[] = "| `{$result_code}` | **{$name}** | {$description} |"; | ||||
} | } | ||||
$result_table = implode("\n", $rows); | $result_table = implode("\n", $rows); | ||||
$valid_unit = array( | |||||
array( | |||||
'name' => 'PassingTest', | |||||
'result' => ArcanistUnitTestResult::RESULT_PASS, | |||||
), | |||||
array( | |||||
'name' => 'FailingTest', | |||||
'result' => ArcanistUnitTestResult::RESULT_FAIL, | |||||
), | |||||
); | |||||
$json = new PhutilJSON(); | |||||
$valid_unit = $json->encodeAsList($valid_unit); | |||||
$title = pht('Reporting Unit Results'); | |||||
$content = pht(<<<EOREMARKUP | |||||
You can report test results when updating the state of a build target. The | |||||
simplest way to do this is to report all the results alongside a `pass` or | |||||
`fail` message, but you can also send a `work` message to report intermediate | |||||
results. | |||||
To provide unit test results, pass a list of results in the `unit` | |||||
parameter. Each result should be a dictionary with these keys: | |||||
%s | |||||
The `result` parameter recognizes these test results: | |||||
%s | |||||
This is a simple, valid value for the `unit` parameter. It reports one passing | |||||
test and one failing test: | |||||
```lang=json | |||||
%s | |||||
``` | |||||
EOREMARKUP | |||||
, | |||||
$unit_table, | |||||
$result_table, | |||||
$valid_unit); | |||||
$content = $this->newRemarkupDocumentationView($content); | |||||
return $this->newDocumentationBoxPage($viewer, $title, $content) | |||||
->setAnchor('unit'); | |||||
} | |||||
private function newLintDocumentationBoxPage(PhabricatorUser $viewer) { | |||||
$head_key = pht('Key'); | |||||
$head_desc = pht('Description'); | |||||
$head_name = pht('Name'); | |||||
$head_type = pht('Type'); | |||||
$rows = array(); | $rows = array(); | ||||
$rows[] = "| {$head_key} | {$head_type} | {$head_desc} |"; | $rows[] = "| {$head_key} | {$head_type} | {$head_desc} |"; | ||||
$rows[] = '|-------------|--------------|--------------|'; | $rows[] = '|-------------|--------------|--------------|'; | ||||
$lint_spec = HarbormasterBuildLintMessage::getParameterSpec(); | $lint_spec = HarbormasterBuildLintMessage::getParameterSpec(); | ||||
foreach ($lint_spec as $key => $parameter) { | foreach ($lint_spec as $key => $parameter) { | ||||
$type = idx($parameter, 'type'); | $type = idx($parameter, 'type'); | ||||
$type = str_replace('|', ' '.pht('or').' ', $type); | $type = str_replace('|', ' '.pht('or').' ', $type); | ||||
$description = idx($parameter, 'description'); | $description = idx($parameter, 'description'); | ||||
$rows[] = "| `{$key}` | //{$type}// | {$description} |"; | $rows[] = "| `{$key}` | //{$type}// | {$description} |"; | ||||
} | } | ||||
$lint_table = implode("\n", $rows); | $lint_table = implode("\n", $rows); | ||||
$rows = array(); | $rows = array(); | ||||
$rows[] = "| {$head_key} | {$head_name} |"; | $rows[] = "| {$head_key} | {$head_name} |"; | ||||
$rows[] = '|-------------|--------------|'; | $rows[] = '|-------------|--------------|'; | ||||
$severities = ArcanistLintSeverity::getLintSeverities(); | $severities = ArcanistLintSeverity::getLintSeverities(); | ||||
foreach ($severities as $key => $name) { | foreach ($severities as $key => $name) { | ||||
$rows[] = "| `{$key}` | **{$name}** |"; | $rows[] = "| `{$key}` | **{$name}** |"; | ||||
} | } | ||||
$severity_table = implode("\n", $rows); | $severity_table = implode("\n", $rows); | ||||
$valid_unit = array( | |||||
array( | |||||
'name' => 'PassingTest', | |||||
'result' => ArcanistUnitTestResult::RESULT_PASS, | |||||
), | |||||
array( | |||||
'name' => 'FailingTest', | |||||
'result' => ArcanistUnitTestResult::RESULT_FAIL, | |||||
), | |||||
); | |||||
$valid_lint = array( | $valid_lint = array( | ||||
array( | array( | ||||
'name' => pht('Syntax Error'), | 'name' => pht('Syntax Error'), | ||||
'code' => 'EXAMPLE1', | 'code' => 'EXAMPLE1', | ||||
'severity' => ArcanistLintSeverity::SEVERITY_ERROR, | 'severity' => ArcanistLintSeverity::SEVERITY_ERROR, | ||||
'path' => 'path/to/example.c', | 'path' => 'path/to/example.c', | ||||
'line' => 17, | 'line' => 17, | ||||
'char' => 3, | 'char' => 3, | ||||
), | ), | ||||
array( | array( | ||||
'name' => pht('Not A Haiku'), | 'name' => pht('Not A Haiku'), | ||||
'code' => 'EXAMPLE2', | 'code' => 'EXAMPLE2', | ||||
'severity' => ArcanistLintSeverity::SEVERITY_ERROR, | 'severity' => ArcanistLintSeverity::SEVERITY_ERROR, | ||||
'path' => 'path/to/source.cpp', | 'path' => 'path/to/source.cpp', | ||||
'line' => 23, | 'line' => 23, | ||||
'char' => 1, | 'char' => 1, | ||||
'description' => pht( | 'description' => pht( | ||||
'This function definition is not a haiku.'), | 'This function definition is not a haiku.'), | ||||
), | ), | ||||
); | ); | ||||
$json = new PhutilJSON(); | $json = new PhutilJSON(); | ||||
$valid_unit = $json->encodeAsList($valid_unit); | |||||
$valid_lint = $json->encodeAsList($valid_lint); | $valid_lint = $json->encodeAsList($valid_lint); | ||||
return pht( | $title = pht('Reporting Lint Results'); | ||||
"Send a message about the status of a build target to Harbormaster, ". | $content = pht(<<<EOREMARKUP | ||||
"notifying the application of build results in an external system.". | Like unit test results, you can report lint results when updating the state | ||||
"\n\n". | of a build target. The `lint` parameter should contain results as a list of | ||||
"Sending Messages\n". | dictionaries with these keys: | ||||
"================\n". | |||||
"If you run external builds, you can use this method to publish build ". | %s | ||||
"results back into Harbormaster after the external system finishes work ". | |||||
"or as it makes progress.". | The `severity` parameter recognizes these severity levels: | ||||
"\n\n". | |||||
"The simplest way to use this method is to call it once after the ". | %s | ||||
"build finishes with a `pass` or `fail` message. This will record the ". | |||||
"build result, and continue the next step in the build if the build was ". | This is a simple, valid value for the `lint` parameter. It reports one error | ||||
"waiting for a result.". | and one warning: | ||||
"\n\n". | |||||
"When you send a status message about a build target, you can ". | ```lang=json | ||||
"optionally include detailed `lint` or `unit` results alongside the ". | %s | ||||
"message. See below for details.". | ``` | ||||
"\n\n". | |||||
"If you want to report intermediate results but a build hasn't ". | EOREMARKUP | ||||
"completed yet, you can use the `work` message. This message doesn't ". | , | ||||
"have any direct effects, but allows you to send additional data to ". | |||||
"update the progress of the build target. The target will continue ". | |||||
"waiting for a completion message, but the UI will update to show the ". | |||||
"progress which has been made.". | |||||
"\n\n". | |||||
"Message Types\n". | |||||
"=============\n". | |||||
"When you send Harbormaster a message, you must include a `type`, ". | |||||
"which describes the overall state of the build. For example, use ". | |||||
"`pass` to tell Harbormaster that a build completed successfully.". | |||||
"\n\n". | |||||
"Supported message types are:". | |||||
"\n\n". | |||||
"%s". | |||||
"\n\n". | |||||
"Unit Results\n". | |||||
"============\n". | |||||
"You can report test results alongside a message. The simplest way to ". | |||||
"do this is to report all the results alongside a `pass` or `fail` ". | |||||
"message, but you can also send a `work` message to report intermediate ". | |||||
"results.\n\n". | |||||
"To provide unit test results, pass a list of results in the `unit` ". | |||||
"parameter. Each result should be a dictionary with these keys:". | |||||
"\n\n". | |||||
"%s". | |||||
"\n\n". | |||||
"The `result` parameter recognizes these test results:". | |||||
"\n\n". | |||||
"%s". | |||||
"\n\n". | |||||
"This is a simple, valid value for the `unit` parameter. It reports ". | |||||
"one passing test and one failing test:\n\n". | |||||
"\n\n". | |||||
"```lang=json\n". | |||||
"%s". | |||||
"```". | |||||
"\n\n". | |||||
"Lint Results\n". | |||||
"============\n". | |||||
"Like unit test results, you can report lint results alongside a ". | |||||
"message. The `lint` parameter should contain results as a list of ". | |||||
"dictionaries with these keys:". | |||||
"\n\n". | |||||
"%s". | |||||
"\n\n". | |||||
"The `severity` parameter recognizes these severity levels:". | |||||
"\n\n". | |||||
"%s". | |||||
"\n\n". | |||||
"This is a simple, valid value for the `lint` parameter. It reports one ". | |||||
"error and one warning:". | |||||
"\n\n". | |||||
"```lang=json\n". | |||||
"%s". | |||||
"```". | |||||
"\n\n", | |||||
$message_table, | |||||
$unit_table, | |||||
$result_table, | |||||
$valid_unit, | |||||
$lint_table, | $lint_table, | ||||
$severity_table, | $severity_table, | ||||
$valid_lint); | $valid_lint); | ||||
$content = $this->newRemarkupDocumentationView($content); | |||||
return $this->newDocumentationBoxPage($viewer, $title, $content) | |||||
->setAnchor('lint'); | |||||
} | } | ||||
protected function defineParamTypes() { | protected function defineParamTypes() { | ||||
$messages = HarbormasterMessageType::getAllMessages(); | $messages = HarbormasterMessageType::getAllMessages(); | ||||
$more_messages = HarbormasterBuildMessageTransaction::getAllMessages(); | |||||
$more_messages = mpull($more_messages, 'getHarbormasterBuildMessageType'); | |||||
$messages = array_merge($messages, $more_messages); | |||||
$messages = array_unique($messages); | |||||
sort($messages); | |||||
$type_const = $this->formatStringConstants($messages); | $type_const = $this->formatStringConstants($messages); | ||||
return array( | return array( | ||||
'buildTargetPHID' => 'required phid', | 'receiver' => 'required string|phid', | ||||
'type' => 'required '.$type_const, | 'type' => 'required '.$type_const, | ||||
'unit' => 'optional list<wild>', | 'unit' => 'optional list<wild>', | ||||
'lint' => 'optional list<wild>', | 'lint' => 'optional list<wild>', | ||||
'buildTargetPHID' => 'deprecated optional phid', | |||||
); | ); | ||||
} | } | ||||
protected function defineReturnType() { | protected function defineReturnType() { | ||||
return 'void'; | return 'void'; | ||||
} | } | ||||
protected function execute(ConduitAPIRequest $request) { | protected function execute(ConduitAPIRequest $request) { | ||||
$viewer = $request->getUser(); | $viewer = $request->getViewer(); | ||||
$receiver_name = $request->getValue('receiver'); | |||||
$build_target_phid = $request->getValue('buildTargetPHID'); | $build_target_phid = $request->getValue('buildTargetPHID'); | ||||
if ($build_target_phid !== null) { | |||||
if ($receiver_name === null) { | |||||
$receiver_name = $build_target_phid; | |||||
} else { | |||||
throw new Exception( | |||||
pht( | |||||
'Call specifies both "receiver" and "buildTargetPHID". '. | |||||
'When using the modern "receiver" parameter, omit the '. | |||||
'deprecated "buildTargetPHID" parameter.')); | |||||
} | |||||
} | |||||
if (!strlen($receiver_name)) { | |||||
throw new Exception( | |||||
pht( | |||||
'Call omits required "receiver" parameter. Specify the PHID '. | |||||
'of the object you want to send a message to.')); | |||||
} | |||||
$message_type = $request->getValue('type'); | $message_type = $request->getValue('type'); | ||||
if (!strlen($message_type)) { | |||||
throw new Exception( | |||||
pht( | |||||
'Call omits required "type" parameter. Specify the type of '. | |||||
'message you want to send.')); | |||||
} | |||||
$build_target = id(new HarbormasterBuildTargetQuery()) | $receiver_object = id(new PhabricatorObjectQuery()) | ||||
->setViewer($viewer) | ->setViewer($viewer) | ||||
->withPHIDs(array($build_target_phid)) | ->withNames(array($receiver_name)) | ||||
->executeOne(); | ->executeOne(); | ||||
if (!$build_target) { | if (!$receiver_object) { | ||||
throw new Exception(pht('No such build target!')); | throw new Exception( | ||||
pht( | |||||
'Unable to load object "%s" to receive message.', | |||||
$receiver_name)); | |||||
} | |||||
$is_target = ($receiver_object instanceof HarbormasterBuildTarget); | |||||
if ($is_target) { | |||||
return $this->sendToTarget($request, $message_type, $receiver_object); | |||||
} | |||||
if ($request->getValue('unit') !== null) { | |||||
throw new Exception( | |||||
pht( | |||||
'Call includes "unit" parameter. This parameter must be omitted '. | |||||
'when the receiver is not a Build Target.')); | |||||
} | |||||
if ($request->getValue('lint') !== null) { | |||||
throw new Exception( | |||||
pht( | |||||
'Call includes "lint" parameter. This parameter must be omitted '. | |||||
'when the receiver is not a Build Target.')); | |||||
} | |||||
$is_build = ($receiver_object instanceof HarbormasterBuild); | |||||
if ($is_build) { | |||||
return $this->sendToBuild($request, $message_type, $receiver_object); | |||||
} | |||||
$is_buildable = ($receiver_object instanceof HarbormasterBuildable); | |||||
if ($is_buildable) { | |||||
return $this->sendToBuildable($request, $message_type, $receiver_object); | |||||
} | } | ||||
throw new Exception( | |||||
pht( | |||||
'Receiver object (of class "%s") is not a valid receiver.', | |||||
get_class($receiver_object))); | |||||
} | |||||
private function sendToTarget( | |||||
ConduitAPIRequest $request, | |||||
$message_type, | |||||
HarbormasterBuildTarget $build_target) { | |||||
$viewer = $request->getViewer(); | |||||
$save = array(); | $save = array(); | ||||
$lint_messages = $request->getValue('lint', array()); | $lint_messages = $request->getValue('lint', array()); | ||||
foreach ($lint_messages as $lint) { | foreach ($lint_messages as $lint) { | ||||
$save[] = HarbormasterBuildLintMessage::newFromDictionary( | $save[] = HarbormasterBuildLintMessage::newFromDictionary( | ||||
$build_target, | $build_target, | ||||
$lint); | $lint); | ||||
} | } | ||||
Show All 26 Lines | PhabricatorWorker::scheduleTask( | ||||
), | ), | ||||
array( | array( | ||||
'objectPHID' => $build->getPHID(), | 'objectPHID' => $build->getPHID(), | ||||
)); | )); | ||||
return null; | return null; | ||||
} | } | ||||
private function sendToBuild( | |||||
ConduitAPIRequest $request, | |||||
$message_type, | |||||
HarbormasterBuild $build) { | |||||
$viewer = $request->getViewer(); | |||||
$xaction = | |||||
HarbormasterBuildMessageTransaction::getTransactionObjectForMessageType( | |||||
$message_type); | |||||
if (!$xaction) { | |||||
throw new Exception( | |||||
pht( | |||||
'Message type "%s" is not supported.', | |||||
$message_type)); | |||||
} | |||||
// NOTE: This is a slightly weaker check than we perform in the web UI. | |||||
// We allow API callers to send a "pause" message to a pausing build, | |||||
// for example, even though the message will have no effect. | |||||
$xaction->assertCanApplyMessage($viewer, $build); | |||||
$build->sendMessage($viewer, $xaction->getHarbormasterBuildMessageType()); | |||||
} | |||||
private function sendToBuildable( | |||||
ConduitAPIRequest $request, | |||||
$message_type, | |||||
HarbormasterBuildable $buildable) { | |||||
$viewer = $request->getViewer(); | |||||
$xaction = | |||||
HarbormasterBuildMessageTransaction::getTransactionObjectForMessageType( | |||||
$message_type); | |||||
if (!$xaction) { | |||||
throw new Exception( | |||||
pht( | |||||
'Message type "%s" is not supported.', | |||||
$message_type)); | |||||
} | |||||
// Reload the Buildable to load Builds. | |||||
$buildable = id(new HarbormasterBuildableQuery()) | |||||
->setViewer($viewer) | |||||
->withIDs(array($buildable->getID())) | |||||
->needBuilds(true) | |||||
->executeOne(); | |||||
$can_send = array(); | |||||
foreach ($buildable->getBuilds() as $build) { | |||||
if ($xaction->canApplyMessage($viewer, $build)) { | |||||
$can_send[] = $build; | |||||
} | |||||
} | |||||
// NOTE: This doesn't actually apply a transaction to the Buildable, | |||||
// but that transaction is purely informational and should probably be | |||||
// implemented as a Message. | |||||
foreach ($can_send as $build) { | |||||
$build->sendMessage($viewer, $xaction->getHarbormasterBuildMessageType()); | |||||
} | |||||
} | |||||
} | } |