Merge pull request #1436 from mailpoet/cron-status-info
Cron status info in help section [MAILPOET-1457]
This commit is contained in:
26
assets/js/src/common/print_boolean.jsx
Normal file
26
assets/js/src/common/print_boolean.jsx
Normal file
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
const PrintBoolean = props => (
|
||||
<span>
|
||||
{(props.children === true && props.truthy) ||
|
||||
(props.children === false && props.falsy) ||
|
||||
(props.unknown)}
|
||||
</span>
|
||||
);
|
||||
|
||||
PrintBoolean.propTypes = {
|
||||
truthy: React.PropTypes.string,
|
||||
falsy: React.PropTypes.string,
|
||||
unknown: React.PropTypes.string,
|
||||
children: React.PropTypes.bool,
|
||||
};
|
||||
|
||||
PrintBoolean.defaultProps = {
|
||||
truthy: MailPoet.I18n.t('yes'),
|
||||
falsy: MailPoet.I18n.t('no'),
|
||||
unknown: MailPoet.I18n.t('unknown'),
|
||||
children: null,
|
||||
};
|
||||
|
||||
module.exports = PrintBoolean;
|
71
assets/js/src/help/cron_status.jsx
Normal file
71
assets/js/src/help/cron_status.jsx
Normal file
@ -0,0 +1,71 @@
|
||||
import MailPoet from 'mailpoet';
|
||||
import React from 'react';
|
||||
import PrintBoolean from 'common/print_boolean.jsx';
|
||||
|
||||
function renderStatusTableRow(title, value) {
|
||||
return (
|
||||
<tr>
|
||||
<td className={'row-title'}>{ title }</td><td>{ value }</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
const CronStatus = (props) => {
|
||||
const status = props.status_data;
|
||||
const activeStatusMapping = {
|
||||
active: MailPoet.I18n.t('cronRunning'),
|
||||
inactive: MailPoet.I18n.t('cronWaiting'),
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<h2>{MailPoet.I18n.t('systemStatusCronStatusTitle')}</h2>
|
||||
<table className={'widefat fixed'} style={{ maxWidth: '400px' }}>
|
||||
<tbody>
|
||||
{renderStatusTableRow(
|
||||
MailPoet.I18n.t('accessible'),
|
||||
<PrintBoolean>{status.accessible}</PrintBoolean>)
|
||||
}
|
||||
{renderStatusTableRow(
|
||||
MailPoet.I18n.t('status'),
|
||||
activeStatusMapping[status.status] ? activeStatusMapping[status.status] : MailPoet.I18n.t('unknown'))
|
||||
}
|
||||
{renderStatusTableRow(
|
||||
MailPoet.I18n.t('lastUpdated'),
|
||||
status.updated_at ? MailPoet.Date.full(status.updated_at * 1000) : MailPoet.I18n.t('unknown'))
|
||||
}
|
||||
{renderStatusTableRow(
|
||||
MailPoet.I18n.t('lastRunStarted'),
|
||||
status.run_accessed_at ? MailPoet.Date.full(status.run_started_at * 1000) : MailPoet.I18n.t('unknown'))
|
||||
}
|
||||
{renderStatusTableRow(
|
||||
MailPoet.I18n.t('lastRunCompleted'),
|
||||
status.run_completed_at ? MailPoet.Date.full(status.run_completed_at * 1000) : MailPoet.I18n.t('unknown'))
|
||||
}
|
||||
{renderStatusTableRow(MailPoet.I18n.t('lastSeenError'), status.last_error || '-')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CronStatus.propTypes = {
|
||||
status_data: React.PropTypes.shape({
|
||||
accessible: React.PropTypes.bool,
|
||||
status: React.PropTypes.string,
|
||||
updated_at: React.PropTypes.number,
|
||||
run_accessed_at: React.PropTypes.number,
|
||||
run_completed_at: React.PropTypes.number,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
CronStatus.defaultProps = {
|
||||
status_data: {
|
||||
accessible: null,
|
||||
status: null,
|
||||
updated_at: null,
|
||||
run_accessed_at: null,
|
||||
run_completed_at: null,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = CronStatus;
|
@ -21,11 +21,11 @@ if (container) {
|
||||
ReactDOM.render((
|
||||
<Router history={history}>
|
||||
<Route path="/" component={App}>
|
||||
<IndexRedirect to="systemStatus" />
|
||||
<IndexRedirect to="knowledgeBase" />
|
||||
{/* Pages */}
|
||||
<Route path="knowledgeBase(/)**" params={{ tab: 'knowledgeBase' }} component={KnowledgeBase} />
|
||||
<Route path="systemStatus(/)**" params={{ tab: 'systemStatus' }} component={SystemStatus} />
|
||||
<Route path="systemInfo(/)**" params={{ tab: 'systemInfo' }} component={SystemInfo} />
|
||||
<Route path="knowledgeBase(/)**" params={{ tab: 'knowledgeBase' }} component={KnowledgeBase} />
|
||||
</Route>
|
||||
</Router>
|
||||
), container);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import MailPoet from 'mailpoet';
|
||||
import React from 'react';
|
||||
import ReactStringReplace from 'react-string-replace';
|
||||
import CronStatus from './cron_status.jsx';
|
||||
import Tabs from './tabs.jsx';
|
||||
|
||||
function renderStatusMessage(status, error, link) {
|
||||
@ -65,6 +66,7 @@ function SystemStatus() {
|
||||
</div>
|
||||
{renderCronSection(systemStatusData)}
|
||||
{renderMSSSection(systemStatusData)}
|
||||
<CronStatus status_data={systemStatusData.cronStatus} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -4,6 +4,11 @@ import classNames from 'classnames';
|
||||
import MailPoet from 'mailpoet';
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
name: 'knowledgeBase',
|
||||
label: MailPoet.I18n.t('tabKnowledgeBaseTitle'),
|
||||
link: '/knowledgeBase',
|
||||
},
|
||||
{
|
||||
name: 'systemStatus',
|
||||
label: MailPoet.I18n.t('tabSystemStatusTitle'),
|
||||
@ -14,11 +19,6 @@ const tabs = [
|
||||
label: MailPoet.I18n.t('tabSystemInfoTitle'),
|
||||
link: '/systemInfo',
|
||||
},
|
||||
{
|
||||
name: 'knowledgeBase',
|
||||
label: MailPoet.I18n.t('tabKnowledgeBaseTitle'),
|
||||
link: '/knowledgeBase',
|
||||
},
|
||||
];
|
||||
|
||||
function Tabs(props) {
|
||||
@ -45,6 +45,6 @@ function Tabs(props) {
|
||||
}
|
||||
|
||||
Tabs.propTypes = { tab: React.PropTypes.string };
|
||||
Tabs.defaultProps = { tab: 'systemStatus' };
|
||||
Tabs.defaultProps = { tab: 'knowledgeBase' };
|
||||
|
||||
module.exports = Tabs;
|
||||
|
@ -448,17 +448,19 @@ class Menu {
|
||||
|
||||
function help() {
|
||||
$system_info_data = Beacon::getData();
|
||||
$system_status_data = array(
|
||||
'cron' => array(
|
||||
$system_status_data = [
|
||||
'cron' => [
|
||||
'url' => CronHelper::getCronUrl(CronDaemon::ACTION_PING),
|
||||
'isReachable' => CronHelper::pingDaemon(true)
|
||||
),
|
||||
'mss' => array(
|
||||
],
|
||||
'mss' => [
|
||||
'enabled' => (Bridge::isMPSendingServiceEnabled()) ?
|
||||
array('isReachable' => Bridge::pingBridge()) :
|
||||
['isReachable' => Bridge::pingBridge()] :
|
||||
false
|
||||
)
|
||||
);
|
||||
],
|
||||
'cronStatus' => CronHelper::getDaemon(),
|
||||
];
|
||||
$system_status_data['cronStatus']['accessible'] = CronHelper::isDaemonAccessible();
|
||||
$this->displayPage(
|
||||
'help.html',
|
||||
array(
|
||||
|
@ -16,15 +16,18 @@ class CronHelper {
|
||||
const DAEMON_EXECUTION_TIMEOUT = 35; // seconds
|
||||
const DAEMON_REQUEST_TIMEOUT = 5; // seconds
|
||||
const DAEMON_SETTING = 'cron_daemon';
|
||||
const DAEMON_STATUS_ACTIVE = 'active';
|
||||
const DAEMON_STATUS_INACTIVE = 'inactive';
|
||||
|
||||
static function createDaemon($token) {
|
||||
$daemon = array(
|
||||
$daemon = [
|
||||
'token' => $token,
|
||||
'status' => self::DAEMON_STATUS_ACTIVE,
|
||||
'run_accessed_at' => null,
|
||||
'run_started_at' => null,
|
||||
'run_completed_at' => null,
|
||||
'last_error' => null,
|
||||
);
|
||||
];
|
||||
self::saveDaemon($daemon);
|
||||
return $daemon;
|
||||
}
|
||||
@ -45,8 +48,12 @@ class CronHelper {
|
||||
);
|
||||
}
|
||||
|
||||
static function deleteDaemon() {
|
||||
return Setting::deleteValue(self::DAEMON_SETTING);
|
||||
static function deactivateDaemon($daemon) {
|
||||
$daemon['status'] = self::DAEMON_STATUS_INACTIVE;
|
||||
return Setting::setValue(
|
||||
self::DAEMON_SETTING,
|
||||
$daemon
|
||||
);
|
||||
}
|
||||
|
||||
static function createToken() {
|
||||
@ -89,7 +96,7 @@ class CronHelper {
|
||||
*/
|
||||
static function isDaemonAccessible() {
|
||||
$daemon = self::getDaemon();
|
||||
if(!$daemon || $daemon['run_accessed_at'] === null) {
|
||||
if(!$daemon || !isset($daemon['run_accessed_at']) || $daemon['run_accessed_at'] === null) {
|
||||
return null;
|
||||
}
|
||||
if($daemon['run_accessed_at'] <= (int)$daemon['run_started_at']) {
|
||||
|
@ -75,7 +75,7 @@ class Daemon {
|
||||
}
|
||||
// after each execution, re-read daemon data in case it changed
|
||||
$daemon = CronHelper::getDaemon();
|
||||
if(!$daemon || $daemon['token'] !== $this->token) {
|
||||
if($this->shouldTerminateExecution($daemon)) {
|
||||
return $this->terminateRequest();
|
||||
}
|
||||
return $this->callSelf();
|
||||
@ -128,4 +128,13 @@ class Daemon {
|
||||
function terminateRequest($message = false) {
|
||||
die($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
private function shouldTerminateExecution(array $daemon = null) {
|
||||
return !$daemon ||
|
||||
$daemon['token'] !== $this->token ||
|
||||
(isset($daemon['status']) && $daemon['status'] !== CronHelper::DAEMON_STATUS_ACTIVE);
|
||||
}
|
||||
}
|
@ -16,7 +16,9 @@ class Supervisor {
|
||||
$daemon = $this->daemon;
|
||||
$execution_timeout_exceeded =
|
||||
(time() - (int)$daemon['updated_at']) >= CronHelper::DAEMON_EXECUTION_TIMEOUT;
|
||||
if($execution_timeout_exceeded) {
|
||||
$daemon_is_inactive =
|
||||
isset($daemon['status']) && $daemon['status'] === CronHelper::DAEMON_STATUS_INACTIVE;
|
||||
if($execution_timeout_exceeded || $daemon_is_inactive) {
|
||||
CronHelper::restartDaemon($this->token);
|
||||
return $this->runDaemon();
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ class WordPress {
|
||||
static function run() {
|
||||
return (self::checkExecutionRequirements()) ?
|
||||
MailPoet::run() :
|
||||
self::cleanup();
|
||||
self::stop();
|
||||
}
|
||||
|
||||
static function checkExecutionRequirements() {
|
||||
@ -60,10 +60,10 @@ class WordPress {
|
||||
);
|
||||
}
|
||||
|
||||
static function cleanup() {
|
||||
static function stop() {
|
||||
$cron_daemon = CronHelper::getDaemon();
|
||||
if($cron_daemon) {
|
||||
CronHelper::deleteDaemon();
|
||||
CronHelper::deactivateDaemon($cron_daemon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ class CronHelperTest extends \MailPoetTest {
|
||||
expect($daemon)->equals(
|
||||
array(
|
||||
'token' => $token,
|
||||
'status' => CronHelper::DAEMON_STATUS_ACTIVE,
|
||||
'updated_at' => $time,
|
||||
'run_accessed_at' => null,
|
||||
'run_started_at' => null,
|
||||
@ -50,6 +51,7 @@ class CronHelperTest extends \MailPoetTest {
|
||||
expect($daemon)->equals(
|
||||
array(
|
||||
'token' => $token,
|
||||
'status' => CronHelper::DAEMON_STATUS_ACTIVE,
|
||||
'updated_at' => $time,
|
||||
'run_accessed_at' => null,
|
||||
'run_started_at' => null,
|
||||
@ -62,6 +64,7 @@ class CronHelperTest extends \MailPoetTest {
|
||||
function testItLoadsDaemon() {
|
||||
$daemon = array(
|
||||
'token' => 'some_token',
|
||||
'status' => CronHelper::DAEMON_STATUS_ACTIVE,
|
||||
'updated_at' => '12345678',
|
||||
'run_accessed_at' => null,
|
||||
'run_started_at' => null,
|
||||
@ -79,6 +82,7 @@ class CronHelperTest extends \MailPoetTest {
|
||||
// when saving daemon, 'updated_at' value should change
|
||||
$daemon = array(
|
||||
'token' => 'some_token',
|
||||
'status' => CronHelper::DAEMON_STATUS_ACTIVE,
|
||||
'updated_at' => '12345678',
|
||||
'run_accessed_at' => null,
|
||||
'run_started_at' => null,
|
||||
@ -98,6 +102,7 @@ class CronHelperTest extends \MailPoetTest {
|
||||
function testItUpdatesDaemonAccessedAt() {
|
||||
$daemon = [
|
||||
'token' => 'some_token',
|
||||
'status' => CronHelper::DAEMON_STATUS_ACTIVE,
|
||||
'updated_at' => 12345678,
|
||||
'run_accessed_at' => null,
|
||||
'run_started_at' => null,
|
||||
@ -131,6 +136,7 @@ class CronHelperTest extends \MailPoetTest {
|
||||
foreach($run_start_values as $run_start) {
|
||||
$daemon = [
|
||||
'token' => 'some_token',
|
||||
'status' => CronHelper::DAEMON_STATUS_ACTIVE,
|
||||
'updated_at' => 12345678,
|
||||
'run_accessed_at' => $time - 10,
|
||||
'run_started_at' => $run_start,
|
||||
@ -149,6 +155,7 @@ class CronHelperTest extends \MailPoetTest {
|
||||
$time = time();
|
||||
$daemon = [
|
||||
'token' => 'some_token',
|
||||
'status' => CronHelper::DAEMON_STATUS_ACTIVE,
|
||||
'updated_at' => 12345678,
|
||||
'run_accessed_at' => $time - 5,
|
||||
'run_started_at' => $time - 4,
|
||||
@ -182,6 +189,7 @@ class CronHelperTest extends \MailPoetTest {
|
||||
foreach($test_inputs as $test_input) {
|
||||
$daemon = [
|
||||
'token' => 'some_token',
|
||||
'status' => CronHelper::DAEMON_STATUS_ACTIVE,
|
||||
'updated_at' => 12345678,
|
||||
'run_accessed_at' => $test_input['run_access'],
|
||||
'run_started_at' => $test_input['run_start'],
|
||||
@ -196,6 +204,26 @@ class CronHelperTest extends \MailPoetTest {
|
||||
}
|
||||
}
|
||||
|
||||
function testItDeactivatesDaemon() {
|
||||
$daemon = [
|
||||
'token' => 'some_token',
|
||||
'status' => CronHelper::DAEMON_STATUS_ACTIVE,
|
||||
'updated_at' => '12345678',
|
||||
'run_accessed_at' => null,
|
||||
'run_started_at' => null,
|
||||
'run_completed_at' => null,
|
||||
'last_error' => null,
|
||||
];
|
||||
Setting::setValue(
|
||||
CronHelper::DAEMON_SETTING,
|
||||
$daemon
|
||||
);
|
||||
|
||||
CronHelper::deactivateDaemon($daemon);
|
||||
$daemon = CronHelper::getDaemon();
|
||||
expect($daemon['status'])->equals(CronHelper::DAEMON_STATUS_INACTIVE);
|
||||
}
|
||||
|
||||
function testItCreatesRandomToken() {
|
||||
// random token is a string of 5 characters
|
||||
$token1 = CronHelper::createToken();
|
||||
|
@ -157,6 +157,22 @@ class DaemonTest extends \MailPoetTest {
|
||||
$daemon->run();
|
||||
}
|
||||
|
||||
function testItTerminatesExecutionWhenDaemonIsDeactivated() {
|
||||
$daemon = Stub::make(new Daemon(true), [
|
||||
'executeScheduleWorker' => null,
|
||||
'executeQueueWorker' => null,
|
||||
'pauseExecution' => null,
|
||||
'terminateRequest' => Expected::exactly(1)
|
||||
], $this);
|
||||
$data = [
|
||||
'token' => 123,
|
||||
'status' => CronHelper::DAEMON_STATUS_INACTIVE,
|
||||
];
|
||||
Setting::setValue(CronHelper::DAEMON_SETTING, $data);
|
||||
$daemon->__construct($data);
|
||||
$daemon->run();
|
||||
}
|
||||
|
||||
function testItUpdatesDaemonTokenDuringExecution() {
|
||||
$daemon = Stub::make(new Daemon(true), array(
|
||||
'executeScheduleWorker' => null,
|
||||
|
@ -50,6 +50,16 @@ class SupervisorTest extends \MailPoetTest {
|
||||
$daemon = $supervisor->checkDaemon();
|
||||
expect(is_int($daemon['updated_at']))->true();
|
||||
expect($daemon['updated_at'])->notEquals($supervisor->daemon['updated_at']);
|
||||
expect($daemon['status'])->equals(CronHelper::DAEMON_STATUS_ACTIVE);
|
||||
}
|
||||
|
||||
function testRestartsDaemonWhenItIsInactive() {
|
||||
if(getenv('WP_TEST_ENABLE_NETWORK_TESTS') !== 'true') return;
|
||||
$supervisor = new Supervisor();
|
||||
$supervisor->daemon['updated_at'] = time();
|
||||
$supervisor->daemon['status'] = CronHelper::DAEMON_STATUS_INACTIVE;
|
||||
$daemon = $supervisor->checkDaemon();
|
||||
expect($daemon['status'])->equals(CronHelper::DAEMON_STATUS_ACTIVE);
|
||||
}
|
||||
|
||||
function _after() {
|
||||
|
@ -77,11 +77,11 @@ class WordPressTest extends \MailPoetTest {
|
||||
expect(WordPress::checkExecutionRequirements())->false();
|
||||
}
|
||||
|
||||
function testItCanDeleteRunningDaemon() {
|
||||
Setting::setValue(CronHelper::DAEMON_SETTING, true);
|
||||
expect(Setting::getValue(CronHelper::DAEMON_SETTING))->notNull();
|
||||
WordPress::cleanup();
|
||||
expect(Setting::getValue(CronHelper::DAEMON_SETTING))->null();
|
||||
function testItCanDeactivateRunningDaemon() {
|
||||
Setting::setValue(CronHelper::DAEMON_SETTING, ['status' => CronHelper::DAEMON_STATUS_ACTIVE]);
|
||||
expect(Setting::getValue(CronHelper::DAEMON_SETTING)['status'])->equals(CronHelper::DAEMON_STATUS_ACTIVE);
|
||||
WordPress::stop();
|
||||
expect(Setting::getValue(CronHelper::DAEMON_SETTING)['status'])->equals(CronHelper::DAEMON_STATUS_INACTIVE);
|
||||
}
|
||||
|
||||
function testItRunsWhenExecutionRequirementsAreMet() {
|
||||
@ -93,11 +93,11 @@ class WordPressTest extends \MailPoetTest {
|
||||
expect(Setting::getValue(CronHelper::DAEMON_SETTING))->notNull();
|
||||
}
|
||||
|
||||
function testItDeletesCronDaemonWhenExecutionRequirementsAreNotMet() {
|
||||
Setting::setValue(CronHelper::DAEMON_SETTING, true);
|
||||
expect(Setting::getValue(CronHelper::DAEMON_SETTING))->notNull();
|
||||
function testItDeactivatesCronDaemonWhenExecutionRequirementsAreNotMet() {
|
||||
Setting::setValue(CronHelper::DAEMON_SETTING, ['status' => CronHelper::DAEMON_STATUS_ACTIVE]);
|
||||
expect(Setting::getValue(CronHelper::DAEMON_SETTING)['status'])->equals(CronHelper::DAEMON_STATUS_ACTIVE);
|
||||
WordPress::run();
|
||||
expect(Setting::getValue(CronHelper::DAEMON_SETTING))->null();
|
||||
expect(Setting::getValue(CronHelper::DAEMON_SETTING)['status'])->equals(CronHelper::DAEMON_STATUS_INACTIVE);
|
||||
}
|
||||
|
||||
function _addMTAConfigAndLog($sent, $status = null) {
|
||||
|
@ -33,6 +33,18 @@
|
||||
'knowledgeBaseButton': __('Visit our Knowledge Base for more articles'),
|
||||
'systemInfoIntro': __('The information below is useful when you need to get in touch with our support. Just copy all the text below and paste it into a message to us.'),
|
||||
'systemInfoDataError': __('Sorry, there was an error, please try again later.'),
|
||||
'systemStatusCronStatusTitle': __('Cron'),
|
||||
'lastUpdated': __('Last updated'),
|
||||
'lastRunStarted': __('Last run started'),
|
||||
'lastRunCompleted': __('Last run completed'),
|
||||
'lastSeenError': __('Last seen error'),
|
||||
'unknown': __('unknown'),
|
||||
'accessible': __('Accessible'),
|
||||
'status': __('Status'),
|
||||
'yes': __('yes'),
|
||||
'no': __('no'),
|
||||
'cronRunning': __('running'),
|
||||
'cronWaiting': __('waiting for the next run'),
|
||||
}) %>
|
||||
<% endblock %>
|
||||
|
||||
|
Reference in New Issue
Block a user