├── .github_changelog_generator
├── phpstan.neon
├── .gitignore
├── .github
├── stale.yml
├── dependabot.yml
└── workflows
│ └── ci.yml
├── .php-cs-fixer.php
├── src
└── Qandidate
│ └── Stack
│ ├── RequestIdGenerator.php
│ ├── UuidRequestIdGenerator.php
│ ├── RequestId
│ └── MonologProcessor.php
│ └── RequestId.php
├── phpstan-baseline.neon
├── phpunit.xml.dist
├── test
└── Qandidate
│ └── Stack
│ ├── UuidRequestIdGeneratorTest.php
│ ├── RequestId
│ └── MonologProcessorTest.php
│ └── RequestIdTest.php
├── LICENSE
├── composer.json
├── Makefile
├── README.md
└── CHANGELOG.md
/.github_changelog_generator:
--------------------------------------------------------------------------------
1 | future-release=2.0.2
2 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | includes:
2 | - phpstan-baseline.neon
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | composer.lock
3 | .php-cs-fixer.cache
4 | .phpunit.result.cache
5 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # see https://probot.github.io/apps/stale/
2 |
3 | # inherit settings from https://github.com/qandidate-labs/.github/blob/main/.github/stale.yml
4 | _extends: .github
5 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: composer
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | - package-ecosystem: github-actions
9 | directory: "/"
10 | schedule:
11 | interval: weekly
12 |
--------------------------------------------------------------------------------
/.php-cs-fixer.php:
--------------------------------------------------------------------------------
1 | setFinder(
6 | \PhpCsFixer\Finder::create()
7 | ->in([
8 | __DIR__ . '/src',
9 | __DIR__ . '/test',
10 | ])
11 | );
12 |
13 | return $config;
14 |
--------------------------------------------------------------------------------
/src/Qandidate/Stack/RequestIdGenerator.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Qandidate\Stack;
15 |
16 | /**
17 | * Generates request ids.
18 | */
19 | interface RequestIdGenerator
20 | {
21 | public function generate(): string;
22 | }
23 |
--------------------------------------------------------------------------------
/phpstan-baseline.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | ignoreErrors:
3 | -
4 | message: "#^Method Qandidate\\\\Stack\\\\RequestId\\\\MonologProcessor\\:\\:__invoke\\(\\) has parameter \\$record with no value type specified in iterable type array\\.$#"
5 | count: 1
6 | path: src/Qandidate/Stack/RequestId/MonologProcessor.php
7 |
8 | -
9 | message: "#^Method Qandidate\\\\Stack\\\\RequestId\\\\MonologProcessor\\:\\:__invoke\\(\\) return type has no value type specified in iterable type array\\.$#"
10 | count: 1
11 | path: src/Qandidate/Stack/RequestId/MonologProcessor.php
12 |
13 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 | ./test/
16 |
17 |
18 |
19 |
20 | ./src/
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/test/Qandidate/Stack/UuidRequestIdGeneratorTest.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Qandidate\Stack;
15 |
16 | use PHPUnit\Framework\TestCase;
17 |
18 | class UuidRequestIdGeneratorTest extends TestCase
19 | {
20 | /**
21 | * @test
22 | */
23 | public function it_generates_a_string()
24 | {
25 | $generator = new UuidRequestIdGenerator();
26 |
27 | $this->assertIsString($generator->generate());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Qandidate/Stack/UuidRequestIdGenerator.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Qandidate\Stack;
15 |
16 | use Ramsey\Uuid\Type\Hexadecimal;
17 | use Ramsey\Uuid\Uuid;
18 |
19 | /**
20 | * Generates a uuid for the request id.
21 | */
22 | class UuidRequestIdGenerator implements RequestIdGenerator
23 | {
24 | /** @var Hexadecimal|int|string|null */
25 | private $nodeId;
26 |
27 | /**
28 | * @param Hexadecimal|int|string|null $nodeId
29 | */
30 | public function __construct($nodeId = null)
31 | {
32 | $this->nodeId = $nodeId;
33 | }
34 |
35 | /**
36 | * {@inheritDoc}
37 | */
38 | public function generate(): string
39 | {
40 | return Uuid::uuid1($this->nodeId)->toString();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Qandidate.com - http://qandidate.com/
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "qandidate/stack-request-id",
3 | "description": "Middleware for adding request id to Symfony Request.",
4 | "license": "MIT",
5 | "require": {
6 | "symfony/http-kernel": "^5.1.5",
7 | "symfony/http-foundation": "^5.0.7",
8 | "ramsey/uuid": "^4.0"
9 | },
10 | "authors": [
11 | {
12 | "name": "Alexander",
13 | "email": "iam.asm89@gmail.com"
14 | },
15 | {
16 | "name": "Fritsjan",
17 | "email": "fritsjan@qandidate.com"
18 | },
19 | {
20 | "name": "Qandidate.com",
21 | "homepage": "http://labs.qandidate.com/"
22 | }
23 | ],
24 | "suggest": {
25 | "symfony/monolog-bundle": "For registering the MonologProcessor"
26 | },
27 | "autoload": {
28 | "psr-0": {
29 | "Qandidate\\Stack": "src/"
30 | }
31 | },
32 | "autoload-dev": {
33 | "psr-0": {
34 | "Qandidate\\Stack": "test/"
35 | }
36 | },
37 | "require-dev": {
38 | "phpunit/phpunit": "^9.5",
39 | "broadway/coding-standard": "^1.2",
40 | "phpstan/phpstan": "^1.0"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .DEFAULT_GOAL:=help
2 |
3 | .PHONY: dependencies
4 | dependencies:
5 | composer install --no-interaction --no-suggest --no-scripts --ansi
6 |
7 | .PHONY: test
8 | test:
9 | vendor/bin/phpunit --testdox --exclude-group=none --colors=always
10 |
11 | .PHONY: qa
12 | qa: php-cs-fixer-ci phpstan
13 |
14 | .PHONY: php-cs-fixer
15 | php-cs-fixer:
16 | vendor/bin/php-cs-fixer fix --no-interaction --allow-risky=yes --diff --verbose
17 |
18 | .PHONY: php-cs-fixer-ci
19 | php-cs-fixer-ci:
20 | vendor/bin/php-cs-fixer fix --dry-run --no-interaction --allow-risky=yes --diff --verbose
21 |
22 | .PHONY: phpstan
23 | phpstan:
24 | vendor/bin/phpstan analyse --level=max src/
25 |
26 | .PHONY: changelog
27 | changelog:
28 | git log $$(git describe --abbrev=0 --tags)...HEAD --no-merges --pretty=format:"* [%h](http://github.com/${TRAVIS_REPO_SLUG}/commit/%H) %s (%cN)"
29 |
30 | .PHONY: license
31 | license:
32 | vendor/bin/docheader check --no-interaction --ansi -vvv {src,test}
33 |
34 | # Based on https://suva.sh/posts/well-documented-makefiles/
35 | help: ## Display this help
36 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n\nTargets:\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
37 |
--------------------------------------------------------------------------------
/src/Qandidate/Stack/RequestId/MonologProcessor.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Qandidate\Stack\RequestId;
15 |
16 | use Symfony\Component\HttpKernel\Event\RequestEvent;
17 |
18 | /**
19 | * Processor to add the request id to monolog records.
20 | */
21 | class MonologProcessor
22 | {
23 | /** @var string */
24 | private $header;
25 |
26 | /** @var string|null */
27 | private $requestId;
28 |
29 | public function __construct(string $header = 'X-Request-Id')
30 | {
31 | $this->header = $header;
32 | }
33 |
34 | public function onKernelRequest(RequestEvent $event): void
35 | {
36 | $request = $event->getRequest();
37 | $this->requestId = (string) $request->headers->get($this->header, '');
38 | }
39 |
40 | public function __invoke(array $record): array
41 | {
42 | if ($this->requestId) {
43 | $record['extra']['request_id'] = $this->requestId;
44 | }
45 |
46 | return $record;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: "CI"
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - "master"
7 | push:
8 | branches:
9 | - "master"
10 | schedule:
11 | - cron: "37 13 * * 1"
12 |
13 | jobs:
14 | tests:
15 | name: "Run tests"
16 | runs-on: "ubuntu-20.04"
17 | strategy:
18 | matrix:
19 | php-version:
20 | - "7.4"
21 | - "8.0"
22 | - "8.1"
23 | - "8.2"
24 | steps:
25 | - name: "Checkout"
26 | uses: "actions/checkout@v4"
27 | - name: "Install PHP"
28 | uses: "shivammathur/setup-php@v2"
29 | with:
30 | php-version: "${{ matrix.php-version }}"
31 | coverage: "none"
32 | env:
33 | fail-fast: true
34 | - name: "Validate composer.json and composer.lock"
35 | run: "composer validate --strict --no-interaction --ansi"
36 | - name: "Install dependencies with Composer"
37 | uses: "ramsey/composer-install@v3"
38 | - name: "Run tests"
39 | run: "make test"
40 |
41 | coding-standards:
42 | name: "Coding standards"
43 | runs-on: "ubuntu-20.04"
44 | steps:
45 | - name: "Checkout"
46 | uses: "actions/checkout@v4"
47 | - name: "Install PHP"
48 | uses: "shivammathur/setup-php@v2"
49 | with:
50 | php-version: "8.0"
51 | coverage: "none"
52 | - name: "Install dependencies with Composer"
53 | uses: "ramsey/composer-install@v3"
54 | - name: "Check coding standards"
55 | run: "make php-cs-fixer-ci"
56 |
57 | static-analysis:
58 | name: "Static analysis"
59 | runs-on: "ubuntu-20.04"
60 | steps:
61 | - name: "Checkout"
62 | uses: "actions/checkout@v4"
63 | - name: "Install PHP"
64 | uses: "shivammathur/setup-php@v2"
65 | with:
66 | php-version: "8.0"
67 | coverage: "none"
68 | - name: "Install dependencies with Composer"
69 | uses: "ramsey/composer-install@v3"
70 | - name: "Run PHPStan"
71 | run: "make phpstan"
72 |
--------------------------------------------------------------------------------
/src/Qandidate/Stack/RequestId.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Qandidate\Stack;
15 |
16 | use Symfony\Component\HttpFoundation\Request;
17 | use Symfony\Component\HttpKernel\HttpKernelInterface;
18 |
19 | /**
20 | * Middleware adding a unique request id to the request if it is not present.
21 | */
22 | class RequestId implements HttpKernelInterface
23 | {
24 | /** @var HttpKernelInterface */
25 | private $app;
26 |
27 | /** @var RequestIdGenerator */
28 | private $generator;
29 |
30 | /** @var string */
31 | private $header;
32 |
33 | /** @var string|null */
34 | private $responseHeader;
35 |
36 | public function __construct(
37 | HttpKernelInterface $app,
38 | RequestIdGenerator $generator,
39 | string $header = 'X-Request-Id',
40 | ?string $responseHeader = null
41 | ) {
42 | $this->app = $app;
43 | $this->generator = $generator;
44 | $this->header = $header;
45 | $this->responseHeader = $responseHeader;
46 | }
47 |
48 | /**
49 | * {@inheritDoc}
50 | */
51 | public function handle(Request $request, int $type = HttpKernelInterface::MASTER_REQUEST, bool $catch = true)
52 | {
53 | if (!$request->headers->has($this->header)) {
54 | $request->headers->set($this->header, $this->generator->generate());
55 | }
56 |
57 | $response = $this->app->handle($request, $type, $catch);
58 |
59 | if (null !== $this->responseHeader) {
60 | $response->headers->set($this->responseHeader, (string) $request->headers->get($this->header, ''));
61 | }
62 |
63 | return $response;
64 | }
65 |
66 | public function enableResponseHeader(string $header = 'X-Request-Id'): void
67 | {
68 | $this->responseHeader = $header;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/test/Qandidate/Stack/RequestId/MonologProcessorTest.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Qandidate\Stack\RequestId;
15 |
16 | use PHPUnit\Framework\TestCase;
17 | use Symfony\Component\HttpFoundation\Request;
18 | use Symfony\Component\HttpKernel\Event\RequestEvent;
19 |
20 | class MonologProcessorTest extends TestCase
21 | {
22 | private $processor;
23 | private $header = 'Foo-Id';
24 |
25 | public function setUp(): void
26 | {
27 | $this->processor = new MonologProcessor($this->header);
28 | }
29 |
30 | /**
31 | * @test
32 | */
33 | public function it_adds_the_request_id_if_it_was_available_in_the_request()
34 | {
35 | $record = ['message' => 'w00t w00t'];
36 | $requestId = 'ea1379-42';
37 | $getResponseEvent = $this->createGetResponseEvent($requestId);
38 |
39 | $this->processor->onKernelRequest($getResponseEvent);
40 |
41 | $expectedRecord = $record;
42 | $expectedRecord['extra']['request_id'] = $requestId;
43 |
44 | $this->assertEquals($expectedRecord, $this->invokeProcessor($record));
45 | }
46 |
47 | /**
48 | * @test
49 | */
50 | public function it_leaves_the_record_untouched_if_no_request_id_was_available_in_the_request()
51 | {
52 | $record = ['message' => 'w00t w00t'];
53 | $getResponseEvent = $this->createGetResponseEvent();
54 |
55 | $this->processor->onKernelRequest($getResponseEvent);
56 |
57 | $expectedRecord = $record;
58 |
59 | $this->assertEquals($expectedRecord, $this->invokeProcessor($record));
60 | }
61 |
62 | /**
63 | * @test
64 | */
65 | public function it_leaves_the_record_untouched_if_no_request_was_handled()
66 | {
67 | $record = ['message' => 'w00t w00t'];
68 |
69 | $expectedRecord = $record;
70 |
71 | $this->assertEquals($expectedRecord, $this->invokeProcessor($record));
72 | }
73 |
74 | private function createGetResponseEvent($requestId = false)
75 | {
76 | $getResponseEventMock = $this->createMock(RequestEvent::class);
77 |
78 | $request = new Request();
79 |
80 | if (false !== $requestId) {
81 | $request->headers->set($this->header, $requestId);
82 | }
83 |
84 | $getResponseEventMock
85 | ->expects($this->any())
86 | ->method('getRequest')
87 | ->will($this->returnValue($request));
88 |
89 | return $getResponseEventMock;
90 | }
91 |
92 | private function invokeProcessor(array $record)
93 | {
94 | return call_user_func_array($this->processor, [$record]);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | stack-request-id
2 | =====
3 | Middleware for adding a request id to your Symfony Requests
4 |
5 | 
6 |
7 | ## Installation
8 | First, add this project to your project's composer.json
9 |
10 | ```
11 | $ composer require qandidate/stack-request-id ^1.0
12 | ```
13 |
14 | ## Setting up
15 | Update your `app.php` to include the middleware:
16 |
17 | Before:
18 | ```php5
19 | use Symfony\Component\HttpFoundation\Request;
20 |
21 | $kernel = new AppKernel($env, $debug);
22 | $kernel->loadClassCache();
23 |
24 | $request = Request::createFromGlobals();
25 | $response = $kernel->handle($request);
26 | $response->send();
27 | $kernel->terminate($request, $response);
28 | ```
29 |
30 | After:
31 | ```php5
32 | use Qandidate\Stack\RequestId;
33 | use Qandidate\Stack\UuidRequestIdGenerator;
34 | use Symfony\Component\HttpFoundation\Request;
35 |
36 | $kernel = new AppKernel($env, $debug);
37 |
38 | // Stack it!
39 | $generator = new UuidRequestIdGenerator(1337);
40 | $stack = new RequestId($kernel, $generator);
41 |
42 | $kernel->loadClassCache();
43 |
44 | $request = Request::createFromGlobals();
45 | $response = $stack->handle($request);
46 | $response->send();
47 | $kernel->terminate($request, $response);
48 | ```
49 |
50 | ## Adding the request id to your monolog logs
51 | If you use Symfony's [MonologBundle] you can add the request id to your monolog logs by adding the following service definition to your services.xml file:
52 |
53 | ```XML
54 |
55 |
56 |
57 |
58 | ```
59 |
60 | [MonologBundle]: https://github.com/symfony/MonologBundle
61 |
62 | ## Adding the request id to responses
63 | If you need to send the request id back with the response you can enable the response header:
64 |
65 | ```php5
66 | $generator = new UuidRequestIdGenerator(1337);
67 | $stack = new RequestId($kernel, $generator);
68 | $stack->enableResponseHeader();
69 | ```
70 |
71 | It is also possible to change response header's name:
72 |
73 | ```php5
74 | $stack->enableResponseHeader('My-Custom-Request-Id');
75 | ```
76 |
77 | If you don't have access to the `RequestId` object instance (StackPHP, for example) the response header can be set via
78 | the fourth argument of the `RequestId` constructor method.
79 |
80 | ```php5
81 | $generator = new UuidRequestIdGenerator(1337);
82 | $stack = new RequestId($kernel, $generator, 'X-Request-Id', 'My-Custom-Request-Id');
83 | ```
84 |
85 | The third argument, for reference, is the name of the header:
86 | - That will be checked for a value before falling back to generating a new request ID,
87 | - Used to store the resulting request ID inside Symfony's request object.
88 |
89 | ## StackPHP's Middleware Builder
90 | If you are already using [StackPHP](http://stackphp.com), just push the `RequestId` class into the builder.
91 |
92 | ```php5
93 | $kernel = new AppKernel('dev', true);
94 |
95 | $generator = new UuidRequestIdGenerator(1337);
96 | $stack = (new Stack\Builder)
97 | ->push('Qandidate\Stack\RequestId', $generator, 'X-Request-Id', 'X-Request-Id')
98 | ->resolve($kernel);
99 |
100 | $kernel->loadClassCache();
101 |
102 | $request = Request::createFromGlobals();
103 | $response = $stack->handle($request);
104 | $response->send();
105 | $kernel->terminate($request, $response);
106 | ```
107 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [2.0.2](https://github.com/qandidate-labs/stack-request-id/tree/2.0.2) (2020-05-07)
4 |
5 | [Full Changelog](https://github.com/qandidate-labs/stack-request-id/compare/2.0.1...2.0.2)
6 |
7 | **Merged pull requests:**
8 |
9 | - Drop support for Symfony 3.4 [\#30](https://github.com/qandidate-labs/stack-request-id/pull/30) ([rgeraads](https://github.com/rgeraads))
10 |
11 | ## [2.0.1](https://github.com/qandidate-labs/stack-request-id/tree/2.0.1) (2020-04-24)
12 |
13 | [Full Changelog](https://github.com/qandidate-labs/stack-request-id/compare/2.0.0...2.0.1)
14 |
15 | **Closed issues:**
16 |
17 | - Support Symfony 5? [\#17](https://github.com/qandidate-labs/stack-request-id/issues/17)
18 |
19 | **Merged pull requests:**
20 |
21 | - Allow ramsey/uuid 4.0 [\#29](https://github.com/qandidate-labs/stack-request-id/pull/29) ([rgeraads](https://github.com/rgeraads))
22 | - Symfony \>=5.0.7 [\#28](https://github.com/qandidate-labs/stack-request-id/pull/28) ([othillo](https://github.com/othillo))
23 | - Symfony \>=5.0.7 [\#27](https://github.com/qandidate-labs/stack-request-id/pull/27) ([othillo](https://github.com/othillo))
24 |
25 | ## [2.0.0](https://github.com/qandidate-labs/stack-request-id/tree/2.0.0) (2020-02-11)
26 |
27 | [Full Changelog](https://github.com/qandidate-labs/stack-request-id/compare/1.1.0...2.0.0)
28 |
29 | **Closed issues:**
30 |
31 | - support Symfony 4 [\#14](https://github.com/qandidate-labs/stack-request-id/issues/14)
32 |
33 | **Merged pull requests:**
34 |
35 | - only test actively supported PHP versions [\#21](https://github.com/qandidate-labs/stack-request-id/pull/21) ([othillo](https://github.com/othillo))
36 |
37 | ## [1.1.0](https://github.com/qandidate-labs/stack-request-id/tree/1.1.0) (2017-12-11)
38 |
39 | [Full Changelog](https://github.com/qandidate-labs/stack-request-id/compare/1.0.0...1.1.0)
40 |
41 | **Closed issues:**
42 |
43 | - What's the current state of this library? [\#11](https://github.com/qandidate-labs/stack-request-id/issues/11)
44 |
45 | **Merged pull requests:**
46 |
47 | - support Symfony 4 [\#16](https://github.com/qandidate-labs/stack-request-id/pull/16) ([othillo](https://github.com/othillo))
48 |
49 | ## [1.0.0](https://github.com/qandidate-labs/stack-request-id/tree/1.0.0) (2017-07-17)
50 |
51 | [Full Changelog](https://github.com/qandidate-labs/stack-request-id/compare/0.4.1...1.0.0)
52 |
53 | **Merged pull requests:**
54 |
55 | - Update version in README [\#13](https://github.com/qandidate-labs/stack-request-id/pull/13) ([wjzijderveld](https://github.com/wjzijderveld))
56 | - test with newer PHP versions [\#12](https://github.com/qandidate-labs/stack-request-id/pull/12) ([othillo](https://github.com/othillo))
57 |
58 | ## [0.4.1](https://github.com/qandidate-labs/stack-request-id/tree/0.4.1) (2016-12-06)
59 |
60 | [Full Changelog](https://github.com/qandidate-labs/stack-request-id/compare/0.4.0...0.4.1)
61 |
62 | **Closed issues:**
63 |
64 | - Release a new version [\#9](https://github.com/qandidate-labs/stack-request-id/issues/9)
65 |
66 | **Merged pull requests:**
67 |
68 | - Added support for both ramsey/uuid 2.0 and 3.0 [\#10](https://github.com/qandidate-labs/stack-request-id/pull/10) ([robinvdvleuten](https://github.com/robinvdvleuten))
69 |
70 | ## [0.4.0](https://github.com/qandidate-labs/stack-request-id/tree/0.4.0) (2016-03-26)
71 |
72 | [Full Changelog](https://github.com/qandidate-labs/stack-request-id/compare/0.3.0...0.4.0)
73 |
74 | ## [0.3.0](https://github.com/qandidate-labs/stack-request-id/tree/0.3.0) (2015-10-19)
75 |
76 | [Full Changelog](https://github.com/qandidate-labs/stack-request-id/compare/0.2.0...0.3.0)
77 |
78 | **Closed issues:**
79 |
80 | - switch to ramsey/uuid? [\#2](https://github.com/qandidate-labs/stack-request-id/issues/2)
81 |
82 | **Merged pull requests:**
83 |
84 | - Add compatibility with symfony 3 [\#7](https://github.com/qandidate-labs/stack-request-id/pull/7) ([olivier34000](https://github.com/olivier34000))
85 | - Enable support for StackPHP's middleware stack builder. [\#5](https://github.com/qandidate-labs/stack-request-id/pull/5) ([zanbaldwin](https://github.com/zanbaldwin))
86 | - Update dependency to maintained uuid project [\#4](https://github.com/qandidate-labs/stack-request-id/pull/4) ([merk](https://github.com/merk))
87 |
88 | ## [0.2.0](https://github.com/qandidate-labs/stack-request-id/tree/0.2.0) (2015-01-30)
89 |
90 | [Full Changelog](https://github.com/qandidate-labs/stack-request-id/compare/0.1.0...0.2.0)
91 |
92 | **Merged pull requests:**
93 |
94 | - Add support for sending the request id with a response. [\#1](https://github.com/qandidate-labs/stack-request-id/pull/1) ([jakzal](https://github.com/jakzal))
95 |
96 | ## [0.1.0](https://github.com/qandidate-labs/stack-request-id/tree/0.1.0) (2014-10-28)
97 |
98 | [Full Changelog](https://github.com/qandidate-labs/stack-request-id/compare/3dd14983adb5f4592dd464a7933f13e7d5815ec3...0.1.0)
99 |
100 |
101 |
102 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
103 |
--------------------------------------------------------------------------------
/test/Qandidate/Stack/RequestIdTest.php:
--------------------------------------------------------------------------------
1 |
9 | *
10 | * For the full copyright and license information, please view the LICENSE
11 | * file that was distributed with this source code.
12 | */
13 |
14 | namespace Qandidate\Stack;
15 |
16 | use PHPUnit\Framework\TestCase;
17 | use Symfony\Component\HttpFoundation\Request;
18 | use Symfony\Component\HttpFoundation\Response;
19 | use Symfony\Component\HttpKernel\HttpKernelInterface;
20 |
21 | class RequestIdTest extends TestCase
22 | {
23 | private $app;
24 | private $requestIdGenerator;
25 | private $stackedApp;
26 | private $header = 'X-Request-Id';
27 |
28 | public function setUp(): void
29 | {
30 | $this->requestIdGenerator = $this->createMock('Qandidate\Stack\RequestIdGenerator');
31 | $this->app = new MockApp($this->header);
32 | $this->stackedApp = new RequestId($this->app, $this->requestIdGenerator, $this->header);
33 | }
34 |
35 | /**
36 | * @test
37 | */
38 | public function it_calls_the_generator_when_no_request_id_is_present()
39 | {
40 | $this->requestIdGenerator->expects($this->once())
41 | ->method('generate');
42 |
43 | $this->stackedApp->handle($this->createRequest());
44 | }
45 |
46 | /**
47 | * @test
48 | */
49 | public function it_sets_the_request_id_in_the_header()
50 | {
51 | $this->requestIdGenerator->expects($this->once())
52 | ->method('generate')
53 | ->will($this->returnValue('yolo'));
54 |
55 | $this->stackedApp->handle($this->createRequest());
56 |
57 | $this->assertEquals('yolo', $this->app->getLastHeaderValue());
58 | }
59 |
60 | /**
61 | * @test
62 | */
63 | public function it_does_not_set_a_new_request_id_if_it_was_already_present()
64 | {
65 | $this->requestIdGenerator->expects($this->never())
66 | ->method('generate');
67 |
68 | $this->stackedApp->handle($this->createRequest('foo'));
69 |
70 | $this->assertEquals('foo', $this->app->getLastHeaderValue());
71 | }
72 |
73 | /**
74 | * @test
75 | */
76 | public function it_sets_the_request_id_in_the_response_header_if_enabled()
77 | {
78 | $this->stackedApp->enableResponseHeader();
79 |
80 | $this->requestIdGenerator->expects($this->any())
81 | ->method('generate')
82 | ->will($this->returnValue('yolo'));
83 |
84 | $response = $this->stackedApp->handle($this->createRequest());
85 |
86 | $this->assertSame('yolo', $response->headers->get($this->header));
87 | }
88 |
89 | /**
90 | * @test
91 | */
92 | public function it_sets_the_request_id_in_a_custom_response_header_if_given()
93 | {
94 | $this->stackedApp->enableResponseHeader('Request-Id');
95 |
96 | $this->requestIdGenerator->expects($this->any())
97 | ->method('generate')
98 | ->will($this->returnValue('yolo'));
99 |
100 | $response = $this->stackedApp->handle($this->createRequest());
101 |
102 | $this->assertSame('yolo', $response->headers->get('Request-Id'));
103 | }
104 |
105 | /**
106 | * @test
107 | */
108 | public function it_can_set_the_response_header_from_the_constructor_argument()
109 | {
110 | $this->requestIdGenerator->expects($this->any())
111 | ->method('generate')
112 | ->will($this->returnValue('yolo'));
113 |
114 | $responseHeader = 'Request-Id';
115 |
116 | $this->stackedApp->enableResponseHeader($responseHeader);
117 | $normalResponse = $this->stackedApp->handle($this->createRequest());
118 |
119 | $alternateStackedApp = new RequestId($this->app, $this->requestIdGenerator, $this->header, $responseHeader);
120 | $alternateResponse = $alternateStackedApp->handle($this->createRequest());
121 |
122 | $this->assertSame('yolo', $alternateResponse->headers->get($responseHeader));
123 | $this->assertSame(
124 | $normalResponse->headers->get($responseHeader),
125 | $alternateResponse->headers->get($responseHeader)
126 | );
127 | }
128 |
129 | /**
130 | * @test
131 | */
132 | public function it_can_override_the_response_header_argument_with_the_response_header_method()
133 | {
134 | $this->requestIdGenerator->expects($this->any())
135 | ->method('generate')
136 | ->will($this->returnValue('yolo'));
137 |
138 | $alternateStackedApp = new RequestId($this->app, $this->requestIdGenerator, $this->header, 'Bad-Request-Id');
139 | $alternateStackedApp->enableResponseHeader('Good-Request-Id');
140 |
141 | $response = $alternateStackedApp->handle($this->CreateRequest());
142 |
143 | $this->assertFalse($response->headers->has('Bad-Request-Id'));
144 | $this->assertTrue($response->headers->has('Good-Request-Id'));
145 | }
146 |
147 | /**
148 | * @test
149 | */
150 | public function it_does_not_set_the_request_id_in_the_response_header_by_default()
151 | {
152 | $this->requestIdGenerator->expects($this->any())
153 | ->method('generate')
154 | ->will($this->returnValue('yolo'));
155 |
156 | $response = $this->stackedApp->handle($this->createRequest());
157 |
158 | $this->assertFalse($response->headers->has($this->header), 'The request id is not added to the response by default');
159 | }
160 |
161 | private function createRequest($requestId = null)
162 | {
163 | $request = new Request();
164 |
165 | if ($requestId) {
166 | $request->headers->set($this->header, $requestId);
167 | }
168 |
169 | return $request;
170 | }
171 | }
172 |
173 | class MockApp implements HttpKernelInterface
174 | {
175 | private $headerValue;
176 | private $recordHeader;
177 |
178 | public function __construct($recordHeader)
179 | {
180 | $this->recordHeader = $recordHeader;
181 | }
182 |
183 | public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
184 | {
185 | $this->headerValue = $request->headers->get($this->recordHeader);
186 |
187 | return new Response();
188 | }
189 |
190 | public function getLastHeaderValue()
191 | {
192 | return $this->headerValue;
193 | }
194 | }
195 |
--------------------------------------------------------------------------------