Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Expose the analytics initial-full-sync milestone (initial_full_sync_finished) on Jetpack's sync status REST response so the dashboard can read it live on every poll, not just at page load.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@

/**
* Listens for the end of an analytics-relevant Jetpack full sync, persists a
* one-time milestone option, and exposes that milestone to the frontend via
* `JetpackScriptData.premium_analytics`.
* one-time milestone option, and exposes that milestone to the frontend both at
* page load (via `JetpackScriptData.premium_analytics`) and live on Jetpack's
* `/jetpack/v4/sync/status` REST response.
*
* Why this exists: the extracted dashboard needs to distinguish "first-time
* sync, please wait" from "we have data, render it." Jetpack's existing
Expand Down Expand Up @@ -51,7 +52,13 @@ class Sync_Status_Tracker {
const MILESTONE_ACTION = 'jetpack_premium_analytics_initial_full_sync_finished';

/**
* Wire up the listener and the script-data filter.
* Jetpack core's sync-status REST route. We enrich this existing response with
* the milestone rather than registering a dedicated endpoint.
*/
const SYNC_STATUS_ROUTE = '/jetpack/v4/sync/status';

/**
* Wire up the listener, the script-data filter, and the sync-status enricher.
*
* Idempotent: safe to call more than once.
*
Expand All @@ -60,6 +67,38 @@ class Sync_Status_Tracker {
public static function configure() {
add_action( 'jetpack_sync_processed_actions', array( self::class, 'on_sync_processed_actions' ) );
add_filter( 'jetpack_admin_js_script_data', array( self::class, 'inject_script_data' ) );
add_filter( 'rest_post_dispatch', array( self::class, 'enrich_sync_status_response' ), 10, 3 );
}

/**
* Append the milestone to Jetpack core's GET /jetpack/v4/sync/status response
* so the dashboard can read it on every poll, not just at page load.
*
* Page-load script-data ({@see inject_script_data()}) is a one-time snapshot;
* reading the milestone here keeps it live for in-session completion without a
* dedicated endpoint. Only the already-authorized, successful status payload is
* touched — other routes and error responses pass through untouched.
*
* @param mixed $response Result to send to the client. Usually a WP_REST_Response.
* @param mixed $server The REST server instance (unused).
* @param mixed $request The request used to generate the response.
* @return mixed
*/
public static function enrich_sync_status_response( $response, $server, $request ) {
if ( ! $request instanceof \WP_REST_Request
|| self::SYNC_STATUS_ROUTE !== $request->get_route()
|| ! $response instanceof \WP_REST_Response
|| $response->is_error() ) {
return $response;
}

$data = $response->get_data();
if ( is_array( $data ) ) {
$data['initial_full_sync_finished'] = (int) get_option( self::INITIAL_FULL_SYNC_OPTION, 0 );
$response->set_data( $data );
}

return $response;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,63 @@ public function test_configure_registers_hooks() {
has_filter( 'jetpack_admin_js_script_data', array( Sync_Status_Tracker::class, 'inject_script_data' ) ),
'tracker should filter jetpack_admin_js_script_data'
);
$this->assertNotFalse(
has_filter( 'rest_post_dispatch', array( Sync_Status_Tracker::class, 'enrich_sync_status_response' ) ),
'tracker should filter rest_post_dispatch to enrich sync status'
);

remove_action( 'jetpack_sync_processed_actions', array( Sync_Status_Tracker::class, 'on_sync_processed_actions' ) );
remove_filter( 'jetpack_admin_js_script_data', array( Sync_Status_Tracker::class, 'inject_script_data' ) );
remove_filter( 'rest_post_dispatch', array( Sync_Status_Tracker::class, 'enrich_sync_status_response' ) );
}

public function test_enrich_adds_milestone_to_sync_status_response() {
update_option( Sync_Status_Tracker::INITIAL_FULL_SYNC_OPTION, 1730000123 );
$request = new \WP_REST_Request( 'GET', Sync_Status_Tracker::SYNC_STATUS_ROUTE );
$response = new \WP_REST_Response( array( 'started' => true ) );

$result = Sync_Status_Tracker::enrich_sync_status_response( $response, null, $request );

$data = $result->get_data();
$this->assertSame( 1730000123, $data['initial_full_sync_finished'] );
$this->assertTrue( $data['started'], 'preserves existing fields' );
}

public function test_enrich_reports_zero_when_milestone_unset() {
$request = new \WP_REST_Request( 'GET', Sync_Status_Tracker::SYNC_STATUS_ROUTE );
$response = new \WP_REST_Response( array() );

$result = Sync_Status_Tracker::enrich_sync_status_response( $response, null, $request );

$this->assertSame( 0, $result->get_data()['initial_full_sync_finished'] );
}

public function test_enrich_ignores_other_routes() {
update_option( Sync_Status_Tracker::INITIAL_FULL_SYNC_OPTION, 1730000123 );
$request = new \WP_REST_Request( 'POST', '/jetpack/v4/sync/full-sync' );
$response = new \WP_REST_Response( array( 'scheduled' => true ) );

$result = Sync_Status_Tracker::enrich_sync_status_response( $response, null, $request );

$this->assertArrayNotHasKey( 'initial_full_sync_finished', $result->get_data() );
}

public function test_enrich_skips_error_responses() {
update_option( Sync_Status_Tracker::INITIAL_FULL_SYNC_OPTION, 1730000123 );
$request = new \WP_REST_Request( 'GET', Sync_Status_Tracker::SYNC_STATUS_ROUTE );
$response = new \WP_REST_Response( array( 'code' => 'forbidden' ), 403 );

$result = Sync_Status_Tracker::enrich_sync_status_response( $response, null, $request );

$this->assertArrayNotHasKey( 'initial_full_sync_finished', $result->get_data() );
}

public function test_enrich_passes_through_non_rest_response() {
$request = new \WP_REST_Request( 'GET', Sync_Status_Tracker::SYNC_STATUS_ROUTE );
$passthrough = new \WP_Error( 'boom' );

$result = Sync_Status_Tracker::enrich_sync_status_response( $passthrough, null, $request );

$this->assertSame( $passthrough, $result );
}
}
Loading