diff --git a/.github/workflows/metrics.yml b/.github/workflows/metrics.yml index da5de8e5312..61d5f3efede 100644 --- a/.github/workflows/metrics.yml +++ b/.github/workflows/metrics.yml @@ -51,3 +51,11 @@ jobs: with: name: performance-results path: ${{ env.WP_ARTIFACTS_PATH }}/*.performance-results*.json + + - name: Publish performance results + if: github.event_name == 'push' + env: + CODEVITALS_PROJECT_TOKEN: ${{ secrets.CODEVITALS_PROJECT_TOKEN }} + run: | + COMMITTED_AT=$(git show -s $GITHUB_SHA --format="%cI") + cd tools/compare-perf && pnpm run log $CODEVITALS_PROJECT_TOKEN trunk $GITHUB_SHA 19f3d0884617d7ecdcf37664f648a51e2987cada $COMMITTED_AT diff --git a/phpcs.xml b/phpcs.xml index fd355d33463..7324030f0aa 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -107,18 +107,24 @@ src/Internal/Admin/ src/Admin/ + src/Blocks/ + src/StoreApi/ src/Internal/Admin/ src/Admin/ + src/Blocks/ + src/StoreApi/ src/Internal/Admin/ src/Admin/ + src/Blocks/ + src/StoreApi/ diff --git a/plugins/woocommerce/changelog/add-blocks-injection-rule b/plugins/woocommerce/changelog/add-blocks-injection-rule new file mode 100644 index 00000000000..df513a6a407 --- /dev/null +++ b/plugins/woocommerce/changelog/add-blocks-injection-rule @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Add InternalInjection sniff exceptions for blocks diff --git a/plugins/woocommerce/changelog/fix-41249 b/plugins/woocommerce/changelog/fix-41249 new file mode 100644 index 00000000000..762c0a25ed5 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-41249 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Delete trashed orders after `EMPTY_TRASH_DAYS` as defined by WordPress (HPOS) diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php b/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php index 5d6a383b636..235f4787816 100644 --- a/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php +++ b/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php @@ -80,6 +80,13 @@ class DataSynchronizer implements BatchProcessorInterface { */ private $error_logger; + /** + * The instance of the LegacyProxy object to use. + * + * @var LegacyProxy + */ + private $legacy_proxy; + /** * The order cache controller. * @@ -103,6 +110,7 @@ class DataSynchronizer implements BatchProcessorInterface { self::add_action( 'woocommerce_refund_created', array( $this, 'handle_updated_order' ), 100 ); self::add_action( 'woocommerce_update_order', array( $this, 'handle_updated_order' ), 100 ); self::add_action( 'wp_scheduled_auto_draft_delete', array( $this, 'delete_auto_draft_orders' ), 9 ); + self::add_action( 'wp_scheduled_delete', array( $this, 'delete_trashed_orders' ), 9 ); self::add_filter( 'updated_option', array( $this, 'process_updated_option' ), 999, 3 ); self::add_filter( 'added_option', array( $this, 'process_added_option' ), 999, 2 ); self::add_filter( 'deleted_option', array( $this, 'process_deleted_option' ), 999 ); @@ -136,6 +144,7 @@ class DataSynchronizer implements BatchProcessorInterface { $this->data_store = $data_store; $this->database_util = $database_util; $this->posts_to_cot_migrator = $posts_to_cot_migrator; + $this->legacy_proxy = $legacy_proxy; $this->error_logger = $legacy_proxy->call_function( 'wc_get_logger' ); $this->order_cache_controller = $order_cache_controller; $this->batch_processing_controller = $batch_processing_controller; @@ -966,6 +975,41 @@ ORDER BY orders.id ASC do_action( 'woocommerce_scheduled_auto_draft_delete' ); } + /** + * Handles deletion of trashed orders after `EMPTY_TRASH_DAYS` as defined by WordPress. + * + * @since 8.5.0 + * + * @return void + */ + private function delete_trashed_orders() { + if ( ! $this->custom_orders_table_is_authoritative() ) { + return; + } + + $delete_timestamp = $this->legacy_proxy->call_function( 'time' ) - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS ); + $args = array( + 'status' => 'trash', + 'limit' => self::ORDERS_SYNC_BATCH_SIZE, + 'date_modified' => '<' . $delete_timestamp, + ); + + $orders = wc_get_orders( $args ); + if ( ! $orders || ! is_array( $orders ) ) { + return; + } + + foreach ( $orders as $order ) { + if ( $order->get_status() !== 'trash' ) { + continue; + } + if ( $order->get_date_modified()->getTimestamp() >= $delete_timestamp ) { + continue; + } + $order->delete( true ); + } + } + /** * Handle the 'woocommerce_feature_description_tip' filter. * diff --git a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php index d64ae7ae3b3..52a4eeab8c6 100644 --- a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php +++ b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php @@ -25,13 +25,18 @@ class DataSynchronizerTests extends HposTestCase { */ public function setUp(): void { parent::setUp(); + + $this->reset_legacy_proxy_mocks(); + $container = wc_get_container(); + $container->reset_all_resolved(); + // Remove the Test Suiteā€™s use of temporary tables https://wordpress.stackexchange.com/a/220308. remove_filter( 'query', array( $this, '_create_temporary_tables' ) ); remove_filter( 'query', array( $this, '_drop_temporary_tables' ) ); OrderHelper::delete_order_custom_tables(); // We need this since non-temporary tables won't drop automatically. OrderHelper::create_order_custom_table_if_not_exist(); OrderHelper::toggle_cot_feature_and_usage( false ); - $this->sut = wc_get_container()->get( DataSynchronizer::class ); + $this->sut = $container->get( DataSynchronizer::class ); } /** @@ -530,6 +535,53 @@ class DataSynchronizerTests extends HposTestCase { $this->assertNotContains( $order1->get_id(), $orders ); } + /** + * Test that trashed orders are deleted after the time set in `EMPTY_TRASH_DAYS`. + */ + public function test_trashed_order_deletion(): void { + $this->toggle_cot_authoritative( true ); + $this->disable_cot_sync(); + + $order = new WC_Order(); + $order->save(); + + // Ensure the placeholder post is there. + $placeholder = get_post( $order->get_id() ); + $this->assertEquals( $order->get_id(), $placeholder->ID ); + + // Trashed orders should be deleted by the collection mechanism. + $order->get_data_store()->delete( $order ); + $this->assertEquals( $order->get_status(), 'trash' ); + $order->save(); + + // Run scheduled deletion. + do_action( 'wp_scheduled_delete' ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.HookCommentWrongStyle + + // Refresh order and ensure it's *not* gone. + $order = wc_get_order( $order->get_id() ); + $this->assertNotNull( $order ); + + // Time-travel into the future so that the time required to delete a trashed order has passed. + $this->register_legacy_proxy_function_mocks( + array( + 'time' => function() { + return time() + DAY_IN_SECONDS * EMPTY_TRASH_DAYS + 1; + }, + ) + ); + + // Run scheduled deletion. + do_action( 'wp_scheduled_delete' ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.HookCommentWrongStyle + + // Ensure the placeholder post is gone. + $placeholder = get_post( $order->get_id() ); + $this->assertNull( $placeholder ); + + // Refresh order and ensure it's gone. + $order = wc_get_order( $order->get_id() ); + $this->assertFalse( $order ); + } + /** * @testDox When HPOS is enabled, the custom orders table is created. */ diff --git a/tools/compare-perf/log-to-codevitals.js b/tools/compare-perf/log-to-codevitals.js new file mode 100644 index 00000000000..3b4e31a6a1e --- /dev/null +++ b/tools/compare-perf/log-to-codevitals.js @@ -0,0 +1,86 @@ +#!/usr/bin/env node +/* eslint-disable no-console */ +const fs = require( 'fs' ); +const path = require( 'path' ); +const https = require( 'https' ); +const [ token, branch, hash, baseHash, timestamp ] = process.argv.slice( 2 ); + +const resultsFiles = [ + { + file: 'editor.performance-results.json', + metricsPrefix: 'editor-', + }, +]; + +const performanceResults = resultsFiles.map( ( { file } ) => + JSON.parse( + fs.readFileSync( + path.join( process.env.WP_ARTIFACTS_PATH, file ), + 'utf8' + ) + ) +); + +const data = new TextEncoder().encode( + JSON.stringify( { + branch, + hash, + baseHash, + timestamp, + metrics: resultsFiles.reduce( ( result, { metricsPrefix }, index ) => { + return { + ...result, + ...Object.fromEntries( + Object.entries( + performanceResults[ index ][ hash ] ?? {} + ).map( ( [ key, value ] ) => [ + metricsPrefix + key, + value, + ] ) + ), + }; + }, {} ), + baseMetrics: resultsFiles.reduce( + ( result, { metricsPrefix }, index ) => { + return { + ...result, + ...Object.fromEntries( + Object.entries( + performanceResults[ index ][ baseHash ] ?? {} + ).map( ( [ key, value ] ) => [ + metricsPrefix + key, + value, + ] ) + ), + }; + }, + {} + ), + } ) +); + +const options = { + hostname: 'www.codevitals.run', + port: 443, + path: '/api/log?token=' + token, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': data.length, + }, +}; + +const req = https.request( options, ( res ) => { + console.log( `statusCode: ${ res.statusCode }` ); + + res.on( 'data', ( d ) => { + process.stdout.write( d ); + } ); +} ); + +req.on( 'error', ( error ) => { + console.error( error ); +} ); + +req.write( data ); +req.end(); diff --git a/tools/compare-perf/package.json b/tools/compare-perf/package.json index 77c193c56ff..36ae6fe2746 100644 --- a/tools/compare-perf/package.json +++ b/tools/compare-perf/package.json @@ -7,7 +7,8 @@ "license": "GPLv2", "repository": "woocommerce/woocommerce", "scripts": { - "compare": "node index.js" + "compare": "node index.js", + "log": "node log-to-codevitals.js" }, "dependencies": { "@wordpress/env": "^8.13.0",