Skip to content

Commit

Permalink
Merge pull request #23 from netlogix/feature/logging-rules
Browse files Browse the repository at this point in the history
  • Loading branch information
paxuclus authored Feb 8, 2023
2 parents 54c037c + 46b65ae commit 4561240
Show file tree
Hide file tree
Showing 13 changed files with 406 additions and 40 deletions.
28 changes: 23 additions & 5 deletions Classes/Integration/NetlogixIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
namespace Netlogix\Sentry\Integration;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\ObjectManagement\CompileTimeObjectManager;
use Neos\Utility\Files;
use Netlogix\Sentry\ExceptionHandler\ExceptionRenderingOptionsResolver;
use Neos\Utility\PositionalArraySorter;
use Netlogix\Sentry\Scope\ScopeProvider;
use Sentry\Event;
use Sentry\EventHint;
Expand Down Expand Up @@ -54,10 +55,27 @@ public static function handleEvent(Event $event, EventHint $hint): ?Event
return $event;
}

if ($hint->exception instanceof Throwable
&& ($optionsResolver = Bootstrap::$staticObjectManager->get(ExceptionRenderingOptionsResolver::class)) !== null) {
$options = $optionsResolver->resolveRenderingOptionsForThrowable($hint->exception);
if (!($options['logException'] ?? true)) {
if (
$hint->exception instanceof Throwable &&
($configurationManager = Bootstrap::$staticObjectManager->get(ConfigurationManager::class)) !== null
) {
$rules = $configurationManager->getConfiguration(
ConfigurationManager::CONFIGURATION_TYPE_SETTINGS,
'Netlogix.Sentry.loggingRules.rules'
) ?? [];

$decision = true;

$positionalArraySorter = new PositionalArraySorter($rules);
$sortedRules = $positionalArraySorter->toArray();

foreach (array_keys($sortedRules) as $rule) {
$decision = Bootstrap::$staticObjectManager
->get($rule)
->decide($hint->exception, $decision);
}

if (!$decision) {
return null;
}
}
Expand Down
27 changes: 27 additions & 0 deletions Classes/LoggingRule/AllowListRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);

namespace Netlogix\Sentry\LoggingRule;

use Neos\Flow\Annotations as Flow;
use Throwable;

class AllowListRule implements LoggingRule
{
/**
* @Flow\InjectConfiguration(path="loggingRules.allowList")
* @var array
*/
protected $allowList = [];

public function decide(Throwable $throwable, bool $previousDecision): bool
{
foreach ($this->allowList as $allowedThrowableClassName) {
if ($throwable instanceof $allowedThrowableClassName) {
return true;
}
}

return $previousDecision;
}
}
27 changes: 27 additions & 0 deletions Classes/LoggingRule/DenyListRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);

namespace Netlogix\Sentry\LoggingRule;

use Neos\Flow\Annotations as Flow;
use Throwable;

class DenyListRule implements LoggingRule
{
/**
* @Flow\InjectConfiguration(path="loggingRules.denyList")
* @var array
*/
protected $denyList = [];

public function decide(Throwable $throwable, bool $previousDecision): bool
{
foreach ($this->denyList as $deniedThrowableClassName) {
if ($throwable instanceof $deniedThrowableClassName) {
return false;
}
}

return $previousDecision;
}
}
24 changes: 24 additions & 0 deletions Classes/LoggingRule/ExceptionHandlerRenderingGroupsRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Netlogix\Sentry\LoggingRule;

use Neos\Flow\Annotations as Flow;
use Netlogix\Sentry\ExceptionHandler\ExceptionRenderingOptionsResolver;
use Throwable;

class ExceptionHandlerRenderingGroupsRule implements LoggingRule
{
/**
* @Flow\Inject
* @var ExceptionRenderingOptionsResolver
*/
protected $optionsResolver;

public function decide(Throwable $throwable, bool $previousDecision): bool
{
$options = $this->optionsResolver->resolveRenderingOptionsForThrowable($throwable);
return $options['logException'] ?? $previousDecision;
}
}
12 changes: 12 additions & 0 deletions Classes/LoggingRule/LoggingRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Netlogix\Sentry\LoggingRule;

use Throwable;

