-
Notifications
You must be signed in to change notification settings - Fork 9
Adds HTTP Client Adapter #2
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
485cd6a
feat: starts client adapter work
JasonTheAdams 182e32a
Merge branch 'trunk' into http-adapter
JasonTheAdams 51a5130
Merge branch 'trunk' into http-adapter
JasonTheAdams c06c468
refactor: rename file to use PSR-4 autoloading
JasonTheAdams 845407a
chore: adds WordPress stubs to PHPStan
JasonTheAdams d0b9208
fix: corrects type issues
JasonTheAdams d7ef812
chore: cleans up phpstan file
JasonTheAdams f7ec38f
feat: adds nyholm/psr7 package for psr17 factory
JasonTheAdams 8010654
feat: adds HTTPlug discovery strategy
JasonTheAdams 4e0247f
feat: adds PSR17 discoverability
JasonTheAdams 3998eb7
refactor: renames class
JasonTheAdams 581de9e
refactor: removes stream args
JasonTheAdams 5c66fde
feat: updates to PHP AI Client 0.2.0
JasonTheAdams 4e80b89
feat: implements ClientWithOptionsInterface
JasonTheAdams de7de24
refactor: switches to using NetworkException
JasonTheAdams 7943ed6
refactor: cleans up unnecessary conditions code
JasonTheAdams 7c5d022
Merge branch 'trunk' into http-adapter
felixarntz 990111a
Use consistent YAML formatting in PHPStan config.
felixarntz acd0e8b
Fix PHPStan errors after updates.
felixarntz a68e34c
Temp folder rename to fix incorrect casing.
felixarntz bf54101
Fix folder name.
felixarntz 167aeb6
Fix PHP warning.
felixarntz c50d84b
Fix bug in HTTP client discovery implementation.
felixarntz a8aaf95
Wire up HTTP client discovery implementation.
felixarntz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| <?php | ||
| /** | ||
| * WordPress AI Client HTTP Client Adapter | ||
| * | ||
| * @package WordPress\AI_Client | ||
| * @since 1.0.0 | ||
| */ | ||
|
|
||
| namespace WordPress\AI_Client\HTTP; | ||
|
|
||
| use Psr\Http\Client\ClientInterface; | ||
| use Psr\Http\Client\ClientExceptionInterface; | ||
| use Psr\Http\Message\RequestInterface; | ||
| use Psr\Http\Message\ResponseInterface; | ||
| use Psr\Http\Message\ResponseFactoryInterface; | ||
| use Psr\Http\Message\StreamFactoryInterface; | ||
|
|
||
| /** | ||
| * PSR-18 HTTP Client adapter using WordPress HTTP API | ||
| * | ||
| * This adapter allows WordPress HTTP functions to be used | ||
| * as a PSR-18 compliant HTTP client. | ||
| * | ||
| * @since 1.0.0 | ||
| */ | ||
| class WP_AI_Client_Client_Adapter implements ClientInterface { | ||
|
|
||
| /** | ||
| * Response factory instance. | ||
| * | ||
| * @var ResponseFactoryInterface | ||
| */ | ||
| private $response_factory; | ||
|
|
||
| /** | ||
| * Stream factory instance. | ||
| * | ||
| * @var StreamFactoryInterface | ||
| */ | ||
| private $stream_factory; | ||
|
|
||
| /** | ||
| * Constructor. | ||
| * | ||
| * @param ResponseFactoryInterface $response_factory PSR-17 Response factory. | ||
| * @param StreamFactoryInterface $stream_factory PSR-17 Stream factory. | ||
| */ | ||
| public function __construct( ResponseFactoryInterface $response_factory, StreamFactoryInterface $stream_factory ) { | ||
| $this->response_factory = $response_factory; | ||
| $this->stream_factory = $stream_factory; | ||
| } | ||
|
|
||
| /** | ||
| * Sends a PSR-7 request and returns a PSR-7 response. | ||
| * | ||
| * @param RequestInterface $request The PSR-7 request. | ||
| * | ||
| * @return ResponseInterface The PSR-7 response. | ||
| * | ||
| * @throws ClientExceptionInterface If an error happens while processing the request. | ||
| */ | ||
| public function sendRequest( RequestInterface $request ): ResponseInterface { | ||
| $args = $this->prepare_wp_args( $request ); | ||
| $url = (string) $request->getUri(); | ||
|
|
||
| $response = \wp_remote_request( $url, $args ); | ||
|
|
||
| if ( \is_wp_error( $response ) ) { | ||
| throw new WP_AI_Client_Network_Exception( | ||
| $response->get_error_message(), | ||
| $request, | ||
| null, | ||
| $response->get_error_code() ? (int) $response->get_error_code() : 0 | ||
| ); | ||
| } | ||
|
|
||
| return $this->create_psr_response( $response ); | ||
| } | ||
|
|
||
| /** | ||
| * Prepare WordPress HTTP API arguments from PSR-7 request. | ||
| * | ||
| * @param RequestInterface $request The PSR-7 request. | ||
| * | ||
| * @return array WordPress HTTP API arguments. | ||
| */ | ||
| private function prepare_wp_args( RequestInterface $request ): array { | ||
| $args = array( | ||
| 'method' => $request->getMethod(), | ||
| 'headers' => $this->prepare_headers( $request ), | ||
| 'body' => $this->prepare_body( $request ), | ||
| 'timeout' => 30, | ||
| 'redirection' => 5, | ||
| 'httpversion' => $request->getProtocolVersion(), | ||
| 'blocking' => true, | ||
| ); | ||
|
|
||
| // Handle streaming requests if needed. | ||
| if ( $request->hasHeader( 'X-Stream' ) ) { | ||
| $args['stream'] = true; | ||
| $args['filename'] = $request->getHeaderLine( 'X-Stream-Filename' ); | ||
| } | ||
|
|
||
| return $args; | ||
| } | ||
|
|
||
| /** | ||
| * Prepare headers for WordPress HTTP API. | ||
| * | ||
| * @param RequestInterface $request The PSR-7 request. | ||
| * | ||
| * @return array Headers array for WordPress HTTP API. | ||
| */ | ||
| private function prepare_headers( RequestInterface $request ): array { | ||
| $headers = array(); | ||
|
|
||
| foreach ( $request->getHeaders() as $name => $values ) { | ||
| // Skip pseudo headers used for streaming. | ||
| if ( strpos( $name, 'X-Stream' ) === 0 ) { | ||
| continue; | ||
| } | ||
|
|
||
| // WordPress expects headers as name => value pairs. | ||
| $headers[ $name ] = implode( ', ', $values ); | ||
| } | ||
|
|
||
| return $headers; | ||
| } | ||
|
|
||
| /** | ||
| * Prepare request body for WordPress HTTP API. | ||
| * | ||
| * @param RequestInterface $request The PSR-7 request. | ||
| * | ||
| * @return string|null The request body. | ||
| */ | ||
| private function prepare_body( RequestInterface $request ): ?string { | ||
| $body = $request->getBody(); | ||
|
|
||
| if ( $body->getSize() === 0 ) { | ||
| return null; | ||
| } | ||
|
|
||
| // Rewind the stream to ensure we read from the beginning. | ||
| if ( $body->isSeekable() ) { | ||
| $body->rewind(); | ||
| } | ||
|
|
||
| return (string) $body; | ||
| } | ||
|
|
||
| /** | ||
| * Create PSR-7 response from WordPress HTTP response. | ||
| * | ||
| * @param array $wp_response WordPress HTTP API response array. | ||
| * | ||
| * @return ResponseInterface PSR-7 response. | ||
| */ | ||
| private function create_psr_response( array $wp_response ): ResponseInterface { | ||
| $status_code = \wp_remote_retrieve_response_code( $wp_response ); | ||
| $reason_phrase = \wp_remote_retrieve_response_message( $wp_response ); | ||
| $headers = \wp_remote_retrieve_headers( $wp_response ); | ||
| $body = \wp_remote_retrieve_body( $wp_response ); | ||
|
|
||
| // Create the PSR-7 response. | ||
| $response = $this->response_factory->createResponse( $status_code, $reason_phrase ); | ||
|
|
||
| // Add headers to response. | ||
| if ( $headers instanceof \WP_HTTP_Requests_Response ) { | ||
| $headers = $headers->get_headers(); | ||
| } | ||
|
|
||
| if ( is_array( $headers ) || $headers instanceof \ArrayAccess ) { | ||
| foreach ( $headers as $name => $value ) { | ||
| $response = $response->withHeader( $name, $value ); | ||
| } | ||
| } | ||
|
|
||
| // Set the response body. | ||
| if ( ! empty( $body ) ) { | ||
| $stream = $this->stream_factory->createStream( $body ); | ||
| $response = $response->withBody( $stream ); | ||
| } | ||
|
|
||
| return $response; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| <?php | ||
| /** | ||
| * WordPress AI Client Network Exception | ||
| * | ||
| * @package WordPress\AI_Client | ||
| * @since 1.0.0 | ||
| */ | ||
|
|
||
| namespace WordPress\AI_Client\HTTP; | ||
|
|
||
| use Psr\Http\Client\NetworkExceptionInterface; | ||
| use Psr\Http\Message\RequestInterface; | ||
|
|
||
| /** | ||
| * Network exception for WordPress HTTP Client adapter. | ||
| * | ||
| * @since 1.0.0 | ||
| */ | ||
| class WP_AI_Client_Network_Exception extends \RuntimeException implements NetworkExceptionInterface { | ||
|
|
||
| /** | ||
| * The request that caused the exception. | ||
| * | ||
| * @var RequestInterface | ||
| */ | ||
| private $request; | ||
|
|
||
| /** | ||
| * Constructor. | ||
| * | ||
| * @param string $message Exception message. | ||
| * @param RequestInterface $request The request that caused the exception. | ||
| * @param \Throwable|null $previous Previous exception. | ||
| * @param int $code Exception code. | ||
| */ | ||
| public function __construct( string $message, RequestInterface $request, ?\Throwable $previous = null, int $code = 0 ) { | ||
| parent::__construct( $message, $code, $previous ); | ||
| $this->request = $request; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the request. | ||
| * | ||
| * @return RequestInterface | ||
| */ | ||
| public function getRequest(): RequestInterface { | ||
| return $this->request; | ||
| } | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.