Add AutomationEmailController

[MAILPOET-5433]
This commit is contained in:
David Remer
2023-06-27 10:29:31 +03:00
committed by Aschepikov
parent edffd4e705
commit 8c2251089f
4 changed files with 275 additions and 69 deletions

View File

@ -0,0 +1,92 @@
<?php declare(strict_types = 1);
namespace MailPoet\Automation\Integrations\MailPoet\Analytics\Controller;
use MailPoet\Automation\Engine\Data\Automation;
use MailPoet\Automation\Engine\Storage\AutomationStorage;
use MailPoet\Automation\Integrations\MailPoet\Actions\SendEmailAction;
use MailPoet\Entities\NewsletterEntity;
use MailPoet\Newsletter\NewslettersRepository;
class AutomationEmailController {
/** @var AutomationStorage */
private $automationStorage;
/** @var NewslettersRepository */
private $newslettersRepository;
public function __construct(
AutomationStorage $automationStorage,
NewslettersRepository $newslettersRepository
) {
$this->automationStorage = $automationStorage;
$this->newslettersRepository = $newslettersRepository;
}
/**
* @param Automation $automation
* @param \DateTimeImmutable $after
* @param \DateTimeImmutable $before
* @return NewsletterEntity[]
*/
public function getAutomationEmailsInTimeSpan(Automation $automation, \DateTimeImmutable $after, \DateTimeImmutable $before): array {
$automationVersions = $this->automationStorage->getAutomationVersionDates($automation->getId());
usort(
$automationVersions,
function (array $a, array $b) {
return $a['created_at'] <=> $b['created_at'];
}
);
// filter automations that were created before the after date
$versionIds = [];
foreach ($automationVersions as $automationVersion) {
if ($automationVersion['created_at'] > $before) {
break;
}
if (!$versionIds || $automationVersion['created_at'] < $after) {
$versionIds = [(int)$automationVersion['id']];
continue;
}
$versionIds[] = (int)$automationVersion['id'];
}
$automations = $this->automationStorage->getAutomationWithDifferentVersions($versionIds);
return $this->getEmailsFromAutomations($automations);
}
/**
* @param Automation[] $automations
* @return NewsletterEntity[]
*/
public function getEmailsFromAutomations(array $automations): array {
$emailSteps = [];
foreach ($automations as $automation) {
$emailSteps = array_merge(
$emailSteps,
array_values(
array_filter(
$automation->getSteps(),
function($step) {
return $step->getKey() === SendEmailAction::KEY;
}
)
)
);
}
$emailIds = array_unique(
array_filter(
array_map(
function($step) {
return $step->getArgs()['email_id'];
},
$emailSteps
)
)
);
return $this->newslettersRepository->findBy(['id' => $emailIds]);
}
}

View File

