Skip to content

Commit

Permalink
Merge pull request #9808 from google/enhancement/9768-health-check-cron
Browse files Browse the repository at this point in the history
  • Loading branch information
nfmohit authored Dec 6, 2024
2 parents 78d1bfa + 3401937 commit bf1bec6
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 40 deletions.
67 changes: 66 additions & 1 deletion includes/Core/Tags/First_Party_Mode/First_Party_Mode.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Google\Site_Kit\Context;
use Google\Site_Kit\Core\Modules\Module_With_Debug_Fields;
use Google\Site_Kit\Core\Storage\Options;
use Google\Site_Kit\Core\Tags\First_Party_Mode\First_Party_Mode_Cron;
use Google\Site_Kit\Core\Util\Method_Proxy_Trait;

/**
Expand Down Expand Up @@ -49,6 +50,14 @@ class First_Party_Mode implements Module_With_Debug_Fields {
*/
protected $rest_controller;

/**
* First_Party_Mode_Cron instance.
*
* @since n.e.x.t
* @var First_Party_Mode_Cron
*/
private $cron;

/**
* Constructor.
*
Expand All @@ -61,7 +70,8 @@ public function __construct( Context $context, Options $options = null ) {
$this->context = $context;
$options = $options ?: new Options( $context );
$this->first_party_mode_settings = new First_Party_Mode_Settings( $options );
$this->rest_controller = new REST_First_Party_Mode_Controller( $this->first_party_mode_settings );
$this->rest_controller = new REST_First_Party_Mode_Controller( $this, $this->first_party_mode_settings );
$this->cron = new First_Party_Mode_Cron( array( $this, 'healthcheck' ) );
}

/**
Expand All @@ -72,6 +82,9 @@ public function __construct( Context $context, Options $options = null ) {
public function register() {
$this->first_party_mode_settings->register();
$this->rest_controller->register();
$this->cron->register();

add_action( 'admin_init', fn () => $this->on_admin_init() );
}

/**
Expand Down Expand Up @@ -136,4 +149,56 @@ public function get_debug_fields() {
),
);
}

/**
* Checks the health of First Party Mode server requirements.
*
* @since n.e.x.t
*
* @return void
*/
public function healthcheck() {
$is_fpm_healthy = $this->is_endpoint_healthy( 'https://g-1234.fps.goog/mpath/healthy' );
$is_script_access_enabled = $this->is_endpoint_healthy( add_query_arg( 'healthCheck', '1', plugins_url( 'fpm/measurement.php', GOOGLESITEKIT_PLUGIN_MAIN_FILE ) ) );

$this->first_party_mode_settings->merge(
array(
'isFPMHealthy' => $is_fpm_healthy,
'isScriptAccessEnabled' => $is_script_access_enabled,
)
);
}

/**
* Schedule cron on admin init.
*
* @since n.e.x.t
*/
public function on_admin_init() {
$this->cron->maybe_schedule_cron();
}

