Add subscriber automation fields

[MAILPOET-5172]
This commit is contained in:
Jan Jakes
2023-05-04 15:34:07 +02:00
committed by Aschepikov
parent 6cfc808248
commit 7444131021
6 changed files with 201 additions and 2 deletions

View File

@@ -5,6 +5,7 @@ namespace MailPoet\Automation\Engine\Storage;
use DateTimeImmutable; use DateTimeImmutable;
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\Data\Subject;
use MailPoet\Automation\Engine\Exceptions; use MailPoet\Automation\Engine\Exceptions;
use MailPoet\Automation\Engine\Integration\Trigger; use MailPoet\Automation\Engine\Integration\Trigger;
use wpdb; use wpdb;
@@ -22,6 +23,9 @@ class AutomationStorage {
/** @var string */ /** @var string */
private $runsTable; private $runsTable;
/** @var string */
private $subjectsTable;
/** @var wpdb */ /** @var wpdb */
private $wpdb; private $wpdb;
@@ -31,6 +35,7 @@ class AutomationStorage {
$this->versionsTable = $wpdb->prefix . 'mailpoet_automation_versions'; $this->versionsTable = $wpdb->prefix . 'mailpoet_automation_versions';
$this->triggersTable = $wpdb->prefix . 'mailpoet_automation_triggers'; $this->triggersTable = $wpdb->prefix . 'mailpoet_automation_triggers';
$this->runsTable = $wpdb->prefix . 'mailpoet_automation_runs'; $this->runsTable = $wpdb->prefix . 'mailpoet_automation_runs';
$this->subjectsTable = $wpdb->prefix . 'mailpoet_automation_run_subjects';
$this->wpdb = $wpdb; $this->wpdb = $wpdb;
} }
@@ -116,6 +121,34 @@ class AutomationStorage {
}, (array)$data); }, (array)$data);
} }
/** @return Automation[] */
public function getAutomationsBySubject(Subject $subject, array $runStatus = null): array {
$automationsTable = esc_sql($this->automationsTable);
$versionsTable = esc_sql($this->versionsTable);
$runsTable = esc_sql($this->runsTable);
$subjectTable = esc_sql($this->subjectsTable);
$statusFilter = $runStatus ? 'AND r.status IN(' . implode(',', array_fill(0, count($runStatus), '%s')) . ')' : '';
$query = (string)$this->wpdb->prepare("
SELECT DISTINCT a.*, v.id AS version_id, v.steps
FROM $automationsTable a
INNER JOIN $versionsTable v ON v.automation_id = a.id
INNER JOIN $runsTable r ON r.automation_id = a.id
INNER JOIN $subjectTable s ON s.automation_run_id = r.id
WHERE v.id = (
SELECT MAX(id) FROM $versionsTable WHERE automation_id = v.automation_id
)
AND s.hash = %s
$statusFilter
ORDER BY a.id DESC
", $subject->getHash(), ...($runStatus ?? []));
$data = $this->wpdb->get_results($query, ARRAY_A);
return array_map(function (array $automationData) {
return Automation::fromArray($automationData);
}, (array)$data);
}
public function getAutomationCount(): int { public function getAutomationCount(): int {
$automationsTable = esc_sql($this->automationsTable); $automationsTable = esc_sql($this->automationsTable);
return (int)$this->wpdb->get_var("SELECT COUNT(*) FROM $automationsTable"); return (int)$this->wpdb->get_var("SELECT COUNT(*) FROM $automationsTable");

View File

@@ -0,0 +1,75 @@
<?php declare(strict_types = 1);
namespace MailPoet\Automation\Integrations\MailPoet\Fields;
use MailPoet\Automation\Engine\Data\Automation;
use MailPoet\Automation\Engine\Data\AutomationRun;
use MailPoet\Automation\Engine\Data\Field;
use MailPoet\Automation\Engine\Data\Subject;
use MailPoet\Automation\Engine\Storage\AutomationStorage;
use MailPoet\Automation\Integrations\MailPoet\Payloads\SubscriberPayload;
use MailPoet\Automation\Integrations\MailPoet\Subjects\SubscriberSubject;
class SubscriberAutomationFieldsFactory {
/** @var AutomationStorage */
private $automationStorage;
public function __construct(
AutomationStorage $automationStorage
) {
$this->automationStorage = $automationStorage;
}
/** @return Field[] */
public function getFields(): array {
$automations = $this->automationStorage->getAutomations(
array_diff(Automation::STATUS_ALL, [Automation::STATUS_TRASH])
);
$args = [
'options' => array_map(function (Automation $automation) {
return [
'id' => $automation->getId(),
'name' => $automation->getName() . " [{$automation->getId()}]",
];
}, $automations),
];
return [
new Field(
'mailpoet:subscriber:automations-entered',
Field::TYPE_ENUM_ARRAY,
__('Automations — entered', 'mailpoet'),
function (SubscriberPayload $payload) {
return $this->getAutomationIds($payload);
},
$args
),
new Field(
'mailpoet:subscriber:automations-processing',
Field::TYPE_ENUM_ARRAY,
__('Automations — processing', 'mailpoet'),
function (SubscriberPayload $payload) {
return $this->getAutomationIds($payload, [AutomationRun::STATUS_RUNNING]);
},
$args
),
new Field(
'mailpoet:subscriber:automations-exited',
Field::TYPE_ENUM_ARRAY,
__('Automations — exited', 'mailpoet'),
function (SubscriberPayload $payload) {
return $this->getAutomationIds($payload, [AutomationRun::STATUS_COMPLETE]);
},
$args
),
];
}
private function getAutomationIds(SubscriberPayload $payload, array $status = null): array {
$subject = new Subject(SubscriberSubject::KEY, ['subscriber_id' => $payload->getId()]);
$automations = $this->automationStorage->getAutomationsBySubject($subject, $status);
return array_map(function (Automation $automation) {
return $automation->getId();
}, $automations);
}
}

View File

@@ -17,6 +17,9 @@ class SubscriberFieldsFactory {
/** @var SegmentsRepository */ /** @var SegmentsRepository */
private $segmentsRepository; private $segmentsRepository;
/** @var SubscriberAutomationFieldsFactory */
private $automationFieldsFactory;
/** @var SubscriberCustomFieldsFactory */ /** @var SubscriberCustomFieldsFactory */
private $customFieldsFactory; private $customFieldsFactory;
@@ -29,12 +32,14 @@ class SubscriberFieldsFactory {
public function __construct( public function __construct(
SegmentsFinder $segmentsFinder, SegmentsFinder $segmentsFinder,
SegmentsRepository $segmentsRepository, SegmentsRepository $segmentsRepository,
SubscriberAutomationFieldsFactory $automationFieldsFactory,
SubscriberCustomFieldsFactory $customFieldsFactory, SubscriberCustomFieldsFactory $customFieldsFactory,
SubscriberStatisticFieldsFactory $statisticFieldsFactory, SubscriberStatisticFieldsFactory $statisticFieldsFactory,
TagRepository $tagRepository TagRepository $tagRepository
) { ) {
$this->segmentsFinder = $segmentsFinder; $this->segmentsFinder = $segmentsFinder;
$this->segmentsRepository = $segmentsRepository; $this->segmentsRepository = $segmentsRepository;
$this->automationFieldsFactory = $automationFieldsFactory;
$this->customFieldsFactory = $customFieldsFactory; $this->customFieldsFactory = $customFieldsFactory;
$this->statisticFieldsFactory = $statisticFieldsFactory; $this->statisticFieldsFactory = $statisticFieldsFactory;
$this->tagRepository = $tagRepository; $this->tagRepository = $tagRepository;
@@ -222,7 +227,8 @@ class SubscriberFieldsFactory {
] ]
), ),
], ],
$this->statisticFieldsFactory->getFields() $this->statisticFieldsFactory->getFields(),
$this->automationFieldsFactory->getFields()
); );
} }
} }