@ -3,10 +3,7 @@
namespace MailPoet\Automation\Integrations\MailPoet\Analytics\Controller; namespace MailPoet\Automation\Integrations\MailPoet\Analytics\Controller;
use MailPoet\Automation\Engine\Data\Automation; use MailPoet\Automation\Engine\Data\Automation;
use MailPoet\Automation\Engine\Storage\AutomationStorage;
use MailPoet\Automation\Integrations\MailPoet\Actions\SendEmailAction;
use MailPoet\Automation\Integrations\MailPoet\Analytics\Entities\QueryWithCompare; use MailPoet\Automation\Integrations\MailPoet\Analytics\Entities\QueryWithCompare;
use MailPoet\Entities\NewsletterEntity;
use MailPoet\Entities\StatisticsClickEntity; use MailPoet\Entities\StatisticsClickEntity;
use MailPoet\Entities\StatisticsOpenEntity; use MailPoet\Entities\StatisticsOpenEntity;
use MailPoet\Newsletter\NewslettersRepository; use MailPoet\Newsletter\NewslettersRepository;
@ -24,24 +21,24 @@ class OverviewStatisticsController {
/** @var NewsletterUrl */ /** @var NewsletterUrl */
private $newsletterUrl; private $newsletterUrl;
/** @var AutomationStorage */ /** @var AutomationEmailController */
private $automationStorage; private $automationEmailController;
public function __construct( public function __construct(
NewslettersRepository $newslettersRepository, NewslettersRepository $newslettersRepository,
NewsletterStatisticsRepository $newsletterStatisticsRepository, NewsletterStatisticsRepository $newsletterStatisticsRepository,
NewsletterUrl $newsletterUrl, NewsletterUrl $newsletterUrl,
AutomationStorage $automationStorage AutomationEmailController $automationEmailController
) { ) {
$this->newslettersRepository = $newslettersRepository; $this->newslettersRepository = $newslettersRepository;
$this->newsletterStatisticsRepository = $newsletterStatisticsRepository; $this->newsletterStatisticsRepository = $newsletterStatisticsRepository;
$this->newsletterUrl = $newsletterUrl; $this->newsletterUrl = $newsletterUrl;
$this->automationStorage = $automationStorage; $this->automationEmailController = $automationEmailController;
} }
public function getStatisticsForAutomation(Automation $automation, QueryWithCompare $query): array { public function getStatisticsForAutomation(Automation $automation, QueryWithCompare $query): array {
$currentEmails = $this->getAutomationEmailsInTimeSpan($automation, $query->getAfter(), $query->getBefore()); $currentEmails = $this->automationEmailController->getAutomationEmailsInTimeSpan($automation, $query->getAfter(), $query->getBefore());
$previousEmails = $this->getAutomationEmailsInTimeSpan($automation, $query->getCompareWithAfter(), $query->getCompareWithBefore()); $previousEmails = $this->automationEmailController->getAutomationEmailsInTimeSpan($automation, $query->getCompareWithAfter(), $query->getCompareWithBefore());
$data = [ $data = [
'sent' => ['current' => 0, 'previous' => 0], 'sent' => ['current' => 0, 'previous' => 0],
'opened' => ['current' => 0, 'previous' => 0], 'opened' => ['current' => 0, 'previous' => 0],
@ -110,64 +107,4 @@ class OverviewStatisticsController {
return $data; return $data;
} }
private function getAutomationEmailsInTimeSpan(Automation $automation, \DateTimeImmutable $after, \DateTimeImmutable $before): array {
$automationVersions = $this->automationStorage->getAutomationVersionDates($automation->getId());
usort(
$automationVersions,
function (array $a, array $b) {
return $a['created_at'] <=> $b['created_at'];
}
);
// filter automations that were created before the after date
$versionIds = [];
foreach ($automationVersions as $automationVersion) {
if ($automationVersion['created_at'] > $before) {
break;
}
if (!$versionIds || $automationVersion['created_at'] < $after) {
$versionIds = [(int)$automationVersion['id']];
continue;
}
$versionIds[] = (int)$automationVersion['id'];
}
$automations = $this->automationStorage->getAutomationWithDifferentVersions($versionIds);
return $this->getEmailsFromAutomations($automations);
}
/**
* @param Automation[] $automations
* @return NewsletterEntity[]
*/
private function getEmailsFromAutomations(array $automations): array {
$emailSteps = [];
foreach ($automations as $automation) {
$emailSteps = array_merge(
$emailSteps,
array_values(
array_filter(
$automation->getSteps(),
function($step) {
return $step->getKey() === SendEmailAction::KEY;
}
)
)
);
}
$emailIds = array_unique(
array_filter(
array_map(
function($step) {
return $step->getArgs()['email_id'];
},
$emailSteps
)
)
);
return $this->newslettersRepository->findBy(['id' => $emailIds]);
}
} }

View File

@ -198,6 +198,7 @@ class ContainerConfigurator implements IContainerConfigurator {
//Automation Analytics //Automation Analytics
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\Analytics\Analytics::class)->setPublic(true); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Analytics\Analytics::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\Analytics\Endpoints\OverviewEndpoint::class)->setPublic(true); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Analytics\Endpoints\OverviewEndpoint::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\Analytics\Controller\AutomationEmailController::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\Analytics\Controller\OverviewStatisticsController::class)->setPublic(true); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Analytics\Controller\OverviewStatisticsController::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\Analytics\Controller\FreeOrderController::class)->setPublic(true); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Analytics\Controller\FreeOrderController::class)->setPublic(true);
$container->autowire(\MailPoet\Automation\Integrations\MailPoet\Analytics\Factories\OrderControllerFactory::class)->setPublic(true); $container->autowire(\MailPoet\Automation\Integrations\MailPoet\Analytics\Factories\OrderControllerFactory::class)->setPublic(true);

View File

