Skip to content

Commit b4c5be8

Browse files
authored
Merge pull request #366 from wpengine/chore-wpgraphql-logging-fix-activation-de-activation-hooks
chore: Bug fixes for plugin activation and de-activation
2 parents c2dfe2c + 327e397 commit b4c5be8

File tree

12 files changed

+129
-161
lines changed

12 files changed

+129
-161
lines changed

plugins/wpgraphql-logging/activation.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99

1010
declare(strict_types=1);
1111

12+
use WPGraphQL\Logging\Plugin;
13+
1214
/**
1315
* Runs when the plugin is activated.
1416
*/
1517
function wpgraphql_logging_activation_callback(): void {
18+
Plugin::activate();
1619
do_action( 'wpgraphql_logging_activate' );
1720
}
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
<?php
2-
3-
declare(strict_types=1);
4-
52
/**
63
* Deactivation Hook
74
*
@@ -10,9 +7,14 @@
107
* @since 0.0.1
118
*/
129

10+
declare(strict_types=1);
11+
12+
use WPGraphQL\Logging\Plugin;
13+
1314
/**
1415
* Runs when the plugin is deactivated.
1516
*/
1617
function wpgraphql_logging_deactivation_callback(): void {
18+
Plugin::deactivate();
1719
do_action( 'wpgraphql_logging_deactivate' );
1820
}

plugins/wpgraphql-logging/phpcs.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<exclude-pattern>*/phpunit.xml*</exclude-pattern>
1616
<exclude-pattern>**/tests/**</exclude-pattern>
1717
<exclude-pattern>*/vendor/*</exclude-pattern>
18+
<exclude-pattern>*/src/Events/QueryEventLifecycle.php</exclude-pattern>
1819

1920
<!-- ===================================== -->
2021
<!-- CLI Arguments & Version Config -->

plugins/wpgraphql-logging/phpstan.neon.dist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@ parameters:
3030
paths:
3131
- wpgraphql-logging.php
3232
- src/
33+
excludePaths:
34+
- src/Events/QueryEventLifecycle.php

plugins/wpgraphql-logging/psalm.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@
1515
<file name="deactivation.php"/>
1616
<file name="vendor/autoload.php"/>
1717
<directory name="src"/>
18+
<ignoreFiles>
19+
<file name="src/Events/QueryEventLifecycle.php"/>
20+
</ignoreFiles>
1821
</projectFiles>
1922

23+
2024
<plugins>
2125
<pluginClass class="PsalmWordPress\Plugin"/>
2226
</plugins>

plugins/wpgraphql-logging/src/Events/QueryEventLifecycle.php

Lines changed: 90 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44

55
namespace WPGraphQL\Logging\Events;
66

7+
use GraphQL\Executor\ExecutionResult;
8+
use GraphQL\Server\OperationParams;
79
use Monolog\Level;
810
use WPGraphQL\Logging\Logger\LoggerService;
11+
use WPGraphQL\Request;
912