interface LoggingRule
{
public function decide(Throwable $throwable, bool $previousDecision): bool;
}
13 changes: 13 additions & 0 deletions Configuration/Settings.LoggingRules.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Netlogix:
Sentry:
loggingRules:
rules:
'Netlogix\Sentry\LoggingRule\ExceptionHandlerRenderingGroupsRule': '10'
'Netlogix\Sentry\LoggingRule\AllowListRule': '20'
'Netlogix\Sentry\LoggingRule\DenyListRule': '30'

allowList: []

denyList:
- 'Neos\Flow\Security\Exception\InvalidHashException'
- 'Neos\Flow\Security\Exception\InvalidArgumentForHashGenerationException'
142 changes: 109 additions & 33 deletions Tests/Unit/Integration/NetlogixIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@

namespace Netlogix\Sentry\Tests\Unit\Integration;

use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
use Neos\Flow\Tests\UnitTestCase;
use Netlogix\Sentry\Exception\Test;
use Netlogix\Sentry\ExceptionHandler\ExceptionRenderingOptionsResolver;
use Netlogix\Sentry\Integration\NetlogixIntegration;
use Netlogix\Sentry\LoggingRule\ExceptionHandlerRenderingGroupsRule;
use Netlogix\Sentry\Scope\ScopeProvider;
use Sentry\Event;
use Sentry\EventHint;
Expand Down Expand Up @@ -52,27 +54,39 @@ public function If_event_hint_does_not_contain_an_exception_it_is_not_filtered()

/**
* @test
* @dataProvider provideLogExceptionExpectations
* @dataProvider provideExceptionLoggingExpectations
*/
public function Event_is_logged_depending_on_logException(array $options, bool $isLogged): void
public function Event_is_logged_depending_on_loggingRules(bool $ruleResult, bool $isLogged): void
{
$objectManager = $this->getMockBuilder(ObjectManagerInterface::class)
->getMock();

$optionsResolver = new ExceptionRenderingOptionsResolver();
$optionsResolver->setOptions([
'renderingGroups' => [
'netlogixSentryTest' => [
'matchingExceptionClassNames' => [Test::class],
'options' => $options
]
]
]);
$configurationManager = $this->getMockBuilder(ConfigurationManager::class)
->disableOriginalConstructor()
->getMock();

$configurationManager
->method('getConfiguration')
->with( ConfigurationManager::CONFIGURATION_TYPE_SETTINGS,
'Netlogix.Sentry.loggingRules.rules')
->willReturn([
'Netlogix\Sentry\LoggingRule\ExceptionHandlerRenderingGroupsRule' => '10'
]);

$exceptionHandlerRenderingGroupsRule = $this->getMockBuilder(ExceptionHandlerRenderingGroupsRule::class)
->disableOriginalConstructor()
->getMock();

$exceptionHandlerRenderingGroupsRule
->method('decide')
->willReturn($ruleResult);

$objectManager
->method('get')
->with(ExceptionRenderingOptionsResolver::class)
->willReturn($optionsResolver);
->will($this->returnValueMap([
[ConfigurationManager::class, $configurationManager],
[ExceptionHandlerRenderingGroupsRule::class, $exceptionHandlerRenderingGroupsRule]
]));

Bootstrap::$staticObjectManager = $objectManager;

Expand All @@ -88,24 +102,48 @@ public function Event_is_logged_depending_on_logException(array $options, bool $
}
}

