feat: QL Session Handler refactored to handle non-GraphQL requests (#870)

* feat: QL Session Handler functionality expanded to support cookies on non-GraphQL requests

* chore: Linter and PHPStan compliance met

* devops: QLSessionHandlerTest patched for suite testing

* chore: Linter and PHPStan compliance met

* fix: More cart session save triggered implemented

* fix: More cart session save triggered implemented

* chore: Linter compliance met

* chore: Linter compliance met

* feat: forgetSession mutation added

* feat: forgetSession mutation added
This commit is contained in:
Geoff Taylor
2024-08-07 12:39:45 -04:00
committed by GitHub
parent d65c6a716d
commit 76e8db510e
24 changed files with 457 additions and 84 deletions
+3 -7
View File
@@ -19,8 +19,7 @@ if ( ! function_exists( 'str_starts_with' ) ) {
* @return bool - True if $haystack starts with $needle, false otherwise.
*/
function str_starts_with( $haystack, $needle ) {
$length = strlen( $needle );
return ( substr( $haystack, 0, $length ) === $needle );
return 0 === strpos( $haystack, $needle ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.str_starts_with
}
}
@@ -38,11 +37,8 @@ if ( ! function_exists( 'str_ends_with' ) ) {
*/
function str_ends_with( $haystack, $needle ) {
$length = strlen( $needle );
if ( 0 === $length ) {
return true;
}
return ( substr( $haystack, -$length ) === $needle );
return 0 === $length
|| strpos( $haystack, $needle, - $length ) === $length - 1;
}
}//end if
+2 -2
View File
@@ -66,11 +66,11 @@ modules:
uploads: '/wp-content/uploads'
WPLoader:
wpRootFolder: '%WP_CORE_DIR%'
dbUrl: 'mysql://%DB_USER%:%DB_PASSWORD%@%DB_HOST%:%DB_PORT%/%DB_NAME%'
dbName: '%DB_NAME%'
dbHost: '%DB_HOST%'
dbName: '%DB_NAME%'
dbUser: '%DB_USER%'
dbPassword: '%DB_PASSWORD%'
dbUrl: 'mysql://%DB_USER%:%DB_PASSWORD%@%DB_HOST%:%DB_PORT%/%DB_NAME%'
tablePrefix: '%WP_TABLE_PREFIX%'
domain: '%WORDPRESS_DOMAIN%'
adminEmail: '%ADMIN_EMAIL%'
+18
View File
@@ -77,6 +77,24 @@ class General extends Section {
'value' => defined( 'NO_QL_SESSION_HANDLER' ) ? 'on' : woographql_setting( 'disable_ql_session_handler', 'off' ),
'disabled' => defined( 'NO_QL_SESSION_HANDLER' ),
],
[
'name' => 'enable_ql_session_handler_on_ajax',
'label' => __( 'Enable QL Session Handler on WC AJAX requests.', 'wp-graphql-woocommerce' ),
'desc' => __( 'Enabling this will enable JSON Web Tokens usage on WC AJAX requests.', 'wp-graphql-woocommerce' )
. ( defined( 'NO_QL_SESSION_HANDLER' ) ? __( ' This setting is disabled. The "NO_QL_SESSION_HANDLER" flag has been triggered with code', 'wp-graphql-woocommerce' ) : '' ),
'type' => 'checkbox',
'value' => defined( 'NO_QL_SESSION_HANDLER' ) ? 'off' : woographql_setting( 'enable_ql_session_handler_on_ajax', 'off' ),
'disabled' => defined( 'NO_QL_SESSION_HANDLER' ),
],
[
'name' => 'enable_ql_session_handler_on_rest',
'label' => __( 'Enable QL Session Handler on WP REST requests.', 'wp-graphql-woocommerce' ),
'desc' => __( 'Enabling this will enable JSON Web Tokens usage on WP REST requests.', 'wp-graphql-woocommerce' )
. ( defined( 'NO_QL_SESSION_HANDLER' ) ? __( ' This setting is disabled. The "NO_QL_SESSION_HANDLER" flag has been triggered with code', 'wp-graphql-woocommerce' ) : '' ),
'type' => 'checkbox',
'value' => defined( 'NO_QL_SESSION_HANDLER' ) ? 'off' : woographql_setting( 'enable_ql_session_handler_on_rest', 'off' ),
'disabled' => defined( 'NO_QL_SESSION_HANDLER' ),
],
[
'name' => 'enable_unsupported_product_type',
'label' => __( 'Enable Unsupported types', 'wp-graphql-woocommerce' ),
+2 -1
View File
@@ -199,6 +199,7 @@ class Type_Registry {
Mutation\Tax_Rate_Create::register_mutation();
Mutation\Tax_Rate_Delete::register_mutation();
Mutation\Tax_Rate_Update::register_mutation();
Mutation\Update_Session::register_mutation();
Mutation\Session_Delete::register_mutation();
Mutation\Session_Update::register_mutation();
}
}
+21 -1
View File
@@ -82,6 +82,26 @@ class WooCommerce_Filters {
return woographql_setting( "{$field}_nonce_param", null );
}
/**
* Returns true if the session handler should be loaded.
*
* @return boolean
*/
public static function should_load_session_handler() {
switch ( true ) {
case \WPGraphQL\Router::is_graphql_http_request():
//phpcs:disable
case 'on' === woographql_setting( 'enable_ql_session_handler_on_ajax', 'off' )
&& ( ! empty( $_GET['wc-ajax'] ) || defined( 'WC_DOING_AJAX' ) ):
//phpcs:enable
case 'on' === woographql_setting( 'enable_ql_session_handler_on_rest', 'off' )
&& ( defined( 'REST_REQUEST' ) && REST_REQUEST ):
return true;
default:
return false;
}
}
/**
* WooCommerce Session Handler callback
*
@@ -89,7 +109,7 @@ class WooCommerce_Filters {
* @return string
*/
public static function woocommerce_session_handler( $session_class ) {
if ( \WPGraphQL\Router::is_graphql_http_request() ) {
if ( self::should_load_session_handler() ) {
$session_class = '\WPGraphQL\WooCommerce\Utils\QL_Session_Handler';
} elseif ( WooGraphQL::auth_router_is_enabled() ) {
require_once get_includes_directory() . 'utils/class-protected-router.php';
+2 -1
View File
@@ -343,6 +343,8 @@ if ( ! class_exists( '\WPGraphQL\WooCommerce\WP_GraphQL_WooCommerce' ) ) :
require $include_directory_path . 'mutation/class-review-update.php';
require $include_directory_path . 'mutation/class-payment-method-delete.php';
require $include_directory_path . 'mutation/class-payment-method-set-default.php';
require $include_directory_path . 'mutation/class-session-delete.php';
require $include_directory_path . 'mutation/class-session-update.php';
require $include_directory_path . 'mutation/class-shipping-zone-create.php';
require $include_directory_path . 'mutation/class-shipping-zone-delete.php';
require $include_directory_path . 'mutation/class-shipping-zone-locations-clear.php';
@@ -356,7 +358,6 @@ if ( ! class_exists( '\WPGraphQL\WooCommerce\WP_GraphQL_WooCommerce' ) ) :
require $include_directory_path . 'mutation/class-tax-rate-create.php';
require $include_directory_path . 'mutation/class-tax-rate-delete.php';
require $include_directory_path . 'mutation/class-tax-rate-update.php';
require $include_directory_path . 'mutation/class-update-session.php';
// Include connection class/function files.
require $include_directory_path . 'connection/wc-cpt-connection-args.php';
+2
View File
@@ -106,6 +106,8 @@ class Cart_Add_Fee {
// Add cart fee.
\WC()->cart->add_fee( ...$cart_fee_args );
do_action( 'woographql_update_session', true );
// Return payload.
return [ 'id' => \sanitize_title( $input['name'] ) ];
};
@@ -76,6 +76,8 @@ class Cart_Apply_Coupon {
$reason = '';
// If validate and successful applied to cart, return payload.
if ( Cart_Mutation::validate_coupon( $input['code'], $reason ) && \WC()->cart->apply_coupon( $input['code'] ) ) {
do_action( 'woographql_update_session', true );
return [ 'code' => $input['code'] ];
}
+2
View File
@@ -94,6 +94,8 @@ class Cart_Empty {
*/
do_action( 'graphql_woocommerce_after_empty_cart', $cloned_cart, $input, $context, $info );
do_action( 'woographql_update_session', true );
return [ 'cart' => $cloned_cart ];
};
}
+2
View File
@@ -270,6 +270,8 @@ class Cart_Fill {
// Recalculate totals.
\WC()->cart->calculate_totals();
do_action( 'woographql_update_session', true );
// Return payload.
return compact(
'added',
@@ -86,6 +86,8 @@ class Cart_Remove_Coupons {
}
}
do_action( 'woographql_update_session', true );
// Return payload.
return [ 'cart' => \WC()->cart ];
};
@@ -96,6 +96,8 @@ class Cart_Remove_Items {
}
}
do_action( 'woographql_update_session', true );
// Return payload.
return [ 'items' => $cart_items ];
};
@@ -82,6 +82,8 @@ class Cart_Restore_Items {
$cart_items = Cart_Mutation::retrieve_cart_items( $input, $context, $info, 'restore' );
do_action( 'woographql_update_session', true );
// Return payload.
return [ 'items' => $cart_items ];
};
@@ -165,6 +165,8 @@ class Cart_Update_Item_Quantities {
$info
);
do_action( 'woographql_update_session', true );
return [
'removed' => $removed_items,
'updated' => array_keys( $updated ),
@@ -78,6 +78,8 @@ class Cart_Update_Shipping_Method {
// Recalculate totals.
\WC()->cart->calculate_totals();
do_action( 'woographql_update_session', true );
return [];
};
}
@@ -153,6 +153,10 @@ class Customer_Update {
// Save customer and get customer ID.
$customer->save();
if ( $session_only ) {
do_action( 'woographql_update_session', true );
}
// Return payload.
return ! empty( $payload ) ? $payload : [ 'id' => 'session' ];
};
@@ -0,0 +1,95 @@
<?php
/**
* Mutation - forgetSession
*
* Registers mutation for deleting sessions from the DB.
*
* @package WPGraphQL\WooCommerce\Mutation
* @since TBD
*/
namespace WPGraphQL\WooCommerce\Mutation;
use WPGraphQL\WooCommerce\Data\Mutation\Cart_Mutation;
/**
* Class - Session_Delete
*/
class Session_Delete {
/**
* Registers mutation
*
* @return void
*/
public static function register_mutation() {
register_graphql_mutation(
'forgetSession',
[
'inputFields' => [],
'outputFields' => self::get_output_fields(),
'mutateAndGetPayload' => self::mutate_and_get_payload(),
]
);
}
/**
* Defines the mutation output field configuration
*
* @return array
*/
public static function get_output_fields() {
return [
'session' => [
'type' => [ 'list_of' => 'MetaData' ],
'resolve' => static function ( $payload ) {
// Guard against missing session data.
if ( empty( $payload['session'] ) ) {
return [];
}
// Prepare session data.
$session = [];
foreach ( $payload['session'] as $key => $value ) {
$meta = new \stdClass();
$meta->id = null;
$meta->key = $key;
$meta->value = maybe_unserialize( $value );
$session[] = $meta;
}
return $session;
},
],
];
}
/**
* Defines the mutation data modification closure.
*
* @return callable
*/
public static function mutate_and_get_payload() {
return static function ( $input ) {
Cart_Mutation::check_session_token();
/**
* Session handler.
*
* @var \WPGraphQL\WooCommerce\Utils\QL_Session_Handler $session
*/
$session = \WC()->session;
// Get session data.
$session_data = $session->get_session_data();
do_action( 'woographql_before_forget_session', $session_data, $input, $session );
// Clear session data.
$session->forget_session();
do_action( 'woographql_after_forget_session', $session_data, $input, $session );
// Return payload.
return [ 'session' => $session_data ];
};
}
}
@@ -15,9 +15,9 @@ use WPGraphQL\WooCommerce\Data\Mutation\Cart_Mutation;
use WPGraphQL\WooCommerce\Model\Customer;
/**
* Class - Update_Session
* Class - Session_Update
*/
class Update_Session {
class Session_Update {
/**
* Registers mutation
*
+100 -61
View File
@@ -9,6 +9,7 @@
namespace WPGraphQL\WooCommerce\Utils;
use WC_Session_Handler;
use WPGraphQL\Router;
use WPGraphQL\WooCommerce\Vendor\Firebase\JWT\JWT;
use WPGraphQL\WooCommerce\Vendor\Firebase\JWT\Key;
@@ -48,12 +49,20 @@ class QL_Session_Handler extends WC_Session_Handler {
*/
protected $_issuing_new_token = false; // @codingStandardsIgnoreLine
/**
* True when a new session cookie has been issued.
*
* @var bool $_issuing_new_cookie
*/
protected $_issuing_new_cookie = false; // @codingStandardsIgnoreLine
/**
* Constructor for the session class.
*/
public function __construct() {
parent::__construct();
$this->_token = apply_filters( 'graphql_woocommerce_cart_session_http_header', 'woocommerce-session' );
$this->_table = $GLOBALS['wpdb']->prefix . 'woocommerce_sessions';
}
/**
@@ -101,20 +110,35 @@ class QL_Session_Handler extends WC_Session_Handler {
Session_Transaction_Manager::get( $this );
/**
* Necessary since Session_Transaction_Manager applies to the reference.
* Necessary since Session_Transaction_Manager applies to the reference.
*
* @var self $this
*/
add_action( 'woocommerce_set_cart_cookies', [ $this, 'set_customer_session_token' ], 10 );
add_action( 'woographql_update_session', [ $this, 'set_customer_session_token' ], 10 );
add_action( 'shutdown', [ $this, 'save_data' ] );
add_action( 'wp_logout', [ $this, 'destroy_session' ] );
if ( ! is_user_logged_in() ) {
add_filter( 'nonce_user_logged_out', [ $this, 'maybe_update_nonce_user_logged_out' ], 10, 2 );
if ( Router::is_graphql_http_request() ) {
add_action( 'woocommerce_set_cart_cookies', [ $this, 'set_customer_session_token' ], 10 );
add_action( 'woographql_update_session', [ $this, 'set_customer_session_token' ], 10 );
add_action( 'shutdown', [ $this, 'save_data' ] );
} else {
add_action( 'woocommerce_set_cart_cookies', [ $this, 'set_customer_session_cookie' ], 10 );
add_action( 'shutdown', [ $this, 'save_data' ], 20 );
add_action( 'wp_logout', [ $this, 'destroy_session' ] );
if ( ! is_user_logged_in() ) {
add_filter( 'nonce_user_logged_out', [ $this, 'maybe_update_nonce_user_logged_out' ], 10, 2 );
}
}
}
/**
* Mark the session as dirty.
*
* To trigger a save of the session data.
*
* @return void
*/
public function mark_dirty() {
$this->_dirty = true;
}
/**
* Setup token and customer ID.
*
@@ -123,6 +147,10 @@ class QL_Session_Handler extends WC_Session_Handler {
* @return void
*/
public function init_session_token() {
/**
* @var object{ iat: int, exp: int, data: object{ customer_id: string } }|false|\WP_Error $token
*/
$token = $this->get_session_token();
// Process existing session if not expired or invalid.
@@ -147,7 +175,9 @@ class QL_Session_Handler extends WC_Session_Handler {
// @phpstan-ignore-next-line
$this->save_data( $guest_session_id );
$this->set_customer_session_token( true );
Router::is_graphql_http_request()
? $this->set_customer_session_token( true )
: $this->set_customer_session_cookie( true );
}
// Update session expiration on each action.
@@ -155,19 +185,23 @@ class QL_Session_Handler extends WC_Session_Handler {
if ( $token->exp < $this->_session_expiration ) {
$this->update_session_timestamp( (string) $this->_customer_id, $this->_session_expiration );
}
} else {
} elseif ( is_wp_error( $token ) ) {
add_filter(
'graphql_woocommerce_session_token_errors',
static function ( $errors ) use ( $token ) {
$errors = $token->get_error_message();
return $errors;
}
);
}
// If token invalid throw warning.
if ( is_wp_error( $token ) ) {
add_filter(
'graphql_woocommerce_session_token_errors',
static function ( $errors ) use ( $token ) {
$errors = $token->get_error_message();
return $errors;
}
);
}
$start_new_session = ! $token || is_wp_error( $token );
if ( ! $start_new_session ) {
return;
}
// Distribute new session token on GraphQL requests, otherwise distribute a new session cookie.
if ( Router::is_graphql_http_request() ) {
// Start new session.
$this->set_session_expiration();
@@ -175,7 +209,9 @@ class QL_Session_Handler extends WC_Session_Handler {
$this->_customer_id = is_user_logged_in() ? get_current_user_id() : $this->generate_customer_id();
$this->_data = $this->get_session_data();
$this->set_customer_session_token( true );
}//end if
} else {
$this->init_session_cookie();
}
}
/**
@@ -259,13 +295,31 @@ class QL_Session_Handler extends WC_Session_Handler {
return apply_filters( 'graphql_woocommerce_cart_session_header', $session_header );
}
/**
* Determine if a JWT is being sent in the page response.
*
* @return bool
*/
public function sending_token() {
return $this->_has_token || $this->_issuing_new_token;
}
/**
* Determine if a HTTP cookie is being sent in the page response.
*
* @return bool
*/
public function sending_cookie() {
return $this->_has_cookie || $this->_issuing_new_cookie;
}
/**
* Creates JSON Web Token for customer session.
*
* @return false|string
*/
public function build_token() {
if ( empty( $this->_session_issued ) ) {
if ( empty( $this->_session_issued ) || ! $this->sending_token() ) {
return false;
}
@@ -362,14 +416,28 @@ class QL_Session_Handler extends WC_Session_Handler {
}
}
/**
* {@inheritDoc}
*
* @return void
*/
public function set_customer_session_cookie( $set ) {
parent::set_customer_session_cookie( $set );
if ( $set ) {
$this->_issuing_new_cookie = true;
}
}
/**
* Return true if the current user has an active session, i.e. a cookie to retrieve values.
*
* @return bool
*/
public function has_session() {
// @codingStandardsIgnoreLine.
return $this->_issuing_new_token || $this->_has_token || is_user_logged_in();
return $this->_issuing_new_token || $this->_has_token || parent::has_session();
}
/**
@@ -379,34 +447,16 @@ class QL_Session_Handler extends WC_Session_Handler {
*/
public function set_session_expiration() {
$this->_session_issued = time();
// 14 Days.
$this->_session_expiration = apply_filters(
// 47 hours.
$this->_session_expiring = apply_filters( 'wc_session_expiring', $this->_session_issued + ( 60 * 60 * 47 ) ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
// 48 hours.
$this->_session_expiration = apply_filters( 'wc_session_expiration', $this->_session_issued + ( 60 * 60 * 48 ) ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
$this->_session_expiration = apply_filters_deprecated(
'graphql_woocommerce_cart_session_expire',
// Seconds * Minutes * Hours * Days.
$this->_session_issued + ( 60 * 60 * 24 * 14 )
[ $this->_session_expiration ],
'TBD',
'wc_session_expiration'
);
// 13 Days.
$this->_session_expiring = $this->_session_expiration - ( 60 * 60 * 24 );
}
/**
* Forget all session data without destroying it.
*
* @return void
*/
public function forget_session() {
if ( isset( $this->_token_to_be_sent ) ) {
unset( $this->_token_to_be_sent );
}
wc_empty_cart();
$this->_data = [];
$this->_dirty = false;
// Start new session.
$this->set_session_expiration();
// Get Customer ID.
$this->_customer_id = is_user_logged_in() ? get_current_user_id() : $this->generate_customer_id();
}
/**
@@ -444,17 +494,6 @@ class QL_Session_Handler extends WC_Session_Handler {
}
}
/**
* Noop for \WC_Session_Handler method.
*
* Prevents potential crticial errors when calling this method.
*
* @param bool $set Should the session cookie be set.
*
* @return void
*/
public function set_customer_session_cookie( $set ) {}
/**
* Returns "client_session_id". "client_session_id_expiration" is used
* to keep "client_session_id" as fresh as possible.
@@ -61,7 +61,14 @@ class Session_Transaction_Manager {
add_action( 'graphql_before_resolve_field', [ $this, 'update_transaction_queue' ], 10, 4 );
add_action( 'graphql_mutation_response', [ $this, 'pop_transaction_id' ], 20, 6 );
add_action( 'woographql_session_transaction_complete', [ $this->session_handler, 'save_if_dirty' ], 10 );
add_action( 'woocommerce_add_to_cart', [ $this->session_handler, 'mark_dirty' ] );
add_action( 'woocommerce_cart_item_removed', [ $this->session_handler, 'mark_dirty' ] );
add_action( 'woocommerce_cart_item_restored', [ $this->session_handler, 'mark_dirty' ] );
add_action( 'woocommerce_cart_item_set_quantity', [ $this->session_handler, 'mark_dirty' ] );
add_action( 'woocommerce_cart_emptied', [ $this->session_handler, 'mark_dirty' ] );
}
/**
@@ -100,6 +107,7 @@ class Session_Transaction_Manager {
'updateShippingMethod',
'updateCustomer',
'updateSession',
'forgetSession',
]
);
}
+2 -2
View File
@@ -15,9 +15,9 @@ class CartFactory {
/**
* Add products to the cart.
*
* @param array ...$products Product to be added to the cart.
* @param array<array<string, mixed>> ...$products Product to be added to the cart.
*
* @return array
* @return array<string>
*/
public function add( ...$products ) {
$keys = [];
@@ -96,12 +96,12 @@ class CartTransactionQueueCest {
);
// Retrieve JWT Authorization Token for later use.
$auth_token = $I->lodashGet( $success, 'login.authToken' );
$auth_token = $I->lodashGet( $success, 'data.login.authToken' );
// Retrieve session token. Add as "Session %s" in the woocommerce-session HTTP header to future requests
// so WooCommerce can identify the user session associated with actions made in the GraphQL requests.
// You can also retrieve the token from the "woocommerce-session" HTTP response header.
$initial_session_token = $I->lodashGet( $success, 'login.sessionToken' );
$initial_session_token = $I->lodashGet( $success, 'data.login.sessionToken' );
$headers = [
'Authorization' => "Bearer {$auth_token}",
+109 -3
View File
@@ -12,8 +12,19 @@ if ( ! defined( 'GRAPHQL_WOOCOMMERCE_SECRET_KEY' ) ) {
}
class QLSessionHandlerTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraphQLTestCase {
public function setUp(): void {
parent::setUp();
// before
unset( $_SERVER['HTTP_WOOCOMMERCE_SESSION'] );
$customer_cookie_key = apply_filters( 'woocommerce_cookie', 'wp_woocommerce_session_' . COOKIEHASH );
wc_setcookie( $customer_cookie_key, 0, time() - HOUR_IN_SECONDS );
unset( $_COOKIE[ $customer_cookie_key ] );
}
public function tearDown(): void {
unset( $_SERVER );
WC()->session->destroy_session();
// after
parent::tearDown();
@@ -27,7 +38,10 @@ class QLSessionHandlerTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGrap
$this->assertInstanceOf( QL_Session_Handler::class, $session );
}
public function test_init_session_token() {
public function test_init_on_graphql_request() {
// Simulate GraphQL HTTP Request.
add_filter( 'graphql_is_graphql_http_request', '__return_true' );
// Create session handler.
$session = new QL_Session_Handler();
@@ -35,7 +49,7 @@ class QLSessionHandlerTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGrap
$this->assertFalse( $session->has_session(), 'Shouldn\'t have a session yet' );
// Initialize session.
$session->init_session_token();
$session->init();
// Assert session has started.
$this->assertTrue( $session->has_session(), 'Should have session.' );
@@ -51,16 +65,99 @@ class QLSessionHandlerTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGrap
usleep( 1000000 );
// Initialize session token for next request.
$session->init_session_token();
remove_action( 'woocommerce_set_cart_cookies', [ $session, 'set_customer_session_token' ] );
remove_action( 'woographql_update_session', [ $session, 'set_customer_session_token' ] );
remove_action( 'shutdown', [ $session, 'save_data' ] );
// Create new session handler.
$session = new QL_Session_Handler();
$session->init();
$new_token = $session->build_token();
$decoded_new_token = JWT::decode( $new_token, new Key( GRAPHQL_WOOCOMMERCE_SECRET_KEY, 'HS256' ) );
// Assert new token is different than old token.
$this->assertNotEquals( $old_token, $new_token, 'New token should not match token from last request.' );
$this->assertGreaterThan( $decoded_old_token->exp, $decoded_new_token->exp );
// Assert customer ID match
$this->assertEquals( $decoded_old_token->data->customer_id, $decoded_new_token->data->customer_id );
}
public function test_init_on_non_graphql_request() {
// Create session handler.
$session = new QL_Session_Handler();
// Assert session hasn't started.
$this->assertFalse( $session->has_session(), 'Shouldn\'t have a session yet' );
// Initialize session.
$session->init();
// Add product to cart and start the session.
$this->factory->cart->add(
$this->factory->product->createSimple(),
$this->factory->product->createSimple(),
$this->factory->product->createSimple(),
$this->factory->product->createSimple(),
$this->factory->product->createSimple(),
$this->factory->product->createSimple(),
$this->factory->product->createSimple(),
);
// Assert session has started.
//$this->assertTrue( $session->sending_cookie(), 'Issuing new customer session cookie.' );
// Assert no tokens are being issued.
$this->assertFalse( $session->sending_token(), 'Should not be issuing a new customer token.' );
$this->assertFalse( $session->build_token(), 'Should not be issuing a new customer token.' );
}
public function test_init_on_non_graphql_request_with_session_token() {
// Simulate GraphQL HTTP Request.
add_filter( 'graphql_is_graphql_http_request', '__return_true' );
// Create session handler.
$session = new QL_Session_Handler();
// Assert session hasn't started.
$this->assertFalse( $session->has_session(), 'Shouldn\'t have a session yet' );
// Initialize session.
$session->init();
// Assert session has started.
$this->assertTrue( $session->has_session(), 'Should have session.' );
// Get token for future request.
$token_to_session = $session->build_token();
// Remove GraphQL HTTP Request filter to simulate normal request.
remove_filter( 'graphql_is_graphql_http_request', '__return_true' );
// Sent token to HTTP header to simulate a new request.
$_SERVER['HTTP_WOOCOMMERCE_SESSION'] = 'Session ' . $token_to_session;
// Create session handler.
$session = new QL_Session_Handler();
// Assert session hasn't started.
$this->assertFalse( $session->has_session(), 'Shouldn\'t have a session yet' );
// Initialize session.
$session->init();
// Assert session has started.
$this->assertTrue( $session->has_session(), 'Should have session.' );
// Assert tokens are being issued.
$this->assertNotFalse( $session->build_token() );
}
public function test_get_session_token() {
// Simulate GraphQL HTTP Request.
add_filter( 'graphql_is_graphql_http_request', '__return_true' );
// Create session handler.
$session = new QL_Session_Handler();
@@ -96,6 +193,9 @@ class QLSessionHandlerTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGrap
}
public function test_build_token() {
// Simulate GraphQL HTTP Request.
add_filter( 'graphql_is_graphql_http_request', '__return_true' );
// Create session handler.
$session = new QL_Session_Handler();
@@ -116,6 +216,9 @@ class QLSessionHandlerTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGrap
}
public function test_set_customer_session_token() {
// Simulate GraphQL HTTP Request.
add_filter( 'graphql_is_graphql_http_request', '__return_true' );
// Create session handler.
$session = new QL_Session_Handler();
@@ -131,6 +234,9 @@ class QLSessionHandlerTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGrap
}
public function test_forget_session() {
// Simulate GraphQL HTTP Request.
add_filter( 'graphql_is_graphql_http_request', '__return_true' );
// Create session handler.
$session = new QL_Session_Handler();
$session->init_session_token();
@@ -1,6 +1,6 @@
<?php
class UpdateSessionMutationTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraphQLTestCase {
class SessionMutationsTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraphQLTestCase {
public function testUpdateSessionMutation() {
// Create registered customer.
$registered = $this->factory->customer->create();
@@ -62,4 +62,71 @@ class UpdateSessionMutationTest extends \Tests\WPGraphQL\WooCommerce\TestCase\Wo
$this->assertQuerySuccessful( $response, $expected );
}
}
public function testForgetSessionMutation() {
// Create registered customer.
$registered = $this->factory->customer->create();
$this->loginAs( $registered );
// Add products to cart.
$this->factory->cart->add(
[
'product_id' => $this->factory->product->createSimple(),
'quantity' => 2,
],
[
'product_id' => $this->factory->product->createSimple(),
'quantity' => 1,
]
);
// Save session.
\WC()->session->save_data();
// Reinitialize session.
\WC()->session->init();
// Confirm cart has items.
$cart_query = '
query {
cart {
contents {
nodes {
key
}
}
}
}
';
$response = $this->graphql( [ 'query' => $cart_query ] );
$this->assertQuerySuccessful(
$response,
[ $this->expectedField( 'cart.contents.nodes', static::NOT_FALSY ) ]
);
// Forget session.
$query = 'mutation {
forgetSession(input: {}) {
session {
id
key
value
}
}
}';
$response = $this->graphql( compact( 'query' ) );
$this->assertQuerySuccessful( $response );
// Reinitialize session.
\WC()->session->init();
// Confirm cart is empty.
$response = $this->graphql( [ 'query' => $cart_query ] );
$this->assertQuerySuccessful(
$response,
[ $this->expectedField( 'cart.contents.nodes', static::IS_FALSY ) ]
);
}
}