From 03b55bf0a43d0ef9db08caa53c32168006fe5b3b Mon Sep 17 00:00:00 2001 From: Jonathan VUILLEMIN Date: Wed, 6 Nov 2019 16:53:27 +0100 Subject: [PATCH 1/3] Provided middleware + client factory --- .gitignore | 5 + CHANGELOG.md | 8 ++ README.md | 88 +++++++++++++- composer.json | 24 ++++ phpunit.xml.dist | 23 ++++ src/Factory/GuzzleClientFactory.php | 49 ++++++++ .../CorrelationIdsGuzzleMiddleware.php | 66 ++++++++++ .../CorrelationIdsGuzzleMiddlewareTest.php | 115 ++++++++++++++++++ .../Unit/Factory/GuzzleClientFactoryTest.php | 66 ++++++++++ 9 files changed, 443 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 composer.json create mode 100644 phpunit.xml.dist create mode 100644 src/Factory/GuzzleClientFactory.php create mode 100644 src/Middleware/CorrelationIdsGuzzleMiddleware.php create mode 100644 tests/Integration/Middleware/CorrelationIdsGuzzleMiddlewareTest.php create mode 100644 tests/Unit/Factory/GuzzleClientFactoryTest.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9bc0d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/vendor/ +/.idea/ +composer.phar +composer.lock +.phpunit.result.cache diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..61268f7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +CHANGELOG +========= + +0.1.0 +----- + +* Added CorrelationIdsGuzzleMiddleware +* Added GuzzleClientFactory to create guzzle client with CorrelationIdsGuzzleMiddleware enabled diff --git a/README.md b/README.md index b248ffa..f18756d 100644 --- a/README.md +++ b/README.md @@ -1 +1,87 @@ -# Correlation Ids Guzzle Middleware Library \ No newline at end of file +# Correlation Ids Guzzle Middleware Library + +> PHP library for correlation ids guzzle middleware based on the [correlation ids library](https://github.com/oat-sa/lib-correlation-ids). + +## Table of contents +- [Installation](#installation) +- [Principles](#principles) +- [Usage](#usage) +- [Tests](#tests) + +## Installation + +```console +$ composer require oat-sa/lib-correlation-ids-guzzle +``` + +## Principles + +This library provides a ready to use [guzzle](http://docs.guzzlephp.org/en/stable/) middleware that forwards, as request headers, the correlation ids fetched from the [correlation ids registry](https://github.com/oat-sa/lib-correlation-ids/blob/master/src/Registry/CorrelationIdsRegistryInterface.php). + +**Notes** +- the current process correlation id will be forwarded as the parent one, +- the root correlation id will be also forwarded. + +More details about calls chaining available on the [correlation ids library](https://github.com/oat-sa/lib-correlation-ids) documentation. + +## Usage + +### With the provided factory + +The `GuzzleClientFactory` creates for you a guzzle client with the middleware already enabled: + +```php +create(['some' => 'options']); + +... + +$client->request('GET', 'http://example.com'); // Will forward correlation ids as request headers automatically. +``` + +### Manually + +You need to push the `CorrelationIdsGuzzleMiddleware` to your handler stack as follow: + +```php +push(Middleware::mapRequest(new CorrelationIdsGuzzleMiddleware($registry))); + +$client = new Client(['handler' => $handlerStack]); + +... + +$client->request('GET', 'http://example.com'); // Will forward correlation ids as request headers automatically. +``` +**Note**: you can customize the log context key names by providing you own [CorrelationIdsHeaderNamesProviderInterface](https://github.com/oat-sa/lib-correlation-ids/blob/master/src/Provider/CorrelationIdsHeaderNamesProviderInterface.php) implementation and pass it to the `CorrelationIdsGuzzleMiddleware` constructor. + +## Tests + +To run tests: +```console +$ vendor/bin/phpunit +``` +**Note**: see [phpunit.xml.dist](phpunit.xml.dist) for available test suites. \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..39e2432 --- /dev/null +++ b/composer.json @@ -0,0 +1,24 @@ +{ + "name": "oat-sa/lib-correlation-ids-guzzle", + "description": "OAT Correlation Ids Guzzle Library", + "type": "library", + "license": "GPL-2.0-only", + "require": { + "php": "^7.1.3", + "oat-sa/lib-correlation-ids": "<1.0", + "guzzlehttp/guzzle": "^6.4" + }, + "require-dev": { + "phpunit/phpunit": "^8.3" + }, + "autoload": { + "psr-4": { + "OAT\\Library\\CorrelationIdsGuzzle\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "OAT\\Library\\CorrelationIdsGuzzle\\Tests\\": "tests/" + } + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..0f7310c --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,23 @@ + + + + + + + tests/Integration + + + tests/Unit + + + + + + src + + + diff --git a/src/Factory/GuzzleClientFactory.php b/src/Factory/GuzzleClientFactory.php new file mode 100644 index 0000000..c92323a --- /dev/null +++ b/src/Factory/GuzzleClientFactory.php @@ -0,0 +1,49 @@ +middleware = $middleware; + } + + public function create(array $configuration = []): Client + { + $configuration['handler'] = $this->pushMiddleware($configuration['handler'] ?? HandlerStack::create()); + + return new Client($configuration); + } + + private function pushMiddleware(HandlerStack $handlerStack): HandlerStack + { + $handlerStack->push(Middleware::mapRequest($this->middleware), CorrelationIdsGuzzleMiddleware::class); + + return $handlerStack; + } +} diff --git a/src/Middleware/CorrelationIdsGuzzleMiddleware.php b/src/Middleware/CorrelationIdsGuzzleMiddleware.php new file mode 100644 index 0000000..0107cb3 --- /dev/null +++ b/src/Middleware/CorrelationIdsGuzzleMiddleware.php @@ -0,0 +1,66 @@ +registry = $registry; + $this->provider = $provider ?? new CorrelationIdsHeaderNamesProvider(); + } + + public function __invoke(RequestInterface $request, array $options = []): RequestInterface + { + $headers = [ + $this->provider->provideParentCorrelationIdHeaderName() => $this->registry->getCurrentCorrelationId(), + $this->provider->provideRootCorrelationIdHeaderName() => $this->determinateRootCorrelationId(), + ]; + + foreach ($headers as $headerName => $headerValue) { + $request = $request->withHeader($headerName, $headerValue); + } + + return $request; + } + + private function determinateRootCorrelationId(): string + { + $candidates = array_filter([ + $this->registry->getRootCorrelationId(), + $this->registry->getParentCorrelationId(), + $this->registry->getCurrentCorrelationId(), + ]); + + return array_shift($candidates) ?? ''; + } +} diff --git a/tests/Integration/Middleware/CorrelationIdsGuzzleMiddlewareTest.php b/tests/Integration/Middleware/CorrelationIdsGuzzleMiddlewareTest.php new file mode 100644 index 0000000..59daa25 --- /dev/null +++ b/tests/Integration/Middleware/CorrelationIdsGuzzleMiddlewareTest.php @@ -0,0 +1,115 @@ +registryMock = $this->createMock(CorrelationIdsRegistryInterface::class); + $this->providerMock = $this->createMock(CorrelationIdsHeaderNamesProviderInterface::class); + } + + protected function tearDown(): void + { + $this->history = []; + } + + public function testItForwardsCorrelationIdsAsRequestHeaders(): void + { + $this->registryMock + ->expects($this->exactly(2)) + ->method('getCurrentCorrelationId') + ->willReturn('current'); + + $this->registryMock + ->expects($this->once()) + ->method('getParentCorrelationId') + ->willReturn('parent'); + + $this->registryMock + ->expects($this->once()) + ->method('getRootCorrelationId') + ->willReturn('root'); + + $this->providerMock + ->expects($this->never()) + ->method('provideCurrentCorrelationIdHeaderName'); + + $this->providerMock + ->expects($this->once()) + ->method('provideParentCorrelationIdHeaderName') + ->willReturn(CorrelationIdsHeaderNamesProviderInterface::DEFAULT_PARENT_CORRELATION_ID_HEADER_NAME); + + $this->providerMock + ->expects($this->once()) + ->method('provideRootCorrelationIdHeaderName') + ->willReturn(CorrelationIdsHeaderNamesProviderInterface::DEFAULT_ROOT_CORRELATION_ID_HEADER_NAME); + + $client = $this->prepareTestGuzzleClient([new Response()]); + $client->request('POST', 'http://example.com'); + + $executedRequestHeaders = current($this->history)['request']->getHeaders(); + + $this->assertEquals( + ['current'], + $executedRequestHeaders[CorrelationIdsHeaderNamesProviderInterface::DEFAULT_PARENT_CORRELATION_ID_HEADER_NAME] + ); + + $this->assertEquals( + ['root'], + $executedRequestHeaders[CorrelationIdsHeaderNamesProviderInterface::DEFAULT_ROOT_CORRELATION_ID_HEADER_NAME] + ); + } + + private function prepareTestGuzzleClient(array $expectedResponses): Client + { + $handlerStack = HandlerStack::create(new MockHandler($expectedResponses)); + + $handlerStack->push( + Middleware::mapRequest(new CorrelationIdsGuzzleMiddleware($this->registryMock, $this->providerMock)) + ); + + $handlerStack->push( + Middleware::history($this->history) + ); + + return new Client(['handler' => $handlerStack]); + } +} diff --git a/tests/Unit/Factory/GuzzleClientFactoryTest.php b/tests/Unit/Factory/GuzzleClientFactoryTest.php new file mode 100644 index 0000000..4d812a0 --- /dev/null +++ b/tests/Unit/Factory/GuzzleClientFactoryTest.php @@ -0,0 +1,66 @@ +middlewareMock = $this->createMock(CorrelationIdsGuzzleMiddleware::class); + + $this->subject = new GuzzleClientFactory($this->middlewareMock); + } + + public function testItCanCreateAGuzzleClientAndReuseProvidedHandlerStackWithTheMiddlewareEnabled(): void + { + $result = $this->subject->create(['handler' => HandlerStack::create()]); + + $this->assertInstanceOf(Client::class, $result); + + $this->assertStringContainsString( + CorrelationIdsGuzzleMiddleware::class, + $result->getConfig('handler')->__toString() + ); + } + + public function testItCanCreateAGuzzleClientAndCreateHandlerStackWithTheMiddlewareEnabled(): void + { + $result = $this->subject->create(); + + $this->assertInstanceOf(Client::class, $result); + + $this->assertStringContainsString( + CorrelationIdsGuzzleMiddleware::class, + $result->getConfig('handler')->__toString() + ); + } +} From a1a218743f9472dc1eab82d8a5a4528ebf2f13b3 Mon Sep 17 00:00:00 2001 From: Jonathan VUILLEMIN Date: Wed, 6 Nov 2019 17:03:02 +0100 Subject: [PATCH 2/3] fixed return type --- src/Factory/GuzzleClientFactory.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Factory/GuzzleClientFactory.php b/src/Factory/GuzzleClientFactory.php index c92323a..4becdf0 100644 --- a/src/Factory/GuzzleClientFactory.php +++ b/src/Factory/GuzzleClientFactory.php @@ -19,6 +19,7 @@ namespace OAT\Library\CorrelationIdsGuzzle\Factory; use GuzzleHttp\Client; +use GuzzleHttp\ClientInterface; use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; use OAT\Library\CorrelationIdsGuzzle\Middleware\CorrelationIdsGuzzleMiddleware; @@ -33,7 +34,7 @@ public function __construct(CorrelationIdsGuzzleMiddleware $middleware) $this->middleware = $middleware; } - public function create(array $configuration = []): Client + public function create(array $configuration = []): ClientInterface { $configuration['handler'] = $this->pushMiddleware($configuration['handler'] ?? HandlerStack::create()); From f438c757213f8109772b1c840a41b1d1977e1b6d Mon Sep 17 00:00:00 2001 From: Jonathan VUILLEMIN Date: Wed, 6 Nov 2019 17:03:35 +0100 Subject: [PATCH 3/3] fixed return type on tests --- tests/Unit/Factory/GuzzleClientFactoryTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Unit/Factory/GuzzleClientFactoryTest.php b/tests/Unit/Factory/GuzzleClientFactoryTest.php index 4d812a0..95b95cb 100644 --- a/tests/Unit/Factory/GuzzleClientFactoryTest.php +++ b/tests/Unit/Factory/GuzzleClientFactoryTest.php @@ -18,7 +18,7 @@ */ namespace OAT\Library\CorrelationIdsGuzzle\Tests\Unit\Factory; -use GuzzleHttp\Client; +use GuzzleHttp\ClientInterface; use GuzzleHttp\HandlerStack; use OAT\Library\CorrelationIdsGuzzle\Factory\GuzzleClientFactory; use OAT\Library\CorrelationIdsGuzzle\Middleware\CorrelationIdsGuzzleMiddleware; @@ -44,7 +44,7 @@ public function testItCanCreateAGuzzleClientAndReuseProvidedHandlerStackWithTheM { $result = $this->subject->create(['handler' => HandlerStack::create()]); - $this->assertInstanceOf(Client::class, $result); + $this->assertInstanceOf(ClientInterface::class, $result); $this->assertStringContainsString( CorrelationIdsGuzzleMiddleware::class, @@ -56,7 +56,7 @@ public function testItCanCreateAGuzzleClientAndCreateHandlerStackWithTheMiddlewa { $result = $this->subject->create(); - $this->assertInstanceOf(Client::class, $result); + $this->assertInstanceOf(ClientInterface::class, $result); $this->assertStringContainsString( CorrelationIdsGuzzleMiddleware::class,