Skip to content

Commit

Permalink
Added Tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ajaaleixo committed Mar 5, 2018
1 parent 6c65dad commit da8cef4
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 0 deletions.
43 changes: 43 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "andreja/laravel-middleware-correlation-id",
"description": "Laravel Package to use a Correlation ID Middleware",
"keywords": [
"laravel",
"correlation-id",
"middleware",
"tracing"
],
"homepage": "https://github.com/ajaaleixo/laravel-middleware-correlation-id",
"require": {
"php" : ">=7.0",
"webpatser/laravel-uuid": "^3.0"
},
"require-dev": {
"phpunit/phpunit": "^7.0",
"orchestra/testbench": "^3.6"
},
"license": "MIT",
"authors": [
{
"name": "André Aleixo",
"email": "[email protected]"
}
],
"autoload": {
"psr-4": {
"Ajaaleixo\\Middleware\\CorrelationId\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Ajaaleixo\\Middleware\\CorrelationId\\Test\\": "tests"
}
},
"extra": {
"laravel": {
"providers": [
"Ajaaleixo\\Middleware\\CorrelationId\\ServiceProvider"
]
}
}
}
20 changes: 20 additions & 0 deletions config/correlationid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

return [

/*
* True: Middleware will inject on Logs whatever sent correlation id on Headers array
* False: Will not propagate
*/
'propagates' => true,

/*
* Used to fetch from Headers array a correlation id
*/
'header_name' => 'X-CORRELATION-ID',

/*
* Used to inject within context array in logs
*/
'param_name' => 'x_correlation_id',
];
22 changes: 22 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
verbose="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Unit Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
</phpunit>
33 changes: 33 additions & 0 deletions src/CorrelationId.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
namespace Ajaaleixo\Middleware\CorrelationId;

use \Webpatser\Uuid\Uuid;

class CorrelationId
{
/**
* Creates a valid RFC 4122 standard correlation id
*
* @return string
*/
public static function id(): string
{
return (string) Uuid::generate(4);
}

/**
* @return string
*/
public static function getHeaderName(): string
{
return config('correlationid.header_name');
}

/**
* @return string
*/
public static function getParamName(): string
{
return config('correlationid.param_name');;
}
}
79 changes: 79 additions & 0 deletions src/CorrelationIdMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
namespace Ajaaleixo\Middleware\CorrelationId;

use Closure;
use Illuminate\Foundation\Application;
use Psr\Log\LoggerInterface;
use Monolog\Logger as MonologLogger;
use Illuminate\Log\Logger as IlluminateLogger;
use Illuminate\Http\Request;

class CorrelationIdMiddleware
{
protected $logger;

public function __construct(Application $application)
{
/* @var $logger LoggerInterface */
$this->logger = $application->get('log');

if (!Request::hasMacro('hasCorrelationId')) {
Request::macro('hasCorrelationId', function() {
if ($this->headers->has(CorrelationId::getHeaderName())) {
return true;
}
return false;
});
}
if (!Request::hasMacro('getCorrelationId')) {
Request::macro('getCorrelationId', function($default = null) {
if ($this->headers->has(CorrelationId::getHeaderName())) {
return $this->headers->get(CorrelationId::getHeaderName());
}
return $default;
});
}
if (!Request::hasMacro('setCorrelationId')) {
Request::macro('setCorrelationId', function($cid) {
$this->headers->set(CorrelationId::getHeaderName(), (string) $cid);
return $this;
});
}
}

/**
* @param Request $request
* @param Closure $next
*
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
// Check if the request header already has a correlation id header
if (!$request->headers->has(CorrelationId::getHeaderName())) {
$request->headers->set(CorrelationId::getHeaderName(), (string)CorrelationId::id());
}

if (!config('correlationid.propagates')) {
return $next($request);
}

$processor = new CorrelationIdProcessor(
CorrelationId::getParamName(),
$request->headers->get(CorrelationId::getHeaderName())
);

if ($this->logger instanceof MonologLogger) {
$this->logger->pushProcessor($processor);
} elseif (method_exists($this->logger, 'getMonolog')) {
$this->logger->getMonolog()->pushProcessor($processor);
} elseif ($this->logger->driver() instanceof IlluminateLogger) {
$logger = $this->logger->driver()->getLogger();
if ($logger instanceof MonologLogger) {
$this->logger->pushProcessor($processor);
}
}

return $next($request);
}
}
31 changes: 31 additions & 0 deletions src/CorrelationIdProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
namespace Ajaaleixo\Middleware\CorrelationId;

class CorrelationIdProcessor
{
protected $correlationId = '';

protected $paramName;

public function __construct(string $paramName, $correlationId = null)
{
$this->paramName = $paramName;
if ($correlationId !== null) {
$this->correlationId = (string) $correlationId;
}
}

public function __invoke(array $record): array
{
if (!empty($this->correlationId)) {
$record['context'][$this->paramName] = $this->correlationId;
}

return $record;
}

public function getCorrelationId(): string
{
return $this->correlationId;
}
}
15 changes: 15 additions & 0 deletions src/CorrelationIdServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
namespace Ajaaleixo\Middleware\CorrelationId;

use Illuminate\Support\ServiceProvider;

class CorrelationIdServiceProvider extends ServiceProvider
{
/**
* Register the middleware
*/
public function register()
{
$this->app['router']->aliasMiddleware('correlation_id', CorrelationIdMiddleware::class);
}
}
100 changes: 100 additions & 0 deletions tests/MiddlewareTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php
namespace Ajaaleixo\Middleware\CorrelationId\Test;