1013
/**
1114
* WPGraphQL Query Event Lifecycle -
1215
*
13-
* POC @TODO - Add pub/sub for query events.
16+
* Handles logging for GraphQL query lifecycle events.
1417
*
1518
* @package WPGraphQL\Logging
1619
*
@@ -29,7 +32,7 @@ class QueryEventLifecycle {
2932
*
3033
* @param \WPGraphQL\Logging\Logger\LoggerService $logger
3134
*/
32-
protected function __construct(readonly LoggerService $logger) {
35+
protected function __construct( readonly LoggerService $logger ) {
3336
}
3437

3538
/**
@@ -42,20 +45,26 @@ public static function init(): QueryEventLifecycle {
4245
self::$instance = new self( $logger );
4346
self::$instance->setup();
4447
}
48+
4549
return self::$instance;
4650
}
4751

4852
/**
4953
* Logs the pre-request event for a GraphQL query.
54+
* This method is hooked into 'do_graphql_request'.
5055
*
51-
* @param string $query The GraphQL query.
52-
* @param mixed $variables The variables for the query.
53-
* @param string $operation_name The name of the operation.
56+
* @param string $query The GraphQL query string.
57+
* @param string|null $operation_name The name of the operation. Made nullable.
58+
* @param array|null $variables The variables for the query. Made nullable.
5459
*/
55-
public function log_pre_request( $query, $variables, $operation_name ): void {
56-
60+
public function log_pre_request( string $query, ?string $operation_name, ?array $variables ): void {
5761
try {
58-
$context = [];
62+
$context = [
63+
'query' => $query,
64+
'variables' => $variables,
65+
'operation_name' => $operation_name,
66+
];
67+
5968
$context = apply_filters( 'wpgraphql_logging_pre_request_context', $context, $query, $variables, $operation_name );
6069
$level = apply_filters( 'wpgraphql_logging_pre_request_level', Level::Info, $query, $variables, $operation_name );
6170
$this->logger->log( $level, 'WPGraphQL Incoming Request', $context );
@@ -67,34 +76,93 @@ public function log_pre_request( $query, $variables, $operation_name ): void {
6776

6877
/**
6978
* Logs the post-request event for a GraphQL query.
79+
* This method is now hooked into 'graphql_after_execute'.
7080
*
71-
* @param mixed $response The response from the GraphQL request.
72-
* @param mixed $result The result of the GraphQL request.
73-
* @param string $operation_name The name of the operation.
74-
* @param string $query The GraphQL query.
75-
* @param array<string, mixed> $variables The variables for the query.
81+
* @param \GraphQL\Executor\ExecutionResult|array<int, \GraphQL\Executor\ExecutionResult> $response The GraphQL execution result(s).
82+
* This can be a single ExecutionResult object or an array of them for batch requests.
83+
* @param \WPGraphQL\Request $request_instance The WPGraphQL Request instance.
7684
*/
77-
public function log_post_request( $response, $result, string $operation_name, string $query, array $variables ): void {
85+
public function log_post_request( $response, Request $request_instance ): void {
86+
// Extract relevant data from the WPGraphQL Request instance
87+
$params = $request_instance->get_params(); // Can be OperationParams or array of OperationParams
88+
$query = null;
89+
$operation_name = null;
90+
$variables = null;
91+
$status_code = 200; // Default success status
92+
93+
// Handle single or batch requests to get query details
94+
if ( $params instanceof OperationParams ) {
95+
$query = $params->query;
96+
$operation_name = $params->operation;
97+
$variables = $params->variables;
98+
} elseif ( is_array( $params ) && ! empty( $params[0] ) && $params[0] instanceof OperationParams ) {
99+
$query = $params[0]->query;
100+
$operation_name = $params[0]->operation;
101+
$variables = $params[0]->variables;
102+
}
103+
104+
// Determine status code if available (WPGraphQL Router sets this)
105+
if ( class_exists( '\WPGraphQL\Router' ) && property_exists( '\WPGraphQL\Router', '$http_status_code' ) ) {
106+
$status_code = \WPGraphQL\Router::$http_status_code;
107+
}
108+
109+
// Extract data and errors from the ExecutionResult object(s)
110+
$response_data = null;
111+
$response_errors = null;
112+
113+
if ( $response instanceof ExecutionResult ) {
114+
$response_data = $response->data;
115+
$response_errors = $response->errors;
116+
} elseif ( is_array( $response ) && ! empty( $response[0] ) && $response[0] instanceof ExecutionResult ) {
117+
// For batch requests, aggregate data/errors from all results
118+
$response_data = array_map( static fn( $res ) => $res->data, $response );
119+
$response_errors = array_reduce( $response, static fn( $carry, $res ) => array_merge( $carry, $res->errors ?? [] ), [] );
120+
if ( empty( $response_errors ) ) {
121+
$response_errors = null; // Ensure it's null if no errors
122+
}
123+
}
124+
78125

79126
try {
80-
$context = [];
127+
$context = [
128+
'query' => $query,
129+
'operation_name' => $operation_name,
130+
'variables' => $variables,
131+
'status_code' => $status_code,
132+
'response_data' => $response_data,
133+
'response_errors' => $response_errors,
134+
];
81135
$level = Level::Info;
82-
$context = apply_filters( 'wpgraphql_logging_post_request_context', $context, $response, $result, $operation_name, $query, $variables );
83-
$level = apply_filters( 'wpgraphql_logging_post_request_level', $level, $response, $result, $operation_name, $query, $variables );
136+
137+
// Apply filters for context and level
138+
$context = apply_filters( 'wpgraphql_logging_post_request_context', $context, $response, $request_instance );
139+
$level = apply_filters( 'wpgraphql_logging_post_request_level', $level, $response, $request_instance );
140+
84141
$this->logger->log( $level, 'WPGraphQL Outgoing Response', $context );
142+
143+
// Log errors specifically if present in the response
144+
if ( ! empty( $response_errors ) ) {
145+
$this->logger->error(
146+
'GraphQL query completed with errors.',
147+
[
148+
'query' => $query,
149+
'operation_name' => $operation_name,
150+
'status_code' => $status_code,
151+
'errors' => array_map( static fn( $error ) => $error->getMessage(), $response_errors ), // Extract message from error object
152+
'full_errors' => $response_errors, // Include full error details for debugging
153+
]
154+
);
155+
}
85156
} catch ( \Throwable $e ) {
86157
// @TODO - Handle logging errors gracefully.
87-
error_log( 'Error in log_post_request: ' . $e->getMessage() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
158+
error_log( 'Error in log_post_request: ' . $e->getMessage() . ' in ' . $e->getFile() . ' on line ' . $e->getLine() );
88159
}
89160
}
90161

91162
/**
92163
* Register actions and filters.
93164
*/
94165
protected function setup(): void {
95-
96-
// @TODO: Update POC and use pub/sub for query events.
97-
98166
/**
99167
* @psalm-suppress HookNotFound
100168
*/
@@ -103,6 +171,6 @@ protected function setup(): void {
103171
/**
104172
* @psalm-suppress HookNotFound
105173
*/
106-
add_action( 'graphql_process_http_request_response', [ $this, 'log_post_request' ], 10, 5 );
174+
add_action( 'graphql_after_execute', [ $this, 'log_post_request' ], 10, 2 );
107175
}
108176
}

plugins/wpgraphql-logging/src/Hooks/.gitkeep

Whitespace-only changes.

plugins/wpgraphql-logging/src/Hooks/PluginHooks.php

Lines changed: 0 additions & 57 deletions
This file was deleted.

plugins/wpgraphql-logging/src/Logger/LoggerService.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class LoggerService {
4444
/**
4545
* The instance of the logger based off the channel name.
4646
*
47-
* @var array<LoggerService>
47+
* @var array<\WPGraphQL\Logging\Logger\LoggerService>
4848
*/
4949
protected static array $instances = [];
5050

@@ -90,16 +90,16 @@ public static function get_instance(
9090
?array $processors = null,
9191
?array $default_context = null
9292
): LoggerService {
93-
if ( isset(self::$instances[$channel]) ) {
94-
return self::$instances[$channel];
93+
if ( isset( self::$instances[ $channel ] ) ) {
94+
return self::$instances[ $channel ];
9595
}
9696

9797
$processors = $processors ?? self::get_default_processors();
9898
$handlers = $handlers ?? self::get_default_handlers();
9999
$default_context = $default_context ?? self::get_default_context();
100100

101-
self::$instances[$channel] = new self( $channel, $handlers, $processors, $default_context );
102-
return self::$instances[$channel];
101+
self::$instances[ $channel ] = new self( $channel, $handlers, $processors, $default_context );
102+
return self::$instances[ $channel ];
103103
}
104104

105105
/**

plugins/wpgraphql-logging/src/Plugin.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace WPGraphQL\Logging;
66

77
use WPGraphQL\Logging\Events\QueryEventLifecycle;
8-
use WPGraphQL\Logging\Hooks\PluginHooks;
8+
use WPGraphQL\Logging\Logger\Database\DatabaseEntity;
99

1010
/**
1111
* Plugin class for WPGraphQL Logging.
@@ -51,10 +51,24 @@ public static function init(): self {
5151
* Initialize the plugin admin, frontend & api functionality.
5252
*/
5353
public function setup(): void {
54-
PluginHooks::init();
5554
QueryEventLifecycle::init();
5655
}
5756

57+
/**
58+
* Activation callback for the plugin.
59+
*/
60+
public static function activate(): void {
61+
DatabaseEntity::create_table();
62+
}
63+
64+
/**
65+
* Deactivation callback for the plugin.
66+
*/
67+
public static function deactivate(): void {
68+
// @TODO: Add configuration to determine if the table should be dropped on deactivation.
69+
DatabaseEntity::drop_table();
70+
}
71+
5872
/**
5973
* Throw error on object clone.
6074
* The whole idea of the singleton design pattern is that there is a single object

0 commit comments

Comments
 (0)