Add remote logging tool to beta tester (#50425)
* Add remote logging beta tester tool * chore: Update log method return type to Promise<boolean> * Update pnpm-lock.yaml * Reformat * Check window.wcSettings?.isRemoteLoggingEnabled * Add changelogs * Fix test * Update toggle_remote_logging * Fix toggle_remote_logging * Improve message * Fix lint
This commit is contained in:
parent
e4bb2c2317
commit
450a4ce3bb
|
@ -75,7 +75,7 @@ addFilter(
|
|||
### API Reference
|
||||
|
||||
- `init(config: RemoteLoggerConfig): void`: Initializes the remote logger with the given configuration.
|
||||
- `log(severity: LogSeverity, message: string, extraData?: object): Promise<void>`: Logs a message with the specified severity and optional extra data.
|
||||
- `log(severity: LogSeverity, message: string, extraData?: object): Promise<boolean>`: Logs a message with the specified severity and optional extra data.
|
||||
- `captureException(error: Error, extraData?: object): void`: Captures an error and sends it to the remote API.
|
||||
|
||||
For more detailed information about types and interfaces, refer to the source code and inline documentation.
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: tweak
|
||||
|
||||
Tweak logic for adding remote logging tool in beta tester
|
|
@ -69,10 +69,10 @@ export class RemoteLogger {
|
|||
severity: Exclude< LogData[ 'severity' ], undefined >,
|
||||
message: string,
|
||||
extraData?: Partial< Exclude< LogData, 'message' | 'severity' > >
|
||||
) {
|
||||
): Promise< boolean > {
|
||||
if ( ! message ) {
|
||||
debug( 'Empty message' );
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
const logData: LogData = mergeLogData( DEFAULT_LOG_DATA, {
|
||||
|
@ -82,7 +82,7 @@ export class RemoteLogger {
|
|||
} );
|
||||
|
||||
debug( 'Logging:', logData );
|
||||
await this.sendLog( logData );
|
||||
return await this.sendLog( logData );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,10 +144,10 @@ export class RemoteLogger {
|
|||
*
|
||||
* @param logData - The log data to be sent.
|
||||
*/
|
||||
private async sendLog( logData: LogData ): Promise< void > {
|
||||
private async sendLog( logData: LogData ): Promise< boolean > {
|
||||
if ( isDevelopmentEnvironment ) {
|
||||
debug( 'Skipping send log in development environment' );
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
const body = new window.FormData();
|
||||
|
@ -166,13 +166,19 @@ export class RemoteLogger {
|
|||
'https://public-api.wordpress.com/rest/v1.1/logstash'
|
||||
) as string;
|
||||
|
||||
await window.fetch( endpoint, {
|
||||
const response = await window.fetch( endpoint, {
|
||||
method: 'POST',
|
||||
body,
|
||||
} );
|
||||
if ( ! response.ok ) {
|
||||
throw new Error( `response body: ${ response.body }` );
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch ( error ) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error( 'Failed to send log to API:', error );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -368,13 +374,13 @@ let logger: RemoteLogger | null = null;
|
|||
*
|
||||
* @return {boolean} - Returns true if remote logging is enabled and the logger is initialized, otherwise false.
|
||||
*/
|
||||
function canLog(): boolean {
|
||||
if ( ! getSetting( 'isRemoteLoggingEnabled', false ) ) {
|
||||
function canLog( _logger: RemoteLogger | null ): _logger is RemoteLogger {
|
||||
if ( ! window.wcSettings?.isRemoteLoggingEnabled ) {
|
||||
debug( 'Remote logging is disabled.' );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! logger ) {
|
||||
if ( ! _logger ) {
|
||||
warnLog( 'RemoteLogger is not initialized. Call init() first.' );
|
||||
return false;
|
||||
}
|
||||
|
@ -390,7 +396,7 @@ function canLog(): boolean {
|
|||
*
|
||||
*/
|
||||
export function init( config: RemoteLoggerConfig ) {
|
||||
if ( ! getSetting( 'isRemoteLoggingEnabled', false ) ) {
|
||||
if ( ! window.wcSettings?.isRemoteLoggingEnabled ) {
|
||||
debug( 'Remote logging is disabled.' );
|
||||
return;
|
||||
}
|
||||
|
@ -424,15 +430,16 @@ export async function log(
|
|||
severity: Exclude< LogData[ 'severity' ], undefined >,
|
||||
message: string,
|
||||
extraData?: Partial< Exclude< LogData, 'message' | 'severity' > >
|
||||
): Promise< void > {
|
||||
if ( ! canLog() ) {
|
||||
return;
|
||||
): Promise< boolean > {
|
||||
if ( ! canLog( logger ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await logger?.log( severity, message, extraData );
|
||||
return await logger.log( severity, message, extraData );
|
||||
} catch ( error ) {
|
||||
errorLog( 'Failed to send log:', error );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,12 +453,12 @@ export async function captureException(
|
|||
error: Error,
|
||||
extraData?: Partial< LogData >
|
||||
) {
|
||||
if ( ! canLog() ) {
|
||||
return;
|
||||
if ( ! canLog( logger ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await logger?.error( error, extraData );
|
||||
await logger.error( error, extraData );
|
||||
} catch ( _error ) {
|
||||
errorLog( 'Failed to send log:', _error );
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
*/
|
||||
import '@wordpress/jest-console';
|
||||
import { addFilter, removeFilter } from '@wordpress/hooks';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
@ -18,12 +16,6 @@ import {
|
|||
} from '../remote-logger';
|
||||
import { fetchMock } from './__mocks__/fetch';
|
||||
|
||||
jest.mock( '@woocommerce/settings', () => ( {
|
||||
getSetting: jest.fn().mockReturnValue( {
|
||||
isRemoteLoggingEnabled: true,
|
||||
} ),
|
||||
} ) );
|
||||
|
||||
jest.mock( 'tracekit', () => ( {
|
||||
computeStackTrace: jest.fn().mockReturnValue( {
|
||||
name: 'Error',
|
||||
|
@ -118,7 +110,9 @@ describe( 'RemoteLogger', () => {
|
|||
const payload = JSON.parse( formData.get( 'error' ) );
|
||||
expect( payload[ 'message' ] ).toBe( 'Test error' );
|
||||
expect( payload[ 'severity' ] ).toBe( 'error' );
|
||||
expect( payload['trace'] ).toContain( '#1 at testFunction (http://example.com/woocommerce/assets/js/admin/app.min.js:1:1)' );
|
||||
expect( payload[ 'trace' ] ).toContain(
|
||||
'#1 at testFunction (http://example.com/woocommerce/assets/js/admin/app.min.js:1:1)'
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should send an error to the API with extra data', async () => {
|
||||
|
@ -141,8 +135,14 @@ describe( 'RemoteLogger', () => {
|
|||
const payload = JSON.parse( formData.get( 'error' ) );
|
||||
expect( payload[ 'message' ] ).toBe( 'Test error' );
|
||||
expect( payload[ 'severity' ] ).toBe( 'warning' );
|
||||
expect( payload['tags'] ).toEqual( ["woocommerce", "js", "custom-tag"]);
|
||||
expect( payload['trace'] ).toContain( '#1 at testFunction (http://example.com/woocommerce/assets/js/admin/app.min.js:1:1)' );
|
||||
expect( payload[ 'tags' ] ).toEqual( [
|
||||
'woocommerce',
|
||||
'js',
|
||||
'custom-tag',
|
||||
] );
|
||||
expect( payload[ 'trace' ] ).toContain(
|
||||
'#1 at testFunction (http://example.com/woocommerce/assets/js/admin/app.min.js:1:1)'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
|
@ -305,31 +305,23 @@ describe( 'RemoteLogger', () => {
|
|||
} );
|
||||
} );
|
||||
|
||||
global.window.wcSettings = {
|
||||
isRemoteLoggingEnabled: true,
|
||||
};
|
||||
|
||||
describe( 'init', () => {
|
||||
beforeEach( () => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
( getSetting as jest.Mock ).mockImplementation(
|
||||
( key, defaultValue ) => {
|
||||
if ( key === 'isRemoteLoggingEnabled' ) {
|
||||
return true;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
);
|
||||
global.window.wcSettings = {
|
||||
isRemoteLoggingEnabled: true,
|
||||
};
|
||||
} );
|
||||
|
||||
it( 'should not initialize or log when remote logging is disabled', () => {
|
||||
// Mock the getSetting function to return false for isRemoteLoggingEnabled
|
||||
( getSetting as jest.Mock ).mockImplementation(
|
||||
( key, defaultValue ) => {
|
||||
if ( key === 'isRemoteLoggingEnabled' ) {
|
||||
return false;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
);
|
||||
|
||||
global.window.wcSettings = {
|
||||
isRemoteLoggingEnabled: false,
|
||||
};
|
||||
init( { errorRateLimitMs: 1000 } );
|
||||
log( 'info', 'Test message' );
|
||||
expect( fetchMock ).not.toHaveBeenCalled();
|
||||
|
@ -353,15 +345,9 @@ describe( 'init', () => {
|
|||
|
||||
describe( 'log', () => {
|
||||
it( 'should not log if remote logging is disabled', () => {
|
||||
( getSetting as jest.Mock ).mockImplementation(
|
||||
( key, defaultValue ) => {
|
||||
if ( key === 'isRemoteLoggingEnabled' ) {
|
||||
return false;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
);
|
||||
|
||||
global.window.wcSettings = {
|
||||
isRemoteLoggingEnabled: false,
|
||||
};
|
||||
log( 'info', 'Test message' );
|
||||
expect( fetchMock ).not.toHaveBeenCalled();
|
||||
} );
|
||||
|
@ -369,15 +355,9 @@ describe( 'log', () => {
|
|||
|
||||
describe( 'captureException', () => {
|
||||
it( 'should not log error if remote logging is disabled', () => {
|
||||
( getSetting as jest.Mock ).mockImplementation(
|
||||
( key, defaultValue ) => {
|
||||
if ( key === 'isRemoteLoggingEnabled' ) {
|
||||
return false;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
);
|
||||
|
||||
global.window.wcSettings = {
|
||||
isRemoteLoggingEnabled: false,
|
||||
};
|
||||
captureException( new Error( 'Test error' ) );
|
||||
expect( fetchMock ).not.toHaveBeenCalled();
|
||||
} );
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
declare global {
|
||||
interface Window {
|
||||
wcTracks: {
|
||||
isEnabled: boolean;
|
||||
};
|
||||
wcSettings?: {
|
||||
isRemoteLoggingEnabled: boolean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,3 +63,4 @@ require 'live-branches/manifest.php';
|
|||
require 'live-branches/install.php';
|
||||
require 'remote-spec-validator/class-wca-test-helper-remote-spec-validator.php';
|
||||
require 'remote-inbox-notifications/class-wca-test-helper-remote-inbox-notifications.php';
|
||||
require 'remote-logging/remote-logging.php';
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Internal\Logging\RemoteLogger;
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/remote-logging/status',
|
||||
'get_remote_logging_status',
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
)
|
||||
);
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/remote-logging/toggle',
|
||||
'toggle_remote_logging',
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
'args' => array(
|
||||
'enable' => array(
|
||||
'required' => true,
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'rest_sanitize_boolean',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/remote-logging/log-event',
|
||||
'log_remote_event',
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
)
|
||||
);
|
||||
|
||||
register_woocommerce_admin_test_helper_rest_route(
|
||||
'/remote-logging/reset-rate-limit',
|
||||
'reset_php_rate_limit',
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Get the remote logging status.
|
||||
*
|
||||
* @return WP_REST_Response The response object.
|
||||
*/
|
||||
function get_remote_logging_status() {
|
||||
$remote_logger = wc_get_container()->get( RemoteLogger::class );
|
||||
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'isEnabled' => $remote_logger->is_remote_logging_allowed(),
|
||||
'wpEnvironment' => wp_get_environment_type(),
|
||||
),
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle remote logging on or off.
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
* @return WP_REST_Response The response object.
|
||||
*/
|
||||
function toggle_remote_logging( $request ) {
|
||||
$enable = $request->get_param( 'enable' );
|
||||
|
||||
if ( $enable ) {
|
||||
update_option( 'woocommerce_feature_remote_logging_enabled', 'yes' );
|
||||
update_option( 'woocommerce_allow_tracking', 'yes' );
|
||||
update_option( 'woocommerce_remote_variant_assignment', 1 );
|
||||
} else {
|
||||
update_option( 'woocommerce_feature_remote_logging_enabled', 'no' );
|
||||
}
|
||||
|
||||
$remote_logger = wc_get_container()->get( RemoteLogger::class );
|
||||
return new WP_REST_Response(
|
||||
array(
|
||||
'isEnabled' => $remote_logger->is_remote_logging_allowed(),
|
||||
),
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log a remote event for testing purposes.
|
||||
*
|
||||
* @return WP_REST_Response The response object.
|
||||
*/
|
||||
function log_remote_event() {
|
||||
$remote_logger = wc_get_container()->get( RemoteLogger::class );
|
||||
$result = $remote_logger->handle(
|
||||
time(),
|
||||
'critical',
|
||||
'Test PHP event from WC Beta Tester',
|
||||
array( 'source' => 'wc-beta-tester' )
|
||||
);
|
||||
|
||||
if ( $result ) {
|
||||
return new WP_REST_Response( array( 'message' => 'Remote event logged successfully.' ), 200 );
|
||||
} else {
|
||||
return new WP_REST_Response( array( 'message' => 'Failed to log remote event.' ), 500 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the PHP rate limit.
|
||||
*
|
||||
* @return WP_REST_Response The response object.
|
||||
*/
|
||||
function reset_php_rate_limit() {
|
||||
global $wpdb;
|
||||
$wpdb->query(
|
||||
"DELETE FROM {$wpdb->prefix}wc_rate_limits"
|
||||
);
|
||||
|
||||
WC_Cache_Helper::invalidate_cache_group( WC_Rate_Limiter::CACHE_GROUP );
|
||||
|
||||
return new WP_REST_Response( array( 'success' => true ), 200 );
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Add remote logging tool
|
|
@ -17,6 +17,7 @@
|
|||
"@types/wordpress__plugins": "3.0.0",
|
||||
"@woocommerce/dependency-extraction-webpack-plugin": "workspace:*",
|
||||
"@woocommerce/eslint-plugin": "workspace:*",
|
||||
"@woocommerce/remote-logging": "workspace:*",
|
||||
"@wordpress/env": "^9.7.0",
|
||||
"@wordpress/prettier-config": "2.17.0",
|
||||
"@wordpress/scripts": "^19.2.4",
|
||||
|
|
|
@ -7,14 +7,13 @@ import { applyFilters } from '@wordpress/hooks';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { AdminNotes } from '../admin-notes';
|
||||
import { default as Tools } from '../tools';
|
||||
import { default as Options } from '../options';
|
||||
import { default as Experiments } from '../experiments';
|
||||
import { default as Features } from '../features';
|
||||
import { default as RestAPIFilters } from '../rest-api-filters';
|
||||
import RemoteSpecValidator from '../remote-spec-validator';
|
||||
import RemoteInboxNotifications from '../remote-inbox-notifications';
|
||||
import RemoteLogging from '../remote-logging';
|
||||
|
||||
const tabs = applyFilters( 'woocommerce_admin_test_helper_tabs', [
|
||||
{
|
||||
|
@ -47,6 +46,11 @@ const tabs = applyFilters( 'woocommerce_admin_test_helper_tabs', [
|
|||
title: 'Remote Inbox Notifications',
|
||||
content: <RemoteInboxNotifications />,
|
||||
},
|
||||
{
|
||||
name: 'remote-logging',
|
||||
title: 'Remote Logging',
|
||||
content: <RemoteLogging />,
|
||||
},
|
||||
] );
|
||||
|
||||
export function App() {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { createRoot } from '@wordpress/element';
|
||||
import { addFilter } from '@wordpress/hooks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -10,6 +11,7 @@ import { App } from './app';
|
|||
import './index.scss';
|
||||
import './example-fills/experimental-woocommerce-wcpay-feature';
|
||||
import { registerProductEditorDevTools } from './product-editor-dev-tools';
|
||||
import { registerExceptionFilter } from './remote-logging/register-exception-filter';
|
||||
|
||||
const appRoot = document.getElementById(
|
||||
'woocommerce-admin-test-helper-app-root'
|
||||
|
@ -20,3 +22,4 @@ if ( appRoot ) {
|
|||
}
|
||||
|
||||
registerProductEditorDevTools();
|
||||
registerExceptionFilter();
|
||||
|
|
|
@ -43,7 +43,6 @@ export function setNotice( notice ) {
|
|||
}
|
||||
|
||||
export function* deleteOption( optionName ) {
|
||||
try {
|
||||
yield apiFetch( {
|
||||
method: 'DELETE',
|
||||
path: `${ API_NAMESPACE }/options/${ optionName }`,
|
||||
|
@ -52,15 +51,18 @@ export function* deleteOption( optionName ) {
|
|||
type: TYPES.DELETE_OPTION,
|
||||
optionName,
|
||||
};
|
||||
} catch {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* saveOption( optionName, newOptionValue ) {
|
||||
try {
|
||||
const payload = {};
|
||||
try {
|
||||
// If the option value is a JSON string, parse it.
|
||||
payload[ optionName ] = JSON.parse( newOptionValue );
|
||||
} catch ( error ) {
|
||||
// If it's not a JSON string, just use the value as is.
|
||||
payload[ optionName ] = newOptionValue;
|
||||
}
|
||||
yield apiFetch( {
|
||||
method: 'POST',
|
||||
path: '/wc-admin/options',
|
||||
|
@ -71,11 +73,11 @@ export function* saveOption( optionName, newOptionValue ) {
|
|||
status: 'success',
|
||||
message: optionName + ' has been saved.',
|
||||
} );
|
||||
} catch {
|
||||
} catch ( error ) {
|
||||
yield setNotice( {
|
||||
status: 'error',
|
||||
message: 'Unable to save ' + optionName,
|
||||
} );
|
||||
throw new Error();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,14 +17,10 @@ export function* getOptions( search ) {
|
|||
|
||||
yield setLoadingState( true );
|
||||
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path,
|
||||
} );
|
||||
yield setOptions( response );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* getOptionForEditing( optionName ) {
|
||||
|
@ -41,7 +37,6 @@ export function* getOptionForEditing( optionName ) {
|
|||
|
||||
const path = '/wc-admin/options?options=' + optionName;
|
||||
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path,
|
||||
} );
|
||||
|
@ -55,7 +50,4 @@ export function* getOptionForEditing( optionName ) {
|
|||
name: optionName,
|
||||
content,
|
||||
} );
|
||||
} catch ( error ) {
|
||||
throw new Error( error );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,323 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from '@wordpress/element';
|
||||
import { Button, ToggleControl, Notice, Spinner } from '@wordpress/components';
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
import { log, init as initRemoteLogging } from '@woocommerce/remote-logging';
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore no types
|
||||
// eslint-disable-next-line @woocommerce/dependency-group
|
||||
import { dispatch } from '@wordpress/data';
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore no types
|
||||
// eslint-disable-next-line @woocommerce/dependency-group
|
||||
import { STORE_KEY as OPTIONS_STORE_NAME } from '../options/data/constants';
|
||||
|
||||
export const API_NAMESPACE = '/wc-admin-test-helper';
|
||||
|
||||
interface RemoteLoggingStatus {
|
||||
isEnabled: boolean;
|
||||
wpEnvironment: string;
|
||||
}
|
||||
|
||||
interface NoticeState {
|
||||
status: 'success' | 'error' | 'warning' | 'info';
|
||||
message: string;
|
||||
}
|
||||
|
||||
function RemoteLogging() {
|
||||
const [ isRemoteLoggingEnabled, setIsRemoteLoggingEnabled ] = useState<
|
||||
boolean | null
|
||||
>( null );
|
||||
const [ wpEnvironment, setWpEnvironment ] = useState< string >( '' );
|
||||
const [ notice, setNotice ] = useState< NoticeState | null >( null );
|
||||
|
||||
useEffect( () => {
|
||||
const fetchRemoteLoggingStatus = async () => {
|
||||
try {
|
||||
const response: RemoteLoggingStatus = await apiFetch( {
|
||||
path: `${ API_NAMESPACE }/remote-logging/status`,
|
||||
} );
|
||||
setIsRemoteLoggingEnabled( response.isEnabled );
|
||||
setWpEnvironment( response.wpEnvironment );
|
||||
} catch ( error ) {
|
||||
setNotice( {
|
||||
status: 'error',
|
||||
message: 'Failed to fetch remote logging status.',
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
fetchRemoteLoggingStatus();
|
||||
}, [] );
|
||||
|
||||
const toggleRemoteLogging = async () => {
|
||||
try {
|
||||
const response: RemoteLoggingStatus = await apiFetch( {
|
||||
path: `${ API_NAMESPACE }/remote-logging/toggle`,
|
||||
method: 'POST',
|
||||
data: { enable: ! isRemoteLoggingEnabled },
|
||||
} );
|
||||
setIsRemoteLoggingEnabled( response.isEnabled );
|
||||
|
||||
window.wcSettings.isRemoteLoggingEnabled = response.isEnabled;
|
||||
} catch ( error ) {
|
||||
setNotice( {
|
||||
status: 'error',
|
||||
message: `Failed to update remote logging status. ${ JSON.stringify(
|
||||
error
|
||||
) }`,
|
||||
} );
|
||||
}
|
||||
|
||||
if ( window.wcSettings.isRemoteLoggingEnabled ) {
|
||||
initRemoteLogging( {
|
||||
errorRateLimitMs: 60000, // 1 minute
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
const simulatePhpException = async ( context: 'core' | 'beta-tester' ) => {
|
||||
try {
|
||||
await dispatch( OPTIONS_STORE_NAME ).saveOption(
|
||||
'wc_beta_tester_simulate_woocommerce_php_error',
|
||||
context
|
||||
);
|
||||
setNotice( {
|
||||
status: 'success',
|
||||
message: `Please refresh your browser to trigger the PHP exception in ${ context } context.`,
|
||||
} );
|
||||
} catch ( error ) {
|
||||
setNotice( {
|
||||
status: 'error',
|
||||
message: `Failed to trigger PHP exception test in ${ context } context. ${ JSON.stringify(
|
||||
error
|
||||
) }`,
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
const logPhpEvent = async () => {
|
||||
try {
|
||||
await apiFetch( {
|
||||
path: `${ API_NAMESPACE }/remote-logging/log-event`,
|
||||
method: 'POST',
|
||||
} );
|
||||
setNotice( {
|
||||
status: 'success',
|
||||
message: 'Remote event logged successfully.',
|
||||
} );
|
||||
} catch ( error ) {
|
||||
setNotice( {
|
||||
status: 'error',
|
||||
message: `Failed to log remote event.`,
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
const resetPhpRateLimit = async () => {
|
||||
try {
|
||||
await apiFetch( {
|
||||
path: `${ API_NAMESPACE }/remote-logging/reset-rate-limit`,
|
||||
method: 'POST',
|
||||
} );
|
||||
setNotice( {
|
||||
status: 'success',
|
||||
message: 'PHP rate limit reset successfully.',
|
||||
} );
|
||||
} catch ( error ) {
|
||||
setNotice( {
|
||||
status: 'error',
|
||||
message: `Failed to reset PHP rate limit. ${ JSON.stringify(
|
||||
error
|
||||
) }`,
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
const simulateException = async ( context: 'core' | 'beta-tester' ) => {
|
||||
try {
|
||||
await dispatch( OPTIONS_STORE_NAME ).saveOption(
|
||||
'wc_beta_tester_simulate_woocommerce_js_error',
|
||||
context
|
||||
);
|
||||
|
||||
if ( context === 'core' ) {
|
||||
setNotice( {
|
||||
status: 'success',
|
||||
message: `Please go to WooCommerce pages to trigger the JS exception in woocommerce context.`,
|
||||
} );
|
||||
} else {
|
||||
setNotice( {
|
||||
status: 'success',
|
||||
message:
|
||||
'Please refresh your browser to trigger the JS exception in woocommerce beta tester context.',
|
||||
} );
|
||||
}
|
||||
} catch ( error ) {
|
||||
setNotice( {
|
||||
status: 'error',
|
||||
message: `Failed to set up JS exception test`,
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
const logJsEvent = async () => {
|
||||
try {
|
||||
const result = await log(
|
||||
'info',
|
||||
'Test JS event from WooCommerce Beta Tester',
|
||||
{
|
||||
extra: {
|
||||
source: 'wc-beta-tester',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if ( ! result ) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
setNotice( {
|
||||
status: 'success',
|
||||
message: 'JS event logged successfully.',
|
||||
} );
|
||||
} catch ( error ) {
|
||||
setNotice( {
|
||||
status: 'error',
|
||||
message:
|
||||
'Failed to log JS event. Try enabling debug mode `window.localStorage.setItem( "debug", "wc:remote-logging" )` to see the details.',
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
const resetJsRateLimit = () => {
|
||||
window.localStorage.removeItem(
|
||||
'wc_remote_logging_last_error_sent_time'
|
||||
);
|
||||
setNotice( {
|
||||
status: 'success',
|
||||
message: 'JS rate limit reset successfully.',
|
||||
} );
|
||||
};
|
||||
|
||||
if ( isRemoteLoggingEnabled === null ) {
|
||||
return <Spinner />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="wc-admin-test-helper-remote-logging">
|
||||
<h2>Remote Logging</h2>
|
||||
{ notice && (
|
||||
<div style={ { marginBottom: '12px' } }>
|
||||
<Notice
|
||||
status={ notice.status }
|
||||
onRemove={ () => setNotice( null ) }
|
||||
>
|
||||
{ notice.message }
|
||||
</Notice>
|
||||
</div>
|
||||
) }
|
||||
|
||||
{ ! isRemoteLoggingEnabled && (
|
||||
<p className="helper-text" style={ { marginBottom: '12px' } }>
|
||||
Enable remote logging to test log event functionality.
|
||||
</p>
|
||||
) }
|
||||
|
||||
{ ( wpEnvironment === 'local' ||
|
||||
wpEnvironment === 'development' ) && (
|
||||
<div style={ { marginBottom: '12px' } }>
|
||||
<Notice status="warning" isDismissible={ false }>
|
||||
Warning: You are in a { wpEnvironment } environment.
|
||||
Remote logging may not work as expected. Please set
|
||||
<code>WP_ENVIRONMENT_TYPE</code> to{ ' ' }
|
||||
<code>production</code>
|
||||
in your wp-config.php file to test remote logging.
|
||||
</Notice>
|
||||
</div>
|
||||
) }
|
||||
|
||||
<ToggleControl
|
||||
label="Enable Remote Logging"
|
||||
checked={ isRemoteLoggingEnabled }
|
||||
onChange={ toggleRemoteLogging }
|
||||
/>
|
||||
|
||||
<hr />
|
||||
<h3>PHP Integration</h3>
|
||||
<p>Test PHP remote logging functionality:</p>
|
||||
<div className="button-group" style={ { marginBottom: '20px' } }>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={ () => simulatePhpException( 'core' ) }
|
||||
style={ { marginRight: '10px' } }
|
||||
>
|
||||
Simulate Core Exception
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={ () => simulatePhpException( 'beta-tester' ) }
|
||||
style={ { marginRight: '10px' } }
|
||||
>
|
||||
Simulate Beta Tester Exception
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={ logPhpEvent }
|
||||
disabled={ ! isRemoteLoggingEnabled }
|
||||
style={ { marginRight: '10px' } }
|
||||
>
|
||||
Log PHP Event
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={ resetPhpRateLimit }
|
||||
disabled={ ! isRemoteLoggingEnabled }
|
||||
>
|
||||
Reset Rate Limit
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<hr className="section-divider" style={ { margin: '20px 0' } } />
|
||||
|
||||
<h3>JavaScript Integration</h3>
|
||||
<p>Test JavaScript remote logging functionality:</p>
|
||||
<div className="button-group" style={ { marginBottom: '20px' } }>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={ () => simulateException( 'core' ) }
|
||||
style={ { marginRight: '10px' } }
|
||||
>
|
||||
Simulate Core Exception
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={ () => simulateException( 'beta-tester' ) }
|
||||
style={ { marginRight: '10px' } }
|
||||
>
|
||||
Simulate Beta Tester Exception
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={ logJsEvent }
|
||||
disabled={ ! isRemoteLoggingEnabled }
|
||||
style={ { marginRight: '10px' } }
|
||||
>
|
||||
Log Event
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={ resetJsRateLimit }
|
||||
disabled={ ! isRemoteLoggingEnabled }
|
||||
>
|
||||
Reset Rate Limit
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default RemoteLogging;
|
|
@ -0,0 +1,93 @@
|
|||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
/* eslint-disable @woocommerce/dependency-group */
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { addFilter } from '@wordpress/hooks';
|
||||
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
// @ts-ignore no types
|
||||
import { dispatch } from '@wordpress/data';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { API_NAMESPACE } from './';
|
||||
// @ts-ignore no types
|
||||
import { STORE_KEY as OPTIONS_STORE_NAME } from '../options/data/constants';
|
||||
|
||||
/**
|
||||
* Retrieves the options for simulating a WooCommerce JavaScript error.
|
||||
*
|
||||
* @return {Promise<Array|null>} The options if available, null otherwise.
|
||||
*/
|
||||
const getSimulateErrorOptions = async () => {
|
||||
try {
|
||||
const path = `${ API_NAMESPACE }/options?search=wc_beta_tester_simulate_woocommerce_js_error`;
|
||||
|
||||
const options = await apiFetch<
|
||||
[
|
||||
{
|
||||
option_value: string;
|
||||
option_name: string;
|
||||
option_id: number;
|
||||
}
|
||||
]
|
||||
>( {
|
||||
path,
|
||||
} );
|
||||
return options && options.length > 0 ? options : null;
|
||||
} catch ( error ) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error( 'Error retrieving simulate error options:', error );
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes the option used for simulating WooCommerce JavaScript errors.
|
||||
*/
|
||||
const deleteSimulateErrorOption = async () => {
|
||||
await dispatch( OPTIONS_STORE_NAME ).deleteOption(
|
||||
'wc_beta_tester_simulate_woocommerce_js_error'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a filter to throw an exception in the WooCommerce core context.
|
||||
*/
|
||||
const addCoreExceptionFilter = () => {
|
||||
addFilter( 'woocommerce_admin_pages_list', 'wc-beta-tester', () => {
|
||||
deleteSimulateErrorOption();
|
||||
|
||||
throw new Error(
|
||||
'Test JS exception in WC Core context via WC Beta Tester'
|
||||
);
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Throws an exception specific to the WooCommerce Beta Tester context.
|
||||
*/
|
||||
const throwBetaTesterException = () => {
|
||||
throw new Error( 'Test JS exception from WooCommerce Beta Tester' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers an exception filter for simulating JavaScript errors in WooCommerce.
|
||||
* This function is used for testing purposes in the WooCommerce Beta Tester plugin.
|
||||
*/
|
||||
export const registerExceptionFilter = async () => {
|
||||
const options = await getSimulateErrorOptions();
|
||||
if ( ! options ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const context = options[ 0 ].option_value;
|
||||
if ( context === 'core' ) {
|
||||
addCoreExceptionFilter();
|
||||
} else {
|
||||
deleteSimulateErrorOption();
|
||||
throwBetaTesterException();
|
||||
}
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
declare global {
|
||||
interface Window {
|
||||
wcSettings: {
|
||||
isRemoteLoggingEnabled: boolean;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
|
@ -138,5 +138,28 @@ add_action(
|
|||
}
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* Simulate a WooCommerce error for remote logging testing.
|
||||
*
|
||||
* @throws Exception A simulated WooCommerce error if the option is set.
|
||||
*/
|
||||
function simulate_woocommerce_error() {
|
||||
throw new Exception( 'Simulated WooCommerce error for remote logging test' );
|
||||
}
|
||||
|
||||
$simulate_error = get_option( 'wc_beta_tester_simulate_woocommerce_php_error', false );
|
||||
|
||||
if ( $simulate_error ) {
|
||||
delete_option( 'wc_beta_tester_simulate_woocommerce_php_error' );
|
||||
|
||||
if ( 'core' === $simulate_error ) {
|
||||
add_action( 'woocommerce_loaded', 'simulate_woocommerce_error' );
|
||||
} elseif ( 'beta-tester' === $simulate_error ) {
|
||||
throw new Exception( 'Test PHP exception from WooCommerce Beta Tester' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Initialize the live branches feature.
|
||||
require_once dirname( __FILE__ ) . '/includes/class-wc-beta-tester-live-branches.php';
|
||||
|
|
|
@ -3916,6 +3916,9 @@ importers:
|
|||
'@woocommerce/eslint-plugin':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/js/eslint-plugin
|
||||
'@woocommerce/remote-logging':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/js/remote-logging
|
||||
'@wordpress/env':
|
||||
specifier: ^9.7.0
|
||||
version: 9.7.0
|
||||
|
@ -21800,7 +21803,7 @@ packages:
|
|||
react-with-direction@1.4.0:
|
||||
resolution: {integrity: sha512-ybHNPiAmaJpoWwugwqry9Hd1Irl2hnNXlo/2SXQBwbLn/jGMauMS2y9jw+ydyX5V9ICryCqObNSthNt5R94xpg==}
|
||||
peerDependencies:
|
||||
react: ^0.14 || ^15 || ^16
|
||||
react: ^17.0.2
|
||||
react-dom: ^0.14 || ^15 || ^16
|
||||
|
||||
react-with-styles-interface-css@4.0.3:
|
||||
|
|
Loading…
Reference in New Issue