Validate that fields in filters are provided by subjects available in the automation
[MAILPOET-5586]
This commit is contained in:
@@ -2,7 +2,11 @@
|
||||
|
||||
namespace MailPoet\Automation\Engine\Validation\AutomationRules;
|
||||
|
||||
use MailPoet\Automation\Engine\Control\SubjectTransformerHandler;
|
||||
use MailPoet\Automation\Engine\Data\Automation;
|
||||
use MailPoet\Automation\Engine\Data\Filter;
|
||||
use MailPoet\Automation\Engine\Integration\Payload;
|
||||
use MailPoet\Automation\Engine\Integration\Subject;
|
||||
use MailPoet\Automation\Engine\Registry;
|
||||
use MailPoet\Automation\Engine\Validation\AutomationGraph\AutomationNode;
|
||||
use MailPoet\Automation\Engine\Validation\AutomationGraph\AutomationNodeVisitor;
|
||||
@@ -13,14 +17,19 @@ class ValidStepFiltersRule implements AutomationNodeVisitor {
|
||||
/** @var Registry */
|
||||
private $registry;
|
||||
|
||||
/** @var SubjectTransformerHandler */
|
||||
private $subjectTransformerHandler;
|
||||
|
||||
/** @var Validator */
|
||||
private $validator;
|
||||
|
||||
public function __construct(
|
||||
Registry $registry,
|
||||
SubjectTransformerHandler $subjectTransformerHandler,
|
||||
Validator $validator
|
||||
) {
|
||||
$this->registry = $registry;
|
||||
$this->subjectTransformerHandler = $subjectTransformerHandler;
|
||||
$this->validator = $validator;
|
||||
}
|
||||
|
||||
@@ -41,6 +50,17 @@ class ValidStepFiltersRule implements AutomationNodeVisitor {
|
||||
$this->validator->validate($registryFilter->getArgsSchema($filter->getCondition()), $filter->getArgs());
|
||||
} catch (ValidationException $e) {
|
||||
$errors[$filter->getId()] = $e->getWpError()->get_error_code();
|
||||
continue;
|
||||
}
|
||||
|
||||
// ensure that the field is available with the provided subjects
|
||||
$subjectKeys = $this->subjectTransformerHandler->getSubjectKeysForAutomation($automation);
|
||||
$filterSubject = $this->getFilterSubject($filter);
|
||||
if (!$filterSubject) {
|
||||
$errors[$filter->getId()] = __('Field not found', 'mailpoet');
|
||||
} elseif (!in_array($filterSubject->getKey(), $subjectKeys, true)) {
|
||||
// translators: %s is the name of a subject (data structure) that provides the field
|
||||
$errors[$filter->getId()] = sprintf(__('A trigger that provides %s is required', 'mailpoet'), $filterSubject->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,4 +76,16 @@ class ValidStepFiltersRule implements AutomationNodeVisitor {
|
||||
|
||||
public function complete(Automation $automation): void {
|
||||
}
|
||||
|
||||
/** @return Subject<Payload> */
|
||||
private function getFilterSubject(Filter $filter): ?Subject {
|
||||
foreach ($this->registry->getSubjects() as $subject) {
|
||||
foreach ($subject->getFields() as $field) {
|
||||
if ($field->getKey() === $filter->getFieldKey()) {
|
||||
return $subject;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -6,33 +6,45 @@ require_once __DIR__ . '/AutomationRuleTest.php';
|
||||
|
||||
use Codeception\Stub\Expected;
|
||||
use MailPoet\Automation\Engine\Control\RootStep;
|
||||
use MailPoet\Automation\Engine\Control\SubjectTransformerHandler;
|
||||
use MailPoet\Automation\Engine\Data\Automation;
|
||||
use MailPoet\Automation\Engine\Data\Field;
|
||||
use MailPoet\Automation\Engine\Data\Filter as FilterData;
|
||||
use MailPoet\Automation\Engine\Data\FilterGroup;
|
||||
use MailPoet\Automation\Engine\Data\Filters;
|
||||
use MailPoet\Automation\Engine\Data\Step;
|
||||
use MailPoet\Automation\Engine\Data\Subject as SubjectData;
|
||||
use MailPoet\Automation\Engine\Integration\Filter;
|
||||
use MailPoet\Automation\Engine\Integration\Payload;
|
||||
use MailPoet\Automation\Engine\Integration\Subject;
|
||||
use MailPoet\Automation\Engine\Registry;
|
||||
use MailPoet\Automation\Engine\Validation\AutomationGraph\AutomationWalker;
|
||||
use MailPoet\Automation\Engine\Validation\AutomationRules\AutomationRuleTest;
|
||||
use MailPoet\Automation\Engine\Validation\AutomationRules\ValidStepFiltersRule;
|
||||
use MailPoet\Validator\Builder;
|
||||
use MailPoet\Validator\Schema\ObjectSchema;
|
||||
use MailPoet\Validator\ValidationException;
|
||||
use MailPoet\Validator\Validator;
|
||||
|
||||
class ValidStepFiltersRuleTest extends AutomationRuleTest {
|
||||
public function testItRunsFiltersValidation(): void {
|
||||
$field = new Field('test:field', Field::TYPE_STRING, 'Test field', [$this, 'callback']);
|
||||
$registry = $this->make(Registry::class, [
|
||||
'steps' => ['core:root' => new RootStep()],
|
||||
'filters' => ['string' => $this->getFilter()],
|
||||
'subjects' => [$this->getSubject('test:subject-key', 'Test subject', [$field])],
|
||||
]);
|
||||
|
||||
$validator = $this->make(Validator::class, [
|
||||
'validate' => Expected::once(),
|
||||
]);
|
||||
|
||||
$filters = [new FilterData('f1', 'string', 'test:key', 'is', ['value' => 'test'])];
|
||||
$rule = new ValidStepFiltersRule($registry, $validator);
|
||||
$transformer = $this->make(SubjectTransformerHandler::class, [
|
||||
'getSubjectKeysForAutomation' => ['test:subject-key'],
|
||||
]);
|
||||
|
||||
$filters = [new FilterData('f1', 'string', 'test:field', 'is', ['value' => 'test'])];
|
||||
$rule = new ValidStepFiltersRule($registry, $transformer, $validator);
|
||||
$automation = $this->getAutomation($filters);
|
||||
(new AutomationWalker())->walk($automation, [$rule]);
|
||||
}
|
||||
@@ -42,13 +54,48 @@ class ValidStepFiltersRuleTest extends AutomationRuleTest {
|
||||
$validator = $this->make(Validator::class, [
|
||||
'validate' => Expected::never(),
|
||||
]);
|
||||
$transformer = $this->make(SubjectTransformerHandler::class);
|
||||
|
||||
$filters = [new FilterData('f1', 'string', 'test:key', 'is', ['value' => 'test'])];
|
||||
$rule = new ValidStepFiltersRule($registry, $validator);
|
||||
$rule = new ValidStepFiltersRule($registry, $transformer, $validator);
|
||||
$automation = $this->getAutomation($filters);
|
||||
(new AutomationWalker())->walk($automation, [$rule]);
|
||||
}
|
||||
|
||||
public function testItValidatesFieldsAreProvidedBySubjects(): void {
|
||||
$field = new Field('test:field', Field::TYPE_STRING, 'Test field', [$this, 'callback']);
|
||||
$registry = $this->make(Registry::class, [
|
||||
'steps' => ['core:root' => new RootStep()],
|
||||
'filters' => ['string' => $this->getFilter()],
|
||||
'subjects' => [$this->getSubject('test:subject', 'Test subject', [$field])],
|
||||
]);
|
||||
|
||||
$validator = $this->make(Validator::class, [
|
||||
'validate' => Expected::exactly(2),
|
||||
]);
|
||||
|
||||
$transformer = $this->make(SubjectTransformerHandler::class, [
|
||||
'getSubjectKeysForAutomation' => [],
|
||||
]);
|
||||
|
||||
$filters = [
|
||||
new FilterData('f1', 'string', 'test:field', 'is', ['value' => 'test']),
|
||||
new FilterData('f2', 'string', 'test:unknown-field', 'is', ['value' => 'test']),
|
||||
];
|
||||
$rule = new ValidStepFiltersRule($registry, $transformer, $validator);
|
||||
$automation = $this->getAutomation($filters);
|
||||
|
||||
$error = null;
|
||||
try {
|
||||
(new AutomationWalker())->walk($automation, [$rule]);
|
||||
} catch (ValidationException $error) {
|
||||
$this->assertCount(2, $error->getErrors());
|
||||
$this->assertSame('A trigger that provides Test subject is required', $error->getErrors()['f1']);
|
||||
$this->assertSame('Field not found', $error->getErrors()['f2']);
|
||||
}
|
||||
$this->assertNotNull($error);
|
||||
}
|
||||
|
||||
private function getAutomation(array $filters): Automation {
|
||||
$filters = new Filters('and', [new FilterGroup('g1', 'and', $filters)]);
|
||||
return $this->make(Automation::class, [
|
||||
@@ -77,4 +124,49 @@ class ValidStepFiltersRuleTest extends AutomationRuleTest {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @return Subject<Payload> */
|
||||
private function getSubject(string $key, string $name, array $fields): Subject {
|
||||
return new class($key, $name, $fields) implements Subject {
|
||||
/** @var string */
|
||||
private $key;
|
||||
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
/** @var array */
|
||||
private $fields;
|
||||
|
||||
public function __construct(
|
||||
string $key,
|
||||
string $name,
|
||||
array $fields
|
||||
) {
|
||||
$this->key = $key;
|
||||
$this->name = $name;
|
||||
$this->fields = $fields;
|
||||
}
|
||||
|
||||
public function getKey(): string {
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getArgsSchema(): ObjectSchema {
|
||||
return Builder::object();
|
||||
}
|
||||
|
||||
public function getFields(): array {
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
public function getPayload(SubjectData $subjectData): Payload {
|
||||
return new class() implements Payload {
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user