Introduce template storage and rebuild create form template endpoint

[MAILPOET-4538]
This commit is contained in:
David Remer
2022-08-18 09:30:08 +03:00
committed by Veljko V
parent 1140ee3129
commit baa4d369af
11 changed files with 146 additions and 138 deletions

View File

@@ -4,6 +4,7 @@ namespace MailPoet\Automation\Engine\Builder;
use MailPoet\Automation\Engine\Data\Workflow;
use MailPoet\Automation\Engine\Storage\WorkflowStorage;
use MailPoet\Automation\Engine\Storage\WorkflowTemplateStorage;
use MailPoet\Automation\Integrations\MailPoet\Templates\WorkflowBuilder;
use MailPoet\UnexpectedValueException;
@@ -11,33 +12,25 @@ class CreateWorkflowFromTemplateController {
/** @var WorkflowStorage */
private $storage;
/** @var WorkflowBuilder */
private $templates;
/** @var WorkflowTemplateStorage */
private $templateStorage;
public function __construct(
WorkflowStorage $storage,
WorkflowBuilder $templates
WorkflowTemplateStorage $templateStorage
) {
$this->storage = $storage;
$this->templates = $templates;
$this->templateStorage = $templateStorage;
}
public function createWorkflow(array $data): Workflow {
$name = $data['name'];
$template = $data['template'];
public function createWorkflow(string $slug): Workflow {
switch ($template) {
case 'delayed-email-after-signup':
$workflow = $this->templates->delayedEmailAfterSignupWorkflow($name);
break;
case 'welcome-email-sequence':
$workflow = $this->templates->welcomeEmailSequence($name);
break;
default:
throw UnexpectedValueException::create()->withMessage('Template not found.');
$template = $this->templateStorage->getTemplateBySlug($slug);
if (! $template) {
throw UnexpectedValueException::create()->withMessage('Template not found.');
}
$this->storage->createWorkflow($workflow);
return $workflow;
$this->storage->createWorkflow($template->getWorkflow());
return $template->getWorkflow();
}
}

View File

@@ -18,8 +18,8 @@ class WorkflowTemplate
self::CATEGORY_WOOCOMMERCE,
];
/** @var int */
private $id;
/** @var string */
private $slug;
/** @var int */
private $category;
@@ -30,18 +30,18 @@ class WorkflowTemplate
/** @var Workflow */
private $workflow;
public function __construct(int $id, int $category, string $description, Workflow $workflow) {
public function __construct(string $slug, int $category, string $description, Workflow $workflow) {
if (! in_array($category, self::ALL_CATEGORIES)) {
throw new RuntimeException("$category is not a valid category.");
}
$this->id = $id;
$this->slug = $slug;
$this->category = $category;
$this->description = $description;
$this->workflow = $workflow;
}
public function getId() : int {
return $this->id;
public function getSlug() : string {
return $this->slug;
}
public function getName() : string {
@@ -62,7 +62,7 @@ class WorkflowTemplate
public function toArray() : array {
return [
'id' => $this->getId(),
'slug' => $this->getSlug(),
'name' => $this->getName(),
'category' => $this->getCategory(),
'description' => $this->getDescription(),

View File

@@ -6,6 +6,8 @@ use MailPoet\Automation\Engine\API\Endpoint;
use MailPoet\Automation\Engine\API\Request;
use MailPoet\Automation\Engine\API\Response;
use MailPoet\Automation\Engine\Builder\CreateWorkflowFromTemplateController;
use MailPoet\RuntimeException;
use MailPoet\UnexpectedValueException;
use MailPoet\Validator\Builder;
class WorkflowsCreateFromTemplateEndpoint extends Endpoint {
@@ -19,15 +21,13 @@ class WorkflowsCreateFromTemplateEndpoint extends Endpoint {
}
public function handle(Request $request): Response {
$data = $request->getParams();
$this->createWorkflowFromTemplateController->createWorkflow($data);
$this->createWorkflowFromTemplateController->createWorkflow((string)$request->getParam('slug'));
return new Response();
}
public static function getRequestSchema(): array {
return [
'name' => Builder::string()->required(),
'template' => Builder::string()->required(),
'slug' => Builder::string()->required(),
];
}
}

View File

@@ -24,6 +24,8 @@ class Hooks {
public const WORKFLOW_BEFORE_SAVE = 'mailpoet/automation/workflow/before_save';
public const WORKFLOW_STEP_BEFORE_SAVE = 'mailpoet/automation/workflow/step/before_save';
public const WORKFLOW_TEMPLATES = 'mailpoet/automation/workflow/templates';
public function doWorkflowBeforeSave(Workflow $workflow): void {
$this->wordPress->doAction(self::WORKFLOW_BEFORE_SAVE, $workflow);
}

View File

@@ -0,0 +1,75 @@
<?php
namespace MailPoet\Automation\Engine\Storage;
use MailPoet\Automation\Engine\Data\WorkflowTemplate;
use MailPoet\Automation\Engine\Hooks;
use MailPoet\Automation\Integrations\Core\Actions\DelayAction;
use MailPoet\Automation\Integrations\MailPoet\Actions\SendEmailAction;
use MailPoet\Automation\Integrations\MailPoet\Templates\WorkflowBuilder;
use MailPoet\Automation\Integrations\MailPoet\Triggers\SegmentSubscribedTrigger;
use MailPoet\WP\Functions as WPFunctions;
class WorkflowTemplateStorage
{
/** @var WorkflowTemplate[] */
private $templates = [];
/** @var WorkflowBuilder */
private $builder;
/** @var WPFunctions */
private $wp;
public function __construct(WorkflowBuilder $builder, WPFunctions $wp) {
$this->builder = $builder;
$this->wp = $wp;
$this->templates = $this->createTemplates();
}
public function getTemplateBySlug(string $slug) : ?WorkflowTemplate {
foreach ($this->templates as $template) {
if ($template->getSlug() === $slug) {
return $template;
}
}
return null;
}
/** @return WorkflowTemplate[] */
public function getTemplates(int $category = null) : array {
if (! $category) {
return $this->templates;
}
return array_values(
array_filter(
$this->templates,
function(WorkflowTemplate $template) use ($category) : bool {
return $template->getCategory() === $category;
}
)
);
}
private function createTemplates() : array {
$simpleWelcomeEmail = new WorkflowTemplate(
'simple-welcome-email',
WorkflowTemplate::CATEGORY_WELCOME,
"Automation template description is going to be here. Let's describe a lot of interesting ideas which incorporated into this beautiful and useful template",
$this->builder->createFromSequence(
__('Simple welcome email', 'mailpoet'),
[
SegmentSubscribedTrigger::class,
DelayAction::class,
SendEmailAction::class,
]
)
);
$templates = $this->wp->applyFilters(Hooks::WORKFLOW_TEMPLATES,[
$simpleWelcomeEmail,
]);
return is_array($templates)?$templates:[];
}
}

View File

@@ -4,6 +4,8 @@ namespace MailPoet\Automation\Integrations\MailPoet\Templates;
use MailPoet\Automation\Engine\Data\Step;
use MailPoet\Automation\Engine\Data\Workflow;
use MailPoet\Automation\Engine\Data\WorkflowTemplate;
use MailPoet\Automation\Engine\Workflows\Trigger;
use MailPoet\Automation\Integrations\Core\Actions\DelayAction;
use MailPoet\Automation\Integrations\MailPoet\Actions\SendEmailAction;
use MailPoet\Automation\Integrations\MailPoet\Triggers\SegmentSubscribedTrigger;
@@ -12,115 +14,28 @@ use MailPoet\Validator\Schema\ObjectSchema;
class WorkflowBuilder {
/** @var DelayAction */
private $delayAction;
/** @var SegmentSubscribedTrigger */
private $segmentSubscribedTrigger;
/** @var SendEmailAction */
private $sendEmailAction;
public function __construct(
SegmentSubscribedTrigger $segmentSubscribedTrigger,
SendEmailAction $sendEmailAction,
DelayAction $delayAction
) {
$this->delayAction = $delayAction;
$this->segmentSubscribedTrigger = $segmentSubscribedTrigger;
$this->sendEmailAction = $sendEmailAction;
}
public function delayedEmailAfterSignupWorkflow(string $name): Workflow {
$triggerStep = $this->segmentSubscribedTriggerStep();
$delayStep = $this->delayStep(null, "HOURS");
$triggerStep->setNextStepId($delayStep->getId());
$sendEmailStep = $this->sendEmailActionStep();
$delayStep->setNextStepId($sendEmailStep->getId());
$steps = [
$triggerStep,
$delayStep,
$sendEmailStep,
];
return new Workflow($name, $steps);
}
public function welcomeEmailSequence(string $name): Workflow {
$triggerStep = $this->segmentSubscribedTriggerStep();
$firstDelayStep = $this->delayStep(null, 'HOURS');
$triggerStep->setNextStepId($firstDelayStep->getId());
$sendFirstEmailStep = $this->sendEmailActionStep();
$firstDelayStep->setNextStepId($sendFirstEmailStep->getId());
$secondDelayStep = $this->delayStep(null, 'HOURS');
$sendFirstEmailStep->setNextStepId($secondDelayStep->getId());
$sendSecondEmailStep = $this->sendEmailActionStep();
$secondDelayStep->setNextStepId($sendSecondEmailStep->getId());
$steps = [
$triggerStep,
$firstDelayStep,
$sendFirstEmailStep,
$secondDelayStep,
$sendSecondEmailStep,
];
return new Workflow($name, $steps);
}
private function delayStep(?int $delay, string $delayType): Step {
return new Step(
$this->uniqueId(),
Step::TYPE_ACTION,
$this->delayAction->getKey(),
null,
[
'delay' => $delay,
'delay_type' => $delayType,
] + $this->getDefaultArgs($this->delayAction->getArgsSchema())
);
}
private function segmentSubscribedTriggerStep(?int $segmentId = null): Step {
return new Step(
$this->uniqueId(),
Step::TYPE_TRIGGER,
$this->segmentSubscribedTrigger->getKey(),
null,
[
'segment_id' => $segmentId,
] + $this->getDefaultArgs($this->segmentSubscribedTrigger->getArgsSchema())
);
}
private function sendEmailActionStep(): Step {
return new Step(
$this->uniqueId(),
Step::TYPE_ACTION,
$this->sendEmailAction->getKey(),
null,
$this->getDefaultArgs($this->sendEmailAction->getArgsSchema())
public function createFromSequence(string $name, array $sequence, array $sequenceArgs = []) : Workflow {
$steps = [];
$nextStep = null;
foreach (array_reverse($sequence) as $index => $stepClass) {
$step = new Step(
$this->uniqueId(),
in_array(Trigger::class, (array) class_implements($stepClass)) ? Step::TYPE_TRIGGER : Step::TYPE_ACTION,
$stepClass::getKey(),
$nextStep,
array_reverse($sequenceArgs)[$index] ?? []
);
$nextStep = $step->getId();
$steps[] = $step;
}
$steps = array_reverse($steps);
return new Workflow(
$name,
$steps
);
}
private function uniqueId(): string {
return Security::generateRandomString(16);
}
private function getDefaultArgs(ObjectSchema $argsSchema): array {
$args = [];
foreach ($argsSchema->toArray()['properties'] ?? [] as $name => $schema) {
if (array_key_exists('default', $schema)) {
$args[$name] = $schema['default'];
}
}
return $args;
}
}

View File

@@ -121,6 +121,7 @@ class ContainerConfigurator implements IContainerConfigurator {
$container->autowire(\MailPoet\Automation\Engine\Migrations\Migrator::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Registry::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Storage\WorkflowRunStorage::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Storage\WorkflowTemplateStorage::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\Storage\WorkflowStorage::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Engine\WordPress::class)->setPublic(true);
// Automation - API endpoints

View File

@@ -1,6 +1,11 @@
parameters:
ignoreErrors:
-
message: "#^Cannot cast mixed to string\\.$#"
count: 1
path: ../../lib/Automation/Engine/Endpoints/Workflows/WorkflowsCreateFromTemplateEndpoint.php
-
message: "#^Cannot access property \\$permissions on mixed\\.$#"
count: 1

View File

@@ -1,6 +1,15 @@
parameters:
ignoreErrors:
-
message: "#^Cannot cast mixed to string\\.$#"
count: 1
path: ../../lib/Automation/Engine/Endpoints/Workflows/WorkflowsCreateFromTemplateEndpoint.php
-
message: "#^Cannot cast mixed to int\\.$#"
count: 1
path: ../../lib/Automation/Engine/Endpoints/Workflows/WorkflowTemplatesGetEndpoint.php
-
message: "#^Cannot access property \\$permissions on mixed\\.$#"
count: 1

View File

@@ -1,5 +1,15 @@
parameters:
ignoreErrors:
-
message: "#^Cannot cast mixed to string\\.$#"
count: 1
path: ../../lib/Automation/Engine/Endpoints/Workflows/WorkflowsCreateFromTemplateEndpoint.php
-
message: "#^Cannot cast mixed to int\\.$#"
count: 1
path: ../../lib/Automation/Engine/Endpoints/Workflows/WorkflowTemplatesGetEndpoint.php
-
message: "#^Cannot cast string|void to string\\.$#"
count: 2

View File

@@ -16,8 +16,7 @@ class WorkflowsCreateFromTemplateTest extends AutomationTest {
$countBefore = count($storage->getWorkflows());
$this->post(self::ENDPOINT_PATH, [
'json' => [
'name' => 'Testing workflow from template',
'template' => 'delayed-email-after-signup'
'slug' => 'simple-welcome-email'
],
]);
$countAfter = count($storage->getWorkflows());
@@ -28,8 +27,7 @@ class WorkflowsCreateFromTemplateTest extends AutomationTest {
$storage = ContainerWrapper::getInstance()->get(WorkflowStorage::class);
$this->post(self::ENDPOINT_PATH, [
'json' => [
'name' => 'Testing workflow from template',
'template' => 'delayed-email-after-signup'
'slug' => 'simple-welcome-email'
],
]);
$allWorkflows = $storage->getWorkflows();