diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 56242bf318de7..667b24ec7f575 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -83,6 +83,7 @@ use Symfony\Component\HtmlSanitizer\HtmlSanitizer; use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig; use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface; +use Symfony\Component\HttpClient\Messenger\PingWebhookMessageHandler; use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\Retry\GenericRetryStrategy; use Symfony\Component\HttpClient\RetryableHttpClient; @@ -2455,6 +2456,10 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder unset($options['vars']); $container->getDefinition('http_client.transport')->setArguments([$options, $config['max_host_connections'] ?? 6]); + if (!class_exists(PingWebhookMessageHandler::class)) { + $container->removeDefinition('http_client.messenger.ping_webhook_handler'); + } + if (!$hasPsr18 = ContainerBuilder::willBeAvailable('psr/http-client', ClientInterface::class, ['symfony/framework-bundle', 'symfony/http-client'])) { $container->removeDefinition('psr18.http_client'); $container->removeAlias(ClientInterface::class); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/http_client.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/http_client.php index 23b794f45b2dd..a4c78d0ec262b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/http_client.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/http_client.php @@ -17,6 +17,7 @@ use Psr\Http\Message\StreamFactoryInterface; use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\HttpClient\HttplugClient; +use Symfony\Component\HttpClient\Messenger\PingWebhookMessageHandler; use Symfony\Component\HttpClient\Psr18Client; use Symfony\Component\HttpClient\Retry\GenericRetryStrategy; use Symfony\Component\HttpClient\UriTemplateHttpClient; @@ -90,5 +91,11 @@ ->args([ [inline_service(\Rize\UriTemplate::class), 'expand'], ]) + + ->set('http_client.messenger.ping_webhook_handler', PingWebhookMessageHandler::class) + ->args([ + service('http_client'), + ]) + ->tag('messenger.message_handler') ; }; diff --git a/src/Symfony/Component/HttpClient/CHANGELOG.md b/src/Symfony/Component/HttpClient/CHANGELOG.md index f395e0f5b2318..ff802cbb54cf5 100644 --- a/src/Symfony/Component/HttpClient/CHANGELOG.md +++ b/src/Symfony/Component/HttpClient/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Add `HarFileResponseFactory` testing utility, allow to replay responses from `.har` files * Add `max_retries` option to `RetryableHttpClient` to adjust the retry logic on a per request level + * Add `PingWehookMessage` and `PingWebhookMessageHandler` 6.3 --- diff --git a/src/Symfony/Component/HttpClient/Messenger/PingWebhookMessage.php b/src/Symfony/Component/HttpClient/Messenger/PingWebhookMessage.php new file mode 100644 index 0000000000000..17fa17f9d3487 --- /dev/null +++ b/src/Symfony/Component/HttpClient/Messenger/PingWebhookMessage.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpClient\Messenger; + +/** + * @author Kevin Bond + */ +final class PingWebhookMessage implements \Stringable +{ + public function __construct( + public readonly string $method, + public readonly string $url, + public readonly array $options = [], + public readonly bool $throw = true, + ) { + } + + public function __toString(): string + { + return "[{$this->method}] {$this->url}"; + } +} diff --git a/src/Symfony/Component/HttpClient/Messenger/PingWebhookMessageHandler.php b/src/Symfony/Component/HttpClient/Messenger/PingWebhookMessageHandler.php new file mode 100644 index 0000000000000..a79eed2a7ee5d --- /dev/null +++ b/src/Symfony/Component/HttpClient/Messenger/PingWebhookMessageHandler.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpClient\Messenger; + +use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; + +/** + * @author Kevin Bond + */ +class PingWebhookMessageHandler +{ + public function __construct( + private readonly HttpClientInterface $httpClient, + ) { + } + + public function __invoke(PingWebhookMessage $message): ResponseInterface + { + $response = $this->httpClient->request($message->method, $message->url, $message->options); + $response->getHeaders($message->throw); + + return $response; + } +} diff --git a/src/Symfony/Component/HttpClient/Tests/Messenger/PingWebhookMessageHandlerTest.php b/src/Symfony/Component/HttpClient/Tests/Messenger/PingWebhookMessageHandlerTest.php new file mode 100644 index 0000000000000..614b632666591 --- /dev/null +++ b/src/Symfony/Component/HttpClient/Tests/Messenger/PingWebhookMessageHandlerTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpClient\Tests\Messenger; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpClient\Exception\ClientException; +use Symfony\Component\HttpClient\Messenger\PingWebhookMessage; +use Symfony\Component\HttpClient\Messenger\PingWebhookMessageHandler; +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\HttpClient\Response\MockResponse; + +/** + * @author Kevin Bond + */ +final class PingWebhookMessageHandlerTest extends TestCase +{ + public function testSuccessfulPing() + { + $client = new MockHttpClient([ + function ($method, $url) { + $this->assertSame('POST', $method); + $this->assertSame('https://endpoint.com/key', $url); + + return new MockResponse('a response'); + }, + ]); + $handler = new PingWebhookMessageHandler($client); + $response = $handler(new PingWebhookMessage('POST', 'https://endpoint.com/key')); + + $this->assertSame(200, $response->getStatusCode()); + $this->assertSame('a response', $response->getContent()); + $this->assertSame('https://endpoint.com/key', $response->getInfo('url')); + } + + public function testPingErrorThrowsException() + { + $client = new MockHttpClient([ + function ($method, $url) { + $this->assertSame('POST', $method); + $this->assertSame('https://endpoint.com/key', $url); + + return new MockResponse('a response', ['http_code' => 404]); + }, + ]); + + $handler = new PingWebhookMessageHandler($client); + + $this->expectException(ClientException::class); + + $handler(new PingWebhookMessage('POST', 'https://endpoint.com/key')); + } + + public function testPingErrorDoesNotThrowException() + { + $client = new MockHttpClient([ + function ($method, $url) { + $this->assertSame('POST', $method); + $this->assertSame('https://endpoint.com/key', $url); + + return new MockResponse('a response', ['http_code' => 404]); + }, + ]); + + $handler = new PingWebhookMessageHandler($client); + $response = $handler(new PingWebhookMessage('POST', 'https://endpoint.com/key', throw: false)); + + $this->assertSame(404, $response->getStatusCode()); + $this->assertSame('a response', $response->getContent(false)); + $this->assertSame('https://endpoint.com/key', $response->getInfo('url')); + } +} diff --git a/src/Symfony/Component/HttpClient/composer.json b/src/Symfony/Component/HttpClient/composer.json index 5601928749856..33fa3b4558004 100644 --- a/src/Symfony/Component/HttpClient/composer.json +++ b/src/Symfony/Component/HttpClient/composer.json @@ -39,6 +39,7 @@ "psr/http-client": "^1.0", "symfony/dependency-injection": "^5.4|^6.0|^7.0", "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", "symfony/process": "^5.4|^6.0|^7.0", "symfony/stopwatch": "^5.4|^6.0|^7.0" },