Commit 024a28be24f for woocommerce
commit 024a28be24f94a609638725232f43e8f344d8c35
Author: Alba Rincón <albarin@users.noreply.github.com>
Date: Thu Apr 23 11:55:58 2026 +0200
Add GraphQL settings section with GET endpoint toggle (#64293)
* Scaffold GraphQL section under WooCommerce Settings → Advanced
* Add GET endpoint toggle to GraphQL settings
* Add unit tests for GraphQL settings section and GET endpoint toggle
* Add changefile(s) from automation for the following project(s): woocommerce
* Add changefile(s) from automation for the following project(s): woocommerce
* Cover register() hooks and assert single /wc/graphql handler
* Add changefile(s) from automation for the following project(s): woocommerce
* Remove duplicate changelog
* Make the setting enabled by default
* Skip GraphQLController tests on PHP < 8.1
* Clean up SettingsTest filters and fix stale @testdox default
---------
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
Co-authored-by: Nestor Soriano <konamiman@konamiman.com>
diff --git a/plugins/woocommerce/changelog/64293-add-graphql-settings-toggle b/plugins/woocommerce/changelog/64293-add-graphql-settings-toggle
new file mode 100644
index 00000000000..1c5fcd10c0d
--- /dev/null
+++ b/plugins/woocommerce/changelog/64293-add-graphql-settings-toggle
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add a GraphQL settings section under Advanced with a toggle for the GET endpoint.
\ No newline at end of file
diff --git a/plugins/woocommerce/src/Internal/Api/GraphQLController.php b/plugins/woocommerce/src/Internal/Api/GraphQLController.php
index 4238804b3c2..16eb3a13ce9 100644
--- a/plugins/woocommerce/src/Internal/Api/GraphQLController.php
+++ b/plugins/woocommerce/src/Internal/Api/GraphQLController.php
@@ -90,11 +90,13 @@ class GraphQLController {
* Register the GraphQL REST route.
*/
public function register(): void {
+ $methods = Main::is_get_endpoint_enabled() ? array( 'GET', 'POST' ) : array( 'POST' );
+
register_rest_route(
'wc',
'/graphql',
array(
- 'methods' => array( 'GET', 'POST' ),
+ 'methods' => $methods,
'callback' => array( $this, 'handle_request' ),
// Auth is handled per-query/mutation.
'permission_callback' => '__return_true',
diff --git a/plugins/woocommerce/src/Internal/Api/Main.php b/plugins/woocommerce/src/Internal/Api/Main.php
index 16f0a43a3c9..faba0442ce3 100644
--- a/plugins/woocommerce/src/Internal/Api/Main.php
+++ b/plugins/woocommerce/src/Internal/Api/Main.php
@@ -20,6 +20,13 @@ class Main {
*/
private const FEATURE_SLUG = 'dual_code_graphql_api';
+ /**
+ * Option name for the "Enable GET endpoint" setting.
+ *
+ * When disabled, the GraphQL route only accepts POST requests.
+ */
+ public const OPTION_GET_ENDPOINT_ENABLED = 'woocommerce_graphql_get_endpoint_enabled';
+
/**
* Cached result of the feature-enabled check, null until first evaluated.
*
@@ -42,6 +49,17 @@ class Main {
return self::$enabled;
}
+ /**
+ * Whether the GraphQL endpoint accepts GET requests.
+ *
+ * Defaults to false. Reads from the option written by the GraphQL
+ * settings section so the REST route registration can decide which
+ * HTTP methods to accept.
+ */
+ public static function is_get_endpoint_enabled(): bool {
+ return wc_string_to_bool( get_option( self::OPTION_GET_ENDPOINT_ENABLED, 'yes' ) );
+ }
+
/**
* Register the GraphQL endpoint when the feature is active.
*
@@ -62,5 +80,8 @@ class Main {
wc_get_container()->get( GraphQLController::class )->register();
}
);
+
+ $settings = wc_get_container()->get( Settings::class );
+ $settings->register();
}
}
diff --git a/plugins/woocommerce/src/Internal/Api/Settings.php b/plugins/woocommerce/src/Internal/Api/Settings.php
new file mode 100644
index 00000000000..3b1678c549b
--- /dev/null
+++ b/plugins/woocommerce/src/Internal/Api/Settings.php
@@ -0,0 +1,71 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Automattic\WooCommerce\Internal\Api;
+
+/**
+ * Settings handling for the GraphQL API.
+ *
+ * Registers the "GraphQL" section under WooCommerce - Settings - Advanced.
+ * Only active when Main::is_enabled() returns true (feature flag on and
+ * PHP 8.1+), so the section is hidden when the feature is disabled.
+ */
+class Settings {
+ /**
+ * Identifier for the GraphQL section under the Advanced settings tab.
+ */
+ public const SECTION_ID = 'graphql';
+
+ /**
+ * Register the filter hooks that expose the GraphQL settings section.
+ */
+ public function register(): void {
+ add_filter( 'woocommerce_get_sections_advanced', array( $this, 'add_section' ) );
+ add_filter( 'woocommerce_get_settings_advanced', array( $this, 'add_settings' ), 10, 2 );
+ }
+
+ /**
+ * Append the GraphQL section to the Advanced settings tab.
+ *
+ * @param array $sections Existing sections keyed by id.
+ * @return array
+ */
+ public function add_section( array $sections ): array {
+ $sections[ self::SECTION_ID ] = __( 'GraphQL', 'woocommerce' );
+ return $sections;
+ }
+
+ /**
+ * Provide the settings fields for the GraphQL section.
+ *
+ * @param array $settings Existing settings for the current section.
+ * @param string $section_id Current section id.
+ * @return array
+ */
+ public function add_settings( array $settings, string $section_id ): array {
+ if ( self::SECTION_ID !== $section_id ) {
+ return $settings;
+ }
+
+ return array(
+ array(
+ 'title' => __( 'GraphQL', 'woocommerce' ),
+ 'desc' => __( 'Configure the WooCommerce GraphQL API.', 'woocommerce' ),
+ 'type' => 'title',
+ 'id' => 'woocommerce_graphql_options',
+ ),
+ array(
+ 'title' => __( 'Enable GET endpoint', 'woocommerce' ),
+ 'desc' => __( 'Allow GraphQL queries over GET in addition to POST', 'woocommerce' ),
+ 'id' => Main::OPTION_GET_ENDPOINT_ENABLED,
+ 'default' => 'yes',
+ 'type' => 'checkbox',
+ ),
+ array(
+ 'type' => 'sectionend',
+ 'id' => 'woocommerce_graphql_options',
+ ),
+ );
+ }
+}
diff --git a/plugins/woocommerce/tests/php/src/Internal/Api/GraphQLControllerTest.php b/plugins/woocommerce/tests/php/src/Internal/Api/GraphQLControllerTest.php
new file mode 100644
index 00000000000..c700f4821a1
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Internal/Api/GraphQLControllerTest.php
@@ -0,0 +1,77 @@
+<?php
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Tests\Internal\Api;
+
+use Automattic\WooCommerce\Internal\Api\GraphQLController;
+use Automattic\WooCommerce\Internal\Api\Main;
+use WC_REST_Unit_Test_Case;
+
+/**
+ * Tests for the GraphQLController class — specifically the HTTP methods
+ * registered on the /wc/graphql route based on the GET endpoint option.
+ */
+class GraphQLControllerTest extends WC_REST_Unit_Test_Case {
+ /**
+ * The System Under Test.
+ *
+ * @var GraphQLController
+ */
+ private $sut;
+
+ /**
+ * Set up before each test.
+ *
+ * Skips on PHP < 8.1 because GraphQLController uses PHP 8.0+ syntax in its
+ * source file (named arguments). In production the class is only loaded
+ * after {@see Main::is_enabled()} gates on PHP 8.1+; these tests bypass
+ * that gate by hitting the DI container directly, so we replicate it here.
+ */
+ public function setUp(): void {
+ parent::setUp();
+
+ if ( PHP_VERSION_ID < 80100 ) {
+ $this->markTestSkipped( 'GraphQLController requires PHP 8.1+.' );
+ }
+
+ $this->sut = wc_get_container()->get( GraphQLController::class );
+ }
+
+ /**
+ * Clean up the GET endpoint option between tests.
+ */
+ public function tearDown(): void {
+ delete_option( Main::OPTION_GET_ENDPOINT_ENABLED );
+ parent::tearDown();
+ }
+
+ /**
+ * @testdox register exposes POST only when the GET endpoint option is disabled.
+ */
+ public function test_register_exposes_post_only_when_get_disabled(): void {
+ update_option( Main::OPTION_GET_ENDPOINT_ENABLED, 'no' );
+
+ $this->sut->register();
+
+ $handlers = rest_get_server()->get_routes()['/wc/graphql'];
+ $this->assertCount( 1, $handlers, 'Exactly one handler should be registered for /wc/graphql.' );
+ $methods = $handlers[0]['methods'];
+ $this->assertTrue( $methods['POST'] ?? false );
+ $this->assertFalse( $methods['GET'] ?? false );
+ }
+
+ /**
+ * @testdox register exposes GET and POST when the GET endpoint option is enabled.
+ */
+ public function test_register_exposes_get_and_post_when_get_enabled(): void {
+ update_option( Main::OPTION_GET_ENDPOINT_ENABLED, 'yes' );
+
+ $this->sut->register();
+
+ $handlers = rest_get_server()->get_routes()['/wc/graphql'];
+ $this->assertCount( 1, $handlers, 'Exactly one handler should be registered for /wc/graphql.' );
+ $methods = $handlers[0]['methods'];
+ $this->assertTrue( $methods['GET'] ?? false );
+ $this->assertTrue( $methods['POST'] ?? false );
+ }
+}
diff --git a/plugins/woocommerce/tests/php/src/Internal/Api/SettingsTest.php b/plugins/woocommerce/tests/php/src/Internal/Api/SettingsTest.php
new file mode 100644
index 00000000000..bd786f59f96
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Internal/Api/SettingsTest.php
@@ -0,0 +1,75 @@
+<?php
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Tests\Internal\Api;
+
+use Automattic\WooCommerce\Internal\Api\Main;
+use Automattic\WooCommerce\Internal\Api\Settings;
+use WC_Unit_Test_Case;
+
+/**
+ * Tests for the GraphQL API Settings class.
+ */
+class SettingsTest extends WC_Unit_Test_Case {
+ /**
+ * The System Under Test.
+ *
+ * @var Settings
+ */
+ private $sut;
+
+ /**
+ * Set up before each test.
+ */
+ public function setUp(): void {
+ parent::setUp();
+ $this->sut = new Settings();
+ }
+
+ /**
+ * Clean up filters registered by tests so global state doesn't leak.
+ */
+ public function tearDown(): void {
+ remove_filter( 'woocommerce_get_sections_advanced', array( $this->sut, 'add_section' ) );
+ remove_filter( 'woocommerce_get_settings_advanced', array( $this->sut, 'add_settings' ), 10 );
+ parent::tearDown();
+ }
+
+ /**
+ * @testdox register hooks add_section and add_settings into WooCommerce's advanced settings filters.
+ */
+ public function test_register_hooks_both_advanced_filters(): void {
+ $this->sut->register();
+
+ $this->assertNotFalse(
+ has_filter( 'woocommerce_get_sections_advanced', array( $this->sut, 'add_section' ) ),
+ 'add_section should be hooked to woocommerce_get_sections_advanced.'
+ );
+ $this->assertNotFalse(
+ has_filter( 'woocommerce_get_settings_advanced', array( $this->sut, 'add_settings' ) ),
+ 'add_settings should be hooked to woocommerce_get_settings_advanced.'
+ );
+ }
+
+ /**
+ * @testdox add_section appends the graphql section while preserving existing ones.
+ */
+ public function test_add_section_appends_graphql_section(): void {
+ $result = $this->sut->add_section( array( 'features' => 'Features' ) );
+
+ $this->assertArrayHasKey( Settings::SECTION_ID, $result );
+ $this->assertArrayHasKey( 'features', $result );
+ }
+
+ /**
+ * @testdox add_settings defines the GET endpoint checkbox with a 'yes' default.
+ */
+ public function test_add_settings_defines_get_endpoint_checkbox(): void {
+ $fields = $this->sut->add_settings( array(), Settings::SECTION_ID );
+ $by_id = array_column( $fields, null, 'id' );
+
+ $this->assertArrayHasKey( Main::OPTION_GET_ENDPOINT_ENABLED, $by_id );
+ $this->assertSame( 'checkbox', $by_id[ Main::OPTION_GET_ENDPOINT_ENABLED ]['type'] );
+ $this->assertSame( 'yes', $by_id[ Main::OPTION_GET_ENDPOINT_ENABLED ]['default'] );
+ }
+}