- Refactors front router and endpoints to use dynamic methods

This commit is contained in:
Vlad
2016-08-24 22:59:12 -04:00
parent 7fa789cfd1
commit c6b72e729b
5 changed files with 309 additions and 36 deletions

View File

@@ -14,19 +14,24 @@ class Track {
const ENDPOINT = 'track'; const ENDPOINT = 'track';
const ACTION_CLICK = 'click'; const ACTION_CLICK = 'click';
const ACTION_OPEN = 'open'; const ACTION_OPEN = 'open';
public $allowed_actions = array( const ALLOWED_ACTIONS = array(
self::ACTION_CLICK, self::ACTION_CLICK,
self::ACTION_OPEN self::ACTION_OPEN
); );
public $data;
function click($data) { function __construct($data) {
$click_event = new Clicks(); $this->data = $this->_processTrackData($data);
return $click_event->track(self::_processTrackData($data));
} }
function open($data) { function click() {
$click_event = new Clicks();
return $click_event->track($this->data);
}
function open() {
$open_event = new Opens(); $open_event = new Opens();
return $open_event->track(self::_processTrackData($data)); return $open_event->track($this->data);
} }
function _processTrackData($data) { function _processTrackData($data) {
@@ -45,10 +50,10 @@ class Track {
if(!empty($data->link_hash)) { if(!empty($data->link_hash)) {
$data->link = NewsletterLink::getByHash($data->link_hash); $data->link = NewsletterLink::getByHash($data->link_hash);
} }
return self::_validateTrackData($data); return $this->_validateTrackData($data);
} }
static function _validateTrackData($data) { function _validateTrackData($data) {
if(!$data->subscriber || !$data->queue || !$data->newsletter) return false; if(!$data->subscriber || !$data->queue || !$data->newsletter) return false;
$subscriber_token_match = $subscriber_token_match =
Subscriber::verifyToken($data->subscriber->email, $data->subscriber_token); Subscriber::verifyToken($data->subscriber->email, $data->subscriber_token);

View File

@@ -12,11 +12,15 @@ class ViewInBrowser {
const ENDPOINT = 'view_in_browser'; const ENDPOINT = 'view_in_browser';
const ACTION_VIEW = 'view'; const ACTION_VIEW = 'view';
public $allowed_actions = array(self::ACTION_VIEW); public $allowed_actions = array(self::ACTION_VIEW);
public $data;
function view($data) { function __construct($data) {
$data = $this->_processBrowserPreviewData($data); $this->data = $this->_processBrowserPreviewData($data);
}
function view() {
$view_in_browser = new NewsletterViewInBrowser(); $view_in_browser = new NewsletterViewInBrowser();
return $this->_displayNewsletter($view_in_browser->view($data)); return $this->_displayNewsletter($view_in_browser->view($this->data));
} }
function _processBrowserPreviewData($data) { function _processBrowserPreviewData($data) {
@@ -54,9 +58,9 @@ class ViewInBrowser {
return $data; return $data;
} }
function _displayNewsletter($rendered_newsletter) { function _displayNewsletter($result) {
header('Content-Type: text/html; charset=utf-8'); header('Content-Type: text/html; charset=utf-8');
echo $rendered_newsletter; echo $result;
exit; exit;
} }

View File

@@ -1,5 +1,6 @@
<?php <?php
namespace MailPoet\Router; namespace MailPoet\Router;
use MailPoet\Util\Helpers; use MailPoet\Util\Helpers;
if(!defined('ABSPATH')) exit; if(!defined('ABSPATH')) exit;
@@ -27,44 +28,31 @@ class Front {
} }
function init() { function init() {
$class = __NAMESPACE__ . "\\Endpoints\\" . ucfirst($this->endpoint); $endpoint_class = __NAMESPACE__ . "\\Endpoints\\" . ucfirst($this->endpoint);
if(!$this->api_request) return; if(!$this->api_request) return;
if(!$this->endpoint || !class_exists($class)) { if(!$this->endpoint || !class_exists($endpoint_class)) {
self::terminateRequest(self::RESPONSE_ERROR, __('Invalid router endpoint.')); return $this->terminateRequest(self::RESPONSE_ERROR, __('Invalid router endpoint.'));
} }
$this->callEndpoint( $endpoint = new $endpoint_class($this->data);
$class, if(!method_exists($endpoint, $this->action) || !in_array($this->action, $endpoint->allowed_actions)) {
$this->action, return $this->terminateRequest(self::RESPONSE_ERROR, __('Invalid router action.'));
$this->data
);
}
function callEndpoint($endpoint, $action, $data) {
$endpoint = new $endpoint();
if(!method_exists($endpoint, $action) || !in_array($action, $endpoint->allowed_actions)) {
self::terminateRequest(self::RESPONSE_ERROR, __('Invalid router action.'));
} }
call_user_func( return call_user_func(
array( array(
$endpoint, $endpoint,
$action $this->action
), )
$data
); );
} }
static function decodeRequestData($data) { static function decodeRequestData($data) {
$data = base64_decode($data); $data = base64_decode($data);
if(is_serialized($data)) { if(is_serialized($data)) {
$data = unserialize($data); $data = unserialize($data);
} }
if(!is_array($data)) { if(!is_array($data)) {
$data = array(); $data = array();
} }
return $data; return $data;
} }
@@ -83,7 +71,7 @@ class Front {
return add_query_arg($params, home_url()); return add_query_arg($params, home_url());
} }
static function terminateRequest($code, $message) { function terminateRequest($code, $message) {
status_header($code, $message); status_header($code, $message);
exit; exit;
} }

View File

@@ -0,0 +1,133 @@
<?php
use Codeception\Util\Stub;
use MailPoet\Models\Newsletter;
use MailPoet\Models\SendingQueue;
use MailPoet\Models\Subscriber;
use MailPoet\Router\Endpoints\ViewInBrowser;
class ViewInBrowserRouterTest extends MailPoetTest {
function _before() {
// create newsletter
$newsletter = Newsletter::create();
$newsletter->type = 'type';
$this->newsletter = $newsletter->save();
// create subscriber
$subscriber = Subscriber::create();
$subscriber->email = 'test@example.com';
$subscriber->first_name = 'First';
$subscriber->last_name = 'Last';
$this->subscriber = $subscriber->save();
// create queue
$queue = SendingQueue::create();
$queue->newsletter_id = $newsletter->id;
$queue->subscribers = array('processed' => array($subscriber->id));
$this->queue = $queue->save();
// build browser preview data
$this->browser_preview_data = array(
'queue_id' => $queue->id,
'subscriber_id' => $subscriber->id,
'newsletter_id' => $newsletter->id,
'subscriber_token' => Subscriber::generateToken($subscriber->email),
'preview' => false
);
// instantiate class
$this->view_in_browser = new ViewInBrowser();
}
function testItAbortsWhenTrackDataIsMissing() {
$view_in_browser = Stub::make($this->view_in_browser, array(
'_abort' => Stub::exactly(3, function() { })
), $this);
// newsletter ID is required
$data = $this->browser_preview_data;
unset($data['newsletter_id']);
$view_in_browser->_processBrowserPreviewData($data);
// subscriber ID is required
$data = $this->browser_preview_data;
unset($data['subscriber_id']);
$view_in_browser->_processBrowserPreviewData($data);
// subscriber token is required
$data = $this->browser_preview_data;
unset($data['subscriber_token']);
$view_in_browser->_processBrowserPreviewData($data);
}
function testItAbortsWhenTrackDataIsInvalid() {
$view_in_browser = Stub::make($this->view_in_browser, array(
'_abort' => Stub::exactly(3, function() { })
), $this);
// newsletter ID is invalid
$data = $this->browser_preview_data;
$data['newsletter_id'] = 99;
$view_in_browser->_processBrowserPreviewData($data);
// subscriber ID is invalid
$data = $this->browser_preview_data;
$data['subscriber_id'] = 99;
$view_in_browser->_processBrowserPreviewData($data);
// subscriber token is invalid
$data = $this->browser_preview_data;
$data['subscriber_token'] = 'invalid';
$view_in_browser->_processBrowserPreviewData($data);
}
function testItFailsValidationWhenSubscriberTokenDoesNotMatch() {
$data = (object)array_merge(
$this->browser_preview_data,
array(
'queue' => $this->queue,
'subscriber' => $this->subscriber,
'newsletter' => $this->newsletter
)
);
$data->subscriber->email = 'random@email.com';
expect($this->view_in_browser->_validateBrowserPreviewData($data))->false();
}
function testItFailsValidationWhenSubscriberIsNotOnProcessedList() {
$data = (object)array_merge(
$this->browser_preview_data,
array(
'queue' => $this->queue,
'subscriber' => $this->subscriber,
'newsletter' => $this->newsletter
)
);
$data->subscriber->id = 99;
expect($this->view_in_browser->_validateBrowserPreviewData($data))->false();
}
function testItDoesNotRequireWpUsersToBeOnProcessedListWhenPreviewIsEnabled() {
$data = (object)array_merge(
$this->browser_preview_data,
array(
'queue' => $this->queue,
'subscriber' => $this->subscriber,
'newsletter' => $this->newsletter
)
);
$data->subscriber->wp_user_id = 99;
$data->preview = true;
expect($this->view_in_browser->_validateBrowserPreviewData($data))->equals($data);
}
function testItCanProcessBrowserPreviewData() {
$processed_data = $this->view_in_browser->_processBrowserPreviewData($this->browser_preview_data);
expect($processed_data->queue->id)->equals($this->queue->id);
expect($processed_data->subscriber->id)->equals($this->subscriber->id);
expect($processed_data->newsletter->id)->equals($this->newsletter->id);
}
function testItCanViewNewsletter() {
$view_in_browser = Stub::make($this->view_in_browser, array(
'_displayNewsletter' => Stub::exactly(1, function() { })
), $this);
$view_in_browser->view($this->browser_preview_data);
}
function _after() {
ORM::raw_execute('TRUNCATE ' . Newsletter::$_table);
ORM::raw_execute('TRUNCATE ' . Subscriber::$_table);
ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table);
}
}

View File

@@ -0,0 +1,143 @@
<?php
use Codeception\Util\Stub;
use MailPoet\Router\Front;
class FrontTest extends MailPoetTest {
function __construct() {
/* $this->api_request = 'mailpoet_api';
$this->api_endpoint = 'view_in_browser';
$this->api_action = 'view';
$this->api_data = base64_encode(serialize(array('data' => 'dummy data')));
$this->url = "http://example.com/?{$this->api_request}&endpoint={$this->api_endpoint}&action={$this->api_action}&data={$this->api_data}";*/
$this->router_data = array(
'mailpoet_api' => '',
'endpoint' => 'view_in_browser',
'action' => 'view',
'data' => base64_encode(serialize(array('data' => 'dummy data')))
);
$this->router = new Front($this->router_data);
}
/* function testItCanGetAPIDataFromGetRequest() {
$data = array('data' => 'dummy data');
$url = 'http://example.com/?mailpoet_api&endpoint=view_in_browser&action=view&data='
. base64_encode(serialize($data));
parse_str(parse_url($url, PHP_URL_QUERY), $_GET);
$router = new Front();
expect($router->api_request)->equals(true);
expect($router->endpoint)->equals('viewInBrowser');
expect($router->action)->equals('view');
expect($router->data)->equals($data);
}
*/
function testItContinuesExecutionWhenAPIRequestNotDetected() {
$router_data = $this->router_data;
unset($router_data['mailpoet_api']);
$router = Stub::construct(
new Front(),
array($router_data),
array(
'terminateRequest' => function() { return 'terminated'; },
'callEndpoint' => function() { return 'endpoint called'; }
)
);
expect($router->init())->null();
}
function testItTerminatesRequestWhenEndpointNotFound() {
$router_data = $this->router_data;
$router_data['endpoint'] = 'invalidEndpoint';
$router = Stub::construct(
new Front(),
array($router_data),
array(
'terminateRequest' => function($code, $error) {
return array(
$code,
$error
);
},
'callEndpointActoin' => function() { return 'endpoint called'; }
)
);
expect($router->init())
->equals(
array(
404,
'Invalid router endpoint.'
)
);
}
function testItCallsEndpointAction() {
$router_data = $this->router_data;
$router = Stub::construct(
new Front(),
array($router_data),
array(
'terminateRequest' => function($code, $error) {
return array(
$code,
$error
);
},
'callEndpointAction' => function($class, $action, $data) {
return array(
$class,
$action,
$data
);
}
)
);
expect($router->init())
->equals(
array(
'MailPoet\Router\Endpoints\ViewInBrowser',
'view',
array('data' => 'dummy data')
)
);
}
function testItAbortsWhenEndpointActionNotFound() {
$router_data = $this->router_data;
$router = Stub::construct(
new Front(),
array($router_data),
array(
'terminateRequest' => function($code, $error) {
return array(
$code,
$error
);
}
)
);
expect($router->callEndpointAction())
->equals(
404,
'Invalid router action.'
);
}
/*
function testItCallsEndpointWhenItIsFound() {
$router_data = $this->router_data;
$router = Stub::construct(
new Front(),
array($router_data),
array(
'terminateRequest' => function() { return 'terminated'; },
'callEndpoint' => function() { return 'endpoint called'; }
)
);
expect($router->init())->equals('endpoint called');
}*/
}