Skip to content

Commit da8cef4

Browse files
committed
Added Tests
1 parent 6c65dad commit da8cef4

File tree

9 files changed

+379
-0
lines changed

9 files changed

+379
-0
lines changed

composer.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "andreja/laravel-middleware-correlation-id",
3+
"description": "Laravel Package to use a Correlation ID Middleware",
4+
"keywords": [
5+
"laravel",
6+
"correlation-id",
7+
"middleware",
8+
"tracing"
9+
],
10+
"homepage": "https://github.com/ajaaleixo/laravel-middleware-correlation-id",
11+
"require": {
12+
"php" : ">=7.0",
13+
"webpatser/laravel-uuid": "^3.0"
14+
},
15+
"require-dev": {
16+
"phpunit/phpunit": "^7.0",
17+
"orchestra/testbench": "^3.6"
18+
},
19+
"license": "MIT",
20+
"authors": [
21+
{
22+
"name": "André Aleixo",
23+
"email": "[email protected]"
24+
}
25+
],
26+
"autoload": {
27+
"psr-4": {
28+
"Ajaaleixo\\Middleware\\CorrelationId\\": "src"
29+
}
30+
},
31+
"autoload-dev": {
32+
"psr-4": {
33+
"Ajaaleixo\\Middleware\\CorrelationId\\Test\\": "tests"
34+
}
35+
},
36+
"extra": {
37+
"laravel": {
38+
"providers": [
39+
"Ajaaleixo\\Middleware\\CorrelationId\\ServiceProvider"
40+
]
41+
}
42+
}
43+
}

config/correlationid.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
return [
4+
5+
/*
6+
* True: Middleware will inject on Logs whatever sent correlation id on Headers array
7+
* False: Will not propagate
8+
*/
9+
'propagates' => true,
10+
11+
/*
12+
* Used to fetch from Headers array a correlation id
13+
*/
14+
'header_name' => 'X-CORRELATION-ID',
15+
16+
/*
17+
* Used to inject within context array in logs
18+
*/
19+
'param_name' => 'x_correlation_id',
20+
];

phpunit.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit bootstrap="vendor/autoload.php"
3+
backupGlobals="false"
4+
backupStaticAttributes="false"
5+
colors="true"
6+
verbose="true"
7+
convertErrorsToExceptions="true"
8+
convertNoticesToExceptions="true"
9+
convertWarningsToExceptions="true"
10+
processIsolation="false"
11+
stopOnFailure="false">
12+
<testsuites>
13+
<testsuite name="Unit Test Suite">
14+
<directory>tests</directory>
15+
</testsuite>
16+
</testsuites>
17+
<filter>
18+
<whitelist>
19+
<directory suffix=".php">src/</directory>
20+
</whitelist>
21+
</filter>
22+
</phpunit>

src/CorrelationId.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
namespace Ajaaleixo\Middleware\CorrelationId;
3+
4+
use \Webpatser\Uuid\Uuid;
5+
6+
class CorrelationId
7+
{
8+
/**
9+
* Creates a valid RFC 4122 standard correlation id
10+
*
11+
* @return string
12+
*/
13+
public static function id(): string
14+
{
15+
return (string) Uuid::generate(4);
16+
}
17+
18+
/**
19+
* @return string
20+
*/
21+
public static function getHeaderName(): string
22+
{
23+
return config('correlationid.header_name');
24+
}
25+
26+
/**
27+
* @return string
28+
*/
29+
public static function getParamName(): string
30+
{
31+
return config('correlationid.param_name');;
32+
}
33+
}

