mirror of
https://github.com/wp-graphql/wp-graphql-woocommerce.git
synced 2026-08-14 12:53:44 +02:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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%'
|
||||
|
||||
@@ -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' ),
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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'] ];
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 ];
|
||||
};
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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
|
||||
*
|
||||
@@ -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',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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}",
|
||||
|
||||
@@ -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 ) ]
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user