View File

@@ -159,6 +159,7 @@ class ContainerConfigurator implements IContainerConfigurator {
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\ContextFactory::class)->setPublic(true); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\ContextFactory::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\MailPoetIntegration::class)->setPublic(true); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\MailPoetIntegration::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\Fields\SubscriberFieldsFactory::class)->setPublic(true)->setShared(false); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Fields\SubscriberFieldsFactory::class)->setPublic(true)->setShared(false);
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\Fields\SubscriberAutomationFieldsFactory::class)->setPublic(true)->setShared(false);
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\Fields\SubscriberCustomFieldsFactory::class)->setPublic(true)->setShared(false); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Fields\SubscriberCustomFieldsFactory::class)->setPublic(true)->setShared(false);
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\Fields\SubscriberStatisticFieldsFactory::class)->setPublic(true)->setShared(false); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Fields\SubscriberStatisticFieldsFactory::class)->setPublic(true)->setShared(false);
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\Subjects\SegmentSubject::class)->setPublic(true)->setShared(false); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Subjects\SegmentSubject::class)->setPublic(true)->setShared(false);

View File

@@ -48,7 +48,7 @@ parameters:
path: ../../lib/Automation/Engine/Storage/AutomationRunStorage.php path: ../../lib/Automation/Engine/Storage/AutomationRunStorage.php
- -
message: "#^Cannot cast string|void to string\\.$#" message: "#^Cannot cast string|void to string\\.$#"
count: 10 count: 11
path: ../../lib/Automation/Engine/Storage/AutomationStorage.php path: ../../lib/Automation/Engine/Storage/AutomationStorage.php
- -
message: '/^Call to static method get_orders_table_name\(\) on an unknown class Automattic\\WooCommerce\\Internal\\DataStores\\Orders\\OrdersTableDataStore\.$/' message: '/^Call to static method get_orders_table_name\(\) on an unknown class Automattic\\WooCommerce\\Internal\\DataStores\\Orders\\OrdersTableDataStore\.$/'