src/CorrelationIdMiddleware.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
namespace Ajaaleixo\Middleware\CorrelationId;
3+
4+
use Closure;
5+
use Illuminate\Foundation\Application;
6+
use Psr\Log\LoggerInterface;
7+
use Monolog\Logger as MonologLogger;
8+
use Illuminate\Log\Logger as IlluminateLogger;
9+
use Illuminate\Http\Request;
10+
11+
class CorrelationIdMiddleware
12+
{
13+
protected $logger;
14+
15+
public function __construct(Application $application)
16+
{
17+
/* @var $logger LoggerInterface */
18+
$this->logger = $application->get('log');
19+
20+
if (!Request::hasMacro('hasCorrelationId')) {
21+
Request::macro('hasCorrelationId', function() {
22+
if ($this->headers->has(CorrelationId::getHeaderName())) {
23+
return true;
24+
}
25+
return false;
26+
});
27+
}
28+
if (!Request::hasMacro('getCorrelationId')) {
29+
Request::macro('getCorrelationId', function($default = null) {
30+
if ($this->headers->has(CorrelationId::getHeaderName())) {
31+
return $this->headers->get(CorrelationId::getHeaderName());
32+
}
33+
return $default;
34+
});
35+
}
36+
if (!Request::hasMacro('setCorrelationId')) {
37+
Request::macro('setCorrelationId', function($cid) {
38+
$this->headers->set(CorrelationId::getHeaderName(), (string) $cid);
39+
return $this;
40+
});
41+
}
42+
}
43+
44+
/**
45+
* @param Request $request
46+
* @param Closure $next
47+
*
48+
* @return mixed
49+
*/
50+
public function handle(Request $request, Closure $next)
51+
{
52+
// Check if the request header already has a correlation id header
53+
if (!$request->headers->has(CorrelationId::getHeaderName())) {
54+
$request->headers->set(CorrelationId::getHeaderName(), (string)CorrelationId::id());
55+
}
56+
57+
if (!config('correlationid.propagates')) {
58+
return $next($request);
59+
}
60+
61+
$processor = new CorrelationIdProcessor(
62+
CorrelationId::getParamName(),
63+
$request->headers->get(CorrelationId::getHeaderName())
64+
);
65+
66+
if ($this->logger instanceof MonologLogger) {
67+
$this->logger->pushProcessor($processor);
68+
} elseif (method_exists($this->logger, 'getMonolog')) {
69+
$this->logger->getMonolog()->pushProcessor($processor);
70+
} elseif ($this->logger->driver() instanceof IlluminateLogger) {
71+
$logger = $this->logger->driver()->getLogger();
72+
if ($logger instanceof MonologLogger) {
73+
$this->logger->pushProcessor($processor);
74+
}
75+
}
76+
77+
return $next($request);
78+
}
79+
}

