Skip to content

Commit

Permalink
Merge commit '1e11d65f58e441451ec3535e0053fca7f16f6248' into release/…
Browse files Browse the repository at this point in the history
…v0.1.2

# Conflicts:
#	composer.lock
  • Loading branch information
jasonbahl committed Aug 10, 2022
2 parents 19d2cfd + 1e11d65 commit 1b4050e
Show file tree
Hide file tree
Showing 13 changed files with 423 additions and 323 deletions.
185 changes: 94 additions & 91 deletions composer.lock

Large diffs are not rendered by default.

Binary file added pr152-wp-graphql-smart-cache.zip
Binary file not shown.
9 changes: 1 addition & 8 deletions src/Admin/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ function () {
if ( $current_setting !== $value ) {
// Action for those listening to purge_all
do_action( 'wpgraphql_cache_purge_all' );

// Purge the local cache results if enabled
$cache_object = new Results();
$cache_object->purge_all();
}
}
return $value;
Expand Down Expand Up @@ -180,10 +176,7 @@ function () {
// Trigger action when cache purge_all is invoked
do_action( 'wpgraphql_cache_purge_all' );

$cache_object = new Results();
if ( true === $cache_object->purge_all() ) {
return gmdate( 'D, d M Y H:i T' );
}
return gmdate( 'D, d M Y H:i T' );
}