@ -0,0 +1,176 @@
<?php declare(strict_types = 1);
namespace MailPoet\Test\Automation\Integrations\MailPoet\Analytics\Controller;
use MailPoet\Automation\Engine\Data\Automation;
use MailPoet\Automation\Engine\Data\NextStep;
use MailPoet\Automation\Engine\Data\Step;
use MailPoet\Automation\Engine\Storage\AutomationStorage;
use MailPoet\Automation\Integrations\MailPoet\Actions\SendEmailAction;
use MailPoet\Automation\Integrations\MailPoet\Analytics\Controller\AutomationEmailController;
use MailPoet\Entities\NewsletterEntity;
use MailPoet\Newsletter\NewslettersRepository;
class AutomationEmailControllerTest extends \MailPoetTest {
/** @var AutomationEmailController */
private $testee;
/** @var NewslettersRepository */
private $newsletterRepository;
/** @var AutomationStorage */
private $automationStorage;
public function _before() {
$this->testee = $this->diContainer->get(AutomationEmailController::class);
$this->newsletterRepository = $this->diContainer->get(NewslettersRepository::class);
$this->automationStorage = $this->diContainer->get(AutomationStorage::class);
}
public function testItReturnsNoEmailWhenNoEmailStepExist() {
$automation = $this->tester->createAutomation('test');
$this->createEmail();
$emails = $this->testee->getEmailsFromAutomations([$automation]);
$this->assertEmpty($emails);
}
public function testItReturnsEmailsWhenEmailStepsExist() {
$newsletter1 = $this->createEmail();
$newsletter2 = $this->createEmail();
$this->createEmail();
$trigger = new Step(
'trigger',
Step::TYPE_TRIGGER,
'trigger',
[],
[
new NextStep('first-email'),
]
);
$firstEmail = new Step(
'first-email',
Step::TYPE_ACTION,
SendEmailAction::KEY,
['email_id' => $newsletter1->getId()],
[
new NextStep('second-email'),
]
);
$secondEmail = new Step(
'second-email',
Step::TYPE_ACTION,
SendEmailAction::KEY,
['email_id' => $newsletter2->getId()],
[]
);
$automation = $this->tester->createAutomation('test', $trigger, $firstEmail, $secondEmail);
$emails = $this->testee->getEmailsFromAutomations([$automation]);
$this->assertCount(2, $emails);
$expectedIds = [$newsletter1->getId(), $newsletter2->getId()];
$actualIds = array_map(function (NewsletterEntity $newsletter) {
return $newsletter->getId();
}, $emails);
$this->assertEquals($expectedIds, $actualIds);
}
public function testItReturnsTheEmailsFromTheCorrectTimespan() {
global $wpdb;
$after = new \DateTimeImmutable('2022-01-01 00:00:00');
$before = new \DateTimeImmutable('2022-02-02 00:00:00');
$automation = $this->tester->createAutomation('test');
$emailBefore = $this->createEmail('emailBefore');
$emailInTimeSpan = $this->createEmail('emailInTimeSpan');
$emailInTimeSpan2 = $this->createEmail('emailInTimeSpan2');
$emailAfter = $this->createEmail('emailAfter');
$root = new Step(
'root',
Step::TYPE_ROOT,
'root',
[],
[
new NextStep('trigger'),
]
);
$trigger = new Step(
'trigger',
Step::TYPE_TRIGGER,
'trigger',
[],
[
new NextStep('first-email'),
]
);
// Create versions
$emailsCreatedAtMap = [
[
'email' => $emailBefore,
'date' => new \DateTimeImmutable('2021-12-02 00:00:00'),
],
[
/**
* This email is in the timespan as it could have been sent in the timespan
* although the creation of the version was before the timespan.
*/
'email' => $emailInTimeSpan,
'date' => new \DateTimeImmutable('2021-12-31 00:00:00'),
],
[
'email' => $emailInTimeSpan2,
'date' => new \DateTimeImmutable('2022-01-03 00:00:00'),
],
[
'email' => $emailAfter,
'date' => new \DateTimeImmutable('2022-06-02 00:00:00'),
],
];
foreach ($emailsCreatedAtMap as $emailCreatedAt) {
$email = $emailCreatedAt['email'];
$emailStep = new Step(
'first-email',
Step::TYPE_ACTION,
SendEmailAction::KEY,
['email_id' => $email->getId()],
[
new NextStep('second-email'),
]
);
$automation->setSteps([$root, $trigger, $emailStep]);
$this->automationStorage->updateAutomation($automation);
$automation = $this->automationStorage->getAutomation($automation->getId());
$this->assertInstanceOf(Automation::class, $automation);
// Update the created_at value of the version
$sql = 'update ' . $wpdb->prefix . 'mailpoet_automation_versions set created_at=%s where id=%d';
$sql = $wpdb->prepare(
$sql,
$emailCreatedAt['date']->format('Y-m-d H:i:s'),
$automation->getVersionId()
);
$this->assertEquals(1, $wpdb->query($sql));
}
$emails = $this->testee->getAutomationEmailsInTimeSpan($automation, $after, $before);
$this->assertCount(2, $emails);
$expectedIds = [$emailInTimeSpan->getId(), $emailInTimeSpan2->getId()];
$actualIds = array_map(function (NewsletterEntity $newsletter) {
return $newsletter->getId();
}, $emails);
$this->assertEquals($expectedIds, $actualIds);
}
private function createEmail(string $subject = 'subject'): NewsletterEntity {
$newsletter = new NewsletterEntity();
$newsletter->setType(NewsletterEntity::TYPE_AUTOMATION);
$newsletter->setSubject($subject);
$newsletter->setStatus(NewsletterEntity::STATUS_DRAFT);
$this->newsletterRepository->persist($newsletter);
$this->newsletterRepository->flush();
return $newsletter;
}
}