Add listing repository for scheduler task subscribers

[MAILPOET-4006]
This commit is contained in:
Rostislav Wolny
2022-03-21 16:21:52 +01:00
committed by Veljko V
parent ed1c9e5202
commit 4f471d26bc
4 changed files with 307 additions and 0 deletions

View File

@@ -417,6 +417,7 @@ class ContainerConfigurator implements IContainerConfigurator {
$container->autowire(\MailPoet\Newsletter\Scheduler\ReEngagementScheduler::class)->setPublic(true);
$container->autowire(\MailPoet\Newsletter\Sending\ScheduledTasksRepository::class)->setPublic(true);
$container->autowire(\MailPoet\Newsletter\Sending\ScheduledTaskSubscribersRepository::class)->setPublic(true);
$container->autowire(\MailPoet\Newsletter\Sending\ScheduledTaskSubscribersListingRepository::class)->setPublic(true);
$container->autowire(\MailPoet\Newsletter\Sending\SendingQueuesRepository::class)->setPublic(true);
$container->autowire(\MailPoet\Newsletter\ViewInBrowser\ViewInBrowserController::class)->setPublic(true);
$container->autowire(\MailPoet\Newsletter\ViewInBrowser\ViewInBrowserRenderer::class)->setPublic(true);

View File

@@ -0,0 +1,127 @@
<?php declare(strict_types=1);
namespace MailPoet\Newsletter\Sending;
use MailPoet\Entities\ScheduledTaskSubscriberEntity;
use MailPoet\Listing\ListingDefinition;
use MailPoet\Listing\ListingRepository;
use MailPoet\Util\Helpers;
use MailPoetVendor\Doctrine\ORM\QueryBuilder;
class ScheduledTaskSubscribersListingRepository extends ListingRepository {
public function getGroups(ListingDefinition $definition): array {
$queryBuilder = clone $this->queryBuilder;
$this->applyFromClause($queryBuilder);
$this->applyParameters($queryBuilder, $definition->getParameters());
// total count
$countQueryBuilder = clone $queryBuilder;
$countQueryBuilder->select('COUNT(sts.subscriber) AS subscriberCount');
$totalCount = intval($countQueryBuilder->getQuery()->getSingleScalarResult());
// Sent count
$sentCountQuery = clone $queryBuilder;
$sentCountQuery->select('COUNT(sts.subscriber) AS subscriberCount');
$sentCountQuery->andWhere('sts.processed = :processedStatus');
$sentCountQuery->andWhere('sts.failed = :failedStatus');
$sentCountQuery->setParameter('processedStatus', ScheduledTaskSubscriberEntity::STATUS_PROCESSED);
$sentCountQuery->setParameter('failedStatus', ScheduledTaskSubscriberEntity::FAIL_STATUS_OK);
$sentCount = intval($sentCountQuery->getQuery()->getSingleScalarResult());
// Failed count
$failedCountQuery = clone $queryBuilder;
$failedCountQuery->select('COUNT(sts.subscriber) AS subscriberCount');
$failedCountQuery->andWhere('sts.failed = :failedStatus');
$failedCountQuery->setParameter('failedStatus', ScheduledTaskSubscriberEntity::FAIL_STATUS_FAILED);
$failedCount = intval($failedCountQuery->getQuery()->getSingleScalarResult());
// Unprocessed count
$unprocessedCountQuery = clone $queryBuilder;
$unprocessedCountQuery->select('COUNT(sts.subscriber) AS subscriberCount');
$unprocessedCountQuery->andWhere('sts.processed = :processedStatus');
$unprocessedCountQuery->setParameter('processedStatus', ScheduledTaskSubscriberEntity::STATUS_UNPROCESSED);
$unprocessedCount = intval($unprocessedCountQuery->getQuery()->getSingleScalarResult());
return [
[
'name' => 'all',
'label' => __('All', 'mailpoet'),
'count' => $totalCount,
],
[
'name' => ScheduledTaskSubscriberEntity::SENDING_STATUS_SENT,
'label' => __('Sent', 'mailpoet'),
'count' => $sentCount,
],
[
'name' => ScheduledTaskSubscriberEntity::SENDING_STATUS_FAILED,
'label' => __('Failed', 'mailpoet'),
'count' => $failedCount,
],
[
'name' => ScheduledTaskSubscriberEntity::SENDING_STATUS_UNPROCESSED,
'label' => __('Unprocessed', 'mailpoet'),
'count' => $unprocessedCount,
],
];
}
protected function applySelectClause(QueryBuilder $queryBuilder) {
$queryBuilder->select("PARTIAL sts.{task,subscriber,processed,failed,error,createdAt,updatedAt}, PARTIAL s.{id, email, firstName, lastName}");
}
protected function applyFromClause(QueryBuilder $queryBuilder) {
$queryBuilder->from(ScheduledTaskSubscriberEntity::class, 'sts')
->leftJoin('sts.subscriber', 's');
}
protected function applyGroup(QueryBuilder $queryBuilder, string $group) {
if ($group === ScheduledTaskSubscriberEntity::SENDING_STATUS_SENT) {
$queryBuilder->andWhere('sts.processed = :processedStatus');
$queryBuilder->andWhere('sts.failed = :failedStatus');
$queryBuilder->setParameter('processedStatus', ScheduledTaskSubscriberEntity::STATUS_PROCESSED);
$queryBuilder->setParameter('failedStatus', ScheduledTaskSubscriberEntity::FAIL_STATUS_OK);
} elseif ($group === ScheduledTaskSubscriberEntity::SENDING_STATUS_FAILED) {
$queryBuilder->andWhere('sts.failed = :failedStatus');
$queryBuilder->setParameter('failedStatus', ScheduledTaskSubscriberEntity::FAIL_STATUS_FAILED);
} elseif ($group === ScheduledTaskSubscriberEntity::SENDING_STATUS_UNPROCESSED) {
$queryBuilder->andWhere('sts.processed = :processedStatus');
$queryBuilder->setParameter('processedStatus', ScheduledTaskSubscriberEntity::STATUS_UNPROCESSED);
}
}
protected function applySorting(QueryBuilder $queryBuilder, string $sortBy, string $sortOrder) {
// ScheduledTaskSubscriber doesn't have id column so the default fallback value 'id'
// generated in MailPoet\Listing\Handler needs to be changed to something else
if ($sortBy === 'id') {
$sortBy = 'subscriber';
}
$queryBuilder->addOrderBy("sts.$sortBy", $sortOrder);
}
protected function applySearch(QueryBuilder $queryBuilder, string $search) {
$search = Helpers::escapeSearch($search);
$queryBuilder
->andWhere('s.email LIKE :search or s.firstName LIKE :search or s.lastName LIKE :search')
->setParameter('search', "%$search%");
}
protected function applyFilters(QueryBuilder $queryBuilder, array $filters) {
// the parent class requires this method, but scheduled task subscribers listing doesn't currently support this feature.
}
protected function applyParameters(QueryBuilder $queryBuilder, array $parameters) {
if (isset($parameters['task_ids']) && !empty($parameters['task_ids'])) {
$queryBuilder->andWhere('sts.task IN (:taskIds)')
->setParameter('taskIds', $parameters['task_ids']);
}
}
public function getCount(ListingDefinition $definition): int {
$queryBuilder = clone $this->queryBuilder;
$this->applyFromClause($queryBuilder);
$this->applyConstraints($queryBuilder, $definition);
$queryBuilder->select("COUNT(DISTINCT sts.subscriber)");
return intval($queryBuilder->getQuery()->getSingleScalarResult());
}
}

View File

@@ -0,0 +1,40 @@
<?php declare(strict_types=1);
namespace MailPoet\Test\DataFactories;
use MailPoet\DI\ContainerWrapper;
use MailPoet\Entities\ScheduledTaskEntity;
use MailPoet\Entities\ScheduledTaskSubscriberEntity;
use MailPoet\Entities\SubscriberEntity;
use MailPoetVendor\Doctrine\ORM\EntityManager;
class ScheduledTaskSubscriber {
/** @var EntityManager */
private $entityManager;
public function __construct() {
$diContainer = ContainerWrapper::getInstance();
$this->entityManager = $diContainer->get(EntityManager::class);
}
public function createUnprocessed(ScheduledTaskEntity $task, SubscriberEntity $subscriberEntity): ScheduledTaskSubscriberEntity {
$taskSubscriber = new ScheduledTaskSubscriberEntity($task, $subscriberEntity);
$this->entityManager->persist($taskSubscriber);
$this->entityManager->flush();
return $taskSubscriber;
}
public function createProcessed(ScheduledTaskEntity $task, SubscriberEntity $subscriberEntity): ScheduledTaskSubscriberEntity {
$taskSubscriber = new ScheduledTaskSubscriberEntity($task, $subscriberEntity, 1);
$this->entityManager->persist($taskSubscriber);
$this->entityManager->flush();
return $taskSubscriber;
}
public function createFailed(ScheduledTaskEntity $task, SubscriberEntity $subscriberEntity, string $error = null): ScheduledTaskSubscriberEntity {
$taskSubscriber = new ScheduledTaskSubscriberEntity($task, $subscriberEntity, 1, 1, $error);
$this->entityManager->persist($taskSubscriber);
$this->entityManager->flush();
return $taskSubscriber;
}
}

View File

@@ -0,0 +1,139 @@
<?php declare(strict_types = 1);
namespace MailPoet\Newsletter\Sending;
use MailPoet\Entities\ScheduledTaskEntity;
use MailPoet\Entities\ScheduledTaskSubscriberEntity;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Listing\Handler;
use MailPoet\Test\DataFactories\ScheduledTask as ScheduledTaskFactory;
use MailPoet\Test\DataFactories\ScheduledTaskSubscriber as TaskSubscriberFactory;
use MailPoet\Test\DataFactories\Subscriber as SubscriberFactory;
use MailPoetVendor\Carbon\Carbon;
class ScheduledTaskSubscribersListingRepositoryTest extends \MailPoetTest {
/** @var Handler */
protected $listingHandler;
/** @var ScheduledTaskSubscribersListingRepository */
private $repository;
/** @var ScheduledTaskFactory */
private $scheduledTaskFactory;
/** @var SubscriberFactory */
private $subscriberFactory;
/** @var TaskSubscriberFactory */
private $taskSubscriberFactory;
/** @var ScheduledTaskEntity */
private $scheduledTask;
public function _before() {
parent::_before();
$this->cleanup();
$this->listingHandler = $this->diContainer->get(Handler::class);
$this->repository = $this->diContainer->get(ScheduledTaskSubscribersListingRepository::class);
$this->scheduledTaskFactory = new ScheduledTaskFactory();
$this->subscriberFactory = new SubscriberFactory();
$this->taskSubscriberFactory = new TaskSubscriberFactory();
// Subscribers
$subscriberUnprocessed = $this->subscriberFactory->withEmail('subscriberUprocessed@email.com')->create();
$subscriberProcessed = $this->subscriberFactory->withEmail('subscriberProcessed@email.com')->create();
$subscriberFailed = $this->subscriberFactory->withEmail('subscriberFailed@email.com')->create();
$this->subscriberFactory->withEmail('subscriberNotIncluded@email.com')->create();
// Scheduled Task
$this->scheduledTask = $this->scheduledTaskFactory->create('sending', ScheduledTaskEntity::STATUS_COMPLETED, Carbon::now()->subDay());
// Task Subscribers
$this->taskSubscriberFactory->createUnprocessed($this->scheduledTask, $subscriberUnprocessed);
$this->taskSubscriberFactory->createProcessed($this->scheduledTask, $subscriberProcessed);
$this->taskSubscriberFactory->createFailed($this->scheduledTask, $subscriberFailed, 'Error Message');
}
public function testItGenerateCorrectGroups() {
$listingData = [
'group' => 'all',
'params' => [ 'task_ids' => [$this->scheduledTask->getId()]],
];
[$all, $sent, $failed, $unprocessed] = $this->repository->getGroups($this->listingHandler->getListingDefinition($listingData));
expect($all['name'])->equals('all');
expect($all['label'])->equals('All');
expect($all['count'])->equals(3);
expect($sent['name'])->equals('sent');
expect($sent['label'])->equals('Sent');
expect($sent['count'])->equals(1);
expect($failed['name'])->equals('failed');
expect($failed['label'])->equals('Failed');
expect($failed['count'])->equals(1);
expect($unprocessed['name'])->equals('unprocessed');
expect($unprocessed['label'])->equals('Unprocessed');
expect($unprocessed['count'])->equals(1);
}
public function testItReturnCorrectDataAndCountForGroupAll() {
$listingData = [
'group' => 'all',
'params' => [ 'task_ids' => [$this->scheduledTask->getId()]],
];
$tasksSubscribers = $this->repository->getData($this->listingHandler->getListingDefinition($listingData));
$count = $this->repository->getCount($this->listingHandler->getListingDefinition($listingData));
expect($tasksSubscribers)->count(3);
expect($count)->equals(3);
$this->assertInstanceOf(ScheduledTaskSubscriberEntity::class, $tasksSubscribers[0]);
$this->assertInstanceOf(SubscriberEntity::class, $tasksSubscribers[0]->getSubscriber());
expect($tasksSubscribers[0]->getSubscriber()->getEmail())->equals('subscriberUprocessed@email.com');
$this->assertInstanceOf(ScheduledTaskSubscriberEntity::class, $tasksSubscribers[1]);
$this->assertInstanceOf(SubscriberEntity::class, $tasksSubscribers[1]->getSubscriber());
expect($tasksSubscribers[1]->getSubscriber()->getEmail())->equals('subscriberProcessed@email.com');
$this->assertInstanceOf(ScheduledTaskSubscriberEntity::class, $tasksSubscribers[2]);
$this->assertInstanceOf(SubscriberEntity::class, $tasksSubscribers[2]->getSubscriber());
expect($tasksSubscribers[2]->getSubscriber()->getEmail())->equals('subscriberFailed@email.com');
}
public function testItCanFilterByGroup() {
$listingData = [
'group' => 'failed',
'params' => [ 'task_ids' => [$this->scheduledTask->getId()]],
];
$tasksSubscribers = $this->repository->getData($this->listingHandler->getListingDefinition($listingData));
$count = $this->repository->getCount($this->listingHandler->getListingDefinition($listingData));
expect($tasksSubscribers)->count(1);
expect($count)->equals(1);
$this->assertInstanceOf(ScheduledTaskSubscriberEntity::class, $tasksSubscribers[0]);
$this->assertInstanceOf(SubscriberEntity::class, $tasksSubscribers[0]->getSubscriber());
expect($tasksSubscribers[0]->getSubscriber()->getEmail())->equals('subscriberFailed@email.com');
}
public function testItCanSearchByEmail() {
$listingData = [
'group' => 'all',
'params' => [ 'task_ids' => [$this->scheduledTask->getId()]],
'search' => 'subscriberProcessed@',
];
$tasksSubscribers = $this->repository->getData($this->listingHandler->getListingDefinition($listingData));
$count = $this->repository->getCount($this->listingHandler->getListingDefinition($listingData));
expect($tasksSubscribers)->count(1);
expect($count)->equals(1);
$this->assertInstanceOf(ScheduledTaskSubscriberEntity::class, $tasksSubscribers[0]);
$this->assertInstanceOf(SubscriberEntity::class, $tasksSubscribers[0]->getSubscriber());
expect($tasksSubscribers[0]->getSubscriber()->getEmail())->equals('subscriberProcessed@email.com');
}
public function cleanup() {
$this->truncateEntity(ScheduledTaskEntity::class);
$this->truncateEntity(SubscriberEntity::class);
$this->truncateEntity(ScheduledTaskSubscriberEntity::class);
}
}