Introduce subject transformation
[MAILPOET-4935]
This commit is contained in:
@@ -10,6 +10,7 @@ use MailPoet\Automation\Engine\Data\AutomationRunLog;
|
|||||||
use MailPoet\Automation\Engine\Data\Step;
|
use MailPoet\Automation\Engine\Data\Step;
|
||||||
use MailPoet\Automation\Engine\Data\StepRunArgs;
|
use MailPoet\Automation\Engine\Data\StepRunArgs;
|
||||||
use MailPoet\Automation\Engine\Data\StepValidationArgs;
|
use MailPoet\Automation\Engine\Data\StepValidationArgs;
|
||||||
|
use MailPoet\Automation\Engine\Data\Subject as SubjectData;
|
||||||
use MailPoet\Automation\Engine\Data\SubjectEntry;
|
use MailPoet\Automation\Engine\Data\SubjectEntry;
|
||||||
use MailPoet\Automation\Engine\Exceptions;
|
use MailPoet\Automation\Engine\Exceptions;
|
||||||
use MailPoet\Automation\Engine\Exceptions\InvalidStateException;
|
use MailPoet\Automation\Engine\Exceptions\InvalidStateException;
|
||||||
@@ -55,6 +56,9 @@ class StepHandler {
|
|||||||
/** @var Registry */
|
/** @var Registry */
|
||||||
private $registry;
|
private $registry;
|
||||||
|
|
||||||
|
/** @var SubjectTransformerHandler */
|
||||||
|
private $subjectTransformerHandler;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ActionScheduler $actionScheduler,
|
ActionScheduler $actionScheduler,
|
||||||
ActionStepRunner $actionStepRunner,
|
ActionStepRunner $actionStepRunner,
|
||||||
@@ -64,7 +68,8 @@ class StepHandler {
|
|||||||
AutomationRunStorage $automationRunStorage,
|
AutomationRunStorage $automationRunStorage,
|
||||||
AutomationRunLogStorage $automationRunLogStorage,
|
AutomationRunLogStorage $automationRunLogStorage,
|
||||||
AutomationStorage $automationStorage,
|
AutomationStorage $automationStorage,
|
||||||
Registry $registry
|
Registry $registry,
|
||||||
|
SubjectTransformerHandler $subjectTransformerHandler
|
||||||
) {
|
) {
|
||||||
$this->actionScheduler = $actionScheduler;
|
$this->actionScheduler = $actionScheduler;
|
||||||
$this->actionStepRunner = $actionStepRunner;
|
$this->actionStepRunner = $actionStepRunner;
|
||||||
@@ -75,6 +80,7 @@ class StepHandler {
|
|||||||
$this->automationRunLogStorage = $automationRunLogStorage;
|
$this->automationRunLogStorage = $automationRunLogStorage;
|
||||||
$this->automationStorage = $automationStorage;
|
$this->automationStorage = $automationStorage;
|
||||||
$this->registry = $registry;
|
$this->registry = $registry;
|
||||||
|
$this->subjectTransformerHandler = $subjectTransformerHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function initialize(): void {
|
public function initialize(): void {
|
||||||
@@ -217,6 +223,7 @@ class StepHandler {
|
|||||||
$subjectEntries = [];
|
$subjectEntries = [];
|
||||||
foreach ($requiredSubjectKeys as $key) {
|
foreach ($requiredSubjectKeys as $key) {
|
||||||
$subjectData = $subjectDataMap[$key] ?? null;
|
$subjectData = $subjectDataMap[$key] ?? null;
|
||||||
|
$subjectData = $subjectData ?? $this->transformSubject($key, $automationRun);
|
||||||
if (!$subjectData) {
|
if (!$subjectData) {
|
||||||
throw Exceptions::subjectDataNotFound($key, $automationRun->getId());
|
throw Exceptions::subjectDataNotFound($key, $automationRun->getId());
|
||||||
}
|
}
|
||||||
@@ -227,6 +234,13 @@ class StepHandler {
|
|||||||
return $subjectEntries;
|
return $subjectEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return SubjectData[]|null
|
||||||
|
*/
|
||||||
|
private function transformSubject(string $target, AutomationRun $automationRun): ?array {
|
||||||
|
return $this->subjectTransformerHandler->transformSubjectData($target, $automationRun);
|
||||||
|
}
|
||||||
|
|
||||||
private function postProcessAutomationRun(int $automationRunId): void {
|
private function postProcessAutomationRun(int $automationRunId): void {
|
||||||
$automationRun = $this->automationRunStorage->getAutomationRun($automationRunId);
|
$automationRun = $this->automationRunStorage->getAutomationRun($automationRunId);
|
||||||
if (!$automationRun) {
|
if (!$automationRun) {
|
||||||
|
@@ -0,0 +1,142 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace MailPoet\Automation\Engine\Control;
|
||||||
|
|
||||||
|
use MailPoet\Automation\Engine\Data\Automation;
|
||||||
|
use MailPoet\Automation\Engine\Data\AutomationRun;
|
||||||
|
use MailPoet\Automation\Engine\Data\Step as StepData;
|
||||||
|
use MailPoet\Automation\Engine\Data\Subject as SubjectData;
|
||||||
|
use MailPoet\Automation\Engine\Integration\Step;
|
||||||
|
use MailPoet\Automation\Engine\Integration\SubjectTransformer;
|
||||||
|
use MailPoet\Automation\Engine\Registry;
|
||||||
|
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||||
|
|
||||||
|
class SubjectTransformerHandler {
|
||||||
|
|
||||||
|
/* @var Registry */
|
||||||
|
private $registry;
|
||||||
|
|
||||||
|
/* @var AutomationStorage */
|
||||||
|
private $automationStorage;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
Registry $registry,
|
||||||
|
AutomationStorage $automationStorage
|
||||||
|
) {
|
||||||
|
$this->registry = $registry;
|
||||||
|
$this->automationStorage = $automationStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return string[] */
|
||||||
|
public function subjectKeysForAutomation(Automation $automation): array {
|
||||||
|
$configuredTriggers = array_filter(
|
||||||
|
$automation->getSteps(),
|
||||||
|
function (StepData $step): bool {
|
||||||
|
return $step->getType() === StepData::TYPE_TRIGGER;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$triggers = array_filter(array_map(
|
||||||
|
function(StepData $step): ?Step {
|
||||||
|
return $this->registry->getStep($step->getKey());
|
||||||
|
},
|
||||||
|
$configuredTriggers
|
||||||
|
));
|
||||||
|
|
||||||
|
$triggerKeys = [];
|
||||||
|
foreach ($triggers as $trigger) {
|
||||||
|
$triggerKeys[$trigger->getKey()] = $trigger->getSubjectKeys();
|
||||||
|
}
|
||||||
|
if (!$triggerKeys) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$triggerKeys = count($triggerKeys) === 1 ? current($triggerKeys) : array_intersect(...array_values($triggerKeys));
|
||||||
|
$possibleKeys = [];
|
||||||
|
foreach ($triggerKeys as $key) {
|
||||||
|
$possibleKeys = array_merge($possibleKeys, $this->getPossibleTransformations($key));
|
||||||
|
}
|
||||||
|
return array_unique(array_values(array_merge($triggerKeys, $possibleKeys)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPossibleTransformations(string $key, string $stopKey = null): array {
|
||||||
|
if (!$stopKey) {
|
||||||
|
$stopKey = $key;
|
||||||
|
}
|
||||||
|
$allTransformer = $this->registry->getSubjectTransformer();
|
||||||
|
$possibleTransformer = array_filter(
|
||||||
|
$allTransformer,
|
||||||
|
function (SubjectTransformer $transformer) use ($key): bool {
|
||||||
|
return $transformer->accepts() === $key;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$possibleKeys = [];
|
||||||
|
foreach ($possibleTransformer as $transformer) {
|
||||||
|
$possibleKey = $transformer->returns();
|
||||||
|
if ($possibleKey === $stopKey) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$possibleKeys[$possibleKey] = $possibleKey;
|
||||||
|
$possibleKeys = array_merge(
|
||||||
|
$possibleKeys,
|
||||||
|
$this->getPossibleTransformations($possibleKey, $stopKey)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $possibleKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return SubjectData[]|null
|
||||||
|
*/
|
||||||
|
public function transformSubjectData(string $target, AutomationRun $automationRun): ?array {
|
||||||
|
$automation = $this->automationStorage->getAutomation($automationRun->getAutomationId(), $automationRun->getVersionId());
|
||||||
|
if (!$automation || !in_array($target, $this->subjectKeysForAutomation($automation), true)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$transformedSubjects = [];
|
||||||
|
$subjects = $automationRun->getSubjects();
|
||||||
|
foreach ($subjects as $subject) {
|
||||||
|
$transformerChain = $this->getTransformerChain($subject->getKey(), $target);
|
||||||
|
if (!$transformerChain) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach ($transformerChain as $transformer) {
|
||||||
|
$subject = $transformer->transform($subject);
|
||||||
|
}
|
||||||
|
$transformedSubjects[] = $subject;
|
||||||
|
}
|
||||||
|
return count($transformedSubjects) > 0 ? $transformedSubjects : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return SubjectTransformer[]
|
||||||
|
*/
|
||||||
|
private function getTransformerChain(string $from, string $to): array {
|
||||||
|
$transformers = $this->registry->getSubjectTransformer();
|
||||||
|
$transformerChain = [];
|
||||||
|
|
||||||
|
//walk the graph of transformers to find the shortest path
|
||||||
|
$queue = [];
|
||||||
|
$queue[] = [$from, []];
|
||||||
|
while (count($queue) > 0) {
|
||||||
|
$current = array_shift($queue);
|
||||||
|
$currentKey = $current[0];
|
||||||
|
$currentPath = $current[1];
|
||||||
|
if ($currentKey === $to) {
|
||||||
|
$transformerChain = $currentPath;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
foreach ($transformers as $transformer) {
|
||||||
|
if ($transformer->accepts() === $currentKey) {
|
||||||
|
$newPath = $currentPath;
|
||||||
|
$newPath[] = $transformer;
|
||||||
|
$queue[] = [$transformer->returns(), $newPath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $transformerChain;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,13 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace MailPoet\Automation\Engine\Integration;
|
||||||
|
|
||||||
|
use MailPoet\Automation\Engine\Data\Subject;
|
||||||
|
|
||||||
|
interface SubjectTransformer {
|
||||||
|
public function transform(Subject $data): Subject;
|
||||||
|
|
||||||
|
public function returns(): string;
|
||||||
|
|
||||||
|
public function accepts(): string;
|
||||||
|
}
|
@@ -7,6 +7,7 @@ use MailPoet\Automation\Engine\Integration\Action;
|
|||||||
use MailPoet\Automation\Engine\Integration\Payload;
|
use MailPoet\Automation\Engine\Integration\Payload;
|
||||||
use MailPoet\Automation\Engine\Integration\Step;
|
use MailPoet\Automation\Engine\Integration\Step;
|
||||||
use MailPoet\Automation\Engine\Integration\Subject;
|
use MailPoet\Automation\Engine\Integration\Subject;
|
||||||
|
use MailPoet\Automation\Engine\Integration\SubjectTransformer;
|
||||||
use MailPoet\Automation\Engine\Integration\Trigger;
|
use MailPoet\Automation\Engine\Integration\Trigger;
|
||||||
|
|
||||||
class Registry {
|
class Registry {
|
||||||
@@ -16,6 +17,9 @@ class Registry {
|
|||||||
/** @var array<string, Subject<Payload>> */
|
/** @var array<string, Subject<Payload>> */
|
||||||
private $subjects = [];
|
private $subjects = [];
|
||||||
|
|
||||||
|
/** @var SubjectTransformer[] */
|
||||||
|
private $subjectTransformer = [];
|
||||||
|
|
||||||
/** @var array<string, Trigger> */
|
/** @var array<string, Trigger> */
|
||||||
private $triggers = [];
|
private $triggers = [];
|
||||||
|
|
||||||
@@ -55,6 +59,14 @@ class Registry {
|
|||||||
return $this->subjects;
|
return $this->subjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addSubjectTransformer(SubjectTransformer $transformer): void {
|
||||||
|
$this->subjectTransformer[] = $transformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubjectTransformer(): array {
|
||||||
|
return $this->subjectTransformer;
|
||||||
|
}
|
||||||
|
|
||||||
public function addStep(Step $step): void {
|
public function addStep(Step $step): void {
|
||||||
if ($step instanceof Trigger) {
|
if ($step instanceof Trigger) {
|
||||||
$this->addTrigger($step);
|
$this->addTrigger($step);
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace MailPoet\Automation\Engine\Validation\AutomationRules;
|
namespace MailPoet\Automation\Engine\Validation\AutomationRules;
|
||||||
|
|
||||||
|
use MailPoet\Automation\Engine\Control\SubjectTransformerHandler;
|
||||||
use MailPoet\Automation\Engine\Data\Automation;
|
use MailPoet\Automation\Engine\Data\Automation;
|
||||||
use MailPoet\Automation\Engine\Data\Step;
|
use MailPoet\Automation\Engine\Data\Step;
|
||||||
use MailPoet\Automation\Engine\Exceptions;
|
use MailPoet\Automation\Engine\Exceptions;
|
||||||
@@ -13,10 +14,15 @@ class ValidStepOrderRule implements AutomationNodeVisitor {
|
|||||||
/** @var Registry */
|
/** @var Registry */
|
||||||
private $registry;
|
private $registry;
|
||||||
|
|
||||||
|
/** @var SubjectTransformerHandler */
|
||||||
|
private $subjectTransformerHandler;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
Registry $registry
|
Registry $registry,
|
||||||
|
SubjectTransformerHandler $subjectTransformerHandler
|
||||||
) {
|
) {
|
||||||
$this->registry = $registry;
|
$this->registry = $registry;
|
||||||
|
$this->subjectTransformerHandler = $subjectTransformerHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function initialize(Automation $automation): void {
|
public function initialize(Automation $automation): void {
|
||||||
@@ -39,7 +45,7 @@ class ValidStepOrderRule implements AutomationNodeVisitor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$subjectKeys = $this->collectSubjectKeys($automation, $node->getParents());
|
$subjectKeys = $this->subjectTransformerHandler->subjectKeysForAutomation($automation);
|
||||||
$missingSubjectKeys = array_diff($requiredSubjectKeys, $subjectKeys);
|
$missingSubjectKeys = array_diff($requiredSubjectKeys, $subjectKeys);
|
||||||
if (count($missingSubjectKeys) > 0) {
|
if (count($missingSubjectKeys) > 0) {
|
||||||
throw Exceptions::missingRequiredSubjects($step, $missingSubjectKeys);
|
throw Exceptions::missingRequiredSubjects($step, $missingSubjectKeys);
|
||||||
@@ -48,24 +54,4 @@ class ValidStepOrderRule implements AutomationNodeVisitor {
|
|||||||
|
|
||||||
public function complete(Automation $automation): void {
|
public function complete(Automation $automation): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Step[] $parents
|
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
private function collectSubjectKeys(Automation $automation, array $parents): array {
|
|
||||||
$triggers = array_filter($parents, function (Step $step) {
|
|
||||||
return $step->getType() === Step::TYPE_TRIGGER;
|
|
||||||
});
|
|
||||||
|
|
||||||
$subjectKeys = [];
|
|
||||||
foreach ($triggers as $trigger) {
|
|
||||||
$registryTrigger = $this->registry->getTrigger($trigger->getKey());
|
|
||||||
if (!$registryTrigger) {
|
|
||||||
throw Exceptions::automationTriggerNotFound($automation->getId(), $trigger->getKey());
|
|
||||||
}
|
|
||||||
$subjectKeys = array_merge($subjectKeys, $registryTrigger->getSubjectKeys());
|
|
||||||
}
|
|
||||||
return array_unique($subjectKeys);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -123,6 +123,7 @@ class ContainerConfigurator implements IContainerConfigurator {
|
|||||||
$container->autowire(\MailPoet\Automation\Engine\Control\ActionScheduler::class)->setPublic(true);
|
$container->autowire(\MailPoet\Automation\Engine\Control\ActionScheduler::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Automation\Engine\Control\RootStep::class)->setPublic(true);
|
$container->autowire(\MailPoet\Automation\Engine\Control\RootStep::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Automation\Engine\Control\StepHandler::class)->setPublic(true);
|
$container->autowire(\MailPoet\Automation\Engine\Control\StepHandler::class)->setPublic(true);
|
||||||
|
$container->autowire(\MailPoet\Automation\Engine\Control\SubjectTransformerHandler::class)->setPublic(true)->setShared(false);
|
||||||
$container->autowire(\MailPoet\Automation\Engine\Control\SubjectLoader::class)->setPublic(true);
|
$container->autowire(\MailPoet\Automation\Engine\Control\SubjectLoader::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Automation\Engine\Control\Steps\ActionStepRunner::class)->setPublic(true);
|
$container->autowire(\MailPoet\Automation\Engine\Control\Steps\ActionStepRunner::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Automation\Engine\Control\TriggerHandler::class)->setPublic(true);
|
$container->autowire(\MailPoet\Automation\Engine\Control\TriggerHandler::class)->setPublic(true);
|
||||||
|
@@ -0,0 +1,199 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace MailPoet\Automation\Engine\Control;
|
||||||
|
|
||||||
|
use MailPoet\Automation\Engine\Data\Automation;
|
||||||
|
use MailPoet\Automation\Engine\Data\AutomationRun;
|
||||||
|
use MailPoet\Automation\Engine\Data\Step;
|
||||||
|
use MailPoet\Automation\Engine\Data\Subject;
|
||||||
|
use MailPoet\Automation\Engine\Integration\SubjectTransformer;
|
||||||
|
use MailPoet\Automation\Engine\Integration\Trigger;
|
||||||
|
use MailPoet\Automation\Engine\Registry;
|
||||||
|
use MailPoet\Automation\Engine\Storage\AutomationStorage;
|
||||||
|
use MailPoetUnitTest;
|
||||||
|
|
||||||
|
class SubjectTransformerHandlerTest extends MailPoetUnitTest {
|
||||||
|
public function testItFindsAllPossibleSubjects(): void {
|
||||||
|
$triggerSubject = 'subject_a';
|
||||||
|
$graphKeys = [
|
||||||
|
$triggerSubject => ['subject_b1', 'subject_b2'],
|
||||||
|
'subject_b1' => ['subject_c1'],
|
||||||
|
'subject_b2' => ['subject_c2'],
|
||||||
|
'subject_c1' => ['subject_d1'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$transformers = [];
|
||||||
|
foreach ($graphKeys as $from => $tos) {
|
||||||
|
foreach ($tos as $to) {
|
||||||
|
$transformer = $this->createMock(SubjectTransformer::class);
|
||||||
|
$transformer->method('returns')->willReturn($to);
|
||||||
|
$transformer->method('accepts')->willReturn($from);
|
||||||
|
$transformers[] = $transformer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$anotherRegisteredTransformer = $this->createMock(SubjectTransformer::class);
|
||||||
|
$anotherRegisteredTransformer->method('returns')->willReturn('yet_another_subject');
|
||||||
|
$anotherRegisteredTransformer->method('accepts')->willReturn('another_unrelated_subject');
|
||||||
|
$transformers[] = $anotherRegisteredTransformer;
|
||||||
|
|
||||||
|
$trigger = $this->createMock(Trigger::class);
|
||||||
|
$trigger->expects($this->any())->method('getKey')->willReturn('trigger');
|
||||||
|
$trigger->expects($this->any())->method('getSubjectKeys')->willReturn([$triggerSubject]);
|
||||||
|
$registry = $this->createMock(Registry::class);
|
||||||
|
$registry->expects($this->any())->method('getSubjectTransformer')->willReturn($transformers);
|
||||||
|
$registry->expects($this->any())->method('getStep')->willReturnCallback(function($key) use ($trigger){
|
||||||
|
return $key === 'trigger' ? $trigger : null;
|
||||||
|
});
|
||||||
|
$storage = $this->createMock(AutomationStorage::class);
|
||||||
|
$testee = new SubjectTransformerHandler($registry, $storage);
|
||||||
|
|
||||||
|
$triggerData = $this->createMock(Step::class);
|
||||||
|
$triggerData->expects($this->any())->method('getType')->willReturn(Step::TYPE_TRIGGER);
|
||||||
|
$triggerData->expects($this->any())->method('getKey')->willReturn('trigger');
|
||||||
|
$automation = $this->createMock(Automation::class);
|
||||||
|
$automation->method('getSteps')->willReturn([$triggerData]);
|
||||||
|
$result = $testee->subjectKeysForAutomation($automation);
|
||||||
|
$this->assertEquals(['subject_a', 'subject_b1', 'subject_c1', 'subject_d1', 'subject_b2', 'subject_c2'], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItDoesNotRunInfiniteWhileFindingAllSubjects(): void {
|
||||||
|
$triggerSubject = 'subject_a';
|
||||||
|
$graphKeys = [
|
||||||
|
$triggerSubject => 'subject_b',
|
||||||
|
'subject_b' => 'subject_c',
|
||||||
|
'subject_c' => 'subject_a',
|
||||||
|
];
|
||||||
|
|
||||||
|
$transformers = [];
|
||||||
|
foreach ($graphKeys as $from => $to) {
|
||||||
|
$transformer = $this->createMock(SubjectTransformer::class);
|
||||||
|
$transformer->method('returns')->willReturn($to);
|
||||||
|
$transformer->method('accepts')->willReturn($from);
|
||||||
|
$transformers[] = $transformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
$trigger = $this->createMock(Trigger::class);
|
||||||
|
$trigger->expects($this->any())->method('getKey')->willReturn('trigger');
|
||||||
|
$trigger->expects($this->any())->method('getSubjectKeys')->willReturn([$triggerSubject]);
|
||||||
|
$registry = $this->createMock(Registry::class);
|
||||||
|
$registry->expects($this->any())->method('getSubjectTransformer')->willReturn($transformers);
|
||||||
|
$registry->expects($this->any())->method('getStep')->willReturnCallback(function($key) use ($trigger){
|
||||||
|
return $key === 'trigger' ? $trigger : null;
|
||||||
|
});
|
||||||
|
$storage = $this->createMock(AutomationStorage::class);
|
||||||
|
$testee = new SubjectTransformerHandler($registry, $storage);
|
||||||
|
|
||||||
|
$triggerData = $this->createMock(Step::class);
|
||||||
|
$triggerData->expects($this->any())->method('getType')->willReturn(Step::TYPE_TRIGGER);
|
||||||
|
$triggerData->expects($this->any())->method('getKey')->willReturn('trigger');
|
||||||
|
$automation = $this->createMock(Automation::class);
|
||||||
|
$automation->method('getSteps')->willReturn([$triggerData]);
|
||||||
|
$result = $testee->subjectKeysForAutomation($automation);
|
||||||
|
$this->assertEquals(['subject_a', 'subject_b', 'subject_c'], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItReturnsOnlyKeysInCommonForMultipleTriggers(): void {
|
||||||
|
$trigger1Keys = ['a', 'b', 'c'];
|
||||||
|
$trigger1 = $this->createMock(Trigger::class);
|
||||||
|
$trigger1->expects($this->any())->method('getKey')->willReturn('trigger1');
|
||||||
|
$trigger1->expects($this->any())->method('getSubjectKeys')->willReturn($trigger1Keys);
|
||||||
|
|
||||||
|
$trigger2Keys = ['b', 'c', 'd'];
|
||||||
|
$trigger2 = $this->createMock(Trigger::class);
|
||||||
|
$trigger2->expects($this->any())->method('getKey')->willReturn('trigger2');
|
||||||
|
$trigger2->expects($this->any())->method('getSubjectKeys')->willReturn($trigger2Keys);
|
||||||
|
|
||||||
|
$registry = $this->createMock(Registry::class);
|
||||||
|
$registry->expects($this->any())->method('getSubjectTransformer')->willReturn([]);
|
||||||
|
$registry->expects($this->any())->method('getStep')->willReturnCallback(function($key) use ($trigger1, $trigger2){
|
||||||
|
if ($key === 'trigger1') {
|
||||||
|
return $trigger1;
|
||||||
|
}
|
||||||
|
if ($key === 'trigger2') {
|
||||||
|
return $trigger2;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
$storage = $this->createMock(AutomationStorage::class);
|
||||||
|
$testee = new SubjectTransformerHandler($registry, $storage);
|
||||||
|
|
||||||
|
$trigger1Data = $this->createMock(Step::class);
|
||||||
|
$trigger1Data->expects($this->any())->method('getType')->willReturn(Step::TYPE_TRIGGER);
|
||||||
|
$trigger1Data->expects($this->any())->method('getKey')->willReturn('trigger1');
|
||||||
|
|
||||||
|
$trigger2Data = $this->createMock(Step::class);
|
||||||
|
$trigger2Data->expects($this->any())->method('getType')->willReturn(Step::TYPE_TRIGGER);
|
||||||
|
$trigger2Data->expects($this->any())->method('getKey')->willReturn('trigger2');
|
||||||
|
|
||||||
|
$automation = $this->createMock(Automation::class);
|
||||||
|
$automation->method('getSteps')->willReturn([$trigger1Data, $trigger2Data]);
|
||||||
|
$result = $testee->subjectKeysForAutomation($automation);
|
||||||
|
$this->assertEquals(['b', 'c'], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItTransformsSubjects(): void {
|
||||||
|
|
||||||
|
$subjectTransformerStart = $this->createMock(SubjectTransformer::class);
|
||||||
|
$subjectTransformerStart->expects($this->any())->method('accepts')->willReturn('from');
|
||||||
|
$subjectTransformerStart->expects($this->any())->method('returns')->willReturn('middle');
|
||||||
|
$subjectTransformerStart->expects($this->any())->method('transform')->willReturnCallback(function($subject) {
|
||||||
|
if ($subject->getKey() === 'from') {
|
||||||
|
return new Subject('middle', []);
|
||||||
|
}
|
||||||
|
return $subject;
|
||||||
|
});
|
||||||
|
|
||||||
|
$subjectTransformerEnd = $this->createMock(SubjectTransformer::class);
|
||||||
|
$subjectTransformerEnd->expects($this->any())->method('accepts')->willReturn('middle');
|
||||||
|
$subjectTransformerEnd->expects($this->any())->method('returns')->willReturn('to');
|
||||||
|
$subjectTransformerEnd->expects($this->any())->method('transform')->willReturnCallback(function($subject) {
|
||||||
|
if ($subject->getKey() === 'middle') {
|
||||||
|
return new Subject('to', []);
|
||||||
|
}
|
||||||
|
return $subject;
|
||||||
|
});
|
||||||
|
|
||||||
|
$transformer = [
|
||||||
|
$subjectTransformerEnd,
|
||||||
|
$subjectTransformerStart,
|
||||||
|
];
|
||||||
|
$automation = $this->createMock(Automation::class);
|
||||||
|
$triggerStep = $this->createMock(Step::class);
|
||||||
|
$triggerStep->expects($this->any())->method('getType')->willReturn(Step::TYPE_TRIGGER);
|
||||||
|
$triggerStep->expects($this->any())->method('getKey')->willReturn('trigger');
|
||||||
|
$automation->expects($this->any())->method('getSteps')->willReturn([$triggerStep]);
|
||||||
|
|
||||||
|
$registry = $this->createMock(Registry::class);
|
||||||
|
$registry->expects($this->any())->method('getStep')->willReturnCallback(
|
||||||
|
function($key) {
|
||||||
|
if ($key !== 'trigger') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$trigger = $this->createMock(Trigger::class);
|
||||||
|
$trigger->expects($this->any())->method('getKey')->willReturn('trigger');
|
||||||
|
$trigger->expects($this->any())->method('getSubjectKeys')->willReturn(['from']);
|
||||||
|
return $trigger;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$registry->expects($this->any())->method('getSubjectTransformer')->willReturn($transformer);
|
||||||
|
$storage = $this->createMock(AutomationStorage::class);
|
||||||
|
$storage->expects($this->any())->method('getAutomation')->willReturnCallback(function($id) use ($automation) {
|
||||||
|
return $id === 1 ? $automation : null;
|
||||||
|
});
|
||||||
|
$testee = new SubjectTransformerHandler($registry, $storage);
|
||||||
|
|
||||||
|
$subject = new Subject('from', ['key' => 'value']);
|
||||||
|
$automationRun = $this->createMock(AutomationRun::class);
|
||||||
|
$automationRun->expects($this->any())->method('getAutomationId')->willReturn(1);
|
||||||
|
$automationRun->expects($this->any())->method('getVersionId')->willReturn(1);
|
||||||
|
$automationRun->expects($this->any())->method('getSubjects')->willReturn([$subject]);
|
||||||
|
$subjects = $testee->transformSubjectData('to', $automationRun);
|
||||||
|
$this->assertNotNull($subjects);
|
||||||
|
$this->assertCount(1, $subjects);
|
||||||
|
$subject = current($subjects);
|
||||||
|
$this->assertInstanceOf(Subject::class, $subject);
|
||||||
|
$this->assertEquals('to', $subject->getKey());
|
||||||
|
}
|
||||||
|
}
|
@@ -5,6 +5,7 @@ namespace MailPoet\Automation\Engine\Validation\AutomationRules;
|
|||||||
require_once __DIR__ . '/AutomationRuleTest.php';
|
require_once __DIR__ . '/AutomationRuleTest.php';
|
||||||
|
|
||||||
use MailPoet\Automation\Engine\Control\RootStep;
|
use MailPoet\Automation\Engine\Control\RootStep;
|
||||||
|
use MailPoet\Automation\Engine\Control\SubjectTransformerHandler;
|
||||||
use MailPoet\Automation\Engine\Data\Automation;
|
use MailPoet\Automation\Engine\Data\Automation;
|
||||||
use MailPoet\Automation\Engine\Data\NextStep;
|
use MailPoet\Automation\Engine\Data\NextStep;
|
||||||
use MailPoet\Automation\Engine\Data\Step;
|
use MailPoet\Automation\Engine\Data\Step;
|
||||||
@@ -23,7 +24,6 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
|
|||||||
$registry->addStep(new RootStep());
|
$registry->addStep(new RootStep());
|
||||||
$registry->addStep($this->getTrigger('test:trigger', ['subject-a']));
|
$registry->addStep($this->getTrigger('test:trigger', ['subject-a']));
|
||||||
$registry->addStep($this->getAction('test:action', ['subject-a', 'subject-b']));
|
$registry->addStep($this->getAction('test:action', ['subject-a', 'subject-b']));
|
||||||
$rule = new ValidStepOrderRule($registry);
|
|
||||||
|
|
||||||
$automation = $this->make(Automation::class, [
|
$automation = $this->make(Automation::class, [
|
||||||
'getId' => 1,
|
'getId' => 1,
|
||||||
@@ -34,6 +34,12 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$subjectTransformer = $this->createMock(SubjectTransformerHandler::class);
|
||||||
|
$subjectTransformer->expects($this->any())->method('subjectKeysForAutomation')->willReturnCallback(function($a) use ($automation) {
|
||||||
|
return $a === $automation ? ['subject-a'] : [];
|
||||||
|
});
|
||||||
|
$rule = new ValidStepOrderRule($registry, $subjectTransformer);
|
||||||
|
|
||||||
$this->expectException(UnexpectedValueException::class);
|
$this->expectException(UnexpectedValueException::class);
|
||||||
$this->expectExceptionMessage("Step with ID 'a' is missing required subjects with keys: subject-b");
|
$this->expectExceptionMessage("Step with ID 'a' is missing required subjects with keys: subject-b");
|
||||||
(new AutomationWalker())->walk($automation, [$rule]);
|
(new AutomationWalker())->walk($automation, [$rule]);
|
||||||
@@ -45,7 +51,6 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
|
|||||||
$registry->addStep($this->getTrigger('test:trigger-a', ['subject-a', 'subject-b']));
|
$registry->addStep($this->getTrigger('test:trigger-a', ['subject-a', 'subject-b']));
|
||||||
$registry->addStep($this->getTrigger('test:trigger-b', ['subject-a']));
|
$registry->addStep($this->getTrigger('test:trigger-b', ['subject-a']));
|
||||||
$registry->addStep($this->getAction('test:action', ['subject-a', 'subject-b']));
|
$registry->addStep($this->getAction('test:action', ['subject-a', 'subject-b']));
|
||||||
$rule = new ValidStepOrderRule($registry);
|
|
||||||
|
|
||||||
$automation = $this->make(Automation::class, [
|
$automation = $this->make(Automation::class, [
|
||||||
'getId' => 1,
|
'getId' => 1,
|
||||||
@@ -57,6 +62,14 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$subjectTransformer = $this->createMock(SubjectTransformerHandler::class);
|
||||||
|
$subjectTransformer->expects($this->any())->method('subjectKeysForAutomation')->willReturnCallback(function($a) use ($automation) {
|
||||||
|
return $a === $automation ? ['subject-a'] : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
$rule = new ValidStepOrderRule($registry, $subjectTransformer);
|
||||||
|
|
||||||
|
|
||||||
$this->expectException(UnexpectedValueException::class);
|
$this->expectException(UnexpectedValueException::class);
|
||||||
$this->expectExceptionMessage("Step with ID 's' is missing required subjects with keys: subject-b");
|
$this->expectExceptionMessage("Step with ID 's' is missing required subjects with keys: subject-b");
|
||||||
(new AutomationWalker())->walk($automation, [$rule]);
|
(new AutomationWalker())->walk($automation, [$rule]);
|
||||||
@@ -67,7 +80,6 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
|
|||||||
$registry->addStep(new RootStep());
|
$registry->addStep(new RootStep());
|
||||||
$registry->addStep($this->getTrigger('test:trigger'));
|
$registry->addStep($this->getTrigger('test:trigger'));
|
||||||
$registry->addStep($this->getAction('test:action'));
|
$registry->addStep($this->getAction('test:action'));
|
||||||
$rule = new ValidStepOrderRule($registry);
|
|
||||||
|
|
||||||
$automation = $this->make(Automation::class, [
|
$automation = $this->make(Automation::class, [
|
||||||
'getSteps' => [
|
'getSteps' => [
|
||||||
@@ -76,6 +88,13 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
|
|||||||
'a' => new Step('a', 'action', 'test:action', [], []),
|
'a' => new Step('a', 'action', 'test:action', [], []),
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$subjectTransformer = $this->createMock(SubjectTransformerHandler::class);
|
||||||
|
$subjectTransformer->expects($this->any())->method('subjectKeysForAutomation')->willReturnCallback(function($a) use ($automation) {
|
||||||
|
return $a === $automation ? ['subject-a'] : [];
|
||||||
|
});
|
||||||
|
$rule = new ValidStepOrderRule($registry, $subjectTransformer);
|
||||||
|
|
||||||
(new AutomationWalker())->walk($automation, [$rule]);
|
(new AutomationWalker())->walk($automation, [$rule]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +103,6 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
|
|||||||
$registry->addStep(new RootStep());
|
$registry->addStep(new RootStep());
|
||||||
$registry->addStep($this->getTrigger('test:trigger', ['subject-a', 'subject-b', 'subject-c']));
|
$registry->addStep($this->getTrigger('test:trigger', ['subject-a', 'subject-b', 'subject-c']));
|
||||||
$registry->addStep($this->getAction('test:action', ['subject-a', 'subject-b']));
|
$registry->addStep($this->getAction('test:action', ['subject-a', 'subject-b']));
|
||||||
$rule = new ValidStepOrderRule($registry);
|
|
||||||
|
|
||||||
$automation = $this->make(Automation::class, [
|
$automation = $this->make(Automation::class, [
|
||||||
'getSteps' => [
|
'getSteps' => [
|
||||||
@@ -93,6 +111,14 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
|
|||||||
'a' => new Step('a', 'action', 'test:action', [], []),
|
'a' => new Step('a', 'action', 'test:action', [], []),
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$subjectTransformer = $this->createMock(SubjectTransformerHandler::class);
|
||||||
|
$subjectTransformer->expects($this->any())->method('subjectKeysForAutomation')->willReturnCallback(function($a) use ($automation) {
|
||||||
|
return $a === $automation ? ['subject-a', 'subject-b'] : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
$rule = new ValidStepOrderRule($registry, $subjectTransformer);
|
||||||
|
|
||||||
(new AutomationWalker())->walk($automation, [$rule]);
|
(new AutomationWalker())->walk($automation, [$rule]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +128,6 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
|
|||||||
$registry->addStep($this->getTrigger('test:trigger-a', ['subject-a', 'subject-b', 'subject-c']));
|
$registry->addStep($this->getTrigger('test:trigger-a', ['subject-a', 'subject-b', 'subject-c']));
|
||||||
$registry->addStep($this->getTrigger('test:trigger-b', ['subject-a', 'subject-b']));
|
$registry->addStep($this->getTrigger('test:trigger-b', ['subject-a', 'subject-b']));
|
||||||
$registry->addStep($this->getAction('test:action', ['subject-a', 'subject-b']));
|
$registry->addStep($this->getAction('test:action', ['subject-a', 'subject-b']));
|
||||||
$rule = new ValidStepOrderRule($registry);
|
|
||||||
|
|
||||||
$automation = $this->make(Automation::class, [
|
$automation = $this->make(Automation::class, [
|
||||||
'getId' => 1,
|
'getId' => 1,
|
||||||
@@ -113,6 +138,13 @@ class ValidStepOrderRuleTest extends AutomationRuleTest {
|
|||||||
's' => new Step('s', 'action', 'test:action', [], []),
|
's' => new Step('s', 'action', 'test:action', [], []),
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$subjectTransformer = $this->createMock(SubjectTransformerHandler::class);
|
||||||
|
$subjectTransformer->expects($this->any())->method('subjectKeysForAutomation')->willReturnCallback(function($a) use ($automation) {
|
||||||
|
return $a === $automation ? ['subject-a', 'subject-b'] : [];
|
||||||
|
});
|
||||||
|
$rule = new ValidStepOrderRule($registry, $subjectTransformer);
|
||||||
|
|
||||||
(new AutomationWalker())->walk($automation, [$rule]);
|
(new AutomationWalker())->walk($automation, [$rule]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user