diff --git a/samples/index.php b/samples/index.php index 0d9d59ef..2f98bcd3 100644 --- a/samples/index.php +++ b/samples/index.php @@ -22,10 +22,16 @@ 'User-Agent' => sprintf('opensearch-php/%s (%s; PHP %s)', Client::VERSION, PHP_OS, PHP_VERSION), ] ]); -$requestFactory = new \OpenSearch\RequestFactory(); -$transport = new OpenSearch\Transport($guzzleClient, $requestFactory); +$guzzleHttpFactory = new \GuzzleHttp\Psr7\HttpFactory(); +$transport = (new OpenSearch\TransportFactory()) + ->setHttpClient($guzzleClient) + ->setPsrRequestFactory($guzzleHttpFactory) + ->setStreamFactory($guzzleHttpFactory) + ->setUriFactory($guzzleHttpFactory) + ->create(); -$client = (new \OpenSearch\ClientBuilder($transport))->build(); +$endpointFactory = new \OpenSearch\EndpointFactory(); +$client = new Client($transport, $endpointFactory, []); $info = $client->info(); @@ -43,8 +49,14 @@ ], ]); -$transport = new OpenSearch\Transport($symfonyPsr18Client, $requestFactory); -$client = (new \OpenSearch\ClientBuilder($transport))->build(); +$transport = (new OpenSearch\TransportFactory()) + ->setHttpClient($symfonyPsr18Client) + ->setPsrRequestFactory($symfonyPsr18Client) + ->setStreamFactory($symfonyPsr18Client) + ->setUriFactory($symfonyPsr18Client) + ->create(); + +$client = new Client($transport, $endpointFactory, []); $info = $client->info(); diff --git a/src/OpenSearch/EndpointFactory.php b/src/OpenSearch/EndpointFactory.php index 65bb5b36..61abbe25 100644 --- a/src/OpenSearch/EndpointFactory.php +++ b/src/OpenSearch/EndpointFactory.php @@ -4,6 +4,7 @@ use OpenSearch\Endpoints\AbstractEndpoint; use OpenSearch\Serializers\SerializerInterface; +use OpenSearch\Serializers\SmartSerializer; use ReflectionClass; /** @@ -16,9 +17,11 @@ class EndpointFactory implements EndpointFactoryInterface */ private array $endpoints = []; - public function __construct( - protected SerializerInterface $serializer, - ) { + private ?SerializerInterface $serializer; + + public function __construct(?SerializerInterface $serializer = null) + { + $this->serializer = $serializer; } /** @@ -33,6 +36,14 @@ public function getEndpoint(string $class): AbstractEndpoint return $this->endpoints[$class]; } + private function getSerializer(): SerializerInterface + { + if ($this->serializer === null) { + $this->serializer = new SmartSerializer(); + } + return $this->serializer; + } + /** * Creates an endpoint. * @@ -47,7 +58,7 @@ private function createEndpoint(string $class): AbstractEndpoint $constructor = $reflection->getConstructor(); if ($constructor && $constructor->getParameters()) { - return new $class($this->serializer); + return new $class($this->getSerializer()); } return new $class(); } diff --git a/src/OpenSearch/EndpointInterface.php b/src/OpenSearch/EndpointInterface.php index c28c63ef..57018f16 100644 --- a/src/OpenSearch/EndpointInterface.php +++ b/src/OpenSearch/EndpointInterface.php @@ -67,9 +67,9 @@ public function setId(int|string|null $docID): static; /** * Get the body of the request. * - * @return array|string + * @return array|string|null */ - public function getBody(): array|string; + public function getBody(): array|string|null; /** * Set the body of the request. diff --git a/src/OpenSearch/Endpoints/AbstractEndpoint.php b/src/OpenSearch/Endpoints/AbstractEndpoint.php index bd4cc821..2f7dcb00 100644 --- a/src/OpenSearch/Endpoints/AbstractEndpoint.php +++ b/src/OpenSearch/Endpoints/AbstractEndpoint.php @@ -150,7 +150,7 @@ public function setId(int|string|null $docID): static return $this; } - public function getBody(): array|string + public function getBody(): array|string|null { return $this->body; } diff --git a/src/OpenSearch/TransportFactory.php b/src/OpenSearch/TransportFactory.php index 40d66af7..1d2cd14b 100644 --- a/src/OpenSearch/TransportFactory.php +++ b/src/OpenSearch/TransportFactory.php @@ -106,7 +106,10 @@ public function setSerializer(Serializers\SerializerInterface $serializer): stat return $this; } - public function createTransport(): HttpTransport + /** + * Creates a new transport. + */ + public function create(): HttpTransport { if ($this->requestFactory === null) { $psrRequestFactory = $this->getPsrRequestFactory(); diff --git a/tests/ClientBuilderTest.php b/tests/ClientBuilderTest.php index 6b201c2e..751e3895 100644 --- a/tests/ClientBuilderTest.php +++ b/tests/ClientBuilderTest.php @@ -25,23 +25,37 @@ use OpenSearch\ClientBuilder; use OpenSearch\Common\Exceptions\OpenSearchException; use OpenSearch\Common\Exceptions\RuntimeException; -use OpenSearch\TransportInterface; use PHPUnit\Framework\TestCase; -/** - * @coversDefaultClass \OpenSearch\ClientBuilder - */ class ClientBuilderTest extends TestCase { /** - * @covers ::create + * @see https://github.com/elastic/elasticsearch-php/issues/993 */ - public function testCreate() + public function testIncludePortInHostHeader() { - $transport = $this->createMock(TransportInterface::class); - $client = (new ClientBuilder($transport))->build(); + $host = "localhost"; + $url = "$host:1234"; + $params = [ + 'client' => [ + 'verbose' => true + ] + ]; + $client = ClientBuilder::create() + ->setConnectionParams($params) + ->setHosts([$url]) + ->includePortInHostHeader(true) + ->build(); $this->assertInstanceOf(Client::class, $client); + + try { + $result = $client->info(); + } catch (OpenSearchException $e) { + $request = $client->transport->getLastConnection()->getLastRequestInfo(); + $this->assertTrue(isset($request['request']['headers']['Host'][0])); + $this->assertEquals($url, $request['request']['headers']['Host'][0]); + } } /** diff --git a/tests/RegisteredNamespaceTest.php b/tests/RegisteredNamespaceTest.php index daee0b75..117b550f 100644 --- a/tests/RegisteredNamespaceTest.php +++ b/tests/RegisteredNamespaceTest.php @@ -68,7 +68,7 @@ public function getName(): string return "foo"; } - public function getObject(OpenSearch\TransportInterface $transport, SerializerInterface $serializer) + public function getObject(Transport $transport, SerializerInterface $serializer) { return new FooNamespace(); } diff --git a/tests/TransportTest.php b/tests/TransportTest.php index fb8e9ec8..96469261 100644 --- a/tests/TransportTest.php +++ b/tests/TransportTest.php @@ -21,86 +21,79 @@ namespace OpenSearch\Tests; -use GuzzleHttp\Client as GuzzleClient; -use GuzzleHttp\Exception\RequestException; -use GuzzleHttp\Handler\MockHandler; -use GuzzleHttp\HandlerStack; -use GuzzleHttp\Psr7\Request; -use GuzzleHttp\Psr7\Response; -use Http\Adapter\Guzzle7\Client as GuzzleAdapter; -use Http\Promise\Promise; -use OpenSearch\RequestFactoryInterface; +use OpenSearch\Common\Exceptions\ServerErrorResponseException; +use OpenSearch\ConnectionPool\AbstractConnectionPool; +use OpenSearch\Connections\Connection; +use OpenSearch\Serializers\SerializerInterface; use OpenSearch\Transport; +use GuzzleHttp\Ring\Future\FutureArray; +use GuzzleHttp\Ring\Future\FutureArrayInterface; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Psr\Http\Client\RequestExceptionInterface; -use Psr\Http\Message\RequestInterface; +use Psr\Log\LoggerInterface; +use React\Promise\Deferred; -/** - * Class TransportTest - * - * @coversDefaultClass \OpenSearch\Transport - */ class TransportTest extends TestCase { /** - * The transport instance under test. + * @var Connection|MockObject */ - private Transport $transport; + private $connection; + /** + * @var AbstractConnectionPool|MockObject + */ + private $connectionPool; + /** + * @var MockObject|LoggerInterface + */ + private $logger; public function setUp(): void { - parent::setUp(); + $this->logger = $this->createMock(LoggerInterface::class); + $this->connectionPool = $this->createMock(AbstractConnectionPool::class); + $this->connection = $this->createMock(Connection::class); + } - $mockHandler = new MockHandler([ - new Response(200, ["content-type" => "text/javascript; charset=utf-8"], '{"foo": "bar"}'), - new RequestException('Error Communicating with Server', $this->createMock(RequestInterface::class)), - ]); + public function testPerformRequestWithServerErrorResponseException404Result() + { + $deferred = new Deferred(); + $deferred->reject(new ServerErrorResponseException('foo', 404)); + $future = new FutureArray($deferred->promise()); - $handlerStack = HandlerStack::create($mockHandler); - $httpClient = new GuzzleAdapter(new GuzzleClient(['handler' => $handlerStack])); + $this->connection->method('performRequest') + ->willReturn($future); - $requestFactory = $this->createMock(RequestFactoryInterface::class); - $requestFactory->method('createRequest')->willReturn($this->createMock(RequestInterface::class)); + $this->connectionPool->method('nextConnection') + ->willReturn($this->connection); - $this->transport = new Transport($httpClient, $requestFactory); - } + $this->connectionPool->expects($this->never()) + ->method('scheduleCheck'); - /** - * @covers ::sendRequest - */ - public function testSendRequest(): void - { - $request = new Request('GET', 'http://localhost:9200'); - $response = $this->transport->sendRequest($request); - $this->assertInstanceOf(Response::class, $response); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('text/javascript; charset=utf-8', $response->getHeaderLine('content-type')); - $this->assertEquals('{"foo": "bar"}', $response->getBody()->getContents()); - - $this->expectException(RequestExceptionInterface::class); - $this->expectExceptionMessage('Error Communicating with Server'); - $this->transport->sendRequest($request); + $transport = new Transport(1, $this->connectionPool, $this->logger); + + $result = $transport->performRequest('GET', '/'); + $this->assertInstanceOf(FutureArrayInterface::class, $result); } - /** - * @covers ::sendAsyncRequest - */ - public function testSendAsyncRequest(): void + public function testPerformRequestWithServerErrorResponseException500Result() { - $request = new Request('GET', 'http://localhost:9200'); - $promise = $this->transport->sendAsyncRequest($request); - $this->assertInstanceOf(Promise::class, $promise); - $response = $promise->wait(); - $this->assertInstanceOf(Response::class, $response); - $this->assertEquals(200, $response->getStatusCode()); - $this->assertEquals('text/javascript; charset=utf-8', $response->getHeaderLine('content-type')); - $this->assertEquals('{"foo": "bar"}', $response->getBody()->getContents()); - - $promise = $this->transport->sendAsyncRequest($request); - $this->assertInstanceOf(Promise::class, $promise); - $this->expectException(RequestExceptionInterface::class); - $this->expectExceptionMessage('Error Communicating with Server'); - $promise->wait(); - } + $deferred = new Deferred(); + $deferred->reject(new ServerErrorResponseException('foo', 500)); + $future = new FutureArray($deferred->promise()); + $this->connection->method('performRequest') + ->willReturn($future); + + $this->connectionPool->method('nextConnection') + ->willReturn($this->connection); + + $this->connectionPool->expects($this->once()) + ->method('scheduleCheck'); + + $transport = new Transport(1, $this->connectionPool, $this->logger); + + $result = $transport->performRequest('GET', '/'); + $this->assertInstanceOf(FutureArrayInterface::class, $result); + } }