Refactor MPAPI subscribeToLists to Doctrine

[MAILPOET-3820]
This commit is contained in:
Brezo Cordero
2021-12-02 16:13:08 -06:00
committed by Veljko V
parent 2bb04ed5bd
commit 81e0772459
6 changed files with 683 additions and 567 deletions

View File

@ -69,12 +69,12 @@ class SubscribersResponseBuilder {
]; ];
} }
public function build(SubscriberEntity $subscriberEntity): array { public function build(SubscriberEntity $subscriberEntity, bool $isMPAPI = false): array {
$data = [ $data = [
'id' => (string)$subscriberEntity->getId(), 'id' => (string)$subscriberEntity->getId(),
'wp_user_id' => $subscriberEntity->getWpUserId(), 'wp_user_id' => $subscriberEntity->getWpUserId(),
'is_woocommerce_user' => $subscriberEntity->getIsWoocommerceUser(), 'is_woocommerce_user' => $subscriberEntity->getIsWoocommerceUser(),
'subscriptions' => $this->buildSubscriptions($subscriberEntity), 'subscriptions' => $this->buildSubscriptions($subscriberEntity, $isMPAPI),
'unsubscribes' => $this->buildUnsubscribes($subscriberEntity), 'unsubscribes' => $this->buildUnsubscribes($subscriberEntity),
'status' => $subscriberEntity->getStatus(), 'status' => $subscriberEntity->getStatus(),
'last_name' => $subscriberEntity->getLastName(), 'last_name' => $subscriberEntity->getLastName(),
@ -93,21 +93,31 @@ class SubscribersResponseBuilder {
'unsubscribe_token' => $subscriberEntity->getUnsubscribeToken(), 'unsubscribe_token' => $subscriberEntity->getUnsubscribeToken(),
'link_token' => $subscriberEntity->getLinkToken(), 'link_token' => $subscriberEntity->getLinkToken(),
]; ];
$data = $this->buildCustomFields($subscriberEntity, $data);
return $data; return $this->buildCustomFields($subscriberEntity, $data);
} }
private function buildSubscriptions(SubscriberEntity $subscriberEntity): array { private function buildSubscriptions(SubscriberEntity $subscriberEntity, bool $isMPAPI = false): array {
$result = []; $result = [];
$subscriptions = $this->subscriberSegmentRepository->findBy(['subscriber' => $subscriberEntity]); $subscriptions = $this->subscriberSegmentRepository->findBy(['subscriber' => $subscriberEntity]);
foreach ($subscriptions as $subscription) { foreach ($subscriptions as $subscription) {
$segment = $subscription->getSegment(); $segment = $subscription->getSegment();
if ($segment instanceof SegmentEntity) { if ($segment instanceof SegmentEntity) {
$result[] = [ $extraData = [];
if ( $isMPAPI ) {
$extraData = [
'id' => $subscription->getId(),
'subscriber_id' => (string)$subscriberEntity->getId(),
'created_at' => $subscription->getCreatedAt()->format(self::DATE_FORMAT),
];
}
$result[] = array_merge(
$extraData,
[
'segment_id' => (string)$segment->getId(), 'segment_id' => (string)$segment->getId(),
'status' => $subscription->getStatus(), 'status' => $subscription->getStatus(),
'updated_at' => $subscription->getUpdatedAt()->format(self::DATE_FORMAT), 'updated_at' => $subscription->getUpdatedAt()->format(self::DATE_FORMAT),
]; ]);
} }
} }
return $result; return $result;

View File

@ -2,60 +2,33 @@
namespace MailPoet\API\MP\v1; namespace MailPoet\API\MP\v1;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Models\Segment; use MailPoet\Models\Segment;
use MailPoet\Models\Subscriber; use MailPoet\Models\Subscriber;
use MailPoet\Models\SubscriberSegment; use MailPoet\Models\SubscriberSegment;
use MailPoet\Newsletter\Scheduler\WelcomeScheduler;
use MailPoet\Settings\SettingsController;
use MailPoet\Subscribers\ConfirmationEmailMailer;
use MailPoet\Subscribers\NewSubscriberNotificationMailer;
use MailPoet\Subscribers\RequiredCustomFieldValidator; use MailPoet\Subscribers\RequiredCustomFieldValidator;
use MailPoet\Subscribers\Source; use MailPoet\Subscribers\Source;
use MailPoet\Subscribers\SubscribersRepository;
use MailPoet\Tasks\Sending;
use MailPoet\Util\Helpers; use MailPoet\Util\Helpers;
use MailPoet\WP\Functions as WPFunctions; use MailPoet\WP\Functions as WPFunctions;
class API { class API {
/** @var NewSubscriberNotificationMailer */
private $newSubscriberNotificationMailer;
/** @var ConfirmationEmailMailer */
private $confirmationEmailMailer;
/** @var RequiredCustomFieldValidator */ /** @var RequiredCustomFieldValidator */
private $requiredCustomFieldValidator; private $requiredCustomFieldValidator;
/** @var WelcomeScheduler */
private $welcomeScheduler;
/** @var SettingsController */
private $settings;
/** @var CustomFields */ /** @var CustomFields */
private $customFields; private $customFields;
/** @var SubscribersRepository */ /** @var Subscribers */
private $subscribersRepository; private $subscribers;
public function __construct( public function __construct(
NewSubscriberNotificationMailer $newSubscriberNotificationMailer,
ConfirmationEmailMailer $confirmationEmailMailer,
RequiredCustomFieldValidator $requiredCustomFieldValidator, RequiredCustomFieldValidator $requiredCustomFieldValidator,
WelcomeScheduler $welcomeScheduler,
CustomFields $customFields, CustomFields $customFields,
SettingsController $settings, Subscribers $subscribers
SubscribersRepository $subscribersRepository
) { ) {
$this->newSubscriberNotificationMailer = $newSubscriberNotificationMailer;
$this->confirmationEmailMailer = $confirmationEmailMailer;
$this->requiredCustomFieldValidator = $requiredCustomFieldValidator; $this->requiredCustomFieldValidator = $requiredCustomFieldValidator;
$this->welcomeScheduler = $welcomeScheduler;
$this->settings = $settings;
$this->customFields = $customFields; $this->customFields = $customFields;
$this->subscribersRepository = $subscribersRepository; $this->subscribers = $subscribers;
} }
public function getSubscriberFields() { public function getSubscriberFields() {
@ -70,99 +43,18 @@ class API {
} }
} }
public function subscribeToList($subscriberId, $listId, $options = []) { /**
* @throws APIException
*/
public function subscribeToList($subscriberId, $listId, $options = []): array {
return $this->subscribeToLists($subscriberId, [$listId], $options); return $this->subscribeToLists($subscriberId, [$listId], $options);
} }
/**
* @throws APIException
*/
public function subscribeToLists($subscriberId, array $listIds, $options = []) { public function subscribeToLists($subscriberId, array $listIds, $options = []) {
$scheduleWelcomeEmail = (isset($options['schedule_welcome_email']) && $options['schedule_welcome_email'] === false) ? false : true; return $this->subscribers->subscribeToLists($subscriberId, $listIds, $options);
$sendConfirmationEmail = (isset($options['send_confirmation_email']) && $options['send_confirmation_email'] === false) ? false : true;
$skipSubscriberNotification = (isset($options['skip_subscriber_notification']) && $options['skip_subscriber_notification'] === true) ? true : false;
$signupConfirmationEnabled = (bool)$this->settings->get('signup_confirmation.enabled');
if (empty($listIds)) {
throw new APIException(__('At least one segment ID is required.', 'mailpoet'), APIException::SEGMENT_REQUIRED);
}
// throw exception when subscriber does not exist
$subscriber = Subscriber::findOne($subscriberId);
if (!$subscriber) {
throw new APIException(__('This subscriber does not exist.', 'mailpoet'), APIException::SUBSCRIBER_NOT_EXISTS);
}
// throw exception when none of the segments exist
$foundSegments = Segment::whereIn('id', $listIds)->findMany();
if (!$foundSegments) {
$exception = WPFunctions::get()->_n('This list does not exist.', 'These lists do not exist.', count($listIds), 'mailpoet');
throw new APIException($exception, APIException::LIST_NOT_EXISTS);
}
// throw exception when trying to subscribe to WP Users or WooCommerce Customers segments
$foundSegmentsIds = [];
foreach ($foundSegments as $foundSegment) {
if ($foundSegment->type === Segment::TYPE_WP_USERS) {
throw new APIException(__(sprintf("Can't subscribe to a WordPress Users list with ID %d.", $foundSegment->id), 'mailpoet'), APIException::SUBSCRIBING_TO_WP_LIST_NOT_ALLOWED);
}
if ($foundSegment->type === Segment::TYPE_WC_USERS) {
throw new APIException(__(sprintf("Can't subscribe to a WooCommerce Customers list with ID %d.", $foundSegment->id), 'mailpoet'), APIException::SUBSCRIBING_TO_WC_LIST_NOT_ALLOWED);
}
if ($foundSegment->type !== Segment::TYPE_DEFAULT) {
throw new APIException(__(sprintf("Can't subscribe to a list with ID %d.", $foundSegment->id), 'mailpoet'), APIException::SUBSCRIBING_TO_LIST_NOT_ALLOWED);
}
$foundSegmentsIds[] = $foundSegment->id;
}
// throw an exception when one or more segments do not exist
if (count($foundSegmentsIds) !== count($listIds)) {
$missingIds = array_values(array_diff($listIds, $foundSegmentsIds));
$exception = sprintf(
WPFunctions::get()->_n('List with ID %s does not exist.', 'Lists with IDs %s do not exist.', count($missingIds), 'mailpoet'),
implode(', ', $missingIds)
);
throw new APIException(sprintf($exception, implode(', ', $missingIds)), APIException::LIST_NOT_EXISTS);
}
SubscriberSegment::subscribeToSegments($subscriber, $foundSegmentsIds);
// set status depending on signup confirmation setting
if ($subscriber->status !== Subscriber::STATUS_SUBSCRIBED) {
if ($signupConfirmationEnabled === true) {
$subscriber->set('status', Subscriber::STATUS_UNCONFIRMED);
} else {
$subscriber->set('status', Subscriber::STATUS_SUBSCRIBED);
}
$subscriber->save();
if ($subscriber->getErrors() !== false) {
throw new APIException(
__(sprintf('Failed to save a status of a subscriber : %s', strtolower(implode(', ', $subscriber->getErrors()))), 'mailpoet'),
APIException::FAILED_TO_SAVE_SUBSCRIBER
);
}
}
// schedule welcome email
if ($scheduleWelcomeEmail && $subscriber->status === Subscriber::STATUS_SUBSCRIBED) {
$this->_scheduleWelcomeNotification($subscriber, $foundSegmentsIds);
}
// send confirmation email
if ($sendConfirmationEmail) {
try {
$this->_sendConfirmationEmail($subscriber);
} catch (\Exception $e) {
throw new APIException(
__(sprintf('Subscriber added to lists, but confirmation email failed to send: %s', strtolower($e->getMessage())), 'mailpoet'),
APIException::CONFIRMATION_FAILED_TO_SEND
);
}
}
if (!$skipSubscriberNotification && ($subscriber->status === Subscriber::STATUS_SUBSCRIBED)) {
$this->sendSubscriberNotification($subscriber, $foundSegmentsIds);
}
return $subscriber->withCustomFields()->withSubscriptions()->asArray();
} }
public function unsubscribeFromList($subscriberId, $listId) { public function unsubscribeFromList($subscriberId, $listId) {
@ -331,30 +223,4 @@ class API {
} }
return $subscriber->withCustomFields()->withSubscriptions()->asArray(); return $subscriber->withCustomFields()->withSubscriptions()->asArray();
} }
protected function _sendConfirmationEmail(Subscriber $subscriber) {
$subscriberEntity = $this->subscribersRepository->findOneById($subscriber->id);
if ($subscriberEntity instanceof SubscriberEntity) {
return $this->confirmationEmailMailer->sendConfirmationEmailOnce($subscriberEntity);
}
}
protected function _scheduleWelcomeNotification(Subscriber $subscriber, array $segments) {
$result = $this->welcomeScheduler->scheduleSubscriberWelcomeNotification($subscriber->id, $segments);
if (is_array($result)) {
foreach ($result as $queue) {
if ($queue instanceof Sending && $queue->getErrors()) {
throw new APIException(
__(sprintf('Subscriber added, but welcome email failed to send: %s', strtolower(implode(', ', $queue->getErrors()))), 'mailpoet'),
APIException::WELCOME_FAILED_TO_SEND
);
}
}
}
return $result;
}
private function sendSubscriberNotification(Subscriber $subscriber, array $segmentIds) {
$this->newSubscriberNotificationMailer->send($subscriber, Segment::whereIn('id', $segmentIds)->findMany());
}
} }

View File

@ -0,0 +1,209 @@
<?php
namespace MailPoet\API\MP\v1;
use MailPoet\API\JSON\ResponseBuilders\SubscribersResponseBuilder;
use MailPoet\Entities\SegmentEntity;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Models\Segment;
use MailPoet\Models\Subscriber;
use MailPoet\Newsletter\Scheduler\WelcomeScheduler;
use MailPoet\Segments\SegmentsRepository;
use MailPoet\Settings\SettingsController;
use MailPoet\Subscribers\ConfirmationEmailMailer;
use MailPoet\Subscribers\NewSubscriberNotificationMailer;
use MailPoet\Subscribers\SubscriberSegmentRepository;
use MailPoet\Subscribers\SubscribersRepository;
use MailPoet\Tasks\Sending;
use MailPoet\WP\Functions as WPFunctions;
class Subscribers {
/** @var SettingsController */
private $settings;
/** @var SubscribersRepository */
private $subscribersRepository;
/** @var SegmentsRepository */
private $segmentsRepository;
/** @var SubscriberSegmentRepository */
private $subscribersSegmentRepository;
/** @var ConfirmationEmailMailer */
private $confirmationEmailMailer;
/** @var WelcomeScheduler */
private $welcomeScheduler;
/** @var SubscribersResponseBuilder */
private $subscribersResponseBuilder;
/** @var NewSubscriberNotificationMailer */
private $newSubscriberNotificationMailer;
public function __construct (
ConfirmationEmailMailer $confirmationEmailMailer,
NewSubscriberNotificationMailer $newSubscriberNotificationMailer,
SegmentsRepository $segmentsRepository,
SettingsController $settings,
SubscriberSegmentRepository $subscriberSegmentRepository,
SubscribersRepository $subscribersRepository,
SubscribersResponseBuilder $subscribersResponseBuilder,
WelcomeScheduler $welcomeScheduler
) {
$this->confirmationEmailMailer = $confirmationEmailMailer;
$this->newSubscriberNotificationMailer = $newSubscriberNotificationMailer;
$this->segmentsRepository = $segmentsRepository;
$this->settings = $settings;
$this->subscribersSegmentRepository = $subscriberSegmentRepository;
$this->subscribersRepository = $subscribersRepository;
$this->subscribersResponseBuilder = $subscribersResponseBuilder;
$this->welcomeScheduler = $welcomeScheduler;
}
/**
* @throws APIException
*/
public function subscribeToLists(
$subscriberId,
array $listIds,
array $options = []
): array {
$scheduleWelcomeEmail = !((isset($options['schedule_welcome_email']) && $options['schedule_welcome_email'] === false));
$sendConfirmationEmail = !((isset($options['send_confirmation_email']) && $options['send_confirmation_email'] === false));
$skipSubscriberNotification = isset($options['skip_subscriber_notification']) && $options['skip_subscriber_notification'] === true;
$signupConfirmationEnabled = (bool)$this->settings->get('signup_confirmation.enabled');
if (empty($listIds)) {
throw new APIException(__('At least one segment ID is required.', 'mailpoet'), APIException::SEGMENT_REQUIRED);
}
if (empty($subscriberId)) {
throw new APIException(__('A subscriber is required.', 'mailpoet'), APIException::SUBSCRIBER_NOT_EXISTS);
}
// throw exception when subscriber does not exist
$subscriber = null;
if (is_int($subscriberId) || (string)(int)$subscriberId === $subscriberId) {
$subscriber = $this->subscribersRepository->findOneById($subscriberId);
} else if (strlen(trim($subscriberId)) > 0) {
$subscriber = $this->subscribersRepository->findOneBy(['email' => $subscriberId]);
}
if (!$subscriber) {
throw new APIException(__('This subscriber does not exist.', 'mailpoet'), APIException::SUBSCRIBER_NOT_EXISTS);
}
// throw exception when none of the segments exist
$foundSegments = $this->segmentsRepository->findBy(['id' => $listIds]);
if (!$foundSegments) {
$exception = WPFunctions::get()->_n('This list does not exist.', 'These lists do not exist.', count($listIds), 'mailpoet');
throw new APIException($exception, APIException::LIST_NOT_EXISTS);
}
// throw exception when trying to subscribe to WP Users or WooCommerce Customers segments
$foundSegmentsIds = [];
foreach ($foundSegments as $foundSegment) {
if ($foundSegment->getType() === SegmentEntity::TYPE_WP_USERS) {
throw new APIException(__(sprintf("Can't subscribe to a WordPress Users list with ID %d.", $foundSegment->getId()), 'mailpoet'), APIException::SUBSCRIBING_TO_WP_LIST_NOT_ALLOWED);
}
if ($foundSegment->getType() === SegmentEntity::TYPE_WC_USERS) {
throw new APIException(__(sprintf("Can't subscribe to a WooCommerce Customers list with ID %d.", $foundSegment->getId()), 'mailpoet'), APIException::SUBSCRIBING_TO_WC_LIST_NOT_ALLOWED);
}
if ($foundSegment->getType() !== SegmentEntity::TYPE_DEFAULT) {
throw new APIException(__(sprintf("Can't subscribe to a list with ID %d.", $foundSegment->getId()), 'mailpoet'), APIException::SUBSCRIBING_TO_LIST_NOT_ALLOWED);
}
$foundSegmentsIds[] = $foundSegment->getId();
}
// throw an exception when one or more segments do not exist
if (count($foundSegmentsIds) !== count($listIds)) {
$missingIds = array_values(array_diff($listIds, $foundSegmentsIds));
$exception = sprintf(
WPFunctions::get()->_n('List with ID %s does not exist.', 'Lists with IDs %s do not exist.', count($missingIds), 'mailpoet'),
implode(', ', $missingIds)
);
throw new APIException(sprintf($exception, implode(', ', $missingIds)), APIException::LIST_NOT_EXISTS);
}
$this->subscribersSegmentRepository->subscribeToSegments($subscriber, $foundSegments);
// set status depending on signup confirmation setting
if ($subscriber->getStatus() !== SubscriberEntity::STATUS_SUBSCRIBED) {
if ($signupConfirmationEnabled === true) {
$subscriber->setStatus(SubscriberEntity::STATUS_UNCONFIRMED);
} else {
$subscriber->setStatus(SubscriberEntity::STATUS_SUBSCRIBED);
}
try {
$this->subscribersRepository->flush();
} catch (\Exception $e) {
throw new APIException(
__(sprintf('Failed to save a status of a subscriber : %s', $e->getMessage()), 'mailpoet'),
APIException::FAILED_TO_SAVE_SUBSCRIBER
);
}
}
// schedule welcome email
if ($scheduleWelcomeEmail && $subscriber->getStatus() === SubscriberEntity::STATUS_SUBSCRIBED) {
$this->_scheduleWelcomeNotification($subscriber, $foundSegmentsIds);
}
// send confirmation email
if ($sendConfirmationEmail) {
$this->_sendConfirmationEmail($subscriber);
}
if (!$skipSubscriberNotification && ($subscriber->getStatus() === SubscriberEntity::STATUS_SUBSCRIBED)) {
$this->sendSubscriberNotification($subscriber, $foundSegmentsIds);
}
$this->subscribersRepository->refresh($subscriber);
return $this->subscribersResponseBuilder->build($subscriber, true );
}
/**
* @throws APIException
*/
protected function _scheduleWelcomeNotification(SubscriberEntity $subscriber, array $segments) {
$result = $this->welcomeScheduler->scheduleSubscriberWelcomeNotification($subscriber->getId(), $segments);
if (is_array($result)) {
foreach ($result as $queue) {
if ($queue instanceof Sending && $queue->getErrors()) {
throw new APIException(
__(sprintf('Subscriber added, but welcome email failed to send: %s', strtolower(implode(', ', $queue->getErrors()))), 'mailpoet'),
APIException::WELCOME_FAILED_TO_SEND
);
}
}
}
}
/**
* @throws APIException
*/
protected function _sendConfirmationEmail(SubscriberEntity $subscriberEntity) {
try {
$this->confirmationEmailMailer->sendConfirmationEmailOnce($subscriberEntity);
} catch (\Exception $e) {
throw new APIException(
__(sprintf('Subscriber added to lists, but confirmation email failed to send: %s', strtolower($e->getMessage())), 'mailpoet'),
APIException::CONFIRMATION_FAILED_TO_SEND
);
}
}
/**
* @throws APIException
*/
private function sendSubscriberNotification(SubscriberEntity $subscriberEntity, array $segmentIds) {
$subscriber = Subscriber::findOne($subscriberEntity->getId());
if (!$subscriber) {
throw new APIException(__('This subscriber does not exist.', 'mailpoet'), APIException::SUBSCRIBER_NOT_EXISTS);
}
$this->newSubscriberNotificationMailer->send($subscriber, Segment::whereIn('id', $segmentIds)->findMany());
}
}

View File

@ -57,6 +57,7 @@ class ContainerConfigurator implements IContainerConfigurator {
$container->autowire(\MailPoet\API\JSON\ErrorHandler::class)->setPublic(true); $container->autowire(\MailPoet\API\JSON\ErrorHandler::class)->setPublic(true);
$container->autowire(\MailPoet\API\MP\v1\CustomFields::class)->setPublic(true); $container->autowire(\MailPoet\API\MP\v1\CustomFields::class)->setPublic(true);
$container->autowire(\MailPoet\API\MP\v1\API::class)->setPublic(true); $container->autowire(\MailPoet\API\MP\v1\API::class)->setPublic(true);
$container->autowire(\MailPoet\API\MP\v1\Subscribers::class)->setPublic(true);
$container->autowire(\MailPoet\API\JSON\v1\Analytics::class)->setPublic(true); $container->autowire(\MailPoet\API\JSON\v1\Analytics::class)->setPublic(true);
$container->autowire(\MailPoet\API\JSON\v1\AutomatedLatestContent::class)->setPublic(true); $container->autowire(\MailPoet\API\JSON\v1\AutomatedLatestContent::class)->setPublic(true);
$container->autowire(\MailPoet\API\JSON\v1\AutomaticEmails::class)->setPublic(true); $container->autowire(\MailPoet\API\JSON\v1\AutomaticEmails::class)->setPublic(true);

View File

@ -4,21 +4,27 @@ namespace MailPoet\Test\API\MP;
use Codeception\Stub; use Codeception\Stub;
use Codeception\Stub\Expected; use Codeception\Stub\Expected;
use Codeception\Util\Fixtures; use MailPoet\API\JSON\ResponseBuilders\SubscribersResponseBuilder;
use MailPoet\API\MP\v1\API;
use MailPoet\API\MP\v1\CustomFields; use MailPoet\API\MP\v1\CustomFields;
use MailPoet\API\MP\v1\Subscribers;
use MailPoet\CustomFields\CustomFieldsRepository; use MailPoet\CustomFields\CustomFieldsRepository;
use MailPoet\Entities\CustomFieldEntity; use MailPoet\Entities\CustomFieldEntity;
use MailPoet\Entities\SegmentEntity;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Models\ScheduledTask; use MailPoet\Models\ScheduledTask;
use MailPoet\Models\Segment;
use MailPoet\Models\SendingQueue; use MailPoet\Models\SendingQueue;
use MailPoet\Models\Subscriber; use MailPoet\Models\Subscriber;
use MailPoet\Newsletter\Scheduler\WelcomeScheduler; use MailPoet\Newsletter\Scheduler\WelcomeScheduler;
use MailPoet\Segments\SegmentsRepository;
use MailPoet\Settings\SettingsController; use MailPoet\Settings\SettingsController;
use MailPoet\Subscribers\ConfirmationEmailMailer; use MailPoet\Subscribers\ConfirmationEmailMailer;
use MailPoet\Subscribers\NewSubscriberNotificationMailer; use MailPoet\Subscribers\NewSubscriberNotificationMailer;
use MailPoet\Subscribers\RequiredCustomFieldValidator; use MailPoet\Subscribers\RequiredCustomFieldValidator;
use MailPoet\Subscribers\SubscriberSegmentRepository;
use MailPoet\Subscribers\SubscribersRepository; use MailPoet\Subscribers\SubscribersRepository;
use MailPoet\Tasks\Sending; use MailPoet\Tasks\Sending;
use MailPoet\Test\DataFactories\Subscriber as SubscriberFactory;
use MailPoetVendor\Idiorm\ORM; use MailPoetVendor\Idiorm\ORM;
class APITest extends \MailPoetTest { class APITest extends \MailPoetTest {
@ -27,114 +33,50 @@ class APITest extends \MailPoetTest {
/** @var CustomFieldsRepository */ /** @var CustomFieldsRepository */
private $customFieldRepository; private $customFieldRepository;
/** @var SubscriberFactory */
private $subscriberFactory;
/** @var SegmentsRepository */
private $segmentRepository;
public function _before(): void { public function _before(): void {
parent::_before(); parent::_before();
$settings = SettingsController::getInstance(); $settings = SettingsController::getInstance();
$settings->set('signup_confirmation.enabled', true); $settings->set('signup_confirmation.enabled', true);
$this->customFieldRepository = $this->diContainer->get(CustomFieldsRepository::class); $this->customFieldRepository = $this->diContainer->get(CustomFieldsRepository::class);
$this->subscriberFactory = new SubscriberFactory();
$this->segmentRepository = $this->diContainer->get(SegmentsRepository::class);
} }
private function getApi() { private function getSubscribers() {
return new \MailPoet\API\MP\v1\API( return new Subscribers(
Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']),
Stub::makeEmpty(ConfirmationEmailMailer::class, ['sendConfirmationEmail']), Stub::makeEmpty(ConfirmationEmailMailer::class, ['sendConfirmationEmail']),
$this->diContainer->get(RequiredCustomFieldValidator::class), Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']),
Stub::makeEmpty(WelcomeScheduler::class), $this->diContainer->get(SegmentsRepository::class),
$this->diContainer->get(CustomFields::class),
SettingsController::getInstance(), SettingsController::getInstance(),
$this->diContainer->get(SubscribersRepository::class) $this->diContainer->get(SubscriberSegmentRepository::class),
$this->diContainer->get(SubscribersRepository::class),
$this->diContainer->get(SubscribersResponseBuilder::class),
Stub::makeEmpty(WelcomeScheduler::class)
); );
} }
public function testItDoesNotSubscribeMissingSubscriberToLists() { private function getApi($subscriberActions = null) {
try { if (!$subscriberActions) {
$this->getApi()->subscribeToLists(false, [1,2,3]); $subscriberActions = $this->getSubscribers();
$this->fail('Subscriber does not exist exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('This subscriber does not exist.');
} }
} return new API(
$this->diContainer->get(RequiredCustomFieldValidator::class),
public function testItDoesNotSubscribeSubscriberToMissingLists() { $this->diContainer->get(CustomFields::class),
$subscriber = Subscriber::create(); $subscriberActions
$subscriber->hydrate(Fixtures::get('subscriber_template'));
$subscriber->save();
// multiple lists error message
try {
$this->getApi()->subscribeToLists($subscriber->id, [1,2,3]);
$this->fail('Missing segments exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('These lists do not exist.');
}
// single list error message
try {
$this->getApi()->subscribeToLists($subscriber->id, [1]);
$this->fail('Missing segments exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('This list does not exist.');
}
}
public function testItDoesNotSubscribeSubscriberToWPUsersList() {
$subscriber = Subscriber::create();
$subscriber->hydrate(Fixtures::get('subscriber_template'));
$subscriber->save();
$segment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_WP_USERS,
]
); );
try {
$this->getApi()->subscribeToLists($subscriber->id, [$segment->id]);
$this->fail('WP Users segment exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals("Can't subscribe to a WordPress Users list with ID {$segment->id}.");
}
} }
public function testItDoesNotSubscribeSubscriberToWooCommerceCustomersList() { private function getSegment($name = 'Default', $type = SegmentEntity::TYPE_DEFAULT) {
$subscriber = Subscriber::create(); $segment = $this->segmentRepository->createOrUpdate($name);
$subscriber->hydrate(Fixtures::get('subscriber_template')); $segment->setType($type);
$subscriber->save(); $this->segmentRepository->flush();
$segment = Segment::createOrUpdate( return $segment;
[
'name' => 'Default',
'type' => Segment::TYPE_WC_USERS,
]
);
try {
$this->getApi()->subscribeToLists($subscriber->id, [$segment->id]);
$this->fail('WooCommerce Customers segment exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals("Can't subscribe to a WooCommerce Customers list with ID {$segment->id}.");
}
}
public function testItDoesNotSubscribeSubscriberToListsWhenOneOrMoreListsAreMissing() {
$subscriber = Subscriber::create();
$subscriber->hydrate(Fixtures::get('subscriber_template'));
$subscriber->save();
$segment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]
);
// multiple lists error message
try {
$this->getApi()->subscribeToLists($subscriber->id, [$segment->id, 90, 100]);
$this->fail('Missing segments with IDs exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('Lists with IDs 90, 100 do not exist.');
}
// single list error message
try {
$this->getApi()->subscribeToLists($subscriber->id, [$segment->id, 90]);
$this->fail('Missing segments with IDs exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('List with ID 90 does not exist.');
}
} }
public function testItUsesMultipleListsSubscribeMethodWhenSubscribingToSingleList() { public function testItUsesMultipleListsSubscribeMethodWhenSubscribingToSingleList() {
@ -156,193 +98,21 @@ class APITest extends \MailPoetTest {
); );
} }
public function testItSubscribesSubscriberToMultipleLists() {
$subscriber = Subscriber::create();
$subscriber->hydrate(Fixtures::get('subscriber_template'));
$subscriber->save();
$segment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]
);
// test if segments are specified
try {
$this->getApi()->subscribeToLists($subscriber->id, []);
$this->fail('Segments are required exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('At least one segment ID is required.');
}
$result = $this->getApi()->subscribeToLists($subscriber->id, [$segment->id]);
expect($result['id'])->equals($subscriber->id);
expect($result['subscriptions'][0]['segment_id'])->equals($segment->id);
}
public function testItSendsConfirmationEmailToASubscriberWhenBeingAddedToList() {
$subscriber = Subscriber::create();
$subscriber->hydrate(Fixtures::get('subscriber_template'));
$subscriber->status = Subscriber::STATUS_UNCONFIRMED;
$subscriber->save();
$segment = Segment::createOrUpdate([
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]);
$segment->save();
$sent = false;
$API = $this->makeEmptyExcept(\MailPoet\API\MP\v1\API::class, 'subscribeToLists', [
'_sendConfirmationEmail' => function () use (&$sent) {
$sent = true;
},
'settings' => SettingsController::getInstance(),
]);
$segments = [$segment->id];
// should not send
$API->subscribeToLists($subscriber->email, $segments, ['send_confirmation_email' => false, 'skip_subscriber_notification' => true]);
expect($sent)->equals(false);
// should send
$API->subscribeToLists($subscriber->email, $segments, ['skip_subscriber_notification' => true]);
expect($sent)->equals(true);
}
public function testItSendsNotifiationEmailWhenBeingAddedToList() {
$subscriber = Subscriber::create();
$subscriber->hydrate(Fixtures::get('subscriber_template'));
$subscriber->status = Subscriber::STATUS_SUBSCRIBED;
$subscriber->save();
$segment = Segment::createOrUpdate([
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]);
$segment->save();
$segments = [$segment->id];
// should not send
$notificationMailer = $this->make(NewSubscriberNotificationMailer::class, ['send' => \Codeception\Stub\Expected::never()]);
$API = new \MailPoet\API\MP\v1\API(
$notificationMailer,
$this->makeEmpty(ConfirmationEmailMailer::class),
$this->makeEmpty(RequiredCustomFieldValidator::class),
Stub::makeEmpty(WelcomeScheduler::class),
$this->diContainer->get(CustomFields::class),
SettingsController::getInstance(),
$this->diContainer->get(SubscribersRepository::class)
);
$API->subscribeToLists($subscriber->email, $segments, ['send_confirmation_email' => false, 'skip_subscriber_notification' => true]);
// should send
$notificationMailer = $this->make(NewSubscriberNotificationMailer::class, ['send' => \Codeception\Stub\Expected::once()]);
$API = new \MailPoet\API\MP\v1\API(
$notificationMailer,
$this->makeEmpty(ConfirmationEmailMailer::class),
$this->makeEmpty(RequiredCustomFieldValidator::class),
Stub::makeEmpty(WelcomeScheduler::class),
$this->diContainer->get(CustomFields::class),
SettingsController::getInstance(),
$this->diContainer->get(SubscribersRepository::class)
);
$API->subscribeToLists($subscriber->email, $segments, ['send_confirmation_email' => false, 'skip_subscriber_notification' => false]);
}
public function testItSubscribesSubscriberWithEmailIdentifier() {
$subscriber = Subscriber::create();
$subscriber->hydrate(Fixtures::get('subscriber_template'));
$subscriber->save();
$segment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]
);
$result = $this->getApi()->subscribeToList($subscriber->email, $segment->id);
expect($result['id'])->equals($subscriber->id);
expect($result['subscriptions'])->notEmpty();
expect($result['subscriptions'][0]['segment_id'])->equals($segment->id);
}
public function testItSchedulesWelcomeNotificationByDefaultAfterSubscriberSubscriberToLists() {
$API = Stub::makeEmptyExcept(
\MailPoet\API\MP\v1\API::class,
'subscribeToLists',
[
'_scheduleWelcomeNotification' => Expected::once(),
'settings' => SettingsController::getInstance(),
], $this);
$subscriber = Subscriber::create();
$subscriber->hydrate(Fixtures::get('subscriber_template'));
$subscriber->status = Subscriber::STATUS_SUBSCRIBED;
$subscriber->save();
$segment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]
);
$API->subscribeToLists($subscriber->id, [$segment->id], ['skip_subscriber_notification' => true]);
}
public function testItDoesNotScheduleWelcomeNotificationAfterSubscribingSubscriberToListsWhenDisabledByOption() {
$API = Stub::makeEmptyExcept(
\MailPoet\API\MP\v1\API::class,
'subscribeToLists',
[
'_scheduleWelcomeNotification' => Expected::never(),
'settings' => SettingsController::getInstance(),
], $this);
$subscriber = Subscriber::create();
$subscriber->hydrate(Fixtures::get('subscriber_template'));
$subscriber->status = Subscriber::STATUS_SUBSCRIBED;
$subscriber->save();
$segment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]
);
$options = ['schedule_welcome_email' => false, 'skip_subscriber_notification' => true];
$API->subscribeToLists($subscriber->id, [$segment->id], $options);
}
public function testItGetsSegments() { public function testItGetsSegments() {
$segment = Segment::createOrUpdate( $segment = $this->getSegment();
[
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]
);
$result = $this->getApi()->getLists(); $result = $this->getApi()->getLists();
expect($result)->count(1); expect($result)->count(1);
expect($result[0]['id'])->equals($segment->id); expect($result[0]['id'])->equals($segment->getId());
} }
public function testItExcludesWPUsersAndWooCommerceCustomersSegmentsWhenGettingSegments() { public function testItExcludesWPUsersAndWooCommerceCustomersSegmentsWhenGettingSegments() {
$defaultSegment = Segment::createOrUpdate( $defaultSegment = $this->getSegment();
[ $this->getSegment('WordPress', SegmentEntity::TYPE_WP_USERS);
'name' => 'Default', $this->getSegment('WooCommerce', SegmentEntity::TYPE_WC_USERS);
'type' => Segment::TYPE_DEFAULT,
]
);
$wpSegment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_WP_USERS,
]
);
$wcSegment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_WC_USERS,
]
);
$result = $this->getApi()->getLists(); $result = $this->getApi()->getLists();
expect($result)->count(1); expect($result)->count(1);
expect($result[0]['id'])->equals($defaultSegment->id); expect($result[0]['id'])->equals($defaultSegment->getId());
} }
public function testItRequiresEmailAddressToAddSubscriber() { public function testItRequiresEmailAddressToAddSubscriber() {
@ -367,11 +137,9 @@ class APITest extends \MailPoetTest {
} }
public function testItDoesNotAddExistingSubscriber() { public function testItDoesNotAddExistingSubscriber() {
$subscriber = Subscriber::create(); $subscriber = $this->subscriberFactory->create();
$subscriber->hydrate(Fixtures::get('subscriber_template'));
$subscriber->save();
try { try {
$this->getApi()->addSubscriber(['email' => $subscriber->email]); $this->getApi()->addSubscriber(['email' => $subscriber->getEmail()]);
$this->fail('Subscriber exists exception should have been thrown.'); $this->fail('Subscriber exists exception should have been thrown.');
} catch (\Exception $e) { } catch (\Exception $e) {
expect($e->getMessage())->equals('This subscriber already exists.'); expect($e->getMessage())->equals('This subscriber already exists.');
@ -439,47 +207,49 @@ class APITest extends \MailPoetTest {
} }
public function testItSubscribesToSegmentsWhenAddingSubscriber() { public function testItSubscribesToSegmentsWhenAddingSubscriber() {
$segment = Segment::createOrUpdate( $segment = $this->getSegment();
[
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]
);
$subscriber = [ $subscriber = [
'email' => 'test@example.com', 'email' => 'test@example.com',
]; ];
$result = $this->getApi()->addSubscriber($subscriber, [$segment->id]); $result = $this->getApi()->addSubscriber($subscriber, [$segment->getId()]);
expect($result['id'])->greaterThan(0); expect($result['id'])->greaterThan(0);
expect($result['email'])->equals($subscriber['email']); expect($result['email'])->equals($subscriber['email']);
expect($result['subscriptions'][0]['segment_id'])->equals($segment->id); expect($result['subscriptions'][0]['segment_id'])->equals($segment->getId());
} }
public function testItSchedulesWelcomeNotificationByDefaultAfterAddingSubscriber() { public function testItSchedulesWelcomeNotificationByDefaultAfterAddingSubscriber() {
$segment = Segment::createOrUpdate( $segment = $this->getSegment();
[
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]
);
$settings = SettingsController::getInstance(); $settings = SettingsController::getInstance();
$settings->set('signup_confirmation.enabled', false); $settings->set('signup_confirmation.enabled', false);
$API = Stub::make(
\MailPoet\API\MP\v1\API::class, $subscriberActions = Stub::make(
Subscribers::class,
[ [
'newSubscriberNotificationMailer' => Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']),
'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']),
'confirmationEmailMailer' => Stub::makeEmpty(ConfirmationEmailMailer::class),
'_scheduleWelcomeNotification' => Expected::once(), '_scheduleWelcomeNotification' => Expected::once(),
'newSubscriberNotificationMailer' => Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']),
'confirmationEmailMailer' => Stub::makeEmpty(ConfirmationEmailMailer::class),
'segmentsRepository' => $this->diContainer->get(SegmentsRepository::class),
'subscribersRepository' => $this->diContainer->get(SubscribersRepository::class),
'subscribersSegmentRepository' => $this->diContainer->get(SubscriberSegmentRepository::class),
'subscribersResponseBuilder' => $this->diContainer->get(SubscribersResponseBuilder::class),
'settings' => $settings, 'settings' => $settings,
'subscribersRepository' => Stub::makeEmpty(SubscribersRepository::class), ],
$this);
$API = Stub::make(
API::class,
[
'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']),
'subscribers' => $subscriberActions,
], ],
$this $this
); );
$subscriber = [ $subscriber = [
'email' => 'test@example.com', 'email' => 'test@example.com',
]; ];
$segments = [$segment->id]; $segments = [$segment->getId()];
$API->addSubscriber($subscriber, $segments); $API->addSubscriber($subscriber, $segments);
} }
@ -490,41 +260,41 @@ class APITest extends \MailPoetTest {
$task->type = 'sending'; $task->type = 'sending';
$task->setError("Big Error"); $task->setError("Big Error");
$sendingStub = Sending::create($task, SendingQueue::create()); $sendingStub = Sending::create($task, SendingQueue::create());
$welcomeScheduler = $this->make('MailPoet\Newsletter\Scheduler\WelcomeScheduler', [ $segment = $this->getSegment();
'scheduleSubscriberWelcomeNotification' => [$sendingStub],
$subscribers = Stub::copy($this->getSubscribers(), [
'welcomeScheduler' => $this->make('MailPoet\Newsletter\Scheduler\WelcomeScheduler', [
'scheduleSubscriberWelcomeNotification' => [$sendingStub],
]),
]); ]);
$segment = Segment::createOrUpdate( $API = $this->getApi($subscribers);
[
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]
);
$API = new \MailPoet\API\MP\v1\API(
Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']),
Stub::makeEmpty(ConfirmationEmailMailer::class, ['sendConfirmationEmailOnce']),
$this->diContainer->get(RequiredCustomFieldValidator::class),
$welcomeScheduler,
$this->diContainer->get(CustomFields::class),
Stub::makeEmpty(SettingsController::class),
$this->diContainer->get(SubscribersRepository::class)
);
$subscriber = [ $subscriber = [
'email' => 'test@example.com', 'email' => 'test@example.com',
]; ];
$segments = [$segment->id()]; $segments = [$segment->getId()];
$this->expectException('\Exception'); $this->expectException('\Exception');
$API->addSubscriber($subscriber, $segments, ['schedule_welcome_email' => true, 'send_confirmation_email' => false]); $API->addSubscriber($subscriber, $segments, ['schedule_welcome_email' => true, 'send_confirmation_email' => false]);
} }
public function testItDoesNotScheduleWelcomeNotificationAfterAddingSubscriberIfStatusIsNotSubscribed() { public function testItDoesNotScheduleWelcomeNotificationAfterAddingSubscriberIfStatusIsNotSubscribed() {
$API = Stub::makeEmptyExcept( $subscribers = Stub::makeEmptyExcept(
\MailPoet\API\MP\v1\API::class, Subscribers::class,
'addSubscriber', 'subscribeToLists',
[ [
'_scheduleWelcomeNotification' => Expected::never(), '_scheduleWelcomeNotification' => Expected::never(),
'newSubscriberNotificationMailer' => Stub::makeEmpty( 'segmentsRepository' => $this->diContainer->get(SegmentsRepository::class),
NewSubscriberNotificationMailer::class, ['send' => Expected::never()] 'subscribersRepository' => $this->diContainer->get(SubscribersRepository::class),
), 'subscribersSegmentRepository' => $this->diContainer->get(SubscriberSegmentRepository::class),
'subscribersResponseBuilder' => $this->diContainer->get(SubscribersResponseBuilder::class),
'settings' => SettingsController::getInstance(),
],
$this);
$API = Stub::makeEmptyExcept(
API::class,
'addSubscriber',
[
'subscribers' => $subscribers,
'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']), 'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']),
], $this); ], $this);
$subscriber = [ $subscriber = [
@ -535,17 +305,28 @@ class APITest extends \MailPoetTest {
} }
public function testItDoesNotScheduleWelcomeNotificationAfterAddingSubscriberWhenDisabledByOption() { public function testItDoesNotScheduleWelcomeNotificationAfterAddingSubscriberWhenDisabledByOption() {
$API = Stub::makeEmptyExcept( $subscribers = Stub::makeEmptyExcept(
\MailPoet\API\MP\v1\API::class, Subscribers::class,
'addSubscriber', 'subscribeToLists',
[ [
'_scheduleWelcomeNotification' => Expected::never(), '_scheduleWelcomeNotification' => Expected::never(),
'newSubscriberNotificationMailer' => Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']), 'segmentsRepository' => $this->diContainer->get(SegmentsRepository::class),
'subscribersRepository' => $this->diContainer->get(SubscribersRepository::class),
'subscribersSegmentRepository' => $this->diContainer->get(SubscriberSegmentRepository::class),
'subscribersResponseBuilder' => $this->diContainer->get(SubscribersResponseBuilder::class),
'settings' => SettingsController::getInstance(),
],
$this);
$API = Stub::makeEmptyExcept(
API::class,
'addSubscriber',
[
'subscribers' => $subscribers,
'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']), 'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']),
], $this); ], $this);
$subscriber = [ $subscriber = [
'email' => 'test@example.com', 'email' => 'test@example.com',
'status' => Subscriber::STATUS_SUBSCRIBED, 'status' => SubscriberEntity::STATUS_SUBSCRIBED,
]; ];
$segments = [1]; $segments = [1];
$options = ['schedule_welcome_email' => false]; $options = ['schedule_welcome_email' => false];
@ -554,7 +335,7 @@ class APITest extends \MailPoetTest {
public function testByDefaultItSendsConfirmationEmailAfterAddingSubscriber() { public function testByDefaultItSendsConfirmationEmailAfterAddingSubscriber() {
$API = $this->makeEmptyExcept( $API = $this->makeEmptyExcept(
\MailPoet\API\MP\v1\API::class, API::class,
'addSubscriber', 'addSubscriber',
[ [
'subscribeToLists' => Expected::once(function ($subscriberId, $segmentsIds, $options) { 'subscribeToLists' => Expected::once(function ($subscriberId, $segmentsIds, $options) {
@ -562,7 +343,6 @@ class APITest extends \MailPoetTest {
expect($options['send_confirmation_email'])->equals(true); expect($options['send_confirmation_email'])->equals(true);
}), }),
'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']), 'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']),
'newSubscriberNotificationMailer' => Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']),
] ]
); );
$subscriber = [ $subscriber = [
@ -576,36 +356,47 @@ class APITest extends \MailPoetTest {
$confirmationMailer = $this->createMock(ConfirmationEmailMailer::class); $confirmationMailer = $this->createMock(ConfirmationEmailMailer::class);
$confirmationMailer->expects($this->once()) $confirmationMailer->expects($this->once())
->method('sendConfirmationEmailOnce') ->method('sendConfirmationEmailOnce')
->willReturnCallback(function () { ->willReturnCallback(function (Subscriber $subscriber) {
throw new \Exception('Big Error'); $subscriber->setError('Big Error');
return false;
}); });
$API = Stub::copy($this->getApi(), [ $subscribers = Stub::copy($this->getSubscribers(), [
'confirmationEmailMailer' => $confirmationMailer, 'confirmationEmailMailer' => $confirmationMailer,
]); ]);
$segment = Segment::createOrUpdate( $API = $this->getApi($subscribers);
[
'name' => 'Default', $segment = $this->getSegment();
'type' => Segment::TYPE_DEFAULT,
]
);
$subscriber = [ $subscriber = [
'email' => 'test@example.com', 'email' => 'test@example.com',
]; ];
$this->expectException('\Exception'); $this->expectException('\Exception');
$this->expectExceptionMessage('Subscriber added to lists, but confirmation email failed to send: big error'); $this->expectExceptionMessage('Subscriber added to lists, but confirmation email failed to send: big error');
$API->addSubscriber($subscriber, [$segment->id], ['send_confirmation_email' => true]); $API->addSubscriber($subscriber, [$segment->getId()], ['send_confirmation_email' => true]);
} }
public function testItDoesNotSendConfirmationEmailAfterAddingSubscriberWhenOptionIsSet() { public function testItDoesNotSendConfirmationEmailAfterAddingSubscriberWhenOptionIsSet() {
$subscribers = Stub::makeEmptyExcept(
Subscribers::class,
'subscribeToLists',
[
'_sendConfirmationEmail' => Expected::never(),
'segmentsRepository' => $this->diContainer->get(SegmentsRepository::class),
'subscribersRepository' => $this->diContainer->get(SubscribersRepository::class),
'subscribersSegmentRepository' => $this->diContainer->get(SubscriberSegmentRepository::class),
'subscribersResponseBuilder' => $this->diContainer->get(SubscribersResponseBuilder::class),
'settings' => SettingsController::getInstance(),
],
$this);
$API = Stub::makeEmptyExcept( $API = Stub::makeEmptyExcept(
\MailPoet\API\MP\v1\API::class, API::class,
'addSubscriber', 'addSubscriber',
[ [
'__sendConfirmationEmail' => Expected::never(), 'subscribers' => $subscribers,
'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']), 'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']),
'newSubscriberNotificationMailer' => Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']),
], $this); ], $this);
$subscriber = [ $subscriber = [
'email' => 'test@example.com', 'email' => 'test@example.com',
]; ];
@ -636,11 +427,10 @@ class APITest extends \MailPoetTest {
} }
public function testItDoesNotAddExistingList() { public function testItDoesNotAddExistingList() {
$segment = Segment::create(); $segment = $this->getSegment('Test Segment');
$segment->name = 'Test segment';
$segment->save();
try { try {
$this->getApi()->addList(['name' => $segment->name]); $this->getApi()->addList(['name' => $segment->getName()]);
$this->fail('List exists exception should have been thrown.'); $this->fail('List exists exception should have been thrown.');
} catch (\Exception $e) { } catch (\Exception $e) {
expect($e->getMessage())->equals('This list already exists.'); expect($e->getMessage())->equals('This list already exists.');
@ -667,19 +457,17 @@ class APITest extends \MailPoetTest {
} }
public function testItDoesNotUnsubscribeSubscriberFromMissingLists() { public function testItDoesNotUnsubscribeSubscriberFromMissingLists() {
$subscriber = Subscriber::create(); $subscriber = $this->subscriberFactory->create();
$subscriber->hydrate(Fixtures::get('subscriber_template'));
$subscriber->save();
// multiple lists error message // multiple lists error message
try { try {
$this->getApi()->unsubscribeFromLists($subscriber->id, [1,2,3]); $this->getApi()->unsubscribeFromLists($subscriber->getId(), [1,2,3]);
$this->fail('Missing segments exception should have been thrown.'); $this->fail('Missing segments exception should have been thrown.');
} catch (\Exception $e) { } catch (\Exception $e) {
expect($e->getMessage())->equals('These lists do not exist.'); expect($e->getMessage())->equals('These lists do not exist.');
} }
// single list error message // single list error message
try { try {
$this->getApi()->unsubscribeFromLists($subscriber->id, [1]); $this->getApi()->unsubscribeFromLists($subscriber->getId(), [1]);
$this->fail('Missing segments exception should have been thrown.'); $this->fail('Missing segments exception should have been thrown.');
} catch (\Exception $e) { } catch (\Exception $e) {
expect($e->getMessage())->equals('This list does not exist.'); expect($e->getMessage())->equals('This list does not exist.');
@ -687,25 +475,18 @@ class APITest extends \MailPoetTest {
} }
public function testItDoesNotUnsubscribeSubscriberFromListsWhenOneOrMoreListsAreMissing() { public function testItDoesNotUnsubscribeSubscriberFromListsWhenOneOrMoreListsAreMissing() {
$subscriber = Subscriber::create(); $subscriber = $this->subscriberFactory->create();
$subscriber->hydrate(Fixtures::get('subscriber_template')); $segment = $this->getSegment();
$subscriber->save();
$segment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]
);
// multiple lists error message // multiple lists error message
try { try {
$this->getApi()->unsubscribeFromLists($subscriber->id, [$segment->id, 90, 100]); $this->getApi()->unsubscribeFromLists($subscriber->getId(), [$segment->getId(), 90, 100]);
$this->fail('Missing segments with IDs exception should have been thrown.'); $this->fail('Missing segments with IDs exception should have been thrown.');
} catch (\Exception $e) { } catch (\Exception $e) {
expect($e->getMessage())->equals('Lists with IDs 90, 100 do not exist.'); expect($e->getMessage())->equals('Lists with IDs 90, 100 do not exist.');
} }
// single list error message // single list error message
try { try {
$this->getApi()->unsubscribeFromLists($subscriber->id, [$segment->id, 90]); $this->getApi()->unsubscribeFromLists($subscriber->getId(), [$segment->getId(), 90]);
$this->fail('Missing segments with IDs exception should have been thrown.'); $this->fail('Missing segments with IDs exception should have been thrown.');
} catch (\Exception $e) { } catch (\Exception $e) {
expect($e->getMessage())->equals('List with ID 90 does not exist.'); expect($e->getMessage())->equals('List with ID 90 does not exist.');
@ -713,45 +494,32 @@ class APITest extends \MailPoetTest {
} }
public function testItDoesNotUnsubscribeSubscriberFromWPUsersList() { public function testItDoesNotUnsubscribeSubscriberFromWPUsersList() {
$subscriber = Subscriber::create(); $subscriber = $this->subscriberFactory->create();
$subscriber->hydrate(Fixtures::get('subscriber_template')); $segment = $this->getSegment('WordPress', SegmentEntity::TYPE_WP_USERS);
$subscriber->save();
$segment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_WP_USERS,
]
);
try { try {
$this->getApi()->unsubscribeFromLists($subscriber->id, [$segment->id]); $this->getApi()->unsubscribeFromLists($subscriber->getId(), [$segment->getId()]);
$this->fail('WP Users segment exception should have been thrown.'); $this->fail('WP Users segment exception should have been thrown.');
} catch (\Exception $e) { } catch (\Exception $e) {
expect($e->getMessage())->equals("Can't unsubscribe from a WordPress Users list with ID {$segment->id}."); expect($e->getMessage())->equals("Can't unsubscribe from a WordPress Users list with ID {$segment->getId()}.");
} }
} }
public function testItDoesNotUnsubscribeSubscriberFromWooCommerceCustomersList() { public function testItDoesNotUnsubscribeSubscriberFromWooCommerceCustomersList() {
$subscriber = Subscriber::create(); $subscriber = $this->subscriberFactory->create();
$subscriber->hydrate(Fixtures::get('subscriber_template')); $segment = $this->getSegment('Default', SegmentEntity::TYPE_WC_USERS);
$subscriber->save();
$segment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_WC_USERS,
]
);
try { try {
$this->getApi()->unsubscribeFromLists($subscriber->id, [$segment->id]); $this->getApi()->unsubscribeFromLists($subscriber->getId(), [$segment->getId()]);
$this->fail('WooCommerce Customers segment exception should have been thrown.'); $this->fail('WooCommerce Customers segment exception should have been thrown.');
} catch (\Exception $e) { } catch (\Exception $e) {
expect($e->getMessage())->equals("Can't unsubscribe from a WooCommerce Customers list with ID {$segment->id}."); expect($e->getMessage())->equals("Can't unsubscribe from a WooCommerce Customers list with ID {$segment->getId()}.");
} }
} }
public function testItUsesMultipleListsUnsubscribeMethodWhenUnsubscribingFromSingleList() { public function testItUsesMultipleListsUnsubscribeMethodWhenUnsubscribingFromSingleList() {
// unsubscribing from single list = converting list ID to an array and using // unsubscribing from single list = converting list ID to an array and using
// multiple lists unsubscribe method // multiple lists unsubscribe method
$API = Stub::make(\MailPoet\API\MP\v1\API::class, [ $API = Stub::make(API::class, [
'unsubscribeFromLists' => function() { 'unsubscribeFromLists' => function() {
return func_get_args(); return func_get_args();
}, },
@ -767,46 +535,32 @@ class APITest extends \MailPoetTest {
} }
public function testItUnsubscribesSubscriberFromMultipleLists() { public function testItUnsubscribesSubscriberFromMultipleLists() {
$subscriber = Subscriber::create(); $subscriber = $this->subscriberFactory->create();
$subscriber->hydrate(Fixtures::get('subscriber_template')); $segment = $this->getSegment();
$subscriber->save();
$segment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]
);
// test if segments are specified // test if segments are specified
try { try {
$this->getApi()->unsubscribeFromLists($subscriber->id, []); $this->getApi()->unsubscribeFromLists($subscriber->getId(), []);
$this->fail('Segments are required exception should have been thrown.'); $this->fail('Segments are required exception should have been thrown.');
} catch (\Exception $e) { } catch (\Exception $e) {
expect($e->getMessage())->equals('At least one segment ID is required.'); expect($e->getMessage())->equals('At least one segment ID is required.');
} }
$result = $this->getApi()->subscribeToLists($subscriber->id, [$segment->id]); $result = $this->getApi()->subscribeToLists($subscriber->getId(), [$segment->getId()]);
expect($result['subscriptions'][0]['status'])->equals(Subscriber::STATUS_SUBSCRIBED); expect($result['subscriptions'][0]['status'])->equals(SubscriberEntity::STATUS_SUBSCRIBED);
$result = $this->getApi()->unsubscribeFromLists($subscriber->id, [$segment->id]); $result = $this->getApi()->unsubscribeFromLists($subscriber->getId(), [$segment->getId()]);
expect($result['subscriptions'][0]['status'])->equals(Subscriber::STATUS_UNSUBSCRIBED); expect($result['subscriptions'][0]['status'])->equals(SubscriberEntity::STATUS_UNSUBSCRIBED);
} }
public function testItGetsSubscriber() { public function testItGetsSubscriber() {
$subscriber = Subscriber::create(); $subscriber = $this->subscriberFactory->create();
$subscriber->hydrate(Fixtures::get('subscriber_template')); $segment = $this->getSegment();
$subscriber->save(); $this->getApi()->subscribeToList($subscriber->getId(), $segment->getId());
$segment = Segment::createOrUpdate(
[
'name' => 'Default',
'type' => Segment::TYPE_DEFAULT,
]
);
$this->getApi()->subscribeToList($subscriber->id, $segment->id);
// successful response // successful response
$result = $this->getApi()->getSubscriber($subscriber->email); $result = $this->getApi()->getSubscriber($subscriber->getEmail());
expect($result['email'])->equals($subscriber->email); expect($result['email'])->equals($subscriber->getEmail());
expect($result['subscriptions'][0]['segment_id'])->equals($segment->id); expect($result['subscriptions'][0]['segment_id'])->equals($segment->getId());
// error response // error response
try { try {
@ -818,9 +572,9 @@ class APITest extends \MailPoetTest {
} }
public function _after() { public function _after() {
ORM::raw_execute('TRUNCATE ' . Subscriber::$_table); $this->truncateEntity(SubscriberEntity::class);
$this->truncateEntity(CustomFieldEntity::class); $this->truncateEntity(CustomFieldEntity::class);
ORM::raw_execute('TRUNCATE ' . Segment::$_table); $this->truncateEntity(SegmentEntity::class);
ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table); ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table);
} }
} }

View File

@ -0,0 +1,276 @@
<?php
namespace MailPoet\Test\API\MP;
use Codeception\Stub;
use Codeception\Stub\Expected;
use MailPoet\API\JSON\ResponseBuilders\SubscribersResponseBuilder;
use MailPoet\API\MP\v1\API;
use MailPoet\API\MP\v1\CustomFields;
use MailPoet\API\MP\v1\Subscribers;
use MailPoet\Entities\SegmentEntity;
use MailPoet\Entities\SubscriberEntity;
use MailPoet\Newsletter\Scheduler\WelcomeScheduler;
use MailPoet\Segments\SegmentsRepository;
use MailPoet\Settings\SettingsController;
use MailPoet\Subscribers\ConfirmationEmailMailer;
use MailPoet\Subscribers\NewSubscriberNotificationMailer;
use MailPoet\Subscribers\RequiredCustomFieldValidator;
use MailPoet\Subscribers\SubscriberSegmentRepository;
use MailPoet\Subscribers\SubscribersRepository;
use MailPoet\Test\DataFactories\Subscriber as SubscriberFactory;
class SubscribersTest extends \MailPoetTest {
/** @var SubscriberFactory */
private $subscriberFactory;
/** @var SegmentsRepository */
private $segmentRepository;
public function _before() {
parent::_before();
$settings = SettingsController::getInstance();
$settings->set('signup_confirmation.enabled', true);
$this->subscriberFactory = new SubscriberFactory();
$this->segmentRepository = $this->diContainer->get(SegmentsRepository::class);
}
private function getSubscribers() {
return new Subscribers(
Stub::makeEmpty(ConfirmationEmailMailer::class, ['sendConfirmationEmail']),
Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']),
$this->diContainer->get(SegmentsRepository::class),
SettingsController::getInstance(),
$this->diContainer->get(SubscriberSegmentRepository::class),
$this->diContainer->get(SubscribersRepository::class),
$this->diContainer->get(SubscribersResponseBuilder::class),
Stub::makeEmpty(WelcomeScheduler::class)
);
}
private function getApi($subscriberActions = null) {
if (!$subscriberActions) {
$subscriberActions = $this->getSubscribers();
}
return new API(
$this->makeEmpty(RequiredCustomFieldValidator::class),
$this->diContainer->get(CustomFields::class),
$subscriberActions
);
}
private function getSegment($name = 'Default', $type = SegmentEntity::TYPE_DEFAULT) {
$segment = $this->segmentRepository->createOrUpdate($name);
$segment->setType($type);
$this->segmentRepository->flush();
return $segment;
}
public function testItDoesNotSubscribeMissingSubscriberToLists() {
try {
$this->getApi()->subscribeToLists(false, [1,2,3]);
$this->fail('Subscriber does not exist exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('A subscriber is required.');
}
}
public function testItDoesNotSubscribeSubscriberToMissingLists() {
$subscriber = $this->subscriberFactory->create();
// multiple lists error message
try {
$this->getApi()->subscribeToLists($subscriber->getId(), [1,2,3]);
$this->fail('Missing segments exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('These lists do not exist.');
}
// single list error message
try {
$this->getApi()->subscribeToLists($subscriber->getId(), [1]);
$this->fail('Missing segments exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('This list does not exist.');
}
}
public function testItDoesNotSubscribeSubscriberToWPUsersList() {
$subscriber = $this->subscriberFactory->create();
$segment = $this->getSegment('WordPress', SegmentEntity::TYPE_WP_USERS);
try {
$this->getApi()->subscribeToLists($subscriber->getId(), [$segment->getId()]);
$this->fail('WP Users segment exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals("Can't subscribe to a WordPress Users list with ID {$segment->getId()}.");
}
}
public function testItDoesNotSubscribeSubscriberToWooCommerceCustomersList() {
$subscriber = $this->subscriberFactory->create();
$segment = $this->getSegment('WooCommerce', SegmentEntity::TYPE_WC_USERS);
try {
$this->getApi()->subscribeToLists($subscriber->getId(), [$segment->getId()]);
$this->fail('WooCommerce Customers segment exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals("Can't subscribe to a WooCommerce Customers list with ID {$segment->getId()}.");
}
}
public function testItDoesNotSubscribeSubscriberToListsWhenOneOrMoreListsAreMissing() {
$subscriber = $this->subscriberFactory->create();
$segment = $this->getSegment();
// multiple lists error message
try {
$this->getApi()->subscribeToLists($subscriber->getId(), [$segment->getId(), 90, 100]);
$this->fail('Missing segments with IDs exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('Lists with IDs 90, 100 do not exist.');
}
// single list error message
try {
$this->getApi()->subscribeToLists($subscriber->getId(), [$segment->getId(), 90]);
$this->fail('Missing segments with IDs exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('List with ID 90 does not exist.');
}
}
public function testItSubscribesSubscriberToMultipleLists() {
$subscriber = $this->subscriberFactory->create();
$segment = $this->getSegment();
// test if segments are specified
try {
$this->getApi()->subscribeToLists($subscriber->getId(), []);
$this->fail('Segments are required exception should have been thrown.');
} catch (\Exception $e) {
expect($e->getMessage())->equals('At least one segment ID is required.');
}
$result = $this->getApi()->subscribeToLists($subscriber->getId(), [$segment->getId()]);
expect($result['id'])->equals($subscriber->getId());
expect($result['subscriptions'][0]['segment_id'])->equals($segment->getId());
}
public function testItSendsConfirmationEmailToASubscriberWhenBeingAddedToList() {
$subscriber = $this->subscriberFactory
->withStatus(SubscriberEntity::STATUS_UNCONFIRMED)
->create();
$segment = $this->getSegment();
$segments = [$segment->getId()];
$sent = false;
$subscribers = $this->makeEmptyExcept(
Subscribers::class,
'subscribeToLists',
[
'_sendConfirmationEmail' => function () use (&$sent) {
$sent = true;
},
'segmentsRepository' => $this->diContainer->get(SegmentsRepository::class),
'subscribersRepository' => $this->diContainer->get(SubscribersRepository::class),
'subscribersSegmentRepository' => $this->diContainer->get(SubscriberSegmentRepository::class),
'subscribersResponseBuilder' => $this->diContainer->get(SubscribersResponseBuilder::class),
'settings' => SettingsController::getInstance(),
]
);
$API = $this->getApi($subscribers);
$API->subscribeToLists($subscriber->getEmail(), $segments, ['send_confirmation_email' => false, 'skip_subscriber_notification' => true]);
expect($sent)->equals(false);
// should send
$API->subscribeToLists($subscriber->getEmail(), $segments, ['skip_subscriber_notification' => true]);
expect($sent)->equals(true);
}
public function testItSendsNotificationEmailWhenBeingAddedToList() {
$subscriber = $this->subscriberFactory
->withStatus(SubscriberEntity::STATUS_SUBSCRIBED)
->create();
$segment = $this->getSegment();
$segments = [$segment->getId()];
$subscriberActions = Stub::copy($this->getSubscribers(), [
'$newSubscriberNotificationMailer' => $this->make(NewSubscriberNotificationMailer::class, ['send' => Expected::never()]),
]);
$API = $this->getApi($subscriberActions);
$API->subscribeToLists($subscriber->getEmail(), $segments, ['send_confirmation_email' => false, 'skip_subscriber_notification' => true]);
// should send
$subscribers = Stub::copy($this->getSubscribers(), [
'newSubscriberNotificationMailer' => $this->make(NewSubscriberNotificationMailer::class, ['send' => Expected::once()]),
]);
$API = $this->getApi($subscribers);
$API->subscribeToLists($subscriber->getEmail(), $segments, ['send_confirmation_email' => false, 'skip_subscriber_notification' => false]);
}
public function testItSubscribesSubscriberWithEmailIdentifier() {
$subscriber = $this->subscriberFactory->create();
$segment = $this->getSegment();
$result = $this->getApi()->subscribeToList($subscriber->getEmail(), $segment->getId());
expect($result['id'])->equals($subscriber->getId());
expect($result['subscriptions'])->notEmpty();
expect($result['subscriptions'][0]['segment_id'])->equals($segment->getId());
}
public function testItSchedulesWelcomeNotificationByDefaultAfterSubscriberSubscriberToLists() {
$subscriber = $this->subscriberFactory
->withStatus(SubscriberEntity::STATUS_SUBSCRIBED)
->create();
$segment = $this->getSegment();
$subscribers = Stub::makeEmptyExcept(
Subscribers::class,
'subscribeToLists',
[
'_scheduleWelcomeNotification' => Expected::once(),
'segmentsRepository' => $this->diContainer->get(SegmentsRepository::class),
'subscribersRepository' => $this->diContainer->get(SubscribersRepository::class),
'subscribersSegmentRepository' => $this->diContainer->get(SubscriberSegmentRepository::class),
'subscribersResponseBuilder' => $this->diContainer->get(SubscribersResponseBuilder::class),
'settings' => SettingsController::getInstance(),
],
$this);
$API = $this->getApi($subscribers);
$API->subscribeToLists($subscriber->getId(), [$segment->getId()], ['skip_subscriber_notification' => true]);
}
public function testItDoesNotScheduleWelcomeNotificationAfterSubscribingSubscriberToListsWhenDisabledByOption() {
$subscriber = $this->subscriberFactory
->withStatus(SubscriberEntity::STATUS_SUBSCRIBED)
->create();
$segment = $this->getSegment();
$options = ['schedule_welcome_email' => false, 'skip_subscriber_notification' => true];
$subscribers = Stub::makeEmptyExcept(
Subscribers::class,
'subscribeToLists',
[
'_scheduleWelcomeNotification' => Expected::never(),
'segmentsRepository' => $this->diContainer->get(SegmentsRepository::class),
'subscribersRepository' => $this->diContainer->get(SubscribersRepository::class),
'subscribersSegmentRepository' => $this->diContainer->get(SubscriberSegmentRepository::class),
'subscribersResponseBuilder' => $this->diContainer->get(SubscribersResponseBuilder::class),
'settings' => SettingsController::getInstance(),
],
$this);
$API = $this->getApi($subscribers);
$API->subscribeToLists($subscriber->getId(), [$segment->getId()], $options);
}
public function _after() {
$this->truncateEntity(SubscriberEntity::class);
$this->truncateEntity(SegmentEntity::class);
}
}