diff --git a/api/api.php b/api/api.php
index 02544cb790a..43ffa5f5754 100644
--- a/api/api.php
+++ b/api/api.php
@@ -29,6 +29,7 @@ function register_woocommerce_admin_test_helper_rest_route( $route, $callback, $
require( 'admin-notes/delete-all-notes.php' );
require( 'admin-notes/add-note.php' );
require( 'tools/trigger-wca-install.php' );
+require( 'tools/trigger-cron-job.php' );
require( 'tools/run-wc-admin-daily.php' );
require( 'options/rest-api.php' );
require( 'tools/delete-all-products.php');
diff --git a/api/tools/trigger-cron-job.php b/api/tools/trigger-cron-job.php
new file mode 100644
index 00000000000..6be1c1de19c
--- /dev/null
+++ b/api/tools/trigger-cron-job.php
@@ -0,0 +1,98 @@
+ 'GET',
+ )
+);
+register_woocommerce_admin_test_helper_rest_route(
+ '/tools/trigger-selected-cron/v1',
+ 'trigger_selected_cron',
+ array(
+ 'methods' => 'POST',
+ 'args' => array(
+ 'hook' => array(
+ 'description' => 'Name of the cron that will be triggered.',
+ 'type' => 'string',
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
+ 'signature' => array(
+ 'description' => 'Signature of the cron to trigger.',
+ 'type' => 'string',
+ 'sanitize_callback' => 'sanitize_text_field',
+ ),
+ ),
+ )
+);
+
+function tools_get_cron_list() {
+ $crons = _get_cron_array();
+ $events = array();
+
+ if ( empty( $crons ) ) {
+ return array();
+ }
+
+ foreach ( $crons as $cron ) {
+ foreach ( $cron as $hook => $data ) {
+ foreach ( $data as $signature => $element ) {
+ $events[ $hook ] = (object) array(
+ 'hook' => $hook,
+ 'signature' => $signature,
+ );
+ }
+ }
+ }
+ return new WP_REST_Response( $events, 200 );
+}
+
+function trigger_selected_cron( $request ) {
+ $hook = $request->get_param( 'hook' );
+ $signature = $request->get_param( 'signature' );
+
+ if ( ! isset( $hook ) || ! isset( $signature ) ) {
+ return;
+ }
+
+ $crons = _get_cron_array();
+ foreach ( $crons as $cron ) {
+ if ( isset( $cron[ $hook ][ $signature ] ) ) {
+ $args = $cron[ $hook ][ $signature ]['args'];
+ delete_transient( 'doing_cron' );
+ $scheduled = schedule_event( $hook, $args );
+
+ if ( false === $scheduled ) {
+ return $scheduled;
+ }
+
+ add_filter( 'cron_request', function( array $cron_request ) {
+ $cron_request['url'] = add_query_arg( 'run-cron', 1, $cron_request['url'] );
+ return $cron_request;
+ } );
+
+ spawn_cron();
+ sleep( 1 );
+ return true;
+ }
+ }
+ return false;
+}
+
+function schedule_event( $hook, $args = array() ) {
+ $event = (object) array(
+ 'hook' => $hook,
+ 'timestamp' => 1,
+ 'schedule' => false,
+ 'args' => $args,
+ );
+ $crons = (array) _get_cron_array();
+ $key = md5( serialize( $event->args ) );
+
+ $crons[ $event->timestamp ][ $event->hook ][ $key ] = array(
+ 'schedule' => $event->schedule,
+ 'args' => $event->args,
+ );
+ uksort( $crons, 'strnatcasecmp' );
+ return _set_cron_array( $crons );
+}
diff --git a/src/admin-notes/add-note.js b/src/admin-notes/add-note.js
index 24bed814cce..3db4d8eea81 100644
--- a/src/admin-notes/add-note.js
+++ b/src/admin-notes/add-note.js
@@ -2,9 +2,8 @@
* External dependencies.
*/
import { useState } from '@wordpress/element';
-import { Button } from '@wordpress/components';
+import { Button, SelectControl } from '@wordpress/components';
import apiFetch from '@wordpress/api-fetch';
-import { SelectControl } from '@wordpress/components';
export const AddNote = () => {
diff --git a/src/index.scss b/src/index.scss
index d91655c9eeb..9b75936e7e9 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -60,6 +60,13 @@
&.command {
white-space: nowrap;
}
+ .trigger-cron-job {
+ width: 40%;
+ padding-top: 4px;
+ .components-base-control__field {
+ margin-bottom: 0;
+ }
+ }
}
}
.components-notice {
diff --git a/src/tools/commands.js b/src/tools/commands/index.js
similarity index 86%
rename from src/tools/commands.js
rename to src/tools/commands/index.js
index 221b74abcc8..0e43792989c 100644
--- a/src/tools/commands.js
+++ b/src/tools/commands/index.js
@@ -1,3 +1,5 @@
+import { TriggerCronJob, TRIGGER_CRON_ACTION_NAME } from './trigger-cron';
+
export default [
{
command: 'Trigger WCA Install',
@@ -39,4 +41,9 @@ export default [
description: 'Delete all products',
action: 'deleteAllProducts',
},
+ {
+ command: 'Run a cron job',
+ description: ,
+ action: TRIGGER_CRON_ACTION_NAME,
+ },
];
diff --git a/src/tools/commands/trigger-cron.js b/src/tools/commands/trigger-cron.js
new file mode 100644
index 00000000000..fc232455ccf
--- /dev/null
+++ b/src/tools/commands/trigger-cron.js
@@ -0,0 +1,48 @@
+/**
+ * External dependencies.
+ */
+import { SelectControl } from '@wordpress/components';
+import { useDispatch, useSelect } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import { STORE_KEY } from '../data/constants';
+
+export const TRIGGER_CRON_ACTION_NAME = 'runSelectedCronJob';
+
+export const TriggerCronJob = () => {
+ const { cronList } = useSelect((select) => {
+ const { getCronJobs } = select(STORE_KEY);
+ return {
+ cronList: getCronJobs(),
+ };
+ });
+ const { updateCommandParams } = useDispatch(STORE_KEY);
+
+ function onCronChange(selectedValue) {
+ const { hook, signature } = cronList[selectedValue];
+ updateCommandParams(TRIGGER_CRON_ACTION_NAME, { hook, signature });
+ }
+
+ function getOptions() {
+ return Object.keys(cronList).map((name) => {
+ return { label: name, value: name };
+ });
+ }
+
+ return (
+
+ {!cronList ? (
+
Loading ...
+ ) : (
+
+ )}
+
+ );
+};
diff --git a/src/tools/data/action-types.js b/src/tools/data/action-types.js
index fa8cd96caf5..b722f161f37 100644
--- a/src/tools/data/action-types.js
+++ b/src/tools/data/action-types.js
@@ -4,6 +4,8 @@ const TYPES = {
ADD_MESSAGE: 'ADD_MESSAGE',
UPDATE_MESSAGE: 'UPDATE_MESSAGE',
REMOVE_MESSAGE: 'REMOVE_MESSAGE',
+ ADD_COMMAND_PARAMS: 'ADD_COMMAND_PARAMS',
+ SET_CRON_JOBS: 'SET_CRON_JOBS',
};
export default TYPES;
diff --git a/src/tools/data/actions.js b/src/tools/data/actions.js
index 46c1e4a0faf..2cad7873301 100644
--- a/src/tools/data/actions.js
+++ b/src/tools/data/actions.js
@@ -9,21 +9,21 @@ import { apiFetch } from '@wordpress/data-controls';
import TYPES from './action-types';
import { API_NAMESPACE } from './constants';
-export function addCurrentlyRunning( command ) {
+export function addCurrentlyRunning(command) {
return {
type: TYPES.ADD_CURRENTLY_RUNNING,
command,
};
}
-export function removeCurrentlyRunning( command ) {
+export function removeCurrentlyRunning(command) {
return {
type: TYPES.REMOVE_CURRENTLY_RUNNING,
command,
};
}
-export function addMessage( source, message ) {
+export function addMessage(source, message) {
return {
type: TYPES.ADD_MESSAGE,
source,
@@ -31,7 +31,7 @@ export function addMessage( source, message ) {
};
}
-export function updateMessage( source, message, status ) {
+export function updateMessage(source, message, status) {
return {
type: TYPES.ADD_MESSAGE,
source,
@@ -40,69 +40,84 @@ export function updateMessage( source, message, status ) {
};
}
-export function removeMessage( source ) {
+export function removeMessage(source) {
return {
type: TYPES.REMOVE_MESSAGE,
source,
};
}
-function* runCommand( commandName, func ) {
+export function updateCommandParams(source, params) {
+ return {
+ type: TYPES.ADD_COMMAND_PARAMS,
+ source,
+ params,
+ };
+}
+
+export function setCronJobs(cronJobs) {
+ return {
+ type: TYPES.SET_CRON_JOBS,
+ cronJobs,
+ };
+}
+
+function* runCommand(commandName, func) {
try {
- yield addCurrentlyRunning( commandName );
- yield addMessage( commandName, 'Executing...' );
+ yield addCurrentlyRunning(commandName);
+ yield addMessage(commandName, 'Executing...');
yield func();
- yield removeCurrentlyRunning( commandName );
- yield updateMessage( commandName, 'Successful!' );
- } catch ( e ) {
- yield updateMessage( commandName, e.message, 'error' );
- yield removeCurrentlyRunning( commandName );
+ yield removeCurrentlyRunning(commandName);
+ yield updateMessage(commandName, 'Successful!');
+ } catch (e) {
+ yield updateMessage(commandName, e.message, 'error');
+ yield removeCurrentlyRunning(commandName);
}
}
export function* triggerWcaInstall() {
- yield runCommand( 'Trigger WCA Install', function* () {
- yield apiFetch( {
+ yield runCommand('Trigger WCA Install', function* () {
+ yield apiFetch({
path: API_NAMESPACE + '/tools/trigger-wca-install/v1',
method: 'POST',
- } );
- } );
+ });
+ });
}
export function* resetOnboardingWizard() {
- yield runCommand( 'Reset Onboarding Wizard', function* () {
+ yield runCommand('Reset Onboarding Wizard', function* () {
const optionsToDelete = [
'woocommerce_task_list_tracked_completed_tasks',
'woocommerce_onboarding_profile',
'_transient_wc_onboarding_themes',
];
- yield apiFetch( {
+ yield apiFetch({
method: 'DELETE',
- path: `${ API_NAMESPACE }/options/${ optionsToDelete.join( ',' ) }`,
- } );
- } );
+ path: `${API_NAMESPACE}/options/${optionsToDelete.join(',')}`,
+ });
+ });
}
export function* resetJetpackConnection() {
- yield runCommand( 'Reset Jetpack Connection', function* () {
- yield apiFetch( {
+ yield runCommand('Reset Jetpack Connection', function* () {
+ yield apiFetch({
method: 'DELETE',
- path: `${ API_NAMESPACE }/options/jetpack_options`,
- } );
- } );
+ path: `${API_NAMESPACE}/options/jetpack_options`,
+ });
+ });
}
export function* enableTrackingDebug() {
- yield runCommand( 'Enable WC Admin Tracking Debug Mode', function* () {
- window.localStorage.setItem( 'debug', 'wc-admin:*' );
- } );
+ yield runCommand('Enable WC Admin Tracking Debug Mode', function* () {
+ window.localStorage.setItem('debug', 'wc-admin:*');
+ });
}
export function* updateStoreAge() {
- yield runCommand( 'Update Installation timestamp', function* () {
+ yield runCommand('Update Installation timestamp', function* () {
const today = new Date();
- const dd = String( today.getDate() ).padStart( 2, '0' );
- const mm = String( today.getMonth() + 1 ).padStart( 2, '0' ); //January is 0!
+ const dd = String(today.getDate()).padStart(2, '0');
+ const mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
const yyyy = today.getFullYear();
// eslint-disable-next-line no-alert
@@ -111,43 +126,52 @@ export function* updateStoreAge() {
yyyy + '/' + mm + '/' + dd
);
- if ( numberOfDays !== null ) {
- const dates = numberOfDays.split( '/' );
+ if (numberOfDays !== null) {
+ const dates = numberOfDays.split('/');
const newTimestamp = Math.round(
- new Date( dates[ 0 ], dates[ 1 ] - 1, dates[ 2 ] ).getTime() /
- 1000
+ new Date(dates[0], dates[1] - 1, dates[2]).getTime() / 1000
);
const payload = {
- woocommerce_admin_install_timestamp: JSON.parse( newTimestamp ),
+ woocommerce_admin_install_timestamp: JSON.parse(newTimestamp),
};
- yield apiFetch( {
+ yield apiFetch({
method: 'POST',
path: '/wc-admin/options',
headers: { 'content-type': 'application/json' },
- body: JSON.stringify( payload ),
- } );
+ body: JSON.stringify(payload),
+ });
}
- } );
+ });
}
export function* runWcAdminDailyJob() {
- yield runCommand( 'Run wc_admin_daily job', function* () {
- yield apiFetch( {
+ yield runCommand('Run wc_admin_daily job', function* () {
+ yield apiFetch({
path: API_NAMESPACE + '/tools/run-wc-admin-daily/v1',
method: 'POST',
- } );
- } );
+ });
+ });
}
export function* deleteAllProducts() {
- if ( ! confirm( 'Are you sure you want to delete all of the products?' ) ) {
+ if (!confirm('Are you sure you want to delete all of the products?')) {
return;
}
-
- yield runCommand( 'Delete all products', function* () {
- yield apiFetch( {
- path: `${ API_NAMESPACE }/tools/delete-all-products/v1`,
+
+ yield runCommand('Delete all products', function* () {
+ yield apiFetch({
+ path: `${API_NAMESPACE}/tools/delete-all-products/v1`,
method: 'POST',
- } );
- } );
+ });
+ });
+}
+
+export function* runSelectedCronJob(params) {
+ yield runCommand('Run selected cron job', function* () {
+ yield apiFetch({
+ path: API_NAMESPACE + '/tools/run-wc-admin-daily/v1',
+ method: 'POST',
+ data: params,
+ });
+ });
}
diff --git a/src/tools/data/index.js b/src/tools/data/index.js
index 968fffd7b4c..e476479ea88 100644
--- a/src/tools/data/index.js
+++ b/src/tools/data/index.js
@@ -8,6 +8,7 @@ import { controls } from '@wordpress/data-controls';
* Internal dependencies
*/
import * as actions from './actions';
+import * as resolvers from './resolvers';
import * as selectors from './selectors';
import reducer from './reducer';
import { STORE_KEY } from './constants';
@@ -15,6 +16,7 @@ import { STORE_KEY } from './constants';
export default registerStore( STORE_KEY, {
actions,
selectors,
+ resolvers,
controls,
reducer,
} );
diff --git a/src/tools/data/reducer.js b/src/tools/data/reducer.js
index 56f9a1a052e..dd534b81932 100644
--- a/src/tools/data/reducer.js
+++ b/src/tools/data/reducer.js
@@ -6,7 +6,9 @@ import TYPES from './action-types';
const DEFAULT_STATE = {
currentlyRunning: {},
errorMessages: [],
+ cronJobs: false,
messages: {},
+ params: [],
status: '',
};
@@ -54,6 +56,18 @@ const reducer = ( state = DEFAULT_STATE, action ) => {
[ action.command ]: false,
},
};
+ case TYPES.SET_CRON_JOBS:
+ return {
+ ...state,
+ cronJobs: action.cronJobs,
+ };
+ case TYPES.ADD_COMMAND_PARAMS:
+ return {
+ ...state,
+ params: {
+ [ action.source ]: action.params,
+ },
+ };
default:
return state;
}
diff --git a/src/tools/data/resolvers.js b/src/tools/data/resolvers.js
new file mode 100644
index 00000000000..d87b0c06897
--- /dev/null
+++ b/src/tools/data/resolvers.js
@@ -0,0 +1,24 @@
+/**
+ * External dependencies
+ */
+import { apiFetch } from '@wordpress/data-controls';
+
+/**
+ * Internal dependencies
+ */
+import { API_NAMESPACE } from './constants';
+import { setCronJobs } from './actions';
+
+export function* getCronJobs() {
+ const path = `${ API_NAMESPACE }/tools/get-cron-list/v1`;
+
+ try {
+ const response = yield apiFetch( {
+ path,
+ method: 'GET',
+ } );
+ yield setCronJobs( response );
+ } catch ( error ) {
+ throw new Error( error );
+ }
+}
diff --git a/src/tools/data/selectors.js b/src/tools/data/selectors.js
index 3cda1c6747a..aa4bdfeaac1 100644
--- a/src/tools/data/selectors.js
+++ b/src/tools/data/selectors.js
@@ -9,3 +9,11 @@ export function getMessages( state ) {
export function getStatus( state ) {
return state.status;
}
+
+export function getCommandParams( state ) {
+ return state.params;
+}
+
+export function getCronJobs( state ) {
+ return state.cronJobs;
+}
diff --git a/src/tools/index.js b/src/tools/index.js
index e3a6d2da95b..5ba21f88b0c 100644
--- a/src/tools/index.js
+++ b/src/tools/index.js
@@ -12,7 +12,7 @@ import { default as commands } from './commands';
import { STORE_KEY } from './data/constants';
import './data';
-function Tools( { actions, currentlyRunningCommands, messages } ) {
+function Tools( { actions, currentlyRunningCommands, messages, comandParams } ) {
actions = actions();
return (
@@ -38,17 +38,18 @@ function Tools( { actions, currentlyRunningCommands, messages } ) {
- { commands.map( ( command, index ) => {
+ { commands.map( ( { action, command, description }, index ) => {
+ const params = comandParams[ action ] ?? false;
return (
- { command.command } |
- { command.description } |
+ { command } |
+ { description } |
|