PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.7.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.7.0
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
← All changes | includes/api/class-performance-endpoint.php +130 -20 1.27.02.7.0 View file →
@@ -353,16 +353,17 @@
353 353 try {
354 354 // Get device type from request
355 355 $device_type = $request->get_param('device_type') ?? 'mobile';
356 356
357 - $opportunities = $this->get_performance_manager()->get_performance_opportunities('', $device_type);
357 + $manager = $this->get_performance_manager();
358 + $opportunities = $manager->get_performance_opportunities('', $device_type);
358 359
359 - return new WP_REST_Response([
360 - 'success' => true,
361 - 'data' => $opportunities,
362 - 'device_type' => $device_type,
363 - 'message' => __('Performance opportunities retrieved successfully', 'thinkrank')
364 - ], 200);
360 + return $this->pagespeed_list_response(
361 + $manager,
362 + $opportunities,
363 + $device_type,
364 + __('Performance opportunities retrieved successfully', 'thinkrank')
365 + );
365 366
366 367 } catch (\Exception $e) {
367 368 return new WP_Error(
368 369 'opportunities_failed',
@@ -384,16 +385,17 @@
384 385 try {
385 386 // Get device type from request
386 387 $device_type = $request->get_param('device_type') ?? 'mobile';
387 388
388 - $diagnostics = $this->get_performance_manager()->get_performance_diagnostics('', $device_type);
389 + $manager = $this->get_performance_manager();
390 + $diagnostics = $manager->get_performance_diagnostics('', $device_type);
389 391
390 - return new WP_REST_Response([
391 - 'success' => true,
392 - 'data' => $diagnostics,
393 - 'device_type' => $device_type,
394 - 'message' => __('Performance diagnostics retrieved successfully', 'thinkrank')
395 - ], 200);
392 + return $this->pagespeed_list_response(
393 + $manager,
394 + $diagnostics,
395 + $device_type,
396 + __('Performance diagnostics retrieved successfully', 'thinkrank')
397 + );
396 398
397 399 } catch (\Exception $e) {
398 400 return new WP_Error(
399 401 'diagnostics_failed',
@@ -414,14 +416,30 @@
414 416 public function collect_performance_data(WP_REST_Request $request) {
415 417 try {
416 418 $results = $this->get_data_collector()->manual_collect();
417 419
418 - return new WP_REST_Response([
419 - 'success' => $results['success'],
420 - 'data' => $results,
421 - 'message' => $results['message']
422 - ], $results['success'] ? 200 : 500);
420 + if (!empty($results['success'])) {
421 + return new WP_REST_Response([
422 + 'success' => true,
423 + 'data' => $results,
424 + 'message' => $results['message']
425 + ], 200);
426 + }
423 427
428 + // A failure here is almost never a server fault: the site is not
429 + // connected, Google cannot reach the URL, or the quota is spent. This
430 + // used to answer 500 for all of them, with a hardcoded message that
431 + // dropped the real reason, so the user could neither tell what was
432 + // wrong nor that it was their configuration rather than a bug.
433 + // Return a WP_Error like every other failure in this file, so clients
434 + // get the normal code/message envelope instead of a 200-shaped body
435 + // carrying a 500.
436 + return new WP_Error(
437 + $this->collection_error_code((string) ($results['error_code'] ?? '')),
438 + $results['message'],
439 + $this->collection_error_data((string) ($results['error_code'] ?? ''))
440 + );
441 +
424 442 } catch (\Exception $e) {
425 443 return new WP_Error(
426 444 'data_collection_failed',
427 445 'Failed to collect performance data: ' . $e->getMessage(),
@@ -429,11 +447,65 @@
429 447 );
430 448 }
431 449 }
432 450
451 + /**
452 + * REST error code for a collection failure class.
453 + *
454 + * @since 1.31.0
455 + * @param string $error_code One of Performance_Data_Collector::ERROR_*.
456 + * @return string
457 + */
458 + private function collection_error_code(string $error_code): string {
459 + $codes = [
460 + Performance_Data_Collector::ERROR_NOT_CONFIGURED => 'pagespeed_not_configured',
461 + Performance_Data_Collector::ERROR_URL_UNREACHABLE => 'site_not_reachable',
462 + Performance_Data_Collector::ERROR_RATE_LIMITED => 'pagespeed_rate_limited',
463 + Performance_Data_Collector::ERROR_RECENT_FAILURE => 'pagespeed_recently_failed',
464 + Performance_Data_Collector::ERROR_STORAGE_FAILED => 'performance_storage_failed',
465 + ];
433 466
467 + return $codes[$error_code] ?? 'data_collection_failed';
468 + }
434 469
435 470 /**
471 + * HTTP status (and Retry-After, where it applies) for a failure class.
472 + *
473 + * @since 1.31.0
474 + * @param string $error_code One of Performance_Data_Collector::ERROR_*.
475 + * @return array Error data for WP_Error.
476 + */
477 + private function collection_error_data(string $error_code): array {
478 + switch ($error_code) {
479 + case Performance_Data_Collector::ERROR_NOT_CONFIGURED:
480 + // Client-side condition: no credential to call PageSpeed with.
481 + return ['status' => 400];
482 +
483 + case Performance_Data_Collector::ERROR_URL_UNREACHABLE:
484 + // The request was well-formed and authorised; the site simply
485 + // cannot be fetched by Google.
486 + return ['status' => 422];
487 +
488 + case Performance_Data_Collector::ERROR_RATE_LIMITED:
489 + return ['status' => 429];
490 +
491 + case Performance_Data_Collector::ERROR_RECENT_FAILURE:
492 + // Nothing was attempted — a recent failure is still remembered.
493 + return ['status' => 503, 'retry_after' => 300];
494 +
495 + case Performance_Data_Collector::ERROR_STORAGE_FAILED:
496 + // Measured fine but the write failed: genuinely our side.
497 + return ['status' => 500];
498 +
499 + default:
500 + // An upstream API error we could not classify.
501 + return ['status' => 502];
502 + }
503 + }
504 +
505 +
506 +
507 + /**
436 508 * Check read permissions
437 509 *
438 510 * @since 1.0.0
439 511 *
@@ -462,8 +534,46 @@
462 534 * @since 1.0.0
463 535 *
464 536 * @return array Arguments array
465 537 */
538 + /**
539 + * Wrap a PageSpeed-backed list, reporting whether it was actually fetched.
540 + *
541 + * An empty list used to come back as `success: true` /
542 + * "retrieved successfully" whether the site was clean, the request had
543 + * failed, or nothing had been attempted for want of a credential — so no
544 + * API or MCP consumer could tell the three apart, and the admin UI told
545 + * everyone to connect Google (#519). The list itself keeps its shape.
546 + *
547 + * @since 2.1.1
548 + *
549 + * @param Performance_Monitoring_Manager $manager Manager that produced the list.
550 + * @param array $data The list.
551 + * @param string $device_type Device the list is for.
552 + * @param string $success_message Message for a completed request.
553 + * @return WP_REST_Response
554 + */
555 + private function pagespeed_list_response(Performance_Monitoring_Manager $manager, array $data, string $device_type, string $success_message): WP_REST_Response {
556 + $error = $manager->get_last_error();
557 +
558 + if ('' !== $error['code']) {
559 + return new WP_REST_Response([
560 + 'success' => false,
561 + 'data' => $data,
562 + 'device_type' => $device_type,
563 + 'error_code' => $error['code'],
564 + 'message' => $error['message'],
565 + ], 200);
566 + }
567 +
568 + return new WP_REST_Response([
569 + 'success' => true,
570 + 'data' => $data,
571 + 'device_type' => $device_type,
572 + 'message' => $success_message,
573 + ], 200);
574 + }
575 +
466 576 private function get_historical_data_args(): array {
467 577 return [
468 578 'days' => [
469 579 'required' => false,
@@ -476,9 +586,9 @@
476 586 'metric' => [
477 587 'required' => false,
478 588 'type' => 'string',
479 589 'default' => 'all',
480 - 'enum' => ['all', 'lcp', 'fid', 'cls', 'inp', 'score'],
590 + 'enum' => ['all', 'lcp', 'cls', 'inp', 'score'],
481 591 'description' => 'Specific metric to retrieve'
482 592 ]
483 593 ];
484 594 }