From a1b3aaf1f8d2497e2bd0d882e08ed1addc45c287 Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 14 May 2017 14:46:53 -0400 Subject: [PATCH] Adds method to create subscriber --- lib/API/MP/v1/API.php | 51 ++++++++++++++ tests/unit/API/MP/APITest.php | 121 ++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) diff --git a/lib/API/MP/v1/API.php b/lib/API/MP/v1/API.php index d3af3fe7ae..0c46a71b6f 100644 --- a/lib/API/MP/v1/API.php +++ b/lib/API/MP/v1/API.php @@ -5,6 +5,7 @@ use MailPoet\Models\CustomField; use MailPoet\Models\Segment; use MailPoet\Models\Subscriber; use MailPoet\Models\SubscriberSegment; +use MailPoet\Newsletter\Scheduler\Scheduler; if(!defined('ABSPATH')) exit; @@ -74,4 +75,54 @@ class API { function getLists() { return Segment::whereNotEqual('type', Segment::TYPE_WP_USERS)->findArray(); } + + function addSubscriber(array $subscriber, $segments = array(), $options = array()) { + // throw exception when subscriber email is missing + if(empty($subscriber['email'])) { + throw new \Exception( + __('Subscriber email address is required.', 'mailpoet') + ); + } + + // throw exception when subscriber already exists + if(Subscriber::findOne($subscriber['email'])) { + throw new \Exception( + __('This subscriber already exists.', 'mailpoet') + ); + } + + // separate data into default and custom fields + list($default_fields, $custom_fields) = CustomField::extractCustomFieldsFromFromObject($subscriber); + // if some required default fields are missing, set their values + $default_fields = Subscriber::setRequiredFieldsDefaultValues($default_fields); + + // add subscriber + $new_subscriber = Subscriber::create(); + $new_subscriber->hydrate($default_fields); + $new_subscriber->save(); + if($new_subscriber->getErrors() !== false) { + throw new \Exception( + __(sprintf('Failed to add subscriber: %s', strtolower(implode(', ', $new_subscriber->getErrors()))), 'mailpoet') + ); + } + if(!empty($custom_fields)) { + $new_subscriber->saveCustomFields($custom_fields); + } + + // subscribe to segments and optionally: 1) send confirmation email, 2) schedule welcome email(s) + if(!empty($segments)) { + $this->subscribeToLists($new_subscriber->id, $segments); + + // send confirmation email + if(!empty($options['send_confirmation_email']) && $new_subscriber->status === Subscriber::STATUS_UNCONFIRMED) { + $this->sendConfirmationEmail($new_subscriber); + } + + // schedule welcome email(s) + if(!empty($options['schedule_welcome_email'])) { + Scheduler::scheduleSubscriberWelcomeNotification($new_subscriber->id, $segments); + } + } + return $new_subscriber->withCustomFields()->withSubscriptions()->asArray(); + } } \ No newline at end of file diff --git a/tests/unit/API/MP/APITest.php b/tests/unit/API/MP/APITest.php index 6d348998b4..c4d32220ad 100644 --- a/tests/unit/API/MP/APITest.php +++ b/tests/unit/API/MP/APITest.php @@ -3,12 +3,18 @@ use Codeception\Util\Fixtures; use MailPoet\API\API; use MailPoet\Models\CustomField; +use MailPoet\Models\Newsletter; +use MailPoet\Models\NewsletterOption; +use MailPoet\Models\NewsletterOptionField; use MailPoet\Models\Segment; +use MailPoet\Models\SendingQueue; use MailPoet\Models\Subscriber; +use MailPoet\Models\SubscriberCustomField; use MailPoet\Models\SubscriberSegment; class MPAPITest extends MailPoetTest { const VERSION = 'v1'; + function testItReturnsSubscriberFields() { $custom_field = CustomField::create(); $custom_field->name = 'test custom field'; @@ -162,10 +168,125 @@ class MPAPITest extends MailPoetTest { expect($result[0]['id'])->equals($default_segment->id); } + function testItRequiresEmailAddressToAddSubscriber() { + try { + API::MP(self::VERSION)->addSubscriber(array()); + $this->fail('Subscriber email address required exception should have been thrown.'); + } catch(\Exception $e) { + expect($e->getMessage())->equals('Subscriber email address is required.'); + } + } + + function testItDoesNotAddExistingSubscriber() { + $subscriber = Subscriber::create(); + $subscriber->hydrate(Fixtures::get('subscriber_template')); + $subscriber->save(); + try { + API::MP(self::VERSION)->addSubscriber(array('email' => $subscriber->email)); + $this->fail('Subscriber exists exception should have been thrown.'); + } catch(\Exception $e) { + expect($e->getMessage())->equals('This subscriber already exists.'); + } + } + + function testItThrowsExceptionWhenSubscriberCannotBeAdded() { + $subscriber = array( + 'email' => 'test' // invalid email + ); + try { + API::MP(self::VERSION)->addSubscriber($subscriber); + $this->fail('Failed to add subscriber exception should have been thrown.'); + } catch(\Exception $e) { + expect($e->getMessage())->contains('Failed to add subscriber:'); + // error message (converted to lowercase) returned by the model + expect($e->getMessage())->contains('your email address is invalid!'); + } + } + + function testItAddsSubscriber() { + $custom_field = CustomField::create(); + $custom_field->name = 'test custom field'; + $custom_field->type = CustomField::TYPE_TEXT; + $custom_field->save(); + + $subscriber = array( + 'email' => 'test@example.com', + 'cf_' . $custom_field->id => 'test' + ); + + $result = API::MP(self::VERSION)->addSubscriber($subscriber); + expect($result['id'])->greaterThan(0); + expect($result['email'])->equals($subscriber['email']); + expect($result['cf_' . $custom_field->id])->equals('test'); + } + + function testItSubscribesToSegmentsWhenAddingSubscriber() { + $segment = Segment::createOrUpdate( + array( + 'name' => 'Default', + 'type' => Segment::TYPE_DEFAULT + ) + ); + $subscriber = array( + 'email' => 'test@example.com' + ); + + $result = API::MP(self::VERSION)->addSubscriber($subscriber, array($segment->id)); + expect($result['id'])->greaterThan(0); + expect($result['email'])->equals($subscriber['email']); + expect($result['subscriptions'][0]['id'])->equals($segment->id); + } + + function testItSendsConfirmationEmailAfterAddingSubscriber() { + $segment = Segment::createOrUpdate( + array( + 'name' => 'Default', + 'type' => Segment::TYPE_DEFAULT + ) + ); + $subscriber = array( + 'email' => 'test@example.com' + ); + $options = array('schedule_welcome_email' => true); + // create newsletter with associated options to send welcome email one day after subscription to segment + $newsletter = Newsletter::create(); + $newsletter->type = Newsletter::TYPE_WELCOME; + $newsletter->status = Newsletter::STATUS_ACTIVE; + $newsletter->save(); + $newsletter_options = array( + 'event' => 'segment', + 'segment' => $segment->id, + 'afterTimeType' => 'days', + 'afterTimeNumber' => 1 + ); + foreach($newsletter_options as $option => $value) { + $newsletter_option_field = NewsletterOptionField::create(); + $newsletter_option_field->name = $option; + $newsletter_option_field->newsletter_type = $newsletter->type; + $newsletter_option_field->save(); + expect($newsletter_option_field->getErrors())->false(); + + $newsletter_option = NewsletterOption::create(); + $newsletter_option->option_field_id = $newsletter_option_field->id; + $newsletter_option->newsletter_id = $newsletter->id; + $newsletter_option->value = $value; + $newsletter_option->save(); + expect($newsletter_option->getErrors())->false(); + } + + expect(SendingQueue::findArray())->count(0); + API::MP(self::VERSION)->addSubscriber($subscriber, array($segment->id), $options); + expect(SendingQueue::findArray())->count(1); + } + function _after() { ORM::raw_execute('TRUNCATE ' . Subscriber::$_table); ORM::raw_execute('TRUNCATE ' . CustomField::$_table); ORM::raw_execute('TRUNCATE ' . Segment::$_table); ORM::raw_execute('TRUNCATE ' . SubscriberSegment::$_table); + ORM::raw_execute('TRUNCATE ' . SubscriberCustomField::$_table); + ORM::raw_execute('TRUNCATE ' . NewsletterOption::$_table); + ORM::raw_execute('TRUNCATE ' . NewsletterOptionField::$_table); + ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table); } } \ No newline at end of file