Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add class_serializers option #245

Merged
merged 9 commits into from Sep 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,7 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]
- Add forward compatibility with Symfony 5 (#235, thanks to @garak)
- Fix Hub initialization for `ErrorListener` (#243, thanks to @teohhanhui)
- Fix compatibility with sentry/sentry 2.2+ (#244)
- Add support for `class_serializers` option (#245)

## 3.1.0 - 2019-07-02
- Add support for Symfony 2.8 (#233, thanks to @nocive)
Expand Down
22 changes: 19 additions & 3 deletions src/DependencyInjection/Configuration.php
Expand Up @@ -57,6 +57,11 @@ public function getConfigTreeBuilder(): TreeBuilder
if (PrettyVersions::getVersion('sentry/sentry')->getPrettyVersion() !== '2.0.0') {
$optionsChildNodes->booleanNode('capture_silenced_errors');
}
if ($this->classSerializersAreSupported()) {
$optionsChildNodes->arrayNode('class_serializers')
->defaultValue([])
->prototype('scalar');
}
$optionsChildNodes->integerNode('context_lines')
->min(0)
->max(99);
Expand All @@ -80,13 +85,13 @@ public function getConfigTreeBuilder(): TreeBuilder
->prototype('scalar')
->validate()
->ifTrue(function ($value): bool {
if (! is_string($value)) {
if (! is_string($value) && '' != $value) {
return true;
}

return '@' !== substr($value, 0, 1);
return '@' !== $value[0];
})
->thenInvalid('Expecting service reference, got %s');
->thenInvalid('Expecting service reference, got "%s"');
$optionsChildNodes->scalarNode('logger');
$optionsChildNodes->integerNode('max_breadcrumbs')
->min(1);
Expand Down Expand Up @@ -163,4 +168,15 @@ private function isNotAValidCallback(): \Closure
return true;
};
}

private function classSerializersAreSupported(): bool
{
try {
new Options(['class_serializers' => []]);

return true;
} catch (\Throwable $throwable) {
return false;
}
}
}
31 changes: 17 additions & 14 deletions src/DependencyInjection/SentryExtension.php
Expand Up @@ -14,7 +14,6 @@
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
Expand Down Expand Up @@ -97,11 +96,22 @@ private function passConfigurationToOptions(ContainerBuilder $container, array $
}

if (\array_key_exists('before_send', $processedOptions)) {
$this->mapCallableValue($options, 'setBeforeSendCallback', $processedOptions['before_send']);
$beforeSendCallable = $this->valueToCallable($processedOptions['before_send']);
$options->addMethodCall('setBeforeSendCallback', [$beforeSendCallable]);
}

if (\array_key_exists('before_breadcrumb', $processedOptions)) {
$this->mapCallableValue($options, 'setBeforeBreadcrumbCallback', $processedOptions['before_breadcrumb']);
$beforeBreadcrumbCallable = $this->valueToCallable($processedOptions['before_breadcrumb']);
$options->addMethodCall('setBeforeBreadcrumbCallback', [$beforeBreadcrumbCallable]);
}

if (\array_key_exists('class_serializers', $processedOptions)) {
$classSerializers = [];
foreach ($processedOptions['class_serializers'] as $class => $serializer) {
$classSerializers[$class] = $this->valueToCallable($serializer);
}

$options->addMethodCall('setClassSerializers', [$classSerializers]);
}

if (\array_key_exists('integrations', $processedOptions)) {
Expand All @@ -114,20 +124,13 @@ private function passConfigurationToOptions(ContainerBuilder $container, array $
}
}

/**
* @param Definition $options
* @param string $method
* @param callable|string $optionValue
*/
private function mapCallableValue(Definition $options, string $method, $optionValue): void
private function valueToCallable($value)
{
if (is_string($optionValue) && 0 === strpos($optionValue, '@')) {
$beforeSend = new Reference(substr($optionValue, 1));
} else {
$beforeSend = $optionValue;
if (is_string($value) && 0 === strpos($value, '@')) {
return new Reference(substr($value, 1));
}

$options->addMethodCall($method, [$beforeSend]);
return $value;
}

/**
Expand Down
1 change: 0 additions & 1 deletion src/EventListener/ConsoleListener.php
Expand Up @@ -3,7 +3,6 @@
namespace Sentry\SentryBundle\EventListener;

use Sentry\SentryBundle\SentryBundle;
use Sentry\State\Hub;
use Sentry\State\HubInterface;
use Sentry\State\Scope;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
Expand Down
1 change: 0 additions & 1 deletion src/EventListener/RequestListener.php
Expand Up @@ -3,7 +3,6 @@
namespace Sentry\SentryBundle\EventListener;

use Sentry\SentryBundle\SentryBundle;
use Sentry\State\Hub;
use Sentry\State\HubInterface;
use Sentry\State\Scope;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
Expand Down
32 changes: 32 additions & 0 deletions test/BaseTestCase.php
@@ -0,0 +1,32 @@
<?php

namespace Sentry\SentryBundle\Test;

use PHPUnit\Framework\TestCase;
use Sentry\Options;
use Sentry\SentryBundle\Test\DependencyInjection\ConfigurationTest;

abstract class BaseTestCase extends TestCase
{
public const SUPPORTED_SENTRY_OPTIONS_COUNT = 23;

protected function classSerializersAreSupported(): bool
{
try {
new Options(['class_serializers' => []]);

return true;
} catch (\Throwable $throwable) {
return false;
}
}

protected function getSupportedOptionsCount(): int
{
if ($this->classSerializersAreSupported()) {
return ConfigurationTest::SUPPORTED_SENTRY_OPTIONS_COUNT + 1;
}

return ConfigurationTest::SUPPORTED_SENTRY_OPTIONS_COUNT;
}
}
29 changes: 22 additions & 7 deletions test/DependencyInjection/ConfigurationTest.php
Expand Up @@ -3,23 +3,21 @@
namespace Sentry\SentryBundle\Test\DependencyInjection;

use Jean85\PrettyVersions;
use PHPUnit\Framework\TestCase;
use Sentry\Options;
use Sentry\SentryBundle\DependencyInjection\Configuration;
use Sentry\SentryBundle\Test\BaseTestCase;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\Definition\Processor;
use Symfony\Component\HttpKernel\Kernel;

class ConfigurationTest extends TestCase
class ConfigurationTest extends BaseTestCase
{
public const SUPPORTED_SENTRY_OPTIONS_COUNT = 23;

public function testDataProviderIsMappingTheRightNumberOfOptions(): void
{
$providerData = $this->optionValuesProvider();
$supportedOptions = \array_unique(\array_column($providerData, 0));

$expectedCount = self::SUPPORTED_SENTRY_OPTIONS_COUNT;
$expectedCount = $this->getSupportedOptionsCount();

if (PrettyVersions::getVersion('sentry/sentry')->getPrettyVersion() !== '2.0.0') {
++$expectedCount;
Expand All @@ -38,7 +36,7 @@ public function testInvalidDataProviderIsMappingTheRightNumberOfOptions(): void
$supportedOptions = \array_unique(\array_column($providerData, 0));

$this->assertCount(
self::SUPPORTED_SENTRY_OPTIONS_COUNT,
$this->getSupportedOptionsCount(),
$supportedOptions,
'Provider for invalid configuration options mismatch: ' . PHP_EOL . print_r($supportedOptions, true)
);
Expand Down Expand Up @@ -76,6 +74,10 @@ public function testConfigurationDefaults(): void
$expectedDefaults['options']['in_app_exclude'][1] = '%kernel.project_dir%/vendor';
}

if ($this->classSerializersAreSupported()) {
$expectedDefaults['options']['class_serializers'] = [];
}

$this->assertEquals($expectedDefaults, $processed);
$this->assertArrayNotHasKey('server_name', $processed['options'], 'server_name has to be fetched at runtime, not before (see #181)');
}
Expand Down Expand Up @@ -127,6 +129,10 @@ public function optionValuesProvider(): array
$options[] = ['capture_silenced_errors', true];
}

if ($this->classSerializersAreSupported()) {
$options[] = ['class_serializers', ['count']];
}

return $options;
}

Expand All @@ -144,7 +150,7 @@ public function testInvalidValues(string $option, $value): void

public function invalidValuesProvider(): array
{
return [
$values = [
['attach_stacktrace', 'string'],
['before_breadcrumb', 'this is not a callable'],
['before_breadcrumb', [$this, 'is not a callable']],
Expand Down Expand Up @@ -184,6 +190,15 @@ public function invalidValuesProvider(): array
['server_name', []],
['tags', 'invalid-unmapped-tag'],
];

if ($this->classSerializersAreSupported()) {
$values[] = ['class_serializers', 'this is not a callable'];
$values[] = ['class_serializers', [$this, 'is not a callable']];
$values[] = ['class_serializers', false];
$values[] = ['class_serializers', -1];
}

return $values;
}

private function processConfiguration(array $values): array
Expand Down
25 changes: 20 additions & 5 deletions test/DependencyInjection/SentryExtensionTest.php
Expand Up @@ -3,12 +3,12 @@
namespace Sentry\SentryBundle\Test\DependencyInjection;

use Jean85\PrettyVersions;
use PHPUnit\Framework\TestCase;
use Sentry\Breadcrumb;
use Sentry\Event;
use Sentry\Integration\IntegrationInterface;
use Sentry\Options;
use Sentry\SentryBundle\DependencyInjection\SentryExtension;
use Sentry\SentryBundle\Test\BaseTestCase;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand All @@ -17,7 +17,7 @@
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Kernel;

class SentryExtensionTest extends TestCase
class SentryExtensionTest extends BaseTestCase
{
private const OPTIONS_TEST_PUBLIC_ALIAS = 'sentry.options.public_alias';

Expand All @@ -27,10 +27,10 @@ public function testDataProviderIsMappingTheRightNumberOfOptions(): void
$supportedOptions = \array_unique(\array_column($providerData, 0));

// subtracted one is `integration`, which cannot be tested with the provider
$expectedCount = ConfigurationTest::SUPPORTED_SENTRY_OPTIONS_COUNT - 1;
$expectedCount = $this->getSupportedOptionsCount();

if (PrettyVersions::getVersion('sentry/sentry')->getPrettyVersion() !== '2.0.0') {
++$expectedCount;
if (PrettyVersions::getVersion('sentry/sentry')->getPrettyVersion() === '2.0.0') {
--$expectedCount;
}

$this->assertCount(
Expand Down Expand Up @@ -138,6 +138,16 @@ public function optionsValueProvider(): array
$options[] = ['capture_silenced_errors', true, 'shouldCaptureSilencedErrors'];
}

if ($this->classSerializersAreSupported()) {
$options['class_serializer'] = [
'class_serializers',
[
self::class => __NAMESPACE__ . '\mockClassSerializer',
],
'getClassSerializers',
];
}

return $options;
}

Expand Down Expand Up @@ -375,6 +385,11 @@ function mockBeforeBreadcrumb(Breadcrumb $breadcrumb): ?Breadcrumb
return null;
}

function mockClassSerializer($object)
{
return ['value' => 'serialized_class'];
}

class CallbackMock
{
public static function callback()
Expand Down