mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-21 21:10:56 +01:00
Make Herald rules more resilient
Summary: Make Herald conditions and actions more resilient (see discussion in D12896). This protects against invalid rules, which may have been valid in the past but are no longer valid. Specifically: - If a rule has an invalid field, the conditions fail and the actions do not execute. - The transcript shows that the rule failed because of an invalid field, and points at the issue. - If a rule has an invalid action, that action fails but other actions execute. - The transcript shows that the action failed. - Everything else (particularly, other rules) continues normally in both cases. - The edit interface is somewhat working when editing an invalid rule, but it could use some further improvements. Test Plan: # Ran this rule on a differential revision and saw the rule fail in the transcript. # Was able to submit a differential without receiving an `ERR-CONDUIT-CORE`. # Edited the Herald rule using the UI and was able to save the rule succesfully. # Ran this rule on a differential revision and saw one success and one failure in the transcript. # Was able to submit a differential without receiving an `ERR-CONDUIT-CORE`. # Edited the Herald rule using the UI. Clicking save caused a `HeraldInvalidActionException` to be thrown, but maybe this is okay. Differential Revision: http://phabricator.local/D41
This commit is contained in:
parent
18fe6d58ae
commit
f5c9b9c014
5 changed files with 65 additions and 14 deletions
|
@ -1203,7 +1203,15 @@ abstract class HeraldAdapter {
|
||||||
$rule_global = HeraldRuleTypeConfig::RULE_TYPE_GLOBAL;
|
$rule_global = HeraldRuleTypeConfig::RULE_TYPE_GLOBAL;
|
||||||
|
|
||||||
$action_type = $action->getAction();
|
$action_type = $action->getAction();
|
||||||
$action_name = idx($this->getActionNameMap($rule_global), $action_type);
|
|
||||||
|
$default = $this->isHeraldCustomKey($action_type)
|
||||||
|
? pht('(Unknown Custom Action "%s") equals', $action_type)
|
||||||
|
: pht('(Unknown Action "%s") equals', $action_type);
|
||||||
|
|
||||||
|
$action_name = idx(
|
||||||
|
$this->getActionNameMap($rule_global),
|
||||||
|
$action_type,
|
||||||
|
$default);
|
||||||
|
|
||||||
$target = $this->renderActionTargetAsText($action, $handles);
|
$target = $this->renderActionTargetAsText($action, $handles);
|
||||||
|
|
||||||
|
@ -1525,7 +1533,9 @@ abstract class HeraldAdapter {
|
||||||
$supported = $this->getActions($rule_type);
|
$supported = $this->getActions($rule_type);
|
||||||
$supported = array_fuse($supported);
|
$supported = array_fuse($supported);
|
||||||
if (empty($supported[$action])) {
|
if (empty($supported[$action])) {
|
||||||
throw new Exception(
|
return new HeraldApplyTranscript(
|
||||||
|
$effect,
|
||||||
|
false,
|
||||||
pht(
|
pht(
|
||||||
'Adapter "%s" does not support action "%s" for rule type "%s".',
|
'Adapter "%s" does not support action "%s" for rule type "%s".',
|
||||||
get_class($this),
|
get_class($this),
|
||||||
|
@ -1548,7 +1558,9 @@ abstract class HeraldAdapter {
|
||||||
$result = $this->handleCustomHeraldEffect($effect);
|
$result = $this->handleCustomHeraldEffect($effect);
|
||||||
|
|
||||||
if (!$result) {
|
if (!$result) {
|
||||||
throw new Exception(
|
return new HeraldApplyTranscript(
|
||||||
|
$effect,
|
||||||
|
false,
|
||||||
pht(
|
pht(
|
||||||
'No custom action exists to handle rule action "%s".',
|
'No custom action exists to handle rule action "%s".',
|
||||||
$action));
|
$action));
|
||||||
|
|
|
@ -154,7 +154,10 @@ final class HeraldDifferentialDiffAdapter extends HeraldDifferentialAdapter {
|
||||||
pht('Blocked diff.'));
|
pht('Blocked diff.'));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception(pht('No rules to handle action "%s"!', $action));
|
$result[] = new HeraldApplyTranscript(
|
||||||
|
$effect,
|
||||||
|
false,
|
||||||
|
pht('No rules to handle action "%s"!', $action));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -320,7 +320,7 @@ final class HeraldRuleController extends HeraldController {
|
||||||
try {
|
try {
|
||||||
$adapter->willSaveAction($rule, $obj);
|
$adapter->willSaveAction($rule, $obj);
|
||||||
} catch (HeraldInvalidActionException $ex) {
|
} catch (HeraldInvalidActionException $ex) {
|
||||||
$errors[] = $ex;
|
$errors[] = $ex->getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
$actions[] = $obj;
|
$actions[] = $obj;
|
||||||
|
@ -354,7 +354,6 @@ final class HeraldRuleController extends HeraldController {
|
||||||
if ($rule->getConditions()) {
|
if ($rule->getConditions()) {
|
||||||
$serial_conditions = array();
|
$serial_conditions = array();
|
||||||
foreach ($rule->getConditions() as $condition) {
|
foreach ($rule->getConditions() as $condition) {
|
||||||
|
|
||||||
$value = $condition->getValue();
|
$value = $condition->getValue();
|
||||||
switch ($condition->getFieldName()) {
|
switch ($condition->getFieldName()) {
|
||||||
case HeraldAdapter::FIELD_TASK_PRIORITY:
|
case HeraldAdapter::FIELD_TASK_PRIORITY:
|
||||||
|
@ -394,10 +393,10 @@ final class HeraldRuleController extends HeraldController {
|
||||||
$serial_actions = array(
|
$serial_actions = array(
|
||||||
array('default', ''),
|
array('default', ''),
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($rule->getActions()) {
|
if ($rule->getActions()) {
|
||||||
$serial_actions = array();
|
$serial_actions = array();
|
||||||
foreach ($rule->getActions() as $action) {
|
foreach ($rule->getActions() as $action) {
|
||||||
|
|
||||||
switch ($action->getAction()) {
|
switch ($action->getAction()) {
|
||||||
case HeraldAdapter::ACTION_FLAG:
|
case HeraldAdapter::ACTION_FLAG:
|
||||||
case HeraldAdapter::ACTION_BLOCK:
|
case HeraldAdapter::ACTION_BLOCK:
|
||||||
|
@ -438,21 +437,39 @@ final class HeraldRuleController extends HeraldController {
|
||||||
// names of, so that saving a rule without touching anything doesn't change
|
// names of, so that saving a rule without touching anything doesn't change
|
||||||
// it.
|
// it.
|
||||||
foreach ($rule->getConditions() as $condition) {
|
foreach ($rule->getConditions() as $condition) {
|
||||||
if (empty($field_map[$condition->getFieldName()])) {
|
$field_name = $condition->getFieldName();
|
||||||
$field_map[$condition->getFieldName()] = pht('<Unknown Field>');
|
|
||||||
|
if (empty($field_map[$field_name])) {
|
||||||
|
$field_map[$field_name] = pht('<Unknown Field "%s">', $field_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$actions = $adapter->getActions($rule->getRuleType());
|
$actions = $adapter->getActions($rule->getRuleType());
|
||||||
$action_map = array_select_keys($all_actions, $actions);
|
$action_map = array_select_keys($all_actions, $actions);
|
||||||
|
|
||||||
|
// Populate any actions which exist in the rule but which we don't know the
|
||||||
|
// names of, so that saving a rule without touching anything doesn't change
|
||||||
|
// it.
|
||||||
|
foreach ($rule->getActions() as $action) {
|
||||||
|
$action_name = $action->getAction();
|
||||||
|
|
||||||
|
if (empty($action_map[$action_name])) {
|
||||||
|
$action_map[$action_name] = pht('<Unknown Action "%s">', $action_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$config_info = array();
|
$config_info = array();
|
||||||
$config_info['fields'] = $field_map;
|
$config_info['fields'] = $field_map;
|
||||||
$config_info['conditions'] = $all_conditions;
|
$config_info['conditions'] = $all_conditions;
|
||||||
$config_info['actions'] = $action_map;
|
$config_info['actions'] = $action_map;
|
||||||
|
|
||||||
foreach ($config_info['fields'] as $field => $name) {
|
foreach ($config_info['fields'] as $field => $name) {
|
||||||
$field_conditions = $adapter->getConditionsForField($field);
|
try {
|
||||||
|
$field_conditions = $adapter->getConditionsForField($field);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$field_conditions = array(HeraldAdapter::CONDITION_UNCONDITIONALLY);
|
||||||
|
}
|
||||||
$config_info['conditionMap'][$field] = $field_conditions;
|
$config_info['conditionMap'][$field] = $field_conditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,9 +485,15 @@ final class HeraldRuleController extends HeraldController {
|
||||||
$config_info['rule_type'] = $rule->getRuleType();
|
$config_info['rule_type'] = $rule->getRuleType();
|
||||||
|
|
||||||
foreach ($config_info['actions'] as $action => $name) {
|
foreach ($config_info['actions'] as $action => $name) {
|
||||||
$config_info['targets'][$action] = $adapter->getValueTypeForAction(
|
try {
|
||||||
$action,
|
$action_value = $adapter->getValueTypeForAction(
|
||||||
$rule->getRuleType());
|
$action,
|
||||||
|
$rule->getRuleType());
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$action_value = array(HeraldAdapter::VALUE_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
$config_info['targets'][$action] = $action_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
$changeflag_options =
|
$changeflag_options =
|
||||||
|
|
|
@ -380,7 +380,10 @@ final class HeraldTranscriptController extends HeraldController {
|
||||||
$item->setState(PHUIObjectItemView::STATE_FAIL);
|
$item->setState(PHUIObjectItemView::STATE_FAIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
$rule = idx($action_names, $apply_xscript->getAction(), pht('Unknown'));
|
$rule = idx(
|
||||||
|
$action_names,
|
||||||
|
$apply_xscript->getAction(),
|
||||||
|
pht('Unknown Action "%s"', $apply_xscript->getAction()));
|
||||||
|
|
||||||
$item->setHeader(pht('%s: %s', $rule, $target));
|
$item->setHeader(pht('%s: %s', $rule, $target));
|
||||||
$item->addAttribute($apply_xscript->getReason());
|
$item->addAttribute($apply_xscript->getReason());
|
||||||
|
|
|
@ -272,6 +272,16 @@ final class HeraldEngine {
|
||||||
$result = false;
|
$result = false;
|
||||||
} else {
|
} else {
|
||||||
foreach ($conditions as $condition) {
|
foreach ($conditions as $condition) {
|
||||||
|
try {
|
||||||
|
$object->getHeraldField($condition->getFieldName());
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$reason = pht(
|
||||||
|
'Field "%s" does not exist!',
|
||||||
|
$condition->getFieldName());
|
||||||
|
$result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$match = $this->doesConditionMatch($rule, $condition, $object);
|
$match = $this->doesConditionMatch($rule, $condition, $object);
|
||||||
|
|
||||||
if (!$all && $match) {
|
if (!$all && $match) {
|
||||||
|
|
Loading…
Reference in a new issue