Skip to content

Commit

Permalink
Merge pull request #2 from oat-sa/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
ekkinox authored Nov 6, 2019
2 parents 5b62bd2 + 12356cc commit 0475e35
Show file tree
Hide file tree
Showing 9 changed files with 444 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/vendor/
/.idea/
composer.phar
composer.lock
.phpunit.result.cache
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CHANGELOG
=========

0.1.0
-----

* Added CorrelationIdsGuzzleMiddleware
* Added GuzzleClientFactory to create guzzle client with CorrelationIdsGuzzleMiddleware enabled
88 changes: 87 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,87 @@
# Correlation Ids Guzzle Middleware Library
# 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
<?php declare(strict_types=1);

use OAT\Library\CorrelationIds\Registry\CorrelationIdsRegistry;
use OAT\Library\CorrelationIds\Registry\CorrelationIdsRegistryInterface;
use OAT\Library\CorrelationIdsGuzzle\Factory\GuzzleClientFactory;
use OAT\Library\CorrelationIdsGuzzle\Middleware\CorrelationIdsGuzzleMiddleware;

/** @var CorrelationIdsRegistryInterface $registry */
$registry = new CorrelationIdsRegistry(...);

$clientFactory = new GuzzleClientFactory(new CorrelationIdsGuzzleMiddleware($registry)));

$client = $clientFactory->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
<?php declare(strict_types=1);

use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use OAT\Library\CorrelationIds\Registry\CorrelationIdsRegistry;
use OAT\Library\CorrelationIds\Registry\CorrelationIdsRegistryInterface;
use OAT\Library\CorrelationIdsGuzzle\Middleware\CorrelationIdsGuzzleMiddleware;

/** @var CorrelationIdsRegistryInterface $registry */
$registry = new CorrelationIdsRegistry(...);

$handlerStack = HandlerStack::create();
$handlerStack->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.
24 changes: 24 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -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/"
}
}
}
23 changes: 23 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>

<!-- https://phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.5/phpunit.xsd"
backupGlobals="false"
colors="true"
>
<testsuites>
<testsuite name="Integration">
<directory>tests/Integration</directory>
</testsuite>
<testsuite name="Unit">
<directory>tests/Unit</directory>
</testsuite>
</testsuites>

<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory>src</directory>
</whitelist>
</filter>
</phpunit>
50 changes: 50 additions & 0 deletions src/Factory/GuzzleClientFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php declare(strict_types=1);
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2019 (original work) Open Assessment Technologies SA;
*/
namespace OAT\Library\CorrelationIdsGuzzle\Factory;

use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use OAT\Library\CorrelationIdsGuzzle\Middleware\CorrelationIdsGuzzleMiddleware;

class GuzzleClientFactory
{
/** @var CorrelationIdsGuzzleMiddleware */
private $middleware;

public function __construct(CorrelationIdsGuzzleMiddleware $middleware)
{
$this->middleware = $middleware;
}

public function create(array $configuration = []): ClientInterface
{
$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;
}
}
66 changes: 66 additions & 0 deletions src/Middleware/CorrelationIdsGuzzleMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php declare(strict_types=1);
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2019 (original work) Open Assessment Technologies SA;
*/
namespace OAT\Library\CorrelationIdsGuzzle\Middleware;

use OAT\Library\CorrelationIds\Provider\CorrelationIdsHeaderNamesProvider;
use OAT\Library\CorrelationIds\Provider\CorrelationIdsHeaderNamesProviderInterface;
use OAT\Library\CorrelationIds\Registry\CorrelationIdsRegistryInterface;
use Psr\Http\Message\RequestInterface;

class CorrelationIdsGuzzleMiddleware
{
/** @var CorrelationIdsRegistryInterface */
private $registry;

/** @var CorrelationIdsHeaderNamesProviderInterface */
private $provider;

public function __construct(
CorrelationIdsRegistryInterface $registry,
CorrelationIdsHeaderNamesProviderInterface $provider = null
) {
$this->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) ?? '';
}
}
115 changes: 115 additions & 0 deletions tests/Integration/Middleware/CorrelationIdsGuzzleMiddlewareTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php declare(strict_types=1);
/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2019 (original work) Open Assessment Technologies SA;
*/
namespace OAT\Library\CorrelationIdsGuzzle\Tests\Integration\Middleware;

use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Response;
use OAT\Library\CorrelationIds\Provider\CorrelationIdsHeaderNamesProviderInterface;
use OAT\Library\CorrelationIds\Registry\CorrelationIdsRegistryInterface;
use OAT\Library\CorrelationIdsGuzzle\Middleware\CorrelationIdsGuzzleMiddleware;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class CorrelationIdsGuzzleMiddlewareTest extends TestCase
{
/** @var CorrelationIdsRegistryInterface|MockObject */
private $registryMock;

/** @var CorrelationIdsHeaderNamesProviderInterface|MockObject */
private $providerMock;

/** @var array */
private $history = [];

protected function setUp(): void
{
$this->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]);
}
}
Loading

0 comments on commit 0475e35

Please sign in to comment.