Add a generic function to determine remote logging eligibility (#49371)

* Add wc remote logger

* Add changelog

* Add remote logging feature

* Update tests and remove private $latest_wc_version;

* Fix lint

* Rename is_user_opted_in -> is_tracking_opted_in
This commit is contained in:
Chi-Hsuan Huang 2024-07-17 16:59:55 +08:00 committed by GitHub
parent b32c742f3f
commit 21d08c7f56
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 311 additions and 1 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Add a generic function to determine remote logging eligibility

View File

@ -0,0 +1,111 @@
<?php
declare( strict_types = 1 );
use Automattic\WooCommerce\Utilities\FeaturesUtil;
/**
* WooCommerce Remote Logger
*
* The WooCommerce remote logger class adds functionality to log WooCommerce errors remotely based on if the customer opted in and several other conditions.
*
* No personal information is logged, only error information and relevant context.
*
* @class WC_Remote_Logger
* @since 9.2.0
* @package WooCommerce\Classes
*/
class WC_Remote_Logger {
/**
* Determines if remote logging is allowed based on the following conditions:
*
* 1. The feature flag for remote error logging is enabled.
* 2. The user has opted into tracking/logging.
* 3. The store is allowed to log based on the variant assignment percentage.
* 4. The current WooCommerce version is the latest so we don't log errors that might have been fixed in a newer version.
*
* @return bool
*/
public function is_remote_logging_allowed() {
if ( ! FeaturesUtil::feature_is_enabled( 'remote_logging' ) ) {
return false;
}
if ( ! $this->is_tracking_opted_in() ) {
return false;
}
if ( ! $this->is_variant_assignment_allowed() ) {
return false;
}
if ( ! $this->is_latest_woocommerce_version() ) {
return false;
}
return true;
}
/**
* Check if the user has opted into tracking/logging.
*
* @return bool
*/
private function is_tracking_opted_in() {
return 'yes' === get_option( 'woocommerce_allow_tracking', 'no' );
}
/**
* Check if the store is allowed to log based on the variant assignment percentage.
*
* @return bool
*/
private function is_variant_assignment_allowed() {
$assignment = get_option( 'woocommerce_remote_variant_assignment', 0 );
return ( $assignment <= 12 ); // Considering 10% of the 0-120 range.
}
/**
* Check if the current WooCommerce version is the latest.
*
* @return bool
*/
private function is_latest_woocommerce_version() {
$latest_wc_version = $this->fetch_latest_woocommerce_version();
if ( is_null( $latest_wc_version ) ) {
return false;
}
return version_compare( WC()->version, $latest_wc_version, '>=' );
}
/**
* Fetch the latest WooCommerce version using the WordPress API and cache it.
*
* @return string|null
*/
private function fetch_latest_woocommerce_version() {
$transient_key = 'latest_woocommerce_version';
$cached_version = get_transient( $transient_key );
if ( $cached_version ) {
return $cached_version;
}
if ( ! function_exists( 'plugins_api' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
}
$plugin_info = plugins_api( 'plugin_information', array( 'slug' => 'woocommerce' ) );
if ( is_wp_error( $plugin_info ) ) {
return null;
}
if ( ! empty( $plugin_info->version ) ) {
$latest_version = $plugin_info->version;
set_transient( $transient_key, $latest_version, DAY_IN_SECONDS );
return $latest_version;
}
return null;
}
}

View File

@ -29,7 +29,6 @@ use Automattic\WooCommerce\Internal\Admin\Marketplace;
use Automattic\WooCommerce\Proxies\LegacyProxy;
use Automattic\WooCommerce\Utilities\{LoggingUtil, RestApiUtil, TimeUtil};
use Automattic\WooCommerce\Admin\WCAdminHelper;
use Automattic\WooCommerce\Admin\Features\Features;
/**
* Main WooCommerce Class.
@ -647,6 +646,7 @@ final class WooCommerce {
include_once WC_ABSPATH . 'includes/class-wc-structured-data.php';
include_once WC_ABSPATH . 'includes/class-wc-shortcodes.php';
include_once WC_ABSPATH . 'includes/class-wc-logger.php';
include_once WC_ABSPATH . 'includes/class-wc-remote-logger.php';
include_once WC_ABSPATH . 'includes/queue/class-wc-action-queue.php';
include_once WC_ABSPATH . 'includes/queue/class-wc-queue.php';
include_once WC_ABSPATH . 'includes/admin/marketplace-suggestions/class-wc-marketplace-updater.php';

View File

@ -247,6 +247,17 @@ class FeaturesController {
'is_legacy' => true,
'option_key' => CustomOrdersTableController::HPOS_FTS_INDEX_OPTION,
),
'remote_logging' => array(
'name' => __( 'Remote Logging', 'woocommerce' ),
'description' => __(
'Enable this feature to log errors and related data to Automattic servers for debugging purposes and to improve WooCommerce',
'woocommerce'
),
'enabled_by_default' => false,
'disable_ui' => true,
'is_legacy' => false,
'is_experimental' => true,
),
);
foreach ( $legacy_features as $slug => $definition ) {

View File

@ -0,0 +1,184 @@
<?php
declare( strict_types = 1 );
/**
* Class WC_Remote_Logger_Test.
*/
class WC_Remote_Logger_Test extends \WC_Unit_Test_Case {
/**
* Set up test
*
* @return void
*/
public function setUp(): void {
parent::setUp();
include_once WC_ABSPATH . 'includes/class-wc-remote-logger.php';
WC()->version = '9.2.0';
}
/**
* Tear down.
*
* @return void
*/
public function tearDown(): void {
$this->cleanup_filters();
delete_option( 'woocommerce_feature_remote_logging_enabled' );
}
/**
* Clean up filters.
*
* @return void
*/
private function cleanup_filters() {
remove_all_filters( 'option_woocommerce_admin_remote_feature_enabled' );
remove_all_filters( 'option_woocommerce_allow_tracking' );
remove_all_filters( 'option_woocommerce_version' );
remove_all_filters( 'option_woocommerce_remote_variant_assignment' );
}
/**
* Test that remote logging is allowed when all conditions are met.
*/
public function test_remote_logging_allowed() {
update_option( 'woocommerce_feature_remote_logging_enabled', 'yes' );
add_filter(
'option_woocommerce_allow_tracking',
function () {
return 'yes';
}
);
add_filter(
'option_woocommerce_remote_variant_assignment',
function () {
return 5;
}
);
add_filter(
'plugins_api',
function ( $result, $action, $args ) {
if ( 'plugin_information' === $action && 'woocommerce' === $args->slug ) {
return (object) array(
'version' => '9.2.0',
);
}
return $result;
},
10,
3
);
$checker = new WC_Remote_Logger();
$this->assertTrue( $checker->is_remote_logging_allowed() );
}
/**
* Test that remote logging is not allowed when the feature flag is disabled.
*/
public function test_remote_logging_not_allowed_feature_flag_disabled() {
update_option( 'woocommerce_feature_remote_logging_enabled', 'no' );
add_filter(
'option_woocommerce_allow_tracking',
function () {
return 'yes';
}
);
add_filter(
'option_woocommerce_remote_variant_assignment',
function () {
return 5;
}
);
set_transient( 'latest_woocommerce_version', '9.2.0', DAY_IN_SECONDS );
$checker = new WC_Remote_Logger();
$this->assertFalse( $checker->is_remote_logging_allowed() );
}
/**
* Test that remote logging is not allowed when user tracking is not opted in.
*/
public function test_remote_logging_not_allowed_tracking_opted_out() {
update_option( 'woocommerce_feature_remote_logging_enabled', 'yes' );
add_filter(
'option_woocommerce_allow_tracking',
function () {
return 'no';
}
);
add_filter(
'option_woocommerce_remote_variant_assignment',
function () {
return 5;
}
);
set_transient( 'latest_woocommerce_version', '9.2.0', DAY_IN_SECONDS );
$checker = new WC_Remote_Logger();
$this->assertFalse( $checker->is_remote_logging_allowed() );
}
/**
* Test that remote logging is not allowed when the WooCommerce version is outdated.
*/
public function test_remote_logging_not_allowed_outdated_version() {
update_option( 'woocommerce_feature_remote_logging_enabled', 'yes' );
add_filter(
'option_woocommerce_allow_tracking',
function () {
return 'yes';
}
);
add_filter(
'option_woocommerce_remote_variant_assignment',
function () {
return 5;
}
);
set_transient( 'latest_woocommerce_version', '9.2.0', DAY_IN_SECONDS );
WC()->version = '9.0.0';
$checker = new WC_Remote_Logger();
$this->assertFalse( $checker->is_remote_logging_allowed() );
}
/**
* Test that remote logging is not allowed when the variant assignment is high.
*/
public function test_remote_logging_not_allowed_high_variant_assignment() {
update_option( 'woocommerce_feature_remote_logging_enabled', 'yes' );
add_filter(
'option_woocommerce_allow_tracking',
function () {
return 'yes';
}
);
add_filter(
'option_woocommerce_version',
function () {
return '9.2.0';
}
);
add_filter(
'option_woocommerce_remote_variant_assignment',
function () {
return 15;
}
);
set_transient( 'latest_woocommerce_version', '9.2.0', DAY_IN_SECONDS );
$checker = new WC_Remote_Logger();
$this->assertFalse( $checker->is_remote_logging_allowed() );
}
}