return $existing_purge_all_time;
Expand Down
53 changes: 8 additions & 45 deletions src/Cache/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class Collection extends Query {

// initialize the cache collection
public function init() {
add_action( 'graphql_return_response', [ $this, 'save_query_mapping_cb' ], 10, 7 );
add_action( 'graphql_return_response', [ $this, 'save_query_mapping_cb' ], 10, 8 );
add_filter( 'pre_graphql_execute_request', [ $this, 'before_executing_query_cb' ], 10, 2 );
add_filter( 'graphql_dataloader_get_model', [ $this, 'data_loaded_process_cb' ], 10, 1 );

Expand Down Expand Up @@ -343,17 +343,6 @@ public function node_key( $id ) {
return 'node:' . $id;
}

/**
* When save or retrieve urls for a specific Unique identifier for this request for use in the collection map
*
* @param string $id Id for the node
*
* @return string unique id for this request
*/
public function url_key( $id ) {
return 'url:' . $id;
}

/**
* @param string $key The identifier to the list
* @param string $content to add
Expand Down Expand Up @@ -381,18 +370,6 @@ public function retrieve_nodes( $id ) {
return $this->get( $key );
}

/**
* Get the list of urls associated with the content/node/list id
*
* @param mixed|string|int $id The content node identifier
*
* @return array The unique list of content stored
*/
public function retrieve_urls( $id ) {
$key = $this->url_key( $id );
return $this->get( $key );
}

/**
* When a query response is being returned to the client, build map for each item and this
* query/queryId That way we will know what to invalidate on data change.
Expand All @@ -405,7 +382,8 @@ public function retrieve_urls( $id ) {
* @param string $operation The name of the Operation
* @param string $query The query string
* @param array $variables The variables for the query
* @param Request The WPGraphQL Request object
* @param Request $request The WPGraphQL Request object
* @param string|null $query_id The query id that GraphQL executed
*
* @return void
*/
Expand All @@ -416,34 +394,19 @@ public function save_query_mapping_cb(
$operation,
$query,
$variables,
$request
$request,
$query_id
) {
$request_key = $this->build_key( $request->params->queryId, $request->params->query, $request->params->variables, $request->params->operation );
$request_key = $this->build_key( $query_id, $query, $variables, $operation );

// Only store mappings of urls when it's a GET request
$map_the_url = false;
if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'GET' === $_SERVER['REQUEST_METHOD'] ) {
$map_the_url = true;
}

// We don't want POSTs during mutations or nothing on the url. cause it'll purge /graphql*
if ( $map_the_url && ! empty( $_SERVER['REQUEST_URI'] ) ) {
//phpcs:ignore
$url_to_save = wp_unslash( $_SERVER['REQUEST_URI'] );

// Save the url this query request came in on, so we can purge it later when something changes
$urls = $this->store_content( $this->url_key( $request_key ), $url_to_save );

//phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.PHP.DevelopmentFunctions.error_log_print_r
error_log( "Graphql Save Urls: $request_key " . print_r( $urls, 1 ) );
}
do_action( 'wpgraphql_cache_save_request', $request_key, $query_id, $query, $variables, $operation, $this->runtime_nodes, $this->list_types );

// Save/add the node ids for this query. When one of these change in the future, we can purge the query
foreach ( $this->runtime_nodes as $node_id ) {
$this->store_content( $this->node_key( $node_id ), $request_key );
}

// For each connection resolver, store the url key
// For each connection resolver, store the list types associated with this graphql query request
if ( ! empty( $this->list_types ) && is_array( $this->list_types ) ) {
$this->list_types = array_unique( $this->list_types );
foreach ( $this->list_types as $type_name ) {
Expand Down
6 changes: 3 additions & 3 deletions src/Cache/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ public function build_key( $query_id, $query, $variables = null, $operation = nu
$user = wp_get_current_user();

$parts = [
'query' => $query,
'variables' => $variables,
'operation' => $operation,
'query' => $query ?: null,
'variables' => $variables ?: null,
'operation' => $operation ?: null,
'user' => $user->ID,
];

Expand Down
142 changes: 100 additions & 42 deletions src/Cache/Results.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,18 @@ class Results extends Query {
const GLOBAL_DEFAULT_TTL = 600;

/**
* The cache key for the executed GraphQL Document
* Indicator of the GraphQL Query execution cached or not.
*
* @var string
* @array bool
*/
protected $cache_key = '';

/**
* The cached response of a GraphQL Query execution. False if it doesn't exist.
*
* @var mixed|bool|array|object
*/
protected $cached_result;
protected $is_cached = [];

public function init() {
$this->cached_result = false;

add_filter( 'pre_graphql_execute_request', [ $this, 'get_query_results_from_cache_cb' ], 10, 2 );
add_action( 'graphql_return_response', [ $this, 'save_query_results_to_cache_cb' ], 10, 7 );
add_action( 'graphql_return_response', [ $this, 'save_query_results_to_cache_cb' ], 10, 8 );
add_action( 'wpgraphql_cache_purge_nodes', [ $this, 'purge_nodes_cb' ], 10, 2 );
add_filter( 'graphql_request_results', [ $this, 'add_cache_key_to_response_extensions' ], 10, 1 );
add_action( 'wpgraphql_cache_purge_all', [ $this, 'purge_all_cb' ], 10, 0 );
add_filter( 'graphql_request_results', [ $this, 'add_cache_key_to_response_extensions' ], 10, 7 );

parent::init();
}
Expand All @@ -47,34 +39,50 @@ public function init() {
*
* @return string|false unique id for this request or false if query not provided
*/
public function the_results_key( $query_id, $query, $variables = null, $operation = null ) {
$this->cache_key = $this->build_key( $query_id, $query, $variables, $operation );
return $this->cache_key;
public function the_results_key( $query_id, $query, $variables = null, $operation_name = null ) {
return $this->build_key( $query_id, $query, $variables, $operation_name );
}


/**
* Add a message to the extensions when a GraphQL request is returned from the GraphQL Object Cache
*
* @param mixed|array|object $response The response of the GraphQL Request
* @param WPSchema $schema The schema object for the root query
* @param string $operation The name of the operation
* @param string $query The query that GraphQL executed
* @param array|null $variables Variables to passed to your GraphQL request
* @param Request $request Instance of the Request
* @param string|null $query_id The query id that GraphQL executed
*
* @return array|mixed
*/
public function add_cache_key_to_response_extensions( $response ) {
$message = [];

// if there's no cache key, or there is no cached_result return the response as-is
if ( ! empty( $this->cache_key ) && ! empty( $this->cached_result ) ) {
$message = [
'message' => __( 'This response was not executed at run-time but has been returned from the GraphQL Object Cache', 'wp-graphql-smart-cache' ),
'cacheKey' => $this->cache_key,
];
}
public function add_cache_key_to_response_extensions(
$response,
$schema,
$operation_name,
$query_string,
$variables,
$request,
$query_id
) {
$key = $this->the_results_key( $query_id, $query_string, $variables, $operation_name );
if ( $key ) {
$message = [];

// If we know that the results were pulled from cache, add messaging
if ( isset( $this->is_cached[ $key ] ) && true === $this->is_cached[ $key ] ) {
$message = [
'message' => __( 'This response was not executed at run-time but has been returned from the GraphQL Object Cache', 'wp-graphql-smart-cache' ),
'cacheKey' => $key,
];
}

if ( is_array( $response ) ) {
$response['extensions']['graphqlSmartCache']['graphqlObjectCache'] = $message;
} if ( is_object( $response ) ) {
$response->extensions['graphqlSmartCache']['graphqlObjectCache'] = $message;
if ( is_array( $response ) ) {
$response['extensions']['graphqlSmartCache']['graphqlObjectCache'] = $message;
} if ( is_object( $response ) ) {
$response->extensions['graphqlSmartCache']['graphqlObjectCache'] = $message;
}
}

// return the modified response with the graphqlSmartCache message in the extensions output
Expand All @@ -91,41 +99,84 @@ public function add_cache_key_to_response_extensions( $response ) {
* @return mixed|array|object|null The response or null if not found in cache
*/
public function get_query_results_from_cache_cb( $result, $request ) {

// if caching is not enabled or the request is authenticated, bail early
// right now we're not supporting GraphQL cache for authenticated requests.
// Possibly in the future.
if ( ! Settings::caching_enabled() || is_user_logged_in() ) {
return $result;
}
$key = $this->the_results_key( $request->params->queryId, $request->params->query, $request->params->variables, $request->params->operation );

// Loop over each request and load the response. If any one are empty, not in cache, return so all get reloaded.
if ( is_array( $request->params ) ) {
$result = [];
foreach ( $request->params as $req ) {
//phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$response = $this->get_result( $req->queryId, $req->query, $req->variables, $req->operation );
// If any one is null, return all are null.
if ( null === $response ) {
return null;
}
$result[] = $response;
}
} else {
//phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$result = $this->get_result( $request->params->queryId, $request->params->query, $request->params->variables, $request->params->operation );
}
return $result;
}

/**
* Unique identifier for this request is normalized query string, operation and variables
*
* @param string $query_id queryId from the graphql query request
* @param string $query query string
* @param array $variables Variables sent with request or null
* @param string $operation Name of operation if specified on the request or null
*
* @return string|null The response or null if not found in cache
*/
public function get_result( $query_id, $query_string, $variables, $operation_name ) {
$key = $this->the_results_key( $query_id, $query_string, $variables, $operation_name );
if ( ! $key ) {
return null;
}

$this->cached_result = $this->get( $key );
$result = $this->get( $key );
if ( false === $result ) {
return null;
}

$this->is_cached[ $key ] = true;

return ( false === $this->cached_result ) ? null : $this->cached_result;
return $result;
}

/**
* When a query response is being returned to the client, build map for each item and this query/queryId
* That way we will know what to invalidate on data change.
*
* @param $filtered_response GraphQL\Executor\ExecutionResult
* @param $response GraphQL\Executor\ExecutionResult
* @param $request WPGraphQL\Request
* @param ExecutionResult $filtered_response The response after GraphQL Execution has been
* completed and passed through filters
* @param ExecutionResult $response The raw, unfiltered response of the GraphQL
* Execution
* @param Schema $schema The WPGraphQL Schema
* @param string $operation The name of the Operation
* @param string $query The query string
* @param array $variables The variables for the query
* @param Request $request The WPGraphQL Request object
* @param string|null $query_id The query id that GraphQL executed
*
* @return void
*/
public function save_query_results_to_cache_cb(
$filtered_response,
$response,
$schema,
$operation,
$operation_name,
$query,
$variables,
$request
$request,
$query_id
) {
// if caching is not enabled or the request is authenticated, bail early
// right now we're not supporting GraphQL cache for authenticated requests.
Expand All @@ -134,7 +185,7 @@ public function save_query_results_to_cache_cb(
return;
}

$key = $this->the_results_key( $request->params->queryId, $request->params->query, $request->params->variables, $request->params->operation );
$key = $this->the_results_key( $query_id, $query, $variables, $operation_name );
if ( ! $key ) {
return;
}
Expand All @@ -145,7 +196,7 @@ public function save_query_results_to_cache_cb(
if ( false === $cached_result ) {
$expiration = \get_graphql_setting( 'global_ttl', self::GLOBAL_DEFAULT_TTL, 'graphql_cache_section' );

$this->save( $key, $response, $expiration );
$this->save( $key, $filtered_response, $expiration );
}
}

Expand Down Expand Up @@ -177,4 +228,11 @@ public function purge_nodes_cb( $id, $nodes ) {
graphql_debug( 'Graphql delete nodes', [ 'nodes' => $nodes ] );
}
}

/**
* Purge the local cache results if enabled
*/
public function purge_all_cb() {
$this->purge_all();
}
}
21 changes: 21 additions & 0 deletions src/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Document {

public function init() {
add_filter( 'graphql_request_data', [ $this, 'graphql_query_contains_queryid_cb' ], 10, 2 );
add_filter( 'graphql_execute_query_params', [ $this, 'graphql_execute_query_params_cb' ], 10, 2 );

add_action( 'post_updated', [ $this, 'after_updated_cb' ], 10, 3 );

Expand Down Expand Up @@ -174,6 +175,26 @@ public function graphql_query_contains_queryid_cb( $parsed_body_params, $request
return $parsed_body_params;
}

/**
* During invoking 'graphql()', not as an http request, if queryId is present, look it up and return the query string
*
* @param string $query The graphql query sring.
* @param mixed|array|OperationParams| $params The graphql request params, containing queryId
*/
public function graphql_execute_query_params_cb( $query, $params ) {
if ( empty( $query ) ) {
//phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
if ( isset( $params->queryId ) ) {
//phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$query_id = $params->queryId;
} elseif ( isset( $params['queryId'] ) ) {
$query_id = $params['queryId'];
}
$query = $this->get( $query_id );
}
return $query;
}

/**
* If existing post is edited, verify query string in content is valid graphql
*
Expand Down
Loading

0 comments on commit 1b4050e

Please sign in to comment.