/**
* Checks if an endpoint is healthy. The endpoint must return a `200 OK` response with the body `ok`.
*
* @since 1.141.0
* @since n.e.x.t Relocated from REST_First_Party_Mode_Controller.
*
* @param string $endpoint The endpoint to check.
* @return bool True if the endpoint is healthy, false otherwise.
*/
protected function is_endpoint_healthy( $endpoint ) {
try {
// phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown,WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$response = file_get_contents( $endpoint );
} catch ( \Exception $e ) {
return false;
}

if ( 'ok' !== $response ) {
return false;
}

return strpos( $http_response_header[0], '200 OK' ) !== false;
}
}
61 changes: 61 additions & 0 deletions includes/Core/Tags/First_Party_Mode/First_Party_Mode_Cron.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php
/**
* Class Google\Site_Kit\Core\Tags\First_Party_Mode\First_Party_Mode_Cron
*
* @package Google\Site_Kit\Core\Tags\First_Party_Mode
* @copyright 2024 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/

namespace Google\Site_Kit\Core\Tags\First_Party_Mode;

/**
* Class to manage First Party Mode cron tasks.
*
* @since n.e.x.t
* @access private
* @ignore
*/
class First_Party_Mode_Cron {

const CRON_ACTION = 'googlesitekit_cron_first_party_mode_healthchecks';

/**
* Cron callback reference.
*
* @var callable
*/
private $cron_callback;

/**
* Constructor.
*
* @since n.e.x.t
*
* @param callable $callback Function to call on the cron action.
*/
public function __construct( callable $callback ) {
$this->cron_callback = $callback;
}

/**
* Registers functionality through WordPress hooks.
*
* @since n.e.x.t
*/
public function register() {
add_action( self::CRON_ACTION, $this->cron_callback );
}

/**
* Schedules cron if not already set.
*
* @since n.e.x.t
*/
public function maybe_schedule_cron() {
if ( ! wp_next_scheduled( self::CRON_ACTION ) && ! wp_installing() ) {
wp_schedule_event( time(), 'hourly', self::CRON_ACTION );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Google\Site_Kit\Core\Permissions\Permissions;
use Google\Site_Kit\Core\REST_API\REST_Route;
use Google\Site_Kit\Core\REST_API\REST_Routes;
use Google\Site_Kit\Core\Tags\First_Party_Mode\First_Party_Mode;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;
Expand All @@ -26,6 +27,14 @@
*/
class REST_First_Party_Mode_Controller {

/**
* First_Party_Mode instance.
*
* @since n.e.x.t
* @var First_Party_Mode
*/
private $first_party_mode;

/**
* First_Party_Mode_Settings instance.
*
Expand All @@ -39,9 +48,11 @@ class REST_First_Party_Mode_Controller {
*
* @since 1.141.0
*
* @param First_Party_Mode $first_party_mode First_Party_Mode instance.
* @param First_Party_Mode_Settings $first_party_mode_settings First_Party_Mode_Settings instance.
*/
public function __construct( First_Party_Mode_Settings $first_party_mode_settings ) {
public function __construct( First_Party_Mode $first_party_mode, First_Party_Mode_Settings $first_party_mode_settings ) {
$this->first_party_mode = $first_party_mode;
$this->first_party_mode_settings = $first_party_mode_settings;
}

Expand Down Expand Up @@ -134,16 +145,7 @@ protected function get_rest_routes() {
array(
'methods' => WP_REST_Server::READABLE,
'callback' => function () {
$is_fpm_healthy = $this->is_endpoint_healthy( 'https://g-1234.fps.goog/mpath/healthy' );
$is_script_access_enabled = $this->is_endpoint_healthy( add_query_arg( 'healthCheck', '1', plugins_url( 'fpm/measurement.php', GOOGLESITEKIT_PLUGIN_MAIN_FILE ) ) );

$this->first_party_mode_settings->merge(
array(
'isFPMHealthy' => $is_fpm_healthy,
'isScriptAccessEnabled' => $is_script_access_enabled,
)
);

$this->first_party_mode->healthcheck();
return new WP_REST_Response( $this->first_party_mode_settings->get() );
},
'permission_callback' => $can_manage_options,
Expand All @@ -152,27 +154,4 @@ protected function get_rest_routes() {
),
);
}

/**
* Checks if an endpoint is healthy. The endpoint must return a `200 OK` response with the body `ok`.
*
* @since 1.141.0
*
* @param string $endpoint The endpoint to check.
* @return bool True if the endpoint is healthy, false otherwise.
*/
protected function is_endpoint_healthy( $endpoint ) {
try {
// phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown,WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$response = file_get_contents( $endpoint );
} catch ( \Exception $e ) {
return false;
}

if ( 'ok' !== $response ) {
return false;
}

return strpos( $http_response_header[0], '200 OK' ) !== false;
}
}
2 changes: 2 additions & 0 deletions includes/Core/Util/Uninstallation.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Google\Site_Kit\Core\Authentication\Google_Proxy;
use Google\Site_Kit\Core\Authentication\Clients\OAuth_Client;
use Google\Site_Kit\Core\Remote_Features\Remote_Features_Cron;
use Google\Site_Kit\Core\Tags\First_Party_Mode\First_Party_Mode_Cron;
use Google\Site_Kit\Modules\Analytics_4\Conversion_Reporting\Conversion_Reporting_Cron;
use Google\Site_Kit\Modules\Analytics_4\Synchronize_AdSenseLinked;
use Google\Site_Kit\Modules\Analytics_4\Synchronize_AdsLinked;
Expand Down Expand Up @@ -60,6 +61,7 @@ class Uninstallation {
Synchronize_AdSenseLinked::CRON_SYNCHRONIZE_ADSENSE_LINKED,
Synchronize_AdsLinked::CRON_SYNCHRONIZE_ADS_LINKED,
Synchronize_Property::CRON_SYNCHRONIZE_PROPERTY,
First_Party_Mode_Cron::CRON_ACTION,
);

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
/**
* First_Party_Mode_CronTest
*
* @package Google\Site_Kit\Tests\Core\Tags\First_Party_Mode
* @copyright 2024 Google LLC
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
* @link https://sitekit.withgoogle.com
*/

namespace Google\Site_Kit\Tests\Core\Tags\First_Party_Mode;

use Google\Site_Kit\Core\Tags\First_Party_Mode\First_Party_Mode_Cron;
use Google\Site_Kit\Tests\MethodSpy;
use Google\Site_Kit\Tests\TestCase;

class First_Party_Mode_CronTest extends TestCase {

public function set_up() {
parent::set_up();
remove_all_actions( First_Party_Mode_Cron::CRON_ACTION );
}

public function test_register() {
$cron = new First_Party_Mode_Cron( '__return_true' );
$this->assertFalse( has_action( First_Party_Mode_Cron::CRON_ACTION ) );

$cron->register();

$this->assertTrue( has_action( First_Party_Mode_Cron::CRON_ACTION ) );
}

public function test_register__given_callable() {
$spy = new MethodSpy();
$cron = new First_Party_Mode_Cron( array( $spy, 'func' ) );
$cron->register();
$this->assertTrue( empty( $spy->invocations['func'] ) );

do_action( First_Party_Mode_Cron::CRON_ACTION );

$this->assertCount( 1, $spy->invocations['func'] );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@

use Google\Site_Kit\Context;
use Google\Site_Kit\Core\Authentication\Authentication;
use Google\Site_Kit\Core\Tags\First_Party_Mode\First_Party_Mode_Settings;
use Google\Site_Kit\Core\Tags\First_Party_Mode\REST_First_Party_Mode_Controller;
use Google\Site_Kit\Core\REST_API\REST_Routes;
use Google\Site_Kit\Core\Storage\Options;
use Google\Site_Kit\Core\Tags\First_Party_Mode\First_Party_Mode;
use Google\Site_Kit\Core\Tags\First_Party_Mode\First_Party_Mode_Settings;
use Google\Site_Kit\Core\Tags\First_Party_Mode\REST_First_Party_Mode_Controller;
use Google\Site_Kit\Tests\Fake_Site_Connection_Trait;
use Google\Site_Kit\Tests\RestTestTrait;
use Google\Site_Kit\Tests\TestCase;
Expand Down Expand Up @@ -56,8 +57,9 @@ public function set_up() {
$this->context = new Context( GOOGLESITEKIT_PLUGIN_MAIN_FILE );
$options = new Options( $this->context );

$first_party_mode = new First_Party_Mode( $this->context );
$this->settings = new First_Party_Mode_Settings( $options );
$this->controller = new REST_First_Party_Mode_Controller( $this->settings );
$this->controller = new REST_First_Party_Mode_Controller( $first_party_mode, $this->settings );
}

public function tear_down() {
Expand Down Expand Up @@ -235,8 +237,8 @@ public function test_get_fpm_server_requirement_status( $data ) {
// Here we mock the `is_endpoint_healthy()` method of the controller. This is necessary because, although we could
// mock `file_get_contents()`, it's not possible to mock the `$http_response_header` variable used within the scope
// of the `is_endpoint_healthy()` method. The rest of the controller's behaviour remains unmocked.
$mock_controller = $this->getMockBuilder( REST_First_Party_Mode_Controller::class )
->setConstructorArgs( array( $this->settings ) )
$mock_controller = $this->getMockBuilder( First_Party_Mode::class )
->setConstructorArgs( array( $this->context ) )
->onlyMethods( array( 'is_endpoint_healthy' ) )
->getMock();

Expand Down

0 comments on commit bf1bec6

Please sign in to comment.