Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optional raw response processing to query context #24

Merged
merged 16 commits into from
Sep 6, 2024
2 changes: 2 additions & 0 deletions inc/config/http-query-context/http-query-context.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ public function get_request_headers( array $input_variables ): array;
public function get_request_body( array $input_variables ): array|null;
public function get_query_name(): string;
public function get_query_runner(): QueryRunnerInterface;
public function is_collection(): bool;
public function process_response( string $raw_response_data, array $input_variables ): string;
}
8 changes: 8 additions & 0 deletions inc/config/query-context/query-context.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,12 @@ public function get_query_name(): string {
public function get_query_runner(): QueryRunnerInterface {
return new QueryRunner( $this );
}

public function is_collection(): bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the use case for this is_collection() method override? Maybe the response needs to be inspected? In that case, it seems like it should get passed the $response_data? Or removed

return $this->output_variables['is_collection'] ?? false;
}

public function process_response( string $raw_response_data, array $input_variables ): string {
return $raw_response_data;
}
}
25 changes: 16 additions & 9 deletions inc/config/query-runner/query-runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,22 +99,23 @@ public function execute( array $input_variables ): array|WP_Error {
}

// The body is a stream... if we need to read it in chunks, etc. we can do so here.
$response_data = $response->getBody()->getContents();
$raw_response_data = $response->getBody()->getContents();

$is_collection = $this->query_context->output_variables['is_collection'] ?? false;

if ( isset( $response_data['errors'][0]['message'] ) ) {
if ( isset( $raw_response_data['errors'][0]['message'] ) ) {
$logger = LoggerManager::instance();
$logger->warning( sprintf( 'Query error: %s', esc_html( $response_data['errors'][0]['message'] ) ) );
$logger->warning( sprintf( 'Query error: %s', esc_html( $raw_response_data['errors'][0]['message'] ) ) );
}

// Optionally process the raw response data using query context custom logic.
$response_data = $this->query_context->process_response( $raw_response_data, $input_variables );

// This method always returns an array, even if it's a single item. This
// ensures a consistent response shape. The requestor is expected to inspect
// is_collection and unwrap if necessary.
$results = $this->map_fields( $response_data, $is_collection );
$results = $this->map_fields( $response_data );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing arg $is_collection means that is_collection() gets called twice. Can we get it back to one call?


return [
'is_collection' => $is_collection,
'is_collection' => $this->query_context->is_collection(),
'metadata' => $this->query_context->get_metadata( $response, $results ),
'results' => $results,
];
Expand All @@ -126,6 +127,12 @@ private function get_field_value( array|string $field_value, string $default_val
: ( $field_value[0] ?? $default_value );

switch ( $field_type ) {
case 'base64':
return base64_decode( $field_value_single );

case 'html':
return $field_value_single;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I confirmed, as expected, core escaping happens further on in execution--somewhere closer to output.

Screenshot 2024-09-06 at 2 21 14 PM Screenshot 2024-09-06 at 2 21 01 PM

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

escapin late :)


case 'price':
return sprintf( '$%s', number_format( $field_value_single, 2 ) );

Expand All @@ -136,15 +143,15 @@ private function get_field_value( array|string $field_value, string $default_val
return $field_value_single;
}

private function map_fields( $response_data, $is_collection = false ): array|null {
private function map_fields( $response_data ): array|null {
$root = $response_data;
$output_variables = $this->query_context->output_variables;

if ( ! empty( $output_variables['root_path'] ) ) {
$json = new JsonObject( $root );
$root = $json->get( $output_variables['root_path'] );
} else {
$root = $is_collection ? $root : [ $root ];
$root = $this->query_context->is_collection() ? $root : [ $root ];
}

if ( empty( $root ) || empty( $output_variables['mappings'] ) ) {
Expand Down
17 changes: 6 additions & 11 deletions tests/inc/config/QueryRunnerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
use Psr\Http\Message\ResponseInterface;
use RemoteDataBlocks\Config\QueryRunner;
use RemoteDataBlocks\Config\QueryRunnerInterface;
use RemoteDataBlocks\Config\HttpQueryContext;
use RemoteDataBlocks\Config\HttpDatasourceConfig;
use RemoteDataBlocks\Config\QueryContext;
use RemoteDataBlocks\HttpClient;
use WP_Error;

Expand All @@ -21,6 +21,8 @@ class QueryRunnerTest extends TestCase {
protected function setUp(): void {
parent::setUp();

$this->http_client = $this->createMock( HttpClient::class );

$this->http_datasource = new class() implements HttpDatasourceConfig {
private $endpoint = 'https://api.example.com';
private $headers = [ 'Content-Type' => 'application/json' ];
Expand All @@ -46,15 +48,15 @@ public function set_headers( array $headers ): void {
}
};

$this->query_context = new class($this->http_datasource) implements HttpQueryContext {
$this->query_context = new class($this->http_datasource, $this->http_client) extends QueryContext {
private $http_datasource;
private $http_client;
private $request_method = 'GET';
private $request_body = [ 'query' => 'test' ];

public function __construct( HttpDatasourceConfig $http_datasource ) {
public function __construct( HttpDatasourceConfig $http_datasource, HttpClient $http_client ) {
$this->http_datasource = $http_datasource;
$this->http_client = new HttpClient();
$this->http_client = $http_client;
}

public function get_endpoint( array $input_variables = [] ): string {
Expand Down Expand Up @@ -89,10 +91,6 @@ public function get_query_runner(): QueryRunnerInterface {
return new QueryRunner( $this, $this->http_client );
}

public function set_http_client( HttpClient $http_client ): void {
$this->http_client = $http_client;
}

public function set_request_method( string $method ): void {
$this->request_method = $method;
}
Expand All @@ -103,9 +101,6 @@ public function set_request_body( array $body ): void {

public array $output_variables = [];
};

$this->http_client = $this->createMock( HttpClient::class );
$this->query_context->set_http_client( $this->http_client );
}

public function testExecuteSuccessfulRequest() {
Expand Down