use Ajaaleixo\Middleware\CorrelationId\CorrelationIdMiddleware;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
use Webpatser\Uuid\Uuid;

class MiddlewareTest extends TestCase
{
protected $correlationIdMiddleware;

public function setUp()
{
parent::setUp();

$this->correlationIdMiddleware = new CorrelationIdMiddleware($this->app);
}

/** @test */
public function has_macros_when_middleware_runs()
{
// Prepare
$request = $this->makeRequestWithCorrelationHeader();

// Test
$this->runMiddleware($this->correlationIdMiddleware, $request);

// Assert
$this->assertTrue($request->hasMacro('hasCorrelationId'));
$this->assertTrue($request->hasMacro('getCorrelationId'));
$this->assertTrue($request->hasMacro('setCorrelationId'));
}

/** @test */
public function correlation_propagates_to_logs()
{
// Prepare
$request = $this->makeRequestWithCorrelationHeader();
$correlationId = $request->header('x-correlation-id');
$logMessage = 'This is my n log entry';
$correlationParam = config('correlationid.param_name');

// Test
$this->runMiddleware($this->correlationIdMiddleware, $request);
Log::info($logMessage);

$lastLogLine = $this->getLastLogLine();
$this->assertContains($logMessage, $lastLogLine);
$this->assertContains($correlationId, $lastLogLine);
$this->assertContains($correlationParam, $lastLogLine);
}

/** @test */
public function correlation_does_not_propagate_to_logs()
{
// Prepare
$request = $this->makeRequestWithoutCorrelationHeader();
$this->app['config']->set('correlationid.propagates', false);
$logMessage = 'This is a log without correlation id';
$correlationParam = config('correlationid.param_name');

// Test
$this->runMiddleware($this->correlationIdMiddleware, $request);
Log::info($logMessage);

$lastLogLine = $this->getLastLogLine();
$this->assertContains($logMessage, $lastLogLine);
$this->assertNotContains($correlationParam, $lastLogLine);
}

protected function runMiddleware($middleware, $request)
{
return $middleware->handle($request, function () {
return (new Response())->setContent('<html></html>');
});
}

protected function makeRequestWithCorrelationHeader()
{
$request = new Request();
$request->headers->add([config('correlationid.header_name') => (string) Uuid::generate(4)]);

return $request;
}

protected function makeRequestWithoutCorrelationHeader()
{
return new Request();
}

protected function getLastLogLine()
{
$content = file_get_contents($this->app['config']['logging']['channels']['single']['path']);
$arrayContent = explode("\n", $content);

return $arrayContent[count($arrayContent)-2];
}
}
36 changes: 36 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
namespace Ajaaleixo\Middleware\CorrelationId\Test;

use Ajaaleixo\Middleware\CorrelationId\CorrelationIdServiceProvider;
use Orchestra\Testbench\TestCase as OrchestraTestCase;

abstract class TestCase extends OrchestraTestCase
{
/**
* @param \Illuminate\Foundation\Application $app
*
* @return array
*/
protected function getPackageProviders($app)
{
return [
CorrelationIdServiceProvider::class,
];
}

/**
* Define environment setup.
*
* @param \Illuminate\Foundation\Application $app
* @return void
*/
protected function getEnvironmentSetUp($app)
{
// Setup default database to use sqlite :memory:
$app['config']->set('correlationid', [
'propagates' => true,
'header_name' => 'X-CORRELATION-ID',
'param_name' => 'x_correlation_id',
]);
}
}

0 comments on commit da8cef4

Please sign in to comment.