diff --git a/lib/API/JSON/ResponseBuilders/SubscribersResponseBuilder.php b/lib/API/JSON/ResponseBuilders/SubscribersResponseBuilder.php index e16130d344..12624323e8 100644 --- a/lib/API/JSON/ResponseBuilders/SubscribersResponseBuilder.php +++ b/lib/API/JSON/ResponseBuilders/SubscribersResponseBuilder.php @@ -69,12 +69,12 @@ class SubscribersResponseBuilder { ]; } - public function build(SubscriberEntity $subscriberEntity): array { + public function build(SubscriberEntity $subscriberEntity, bool $isMPAPI = false): array { $data = [ 'id' => (string)$subscriberEntity->getId(), 'wp_user_id' => $subscriberEntity->getWpUserId(), 'is_woocommerce_user' => $subscriberEntity->getIsWoocommerceUser(), - 'subscriptions' => $this->buildSubscriptions($subscriberEntity), + 'subscriptions' => $this->buildSubscriptions($subscriberEntity, $isMPAPI), 'unsubscribes' => $this->buildUnsubscribes($subscriberEntity), 'status' => $subscriberEntity->getStatus(), 'last_name' => $subscriberEntity->getLastName(), @@ -93,21 +93,31 @@ class SubscribersResponseBuilder { 'unsubscribe_token' => $subscriberEntity->getUnsubscribeToken(), '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 = []; $subscriptions = $this->subscriberSegmentRepository->findBy(['subscriber' => $subscriberEntity]); foreach ($subscriptions as $subscription) { $segment = $subscription->getSegment(); 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(), 'status' => $subscription->getStatus(), 'updated_at' => $subscription->getUpdatedAt()->format(self::DATE_FORMAT), - ]; + ]); } } return $result; diff --git a/lib/API/MP/v1/API.php b/lib/API/MP/v1/API.php index 4a4f4d6442..239fa36ff7 100644 --- a/lib/API/MP/v1/API.php +++ b/lib/API/MP/v1/API.php @@ -2,60 +2,33 @@ namespace MailPoet\API\MP\v1; -use MailPoet\Entities\SubscriberEntity; use MailPoet\Models\Segment; use MailPoet\Models\Subscriber; 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\Source; -use MailPoet\Subscribers\SubscribersRepository; -use MailPoet\Tasks\Sending; use MailPoet\Util\Helpers; use MailPoet\WP\Functions as WPFunctions; class API { - /** @var NewSubscriberNotificationMailer */ - private $newSubscriberNotificationMailer; - - /** @var ConfirmationEmailMailer */ - private $confirmationEmailMailer; - /** @var RequiredCustomFieldValidator */ private $requiredCustomFieldValidator; - /** @var WelcomeScheduler */ - private $welcomeScheduler; - - /** @var SettingsController */ - private $settings; - /** @var CustomFields */ private $customFields; - /** @var SubscribersRepository */ - private $subscribersRepository; + /** @var Subscribers */ + private $subscribers; public function __construct( - NewSubscriberNotificationMailer $newSubscriberNotificationMailer, - ConfirmationEmailMailer $confirmationEmailMailer, RequiredCustomFieldValidator $requiredCustomFieldValidator, - WelcomeScheduler $welcomeScheduler, CustomFields $customFields, - SettingsController $settings, - SubscribersRepository $subscribersRepository + Subscribers $subscribers ) { - $this->newSubscriberNotificationMailer = $newSubscriberNotificationMailer; - $this->confirmationEmailMailer = $confirmationEmailMailer; $this->requiredCustomFieldValidator = $requiredCustomFieldValidator; - $this->welcomeScheduler = $welcomeScheduler; - $this->settings = $settings; $this->customFields = $customFields; - $this->subscribersRepository = $subscribersRepository; + $this->subscribers = $subscribers; } 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); } + /** + * @throws APIException + */ public function subscribeToLists($subscriberId, array $listIds, $options = []) { - $scheduleWelcomeEmail = (isset($options['schedule_welcome_email']) && $options['schedule_welcome_email'] === false) ? false : true; - $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(); + return $this->subscribers->subscribeToLists($subscriberId, $listIds, $options); } public function unsubscribeFromList($subscriberId, $listId) { @@ -331,30 +223,4 @@ class API { } 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()); - } } diff --git a/lib/API/MP/v1/Subscribers.php b/lib/API/MP/v1/Subscribers.php new file mode 100644 index 0000000000..0d86b89b18 --- /dev/null +++ b/lib/API/MP/v1/Subscribers.php @@ -0,0 +1,209 @@ +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()); + } +} diff --git a/lib/DI/ContainerConfigurator.php b/lib/DI/ContainerConfigurator.php index c25ffa29ef..5529767664 100644 --- a/lib/DI/ContainerConfigurator.php +++ b/lib/DI/ContainerConfigurator.php @@ -57,6 +57,7 @@ class ContainerConfigurator implements IContainerConfigurator { $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\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\AutomatedLatestContent::class)->setPublic(true); $container->autowire(\MailPoet\API\JSON\v1\AutomaticEmails::class)->setPublic(true); diff --git a/tests/integration/API/MP/APITest.php b/tests/integration/API/MP/APITest.php index 14b5b12b4a..3476edae1e 100644 --- a/tests/integration/API/MP/APITest.php +++ b/tests/integration/API/MP/APITest.php @@ -4,21 +4,27 @@ namespace MailPoet\Test\API\MP; use Codeception\Stub; 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\Subscribers; use MailPoet\CustomFields\CustomFieldsRepository; use MailPoet\Entities\CustomFieldEntity; +use MailPoet\Entities\SegmentEntity; +use MailPoet\Entities\SubscriberEntity; use MailPoet\Models\ScheduledTask; -use MailPoet\Models\Segment; use MailPoet\Models\SendingQueue; 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\RequiredCustomFieldValidator; +use MailPoet\Subscribers\SubscriberSegmentRepository; use MailPoet\Subscribers\SubscribersRepository; use MailPoet\Tasks\Sending; +use MailPoet\Test\DataFactories\Subscriber as SubscriberFactory; use MailPoetVendor\Idiorm\ORM; class APITest extends \MailPoetTest { @@ -27,114 +33,50 @@ class APITest extends \MailPoetTest { /** @var CustomFieldsRepository */ private $customFieldRepository; + /** @var SubscriberFactory */ + private $subscriberFactory; + + /** @var SegmentsRepository */ + private $segmentRepository; + public function _before(): void { parent::_before(); $settings = SettingsController::getInstance(); $settings->set('signup_confirmation.enabled', true); $this->customFieldRepository = $this->diContainer->get(CustomFieldsRepository::class); + $this->subscriberFactory = new SubscriberFactory(); + $this->segmentRepository = $this->diContainer->get(SegmentsRepository::class); } - private function getApi() { - return new \MailPoet\API\MP\v1\API( - Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']), + private function getSubscribers() { + return new Subscribers( Stub::makeEmpty(ConfirmationEmailMailer::class, ['sendConfirmationEmail']), - $this->diContainer->get(RequiredCustomFieldValidator::class), - Stub::makeEmpty(WelcomeScheduler::class), - $this->diContainer->get(CustomFields::class), + Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']), + $this->diContainer->get(SegmentsRepository::class), 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() { - 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('This subscriber does not exist.'); + private function getApi($subscriberActions = null) { + if (!$subscriberActions) { + $subscriberActions = $this->getSubscribers(); } - } - - public function testItDoesNotSubscribeSubscriberToMissingLists() { - $subscriber = Subscriber::create(); - $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, - ] + return new API( + $this->diContainer->get(RequiredCustomFieldValidator::class), + $this->diContainer->get(CustomFields::class), + $subscriberActions ); - 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() { - $subscriber = Subscriber::create(); - $subscriber->hydrate(Fixtures::get('subscriber_template')); - $subscriber->save(); - $segment = Segment::createOrUpdate( - [ - '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.'); - } + private function getSegment($name = 'Default', $type = SegmentEntity::TYPE_DEFAULT) { + $segment = $this->segmentRepository->createOrUpdate($name); + $segment->setType($type); + $this->segmentRepository->flush(); + return $segment; } 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() { - $segment = Segment::createOrUpdate( - [ - 'name' => 'Default', - 'type' => Segment::TYPE_DEFAULT, - ] - ); + $segment = $this->getSegment(); $result = $this->getApi()->getLists(); expect($result)->count(1); - expect($result[0]['id'])->equals($segment->id); + expect($result[0]['id'])->equals($segment->getId()); } public function testItExcludesWPUsersAndWooCommerceCustomersSegmentsWhenGettingSegments() { - $defaultSegment = Segment::createOrUpdate( - [ - 'name' => 'Default', - 'type' => Segment::TYPE_DEFAULT, - ] - ); - $wpSegment = Segment::createOrUpdate( - [ - 'name' => 'Default', - 'type' => Segment::TYPE_WP_USERS, - ] - ); - $wcSegment = Segment::createOrUpdate( - [ - 'name' => 'Default', - 'type' => Segment::TYPE_WC_USERS, - ] - ); + $defaultSegment = $this->getSegment(); + $this->getSegment('WordPress', SegmentEntity::TYPE_WP_USERS); + $this->getSegment('WooCommerce', SegmentEntity::TYPE_WC_USERS); + $result = $this->getApi()->getLists(); expect($result)->count(1); - expect($result[0]['id'])->equals($defaultSegment->id); + expect($result[0]['id'])->equals($defaultSegment->getId()); } public function testItRequiresEmailAddressToAddSubscriber() { @@ -367,11 +137,9 @@ class APITest extends \MailPoetTest { } public function testItDoesNotAddExistingSubscriber() { - $subscriber = Subscriber::create(); - $subscriber->hydrate(Fixtures::get('subscriber_template')); - $subscriber->save(); + $subscriber = $this->subscriberFactory->create(); try { - $this->getApi()->addSubscriber(['email' => $subscriber->email]); + $this->getApi()->addSubscriber(['email' => $subscriber->getEmail()]); $this->fail('Subscriber exists exception should have been thrown.'); } catch (\Exception $e) { expect($e->getMessage())->equals('This subscriber already exists.'); @@ -439,47 +207,49 @@ class APITest extends \MailPoetTest { } public function testItSubscribesToSegmentsWhenAddingSubscriber() { - $segment = Segment::createOrUpdate( - [ - 'name' => 'Default', - 'type' => Segment::TYPE_DEFAULT, - ] - ); + $segment = $this->getSegment(); $subscriber = [ '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['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() { - $segment = Segment::createOrUpdate( - [ - 'name' => 'Default', - 'type' => Segment::TYPE_DEFAULT, - ] - ); + $segment = $this->getSegment(); $settings = SettingsController::getInstance(); $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(), + '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, - 'subscribersRepository' => Stub::makeEmpty(SubscribersRepository::class), + ], + $this); + + $API = Stub::make( + API::class, + [ + 'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']), + 'subscribers' => $subscriberActions, ], $this ); + $subscriber = [ 'email' => 'test@example.com', ]; - $segments = [$segment->id]; + $segments = [$segment->getId()]; $API->addSubscriber($subscriber, $segments); } @@ -490,41 +260,41 @@ class APITest extends \MailPoetTest { $task->type = 'sending'; $task->setError("Big Error"); $sendingStub = Sending::create($task, SendingQueue::create()); - $welcomeScheduler = $this->make('MailPoet\Newsletter\Scheduler\WelcomeScheduler', [ - 'scheduleSubscriberWelcomeNotification' => [$sendingStub], + $segment = $this->getSegment(); + + $subscribers = Stub::copy($this->getSubscribers(), [ + 'welcomeScheduler' => $this->make('MailPoet\Newsletter\Scheduler\WelcomeScheduler', [ + 'scheduleSubscriberWelcomeNotification' => [$sendingStub], + ]), ]); - $segment = Segment::createOrUpdate( - [ - '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) - ); + $API = $this->getApi($subscribers); + $subscriber = [ 'email' => 'test@example.com', ]; - $segments = [$segment->id()]; + $segments = [$segment->getId()]; $this->expectException('\Exception'); $API->addSubscriber($subscriber, $segments, ['schedule_welcome_email' => true, 'send_confirmation_email' => false]); } public function testItDoesNotScheduleWelcomeNotificationAfterAddingSubscriberIfStatusIsNotSubscribed() { - $API = Stub::makeEmptyExcept( - \MailPoet\API\MP\v1\API::class, - 'addSubscriber', + $subscribers = Stub::makeEmptyExcept( + Subscribers::class, + 'subscribeToLists', [ '_scheduleWelcomeNotification' => Expected::never(), - 'newSubscriberNotificationMailer' => Stub::makeEmpty( - NewSubscriberNotificationMailer::class, ['send' => 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::class, + 'addSubscriber', + [ + 'subscribers' => $subscribers, 'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']), ], $this); $subscriber = [ @@ -535,17 +305,28 @@ class APITest extends \MailPoetTest { } public function testItDoesNotScheduleWelcomeNotificationAfterAddingSubscriberWhenDisabledByOption() { - $API = Stub::makeEmptyExcept( - \MailPoet\API\MP\v1\API::class, - 'addSubscriber', + $subscribers = Stub::makeEmptyExcept( + Subscribers::class, + 'subscribeToLists', [ '_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']), ], $this); $subscriber = [ 'email' => 'test@example.com', - 'status' => Subscriber::STATUS_SUBSCRIBED, + 'status' => SubscriberEntity::STATUS_SUBSCRIBED, ]; $segments = [1]; $options = ['schedule_welcome_email' => false]; @@ -554,7 +335,7 @@ class APITest extends \MailPoetTest { public function testByDefaultItSendsConfirmationEmailAfterAddingSubscriber() { $API = $this->makeEmptyExcept( - \MailPoet\API\MP\v1\API::class, + API::class, 'addSubscriber', [ 'subscribeToLists' => Expected::once(function ($subscriberId, $segmentsIds, $options) { @@ -562,7 +343,6 @@ class APITest extends \MailPoetTest { expect($options['send_confirmation_email'])->equals(true); }), 'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']), - 'newSubscriberNotificationMailer' => Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']), ] ); $subscriber = [ @@ -576,36 +356,47 @@ class APITest extends \MailPoetTest { $confirmationMailer = $this->createMock(ConfirmationEmailMailer::class); $confirmationMailer->expects($this->once()) ->method('sendConfirmationEmailOnce') - ->willReturnCallback(function () { - throw new \Exception('Big Error'); + ->willReturnCallback(function (Subscriber $subscriber) { + $subscriber->setError('Big Error'); + return false; }); - $API = Stub::copy($this->getApi(), [ + $subscribers = Stub::copy($this->getSubscribers(), [ 'confirmationEmailMailer' => $confirmationMailer, ]); - $segment = Segment::createOrUpdate( - [ - 'name' => 'Default', - 'type' => Segment::TYPE_DEFAULT, - ] - ); + $API = $this->getApi($subscribers); + + $segment = $this->getSegment(); + $subscriber = [ 'email' => 'test@example.com', ]; $this->expectException('\Exception'); $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() { + $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( - \MailPoet\API\MP\v1\API::class, + API::class, 'addSubscriber', [ - '__sendConfirmationEmail' => Expected::never(), + 'subscribers' => $subscribers, 'requiredCustomFieldValidator' => Stub::makeEmpty(RequiredCustomFieldValidator::class, ['validate']), - 'newSubscriberNotificationMailer' => Stub::makeEmpty(NewSubscriberNotificationMailer::class, ['send']), ], $this); + $subscriber = [ 'email' => 'test@example.com', ]; @@ -636,11 +427,10 @@ class APITest extends \MailPoetTest { } public function testItDoesNotAddExistingList() { - $segment = Segment::create(); - $segment->name = 'Test segment'; - $segment->save(); + $segment = $this->getSegment('Test Segment'); + try { - $this->getApi()->addList(['name' => $segment->name]); + $this->getApi()->addList(['name' => $segment->getName()]); $this->fail('List exists exception should have been thrown.'); } catch (\Exception $e) { expect($e->getMessage())->equals('This list already exists.'); @@ -667,19 +457,17 @@ class APITest extends \MailPoetTest { } public function testItDoesNotUnsubscribeSubscriberFromMissingLists() { - $subscriber = Subscriber::create(); - $subscriber->hydrate(Fixtures::get('subscriber_template')); - $subscriber->save(); + $subscriber = $this->subscriberFactory->create(); // multiple lists error message 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.'); } catch (\Exception $e) { expect($e->getMessage())->equals('These lists do not exist.'); } // single list error message try { - $this->getApi()->unsubscribeFromLists($subscriber->id, [1]); + $this->getApi()->unsubscribeFromLists($subscriber->getId(), [1]); $this->fail('Missing segments exception should have been thrown.'); } catch (\Exception $e) { expect($e->getMessage())->equals('This list does not exist.'); @@ -687,25 +475,18 @@ class APITest extends \MailPoetTest { } public function testItDoesNotUnsubscribeSubscriberFromListsWhenOneOrMoreListsAreMissing() { - $subscriber = Subscriber::create(); - $subscriber->hydrate(Fixtures::get('subscriber_template')); - $subscriber->save(); - $segment = Segment::createOrUpdate( - [ - 'name' => 'Default', - 'type' => Segment::TYPE_DEFAULT, - ] - ); + $subscriber = $this->subscriberFactory->create(); + $segment = $this->getSegment(); // multiple lists error message 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.'); } catch (\Exception $e) { expect($e->getMessage())->equals('Lists with IDs 90, 100 do not exist.'); } // single list error message 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.'); } catch (\Exception $e) { expect($e->getMessage())->equals('List with ID 90 does not exist.'); @@ -713,45 +494,32 @@ class APITest extends \MailPoetTest { } public function testItDoesNotUnsubscribeSubscriberFromWPUsersList() { - $subscriber = Subscriber::create(); - $subscriber->hydrate(Fixtures::get('subscriber_template')); - $subscriber->save(); - $segment = Segment::createOrUpdate( - [ - 'name' => 'Default', - 'type' => Segment::TYPE_WP_USERS, - ] - ); + $subscriber = $this->subscriberFactory->create(); + $segment = $this->getSegment('WordPress', SegmentEntity::TYPE_WP_USERS); 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.'); } 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() { - $subscriber = Subscriber::create(); - $subscriber->hydrate(Fixtures::get('subscriber_template')); - $subscriber->save(); - $segment = Segment::createOrUpdate( - [ - 'name' => 'Default', - 'type' => Segment::TYPE_WC_USERS, - ] - ); + $subscriber = $this->subscriberFactory->create(); + $segment = $this->getSegment('Default', SegmentEntity::TYPE_WC_USERS); + 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.'); } 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() { // unsubscribing from single list = converting list ID to an array and using // multiple lists unsubscribe method - $API = Stub::make(\MailPoet\API\MP\v1\API::class, [ + $API = Stub::make(API::class, [ 'unsubscribeFromLists' => function() { return func_get_args(); }, @@ -767,46 +535,32 @@ class APITest extends \MailPoetTest { } public function testItUnsubscribesSubscriberFromMultipleLists() { - $subscriber = Subscriber::create(); - $subscriber->hydrate(Fixtures::get('subscriber_template')); - $subscriber->save(); - $segment = Segment::createOrUpdate( - [ - 'name' => 'Default', - 'type' => Segment::TYPE_DEFAULT, - ] - ); + $subscriber = $this->subscriberFactory->create(); + $segment = $this->getSegment(); // test if segments are specified try { - $this->getApi()->unsubscribeFromLists($subscriber->id, []); + $this->getApi()->unsubscribeFromLists($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->id, [$segment->id]); - expect($result['subscriptions'][0]['status'])->equals(Subscriber::STATUS_SUBSCRIBED); - $result = $this->getApi()->unsubscribeFromLists($subscriber->id, [$segment->id]); - expect($result['subscriptions'][0]['status'])->equals(Subscriber::STATUS_UNSUBSCRIBED); + $result = $this->getApi()->subscribeToLists($subscriber->getId(), [$segment->getId()]); + expect($result['subscriptions'][0]['status'])->equals(SubscriberEntity::STATUS_SUBSCRIBED); + $result = $this->getApi()->unsubscribeFromLists($subscriber->getId(), [$segment->getId()]); + expect($result['subscriptions'][0]['status'])->equals(SubscriberEntity::STATUS_UNSUBSCRIBED); } public function testItGetsSubscriber() { - $subscriber = Subscriber::create(); - $subscriber->hydrate(Fixtures::get('subscriber_template')); - $subscriber->save(); - $segment = Segment::createOrUpdate( - [ - 'name' => 'Default', - 'type' => Segment::TYPE_DEFAULT, - ] - ); - $this->getApi()->subscribeToList($subscriber->id, $segment->id); + $subscriber = $this->subscriberFactory->create(); + $segment = $this->getSegment(); + $this->getApi()->subscribeToList($subscriber->getId(), $segment->getId()); // successful response - $result = $this->getApi()->getSubscriber($subscriber->email); - expect($result['email'])->equals($subscriber->email); - expect($result['subscriptions'][0]['segment_id'])->equals($segment->id); + $result = $this->getApi()->getSubscriber($subscriber->getEmail()); + expect($result['email'])->equals($subscriber->getEmail()); + expect($result['subscriptions'][0]['segment_id'])->equals($segment->getId()); // error response try { @@ -818,9 +572,9 @@ class APITest extends \MailPoetTest { } public function _after() { - ORM::raw_execute('TRUNCATE ' . Subscriber::$_table); + $this->truncateEntity(SubscriberEntity::class); $this->truncateEntity(CustomFieldEntity::class); - ORM::raw_execute('TRUNCATE ' . Segment::$_table); + $this->truncateEntity(SegmentEntity::class); ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table); } } diff --git a/tests/integration/API/MP/SubscribersTest.php b/tests/integration/API/MP/SubscribersTest.php new file mode 100644 index 0000000000..4726c595d2 --- /dev/null +++ b/tests/integration/API/MP/SubscribersTest.php @@ -0,0 +1,276 @@ +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); + } +}