View File

@@ -0,0 +1,84 @@
<?php declare(strict_types = 1);
namespace integration\Automation\Integrations\MailPoet\Fields;
use MailPoet\Automation\Engine\Data\Automation;
use MailPoet\Automation\Engine\Data\AutomationRun;
use MailPoet\Automation\Engine\Data\Subject;
use MailPoet\Automation\Engine\Storage\AutomationRunStorage;
use MailPoet\Automation\Engine\Storage\AutomationStorage;
use MailPoet\Automation\Integrations\MailPoet\Fields\SubscriberAutomationFieldsFactory;
use MailPoet\Automation\Integrations\MailPoet\Payloads\SubscriberPayload;
use MailPoet\Automation\Integrations\MailPoet\Subjects\SubscriberSubject;
use MailPoet\Test\DataFactories\Subscriber as SubscriberFactory;
use MailPoetTest;
class SubscriberAutomationFieldsFactoryTest extends MailPoetTest {
public function testItCreatesAutomationFields(): void {
$draft = $this->createAutomation('Draft', Automation::STATUS_DRAFT);
$active = $this->createAutomation('Active', Automation::STATUS_ACTIVE);
$deactivating = $this->createAutomation('Deactivating', Automation::STATUS_DEACTIVATING);
$this->createAutomation('Trash', Automation::STATUS_TRASH); // not included
$fields = $this->getFieldsMap();
// check definitions
$this->assertCount(3, $fields);
$expected = [
'mailpoet:subscriber:automations-entered' => 'Automations — entered',
'mailpoet:subscriber:automations-processing' => 'Automations — processing',
'mailpoet:subscriber:automations-exited' => 'Automations — exited',
];
foreach ($expected as $key => $name) {
$field = $fields[$key];
$this->assertSame($name, $field->getName());
$this->assertSame('enum_array', $field->getType());
$this->assertSame(['options' => [
['id' => $deactivating->getId(), 'name' => "Deactivating [{$deactivating->getId()}]"],
['id' => $active->getId(), 'name' => "Active [{$active->getId()}]"],
['id' => $draft->getId(), 'name' => "Draft [{$draft->getId()}]"],
]], $field->getArgs());
}
// check values
$subscriber = (new SubscriberFactory())->create();
$subject = new Subject(SubscriberSubject::KEY, ['subscriber_id' => $subscriber->getId()]);
$this->createAutomationRun($draft, AutomationRun::STATUS_COMPLETE, [$subject]);
$this->createAutomationRun($active, AutomationRun::STATUS_CANCELLED, [$subject]);
$this->createAutomationRun($active, AutomationRun::STATUS_RUNNING, [$subject]);
$this->createAutomationRun($active, AutomationRun::STATUS_COMPLETE, [$subject]);
$this->createAutomationRun($deactivating, AutomationRun::STATUS_RUNNING, [$subject]);
$payload = new SubscriberPayload($subscriber);
$entered = $fields['mailpoet:subscriber:automations-entered'];
$processing = $fields['mailpoet:subscriber:automations-processing'];
$exited = $fields['mailpoet:subscriber:automations-exited'];
$this->assertSame([$deactivating->getId(), $active->getId(), $draft->getId()], $entered->getValue($payload));
$this->assertSame([$deactivating->getId(), $active->getId()], $processing->getValue($payload));
$this->assertSame([$active->getId(), $draft->getId()], $exited->getValue($payload));
}
private function getFieldsMap(): array {
$factory = $this->diContainer->get(SubscriberAutomationFieldsFactory::class);
$fields = [];
foreach ($factory->getFields() as $field) {
$fields[$field->getKey()] = $field;
}
return $fields;
}
private function createAutomation(string $name, string $status): Automation {
$automation = $this->tester->createAutomation($name);
$automation->setStatus($status);
$this->diContainer->get(AutomationStorage::class)->updateAutomation($automation);
return $automation;
}
private function createAutomationRun(Automation $automation, string $status, array $subjects): AutomationRun {
$run = $this->tester->createAutomationRun($automation, $subjects);
$this->diContainer->get(AutomationRunStorage::class)->updateStatus($run->getId(), $status);
return $run;
}
}