Add cron worker for reporting subscriber stats

[MAILPOET-3868]
This commit is contained in:
Rostislav Wolny
2021-10-28 16:28:30 +02:00
committed by Veljko V
parent b07326aaa4
commit ffe291c521
12 changed files with 149 additions and 31 deletions

View File

@@ -131,4 +131,17 @@ class ServicesChecker {
$mssKeyPendingApproval = $isApproved === false || $isApproved === 'false'; // API unfortunately saves this as a string
return $mssActive && $mssKeyValid && $mssKeyPendingApproval;
}
/**
* Returns MSS or Premium valid key.
*/
public function getAnyValidKey(): ?string {
if ($this->isMailPoetAPIKeyValid(false, true)) {
return $this->settings->get(Bridge::API_KEY_SETTING_NAME);
}
if ($this->isPremiumKeyValid(false)) {
return $this->settings->get(Bridge::PREMIUM_KEY_SETTING_NAME);
}
return null;
}
}

View File

@@ -69,6 +69,7 @@ class Daemon {
yield $this->workersFactory->createQueueWorker(); // not CronWorkerInterface compatible
yield $this->workersFactory->createSendingServiceKeyCheckWorker();
yield $this->workersFactory->createPremiumKeyCheckWorker();
yield $this->workersFactory->createSubscribersStatsReportWorker();
yield $this->workersFactory->createBounceWorker();
yield $this->workersFactory->createExportFilesCleanupWorker();
yield $this->workersFactory->createBeamerkWorker();

View File

@@ -0,0 +1,62 @@
<?php
namespace MailPoet\Cron\Workers;
use MailPoet\Config\ServicesChecker;
use MailPoet\Cron\CronWorkerScheduler;
use MailPoet\Entities\ScheduledTaskEntity;
use MailPoet\Services\Bridge;
use MailPoet\WP\Functions as WPFunctions;
use MailPoetVendor\Carbon\Carbon;
class SubscribersStatsReport extends SimpleWorker {
const TASK_TYPE = 'subscribers_stats_report';
/** @var Bridge */
private $bridge;
/** @var ServicesChecker */
private $serviceChecker;
/** @var CronWorkerScheduler */
private $workerScheduler;
public function __construct(
Bridge $bridge,
ServicesChecker $servicesChecker,
CronWorkerScheduler $workerScheduler,
WPFunctions $wp
) {
parent::__construct($wp);
$this->bridge = $bridge;
$this->serviceChecker = $servicesChecker;
$this->workerScheduler = $workerScheduler;
}
public function checkProcessingRequirements() {
return (bool)$this->serviceChecker->getAnyValidKey();
}
public function processTaskStrategy(ScheduledTaskEntity $task, $timer): bool {
$key = $this->serviceChecker->getAnyValidKey();
if ($key === null) {
return false;
}
$result = $this->bridge->updateSubscriberCount($key);
// We have a valid key, but request failed
if ($result === false) {
$this->workerScheduler->rescheduleProgressively($task);
}
return $result;
}
public function getNextRunDate() {
$date = Carbon::createFromTimestamp($this->wp->currentTime('timestamp'));
// Spread the check within 6 hours after midnight so that all plugins don't ping the service at the same time
return $date->startOfDay()
->addDay()
->addHours(rand(0, 5))
->addMinutes(rand(0, 59))
->addSeconds(rand(0, 59));
}
}

View File

@@ -122,4 +122,9 @@ class WorkersFactory {
public function createReEngagementEmailsSchedulerWorker() {
return $this->container->get(ReEngagementEmailsScheduler::class);
}
/** @return SubscribersStatsReport */
public function createSubscribersStatsReportWorker() {
return $this->container->get(SubscribersStatsReport::class);
}
}

View File

@@ -173,6 +173,7 @@ class ContainerConfigurator implements IContainerConfigurator {
$container->autowire(\MailPoet\Cron\Workers\SubscribersEngagementScore::class)->setPublic(true);
$container->autowire(\MailPoet\Cron\Workers\SubscribersLastEngagement::class)->setPublic(true);
$container->autowire(\MailPoet\Cron\Workers\SubscribersCountCacheRecalculation::class)->setPublic(true);
$container->autowire(\MailPoet\Cron\Workers\SubscribersStatsReport::class)->setPublic(true);
// Custom field
$container->autowire(\MailPoet\CustomFields\ApiDataSanitizer::class);
$container->autowire(\MailPoet\CustomFields\CustomFieldsRepository::class)->setPublic(true);

View File

@@ -214,20 +214,8 @@ class Bridge {
return $state;
}
public function updateSubscriberCount($result) {
if (
(
!empty($result['state'])
&& (
$result['state'] === self::KEY_VALID
|| $result['state'] === self::KEY_EXPIRING
)
)
&& ($this->api instanceof API)
) {
return $this->api->updateSubscriberCount($this->subscribersFeature->getSubscribersCount());
}
return null;
public function updateSubscriberCount(string $key): bool {
return $this->getApi($key)->updateSubscriberCount($this->subscribersFeature->getSubscribersCount());
}
public static function invalidateKey() {

View File

@@ -143,7 +143,7 @@ class API {
return false;
}
public function updateSubscriberCount($count) {
public function updateSubscriberCount($count): bool {
$result = $this->request(
$this->urlStats,
['subscriber_count' => (int)$count],

View File

@@ -160,6 +160,67 @@ class ServicesCheckerTest extends \MailPoetTest {
expect($result)->false();
}
public function testItReturnsAnyValidKey() {
$premiumKey = 'premium_key';
$mssKey = 'mss_key';
$this->settings->set(Bridge::PREMIUM_KEY_SETTING_NAME, $premiumKey);
$this->settings->set(Bridge::API_KEY_SETTING_NAME, $mssKey);
// Only MSS is Valid
$this->settings->set(
Bridge::PREMIUM_KEY_STATE_SETTING_NAME,
[
'state' => '',
]
);
$this->settings->set(
Bridge::API_KEY_STATE_SETTING_NAME,
[
'state' => Bridge::KEY_VALID,
]
);
expect($this->servicesChecker->getAnyValidKey())->equals($mssKey);
// Only Premium is Valid
$this->settings->set(
Bridge::PREMIUM_KEY_STATE_SETTING_NAME,
[
'state' => Bridge::KEY_VALID,
]
);
$this->settings->set(
Bridge::API_KEY_STATE_SETTING_NAME,
[
'state' => '',
]
);
expect($this->servicesChecker->getAnyValidKey())->equals($premiumKey);
// Both Valid (lets use MSS in that case)
$this->settings->set(
Bridge::API_KEY_STATE_SETTING_NAME,
[
'state' => Bridge::KEY_VALID,
]
);
expect($this->servicesChecker->getAnyValidKey())->equals($mssKey);
// None valid
// Only MSS is Valid
$this->settings->set(
Bridge::PREMIUM_KEY_STATE_SETTING_NAME,
[
'state' => '',
]
);
$this->settings->set(
Bridge::API_KEY_STATE_SETTING_NAME,
[
'state' => '',
]
);
expect($this->servicesChecker->getAnyValidKey())->null();
}
private function setMailPoetSendingMethod() {
$this->settings->set(
Mailer::MAILER_CONFIG_SETTING_NAME,

View File

@@ -292,6 +292,7 @@ class DaemonHttpRunnerTest extends \MailPoetTest {
'createStatsNotificationsWorkerForAutomatedEmails' => $worker,
'createSendingServiceKeyCheckWorker' => $worker,
'createPremiumKeyCheckWorker' => $worker,
'createSubscribersStatsReportWorker' => $worker,
'createBounceWorker' => $worker,
'createMigrationWorker' => $worker,
'createWooCommerceSyncWorker' => $worker,

View File

@@ -48,6 +48,7 @@ class DaemonTest extends \MailPoetTest {
'createStatsNotificationsWorkerForAutomatedEmails' => $this->createSimpleWorkerMock(),
'createSendingServiceKeyCheckWorker' => $this->createSimpleWorkerMock(),
'createPremiumKeyCheckWorker' => $this->createSimpleWorkerMock(),
'createSubscribersStatsReportWorker' => $this->createSimpleWorkerMock(),
'createBounceWorker' => $this->createSimpleWorkerMock(),
'createMigrationWorker' => $this->createSimpleWorkerMock(),
'createWooCommerceSyncWorker' => $this->createSimpleWorkerMock(),

View File

@@ -201,21 +201,6 @@ class BridgeTest extends \MailPoetTest {
}
}
public function testItUpdatesSubscriberCount() {
// it performs update if the key is valid or expiring
$result = [];
$result['state'] = Bridge::KEY_VALID;
$updated = $this->bridge->updateSubscriberCount($result);
expect($updated)->true();
$result['state'] = Bridge::KEY_EXPIRING;
$updated = $this->bridge->updateSubscriberCount($result);
expect($updated)->true();
// it does not perform update if the key is invalid
$result['state'] = Bridge::KEY_INVALID;
$updated = $this->bridge->updateSubscriberCount($result);
expect($updated)->null();
}
public function testItInvalidatesMSSKey() {
$this->settings->set(
Bridge::API_KEY_STATE_SETTING_NAME,

View File

@@ -29,7 +29,7 @@ class BridgeTestMockAPI extends API {
return $this->processPremiumResponse($code);
}
public function updateSubscriberCount($count) {
public function updateSubscriberCount($count): bool {
return true;
}