public function provideLogExceptionExpectations(): iterable
/**
* @test
*/
public function Event_is_logged_when_no_rules_are_defined(): void
{
yield 'If logException is false, null is returned' => [
'options' => [
'logException' => false,
],
'isLogged' => false,
];
$objectManager = $this->getMockBuilder(ObjectManagerInterface::class)
->getMock();

yield 'If logException is true, the exception is logged' => [
'options' => [
'logException' => true,
],
'isLogged' => true,
$configurationManager = $this->getMockBuilder(ConfigurationManager::class)
->disableOriginalConstructor()
->getMock();

$configurationManager
->method('getConfiguration')
->with( ConfigurationManager::CONFIGURATION_TYPE_SETTINGS,
'Netlogix.Sentry.loggingRules.rules')
->willReturn([]);

$objectManager
->method('get')
->with(ConfigurationManager::class)
->willReturn($configurationManager);

Bootstrap::$staticObjectManager = $objectManager;

$throwable = new Test('foo', 1612089648);

$event = Event::createEvent();
$hint = EventHint::fromArray(['exception' => $throwable]);

self::assertSame($event, NetlogixIntegration::handleEvent($event, $hint));
}

public function provideExceptionLoggingExpectations(): iterable
{
yield 'If ruleResult is false, null is returned' => [
'ruleResult' => false,
'isLogged' => false,
];

yield 'If logException is unset, the exception is logged' => [
'options' => [],
yield 'If ruleResult is true, the exception is logged' => [
'ruleResult' => true,
'isLogged' => true,
];
}
Expand Down Expand Up @@ -156,10 +194,23 @@ public function Event_is_enriched_with_ScopeProvider_data(): void
->method('collectUser')
->willReturn(['username' => 'lars']);

$configurationManager = $this->getMockBuilder(ConfigurationManager::class)
->disableOriginalConstructor()
->getMock();

$configurationManager
->method('getConfiguration')
->with( ConfigurationManager::CONFIGURATION_TYPE_SETTINGS,
'Netlogix.Sentry.loggingRules.rules')
->willReturn([]);

$objectManager
->method('get')
->withConsecutive([ExceptionRenderingOptionsResolver::class], [ScopeProvider::class])
->willReturnOnConsecutiveCalls(new ExceptionRenderingOptionsResolver(), $scopeProvider);
->will($this->returnValueMap([
[ConfigurationManager::class, $configurationManager],
[ExceptionHandlerRenderingGroupsRule::class, new ExceptionRenderingOptionsResolver()],
[ScopeProvider::class, $scopeProvider],
]));

Bootstrap::$staticObjectManager = $objectManager;

Expand Down Expand Up @@ -216,10 +267,22 @@ public function Events_are_also_enriched_if_hint_does_not_contain_a_throwable():
->method('collectUser')
->willReturn([]);

$configurationManager = $this->getMockBuilder(ConfigurationManager::class)
->disableOriginalConstructor()
->getMock();

$configurationManager
->method('getConfiguration')
->with( ConfigurationManager::CONFIGURATION_TYPE_SETTINGS,
'Netlogix.Sentry.loggingRules.rules')
->willReturn([]);

$objectManager
->method('get')
->with(ScopeProvider::class)
->willReturn($scopeProvider);
->will($this->returnValueMap([
[ConfigurationManager::class, $configurationManager],
[ScopeProvider::class, $scopeProvider],
]));

Bootstrap::$staticObjectManager = $objectManager;

Expand Down Expand Up @@ -251,10 +314,23 @@ public function ScopeProvider_receives_the_current_Exception(): void
->method('withThrowable')
->with($throwable);

$configurationManager = $this->getMockBuilder(ConfigurationManager::class)
->disableOriginalConstructor()
->getMock();

$configurationManager
->method('getConfiguration')
->with( ConfigurationManager::CONFIGURATION_TYPE_SETTINGS,
'Netlogix.Sentry.loggingRules.rules')
->willReturn([]);

$objectManager
->method('get')
->withConsecutive([ExceptionRenderingOptionsResolver::class], [ScopeProvider::class])
->willReturnOnConsecutiveCalls(new ExceptionRenderingOptionsResolver(), $scopeProvider);
->will($this->returnValueMap([
[ConfigurationManager::class, $configurationManager],
[ExceptionRenderingOptionsResolver::class, new ExceptionRenderingOptionsResolver()],
[ScopeProvider::class, $scopeProvider],
]));

Bootstrap::$staticObjectManager = $objectManager;

Expand Down
38 changes: 38 additions & 0 deletions Tests/Unit/LoggingRule/AllowListRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Netlogix\Sentry\Tests\Unit\LoggingRule;

use Neos\Flow\Tests\UnitTestCase;
use Netlogix\Sentry\Exception\Test;
use Netlogix\Sentry\LoggingRule\AllowListRule;

class AllowListRuleTest extends UnitTestCase
{
/**
* @test
*/
public function if_allow_list_contains_class_decision_should_be_true(): void
{
$allowListRule = $this->getAccessibleMock(AllowListRule::class, ['dummy']);
$allowListRule->_set('allowList', [
Test::class
]);

self::assertEquals(true, $allowListRule->decide(new Test(), false));
}

/**
* @test
*/
public function if_allow_list_not_contains_class_decision_should_be_equal_to_previous_decision(): void
{
$allowListRule = $this->getAccessibleMock(AllowListRule::class, ['dummy']);
$allowListRule->_set('allowList', []);

$previousDecision = false;

self::assertEquals($previousDecision, $allowListRule->decide(new Test(), $previousDecision));
}
}
Loading

0 comments on commit 4561240

Please sign in to comment.