src/CorrelationIdProcessor.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
namespace Ajaaleixo\Middleware\CorrelationId;
3+
4+
class CorrelationIdProcessor
5+
{
6+
protected $correlationId = '';
7+
8+
protected $paramName;
9+
10+
public function __construct(string $paramName, $correlationId = null)
11+
{
12+
$this->paramName = $paramName;
13+
if ($correlationId !== null) {
14+
$this->correlationId = (string) $correlationId;
15+
}
16+
}
17+
18+
public function __invoke(array $record): array
19+
{
20+
if (!empty($this->correlationId)) {
21+
$record['context'][$this->paramName] = $this->correlationId;
22+
}
23+
24+
return $record;
25+
}
26+
27+
public function getCorrelationId(): string
28+
{
29+
return $this->correlationId;
30+
}
31+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
namespace Ajaaleixo\Middleware\CorrelationId;
3+
4+
use Illuminate\Support\ServiceProvider;
5+
6+
class CorrelationIdServiceProvider extends ServiceProvider
7+
{
8+
/**
9+
* Register the middleware
10+
*/
11+
public function register()
12+
{
13+
$this->app['router']->aliasMiddleware('correlation_id', CorrelationIdMiddleware::class);
14+
}
15+
}

tests/MiddlewareTest.php

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
namespace Ajaaleixo\Middleware\CorrelationId\Test;
3+
4+
use Ajaaleixo\Middleware\CorrelationId\CorrelationIdMiddleware;
5+
use Illuminate\Http\Request;
6+
use Illuminate\Http\Response;
7+
use Illuminate\Support\Facades\Log;
8+
use Webpatser\Uuid\Uuid;
9+
10+
class MiddlewareTest extends TestCase
11+
{
12+
protected $correlationIdMiddleware;
13+
14+
public function setUp()
15+
{
16+
parent::setUp();
17+
18+
$this->correlationIdMiddleware = new CorrelationIdMiddleware($this->app);
19+
}
20+
21+
/** @test */
22+
public function has_macros_when_middleware_runs()
23+
{
24+
// Prepare
25+
$request = $this->makeRequestWithCorrelationHeader();
26+
27+
// Test
28+
$this->runMiddleware($this->correlationIdMiddleware, $request);
29+
30+
// Assert
31+
$this->assertTrue($request->hasMacro('hasCorrelationId'));
32+
$this->assertTrue($request->hasMacro('getCorrelationId'));
33+
$this->assertTrue($request->hasMacro('setCorrelationId'));
34+
}
35+
36+
/** @test */
37+
public function correlation_propagates_to_logs()
38+
{
39+
// Prepare
40+
$request = $this->makeRequestWithCorrelationHeader();
41+
$correlationId = $request->header('x-correlation-id');
42+
$logMessage = 'This is my n log entry';
43+
$correlationParam = config('correlationid.param_name');
44+
45+
// Test
46+
$this->runMiddleware($this->correlationIdMiddleware, $request);
47+
Log::info($logMessage);
48+
49+
$lastLogLine = $this->getLastLogLine();
50+
$this->assertContains($logMessage, $lastLogLine);
51+
$this->assertContains($correlationId, $lastLogLine);
52+
$this->assertContains($correlationParam, $lastLogLine);
53+
}
54+
55+
/** @test */
56+
public function correlation_does_not_propagate_to_logs()
57+
{
58+
// Prepare
59+
$request = $this->makeRequestWithoutCorrelationHeader();
60+
$this->app['config']->set('correlationid.propagates', false);
61+
$logMessage = 'This is a log without correlation id';
62+
$correlationParam = config('correlationid.param_name');
63+
64+
// Test
65+
$this->runMiddleware($this->correlationIdMiddleware, $request);
66+
Log::info($logMessage);
67+
68+
$lastLogLine = $this->getLastLogLine();
69+
$this->assertContains($logMessage, $lastLogLine);
70+
$this->assertNotContains($correlationParam, $lastLogLine);
71+
}
72+
73+
protected function runMiddleware($middleware, $request)
74+
{
75+
return $middleware->handle($request, function () {
76+
return (new Response())->setContent('<html></html>');
77+
});
78+
}
79+
80+
protected function makeRequestWithCorrelationHeader()
81+
{
82+
$request = new Request();
83+
$request->headers->add([config('correlationid.header_name') => (string) Uuid::generate(4)]);
84+
85+
return $request;
86+
}
87+
88+
protected function makeRequestWithoutCorrelationHeader()
89+
{
90+
return new Request();
91+
}
92+
93+
protected function getLastLogLine()
94+
{
95+
$content = file_get_contents($this->app['config']['logging']['channels']['single']['path']);
96+
$arrayContent = explode("\n", $content);
97+
98+
return $arrayContent[count($arrayContent)-2];
99+
}
100+
}

tests/TestCase.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
namespace Ajaaleixo\Middleware\CorrelationId\Test;
3+
4+
use Ajaaleixo\Middleware\CorrelationId\CorrelationIdServiceProvider;
5+
use Orchestra\Testbench\TestCase as OrchestraTestCase;
6+
7+
abstract class TestCase extends OrchestraTestCase
8+
{
9+
/**
10+
* @param \Illuminate\Foundation\Application $app
11+
*
12+
* @return array
13+
*/
14+
protected function getPackageProviders($app)
15+
{
16+
return [
17+
CorrelationIdServiceProvider::class,
18+
];
19+
}
20+
21+
/**
22+
* Define environment setup.
23+
*
24+
* @param \Illuminate\Foundation\Application $app
25+
* @return void
26+
*/
27+
protected function getEnvironmentSetUp($app)
28+
{
29+
// Setup default database to use sqlite :memory:
30+
$app['config']->set('correlationid', [
31+
'propagates' => true,
32+
'header_name' => 'X-CORRELATION-ID',
33+
'param_name' => 'x_correlation_id',
34+
]);
35+
}
36+
}

0 commit comments

Comments
 (0)