mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
feat: surface expression validation errors when creating or updating rules
This commit is contained in:
parent
b572c1dcd3
commit
438f602961
@ -74,15 +74,14 @@ class ExpressionController extends Controller
|
||||
$expressionLanguage = ExpressionLanguageFactory::get();
|
||||
$evaluator = new ActionExpressionEvaluator($expressionLanguage, $expr);
|
||||
|
||||
try {
|
||||
$evaluator->lint();
|
||||
if ($evaluator->isValid()) {
|
||||
return response()->json([
|
||||
"valid" => true,
|
||||
]);
|
||||
} catch (SyntaxError $e) {
|
||||
} else {
|
||||
return response()->json([
|
||||
"valid" => false,
|
||||
"error" => $e->getMessage()
|
||||
"error" => $evaluator->getValidationError()->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ class StoreRequest extends FormRequest
|
||||
'triggers.*.stop_processing' => [new IsBoolean()],
|
||||
'triggers.*.active' => [new IsBoolean()],
|
||||
'actions.*.type' => 'required|in:'.implode(',', $validActions),
|
||||
'actions.*.value' => 'required_if:actions.*.type,'.$contextActions.'|ruleActionValue',
|
||||
'actions.*.value' => 'required_if:actions.*.type,'.$contextActions.'|ruleActionExpression|ruleActionValue',
|
||||
'actions.*.stop_processing' => [new IsBoolean()],
|
||||
'actions.*.active' => [new IsBoolean()],
|
||||
'strict' => [new IsBoolean()],
|
||||
|
@ -140,7 +140,7 @@ class UpdateRequest extends FormRequest
|
||||
'triggers.*.stop_processing' => [new IsBoolean()],
|
||||
'triggers.*.active' => [new IsBoolean()],
|
||||
'actions.*.type' => 'required|in:'.implode(',', $validActions),
|
||||
'actions.*.value' => 'required_if:actions.*.type,'.$contextActions.'|ruleActionValue',
|
||||
'actions.*.value' => 'required_if:actions.*.type,'.$contextActions.'|ruleActionExpression|ruleActionValue',
|
||||
'actions.*.stop_processing' => [new IsBoolean()],
|
||||
'actions.*.active' => [new IsBoolean()],
|
||||
'strict' => [new IsBoolean()],
|
||||
|
@ -147,7 +147,7 @@ class RuleFormRequest extends FormRequest
|
||||
'triggers.*.type' => 'required|in:'.implode(',', $validTriggers),
|
||||
'triggers.*.value' => sprintf('required_if:triggers.*.type,%s|max:1024|min:1|ruleTriggerValue', $contextTriggers),
|
||||
'actions.*.type' => 'required|in:'.implode(',', $validActions),
|
||||
'actions.*.value' => sprintf('required_if:actions.*.type,%s|min:0|max:1024|ruleActionValue', $contextActions),
|
||||
'actions.*.value' => sprintf('required_if:actions.*.type,%s|min:0|max:1024|ruleActionExpression|ruleActionValue', $contextActions),
|
||||
'strict' => 'in:0,1',
|
||||
];
|
||||
|
||||
|
@ -31,9 +31,10 @@ class ActionExpressionEvaluator
|
||||
{
|
||||
private static array $NAMES = array("transaction");
|
||||
|
||||
private ExpressionLanguage $expressionLanguage;
|
||||
private string $expr;
|
||||
private bool $isExpression;
|
||||
private ExpressionLanguage $expressionLanguage;
|
||||
private ?SyntaxError $validationError;
|
||||
|
||||
public function __construct(ExpressionLanguage $expressionLanguage, string $expr)
|
||||
{
|
||||
@ -41,6 +42,7 @@ class ActionExpressionEvaluator
|
||||
$this->expr = $expr;
|
||||
|
||||
$this->isExpression = self::isExpression($expr);
|
||||
$this->validationError = $this->validate();
|
||||
}
|
||||
|
||||
private static function isExpression(string $expr): bool
|
||||
@ -48,17 +50,17 @@ class ActionExpressionEvaluator
|
||||
return str_starts_with($expr, "=");
|
||||
}
|
||||
|
||||
public function isValid(): bool
|
||||
private function validate(): ?SyntaxError
|
||||
{
|
||||
if (!$this->isExpression) {
|
||||
return true;
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->lint(array());
|
||||
return true;
|
||||
$this->lint();
|
||||
return null;
|
||||
} catch (SyntaxError $e) {
|
||||
return false;
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +69,7 @@ class ActionExpressionEvaluator
|
||||
$this->expressionLanguage->lint($expr, self::$NAMES);
|
||||
}
|
||||
|
||||
public function lint(): void
|
||||
private function lint(): void
|
||||
{
|
||||
if (!$this->isExpression) {
|
||||
return;
|
||||
@ -76,6 +78,16 @@ class ActionExpressionEvaluator
|
||||
$this->lintExpression(substr($this->expr, 1));
|
||||
}
|
||||
|
||||
public function isValid(): bool
|
||||
{
|
||||
return $this->validationError === null;
|
||||
}
|
||||
|
||||
public function getValidationError()
|
||||
{
|
||||
return $this->validationError;
|
||||
}
|
||||
|
||||
private function evaluateExpression(string $expr, array $journal): string
|
||||
{
|
||||
$result = $this->expressionLanguage->evaluate($expr, [
|
||||
|
@ -35,6 +35,8 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Services\Password\Verifier;
|
||||
use FireflyIII\Support\ParseDateString;
|
||||
use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator;
|
||||
use FireflyIII\TransactionRules\Factory\ExpressionLanguageFactory;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Validation\Validator;
|
||||
use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException;
|
||||
@ -253,6 +255,31 @@ class FireflyValidator extends Validator
|
||||
return 1 === $count;
|
||||
}
|
||||
|
||||
public function validateRuleActionExpression(string $attribute, string $value = null): bool
|
||||
{
|
||||
$value ??= '';
|
||||
|
||||
$el = ExpressionLanguageFactory::get();
|
||||
$evaluator = new ActionExpressionEvaluator($el, $value);
|
||||
|
||||
return $evaluator->isValid();
|
||||
}
|
||||
|
||||
public function replaceRuleActionExpression(string $message, string $attribute): string
|
||||
{
|
||||
$value = $this->getValue($attribute);
|
||||
|
||||
$el = ExpressionLanguageFactory::get();
|
||||
$evaluator = new ActionExpressionEvaluator($el, $value);
|
||||
$err = $evaluator->getValidationError();
|
||||
|
||||
if ($err == null) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
return str_replace(":error", $err->getMessage(), $message);
|
||||
}
|
||||
|
||||
public function validateRuleActionValue(string $attribute, string $value = null): bool
|
||||
{
|
||||
// first, get the index from this string:
|
||||
@ -268,6 +295,11 @@ class FireflyValidator extends Validator
|
||||
return false;
|
||||
}
|
||||
|
||||
// if value is an expression, assume valid
|
||||
if (str_starts_with($value, '=')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if it's set_budget, verify the budget name:
|
||||
if ('set_budget' === $actionType) {
|
||||
/** @var BudgetRepositoryInterface $repository */
|
||||
|
@ -46,6 +46,7 @@ return [
|
||||
'reconciled_forbidden_field' => 'This transaction is already reconciled, you cannot change the ":field"',
|
||||
'deleted_user' => 'Due to security constraints, you cannot register using this email address.',
|
||||
'rule_trigger_value' => 'This value is invalid for the selected trigger.',
|
||||
'rule_action_expression' => 'Invalid expression. :error',
|
||||
'rule_action_value' => 'This value is invalid for the selected action.',
|
||||
'file_already_attached' => 'Uploaded file ":name" is already attached to this object.',
|
||||
'file_attached' => 'Successfully uploaded file ":name".',
|
||||
|
Loading…
Reference in New Issue
Block a user