Passed
Pull Request — master (#19)
by
unknown
02:47
created

resolveIntegrationsFromUserConfig()   B

Complexity

Conditions 8
Paths 20

Size

Total Lines 50
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 9.4924

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 8
eloc 27
nc 20
nop 0
dl 0
loc 50
ccs 15
cts 21
cp 0.7143
crap 9.4924
rs 8.4444
c 3
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Sentry;
6
7
use Psr\Container\ContainerInterface;
8
use Psr\Log\LoggerInterface;
9
use RuntimeException;
10
use Sentry\ClientBuilder;
11
use Sentry\Integration as SdkIntegration;
12
use Sentry\Integration\IntegrationInterface;
13
use Sentry\Options;
14
use Sentry\SentrySdk;
15
use Sentry\State\HubInterface;
16
use Sentry\Transport\TransportFactoryInterface;
17
use Yiisoft\Yii\Sentry\Http\YiiRequestFetcher;
18
use Yiisoft\Yii\Sentry\Integration\ExceptionContextIntegration;
19
use Yiisoft\Yii\Sentry\Integration\Integration;
20
21
use function is_string;
22
23
final class HubBootstrapper
24
{
25
    private const DEFAULT_INTEGRATIONS = [
26
        ExceptionContextIntegration::class,
27
    ];
28
29 14
    public function __construct(
30
        private Options $options,
31
        private YiiSentryConfig $configuration,
32
        private TransportFactoryInterface $transportFactory,
33
        private LoggerInterface $logger,
34
        private HubInterface $hub,
35
        private ContainerInterface $container,
36
    ) {
37
    }
38
39 14
    public function bootstrap(): void
40
    {
41 14
        $this->options->setIntegrations(fn (array $integrations) => $this->prepareIntegrations($integrations));
42
43 14
        $clientBuilder = new ClientBuilder($this->options);
44
        $clientBuilder
45 14
            ->setTransportFactory($this->transportFactory)
46 14
            ->setLogger($this->logger);
47
48 14
        $client = $clientBuilder->getClient();
49
50 14
        $this->hub->bindClient($client);
51 14
        SentrySdk::setCurrentHub($this->hub);
52
    }
53
54
    /**
55
     * @param IntegrationInterface[] $integrations
56
     *
57
     * @return IntegrationInterface[]
58
     */
59 14
    public function prepareIntegrations(array $integrations): array
60
    {
61 14
        $userIntegrations = $this->resolveIntegrationsFromUserConfig();
62 14
        if (!$this->options->hasDefaultIntegrations()) {
63
            return array_merge($integrations, $userIntegrations);
64
        }
65
66 14
        $integrations = array_filter(
67
            $integrations,
68
            static fn (SdkIntegration\IntegrationInterface $integration): bool => !(
69 14
                $integration instanceof SdkIntegration\ErrorListenerIntegration ||
70
                $integration instanceof SdkIntegration\ExceptionListenerIntegration ||
71
                $integration instanceof SdkIntegration\FatalErrorListenerIntegration ||
72
                // We also remove the default request integration so it can be readded after with a Yii3
73
                // specific request fetcher. This way we can resolve the request from Yii3 instead of
74
                // constructing it from the global state.
75
                $integration instanceof SdkIntegration\RequestIntegration
76
            )
77
        );
78 14
        $integrations[] = new SdkIntegration\RequestIntegration(
79 14
            new YiiRequestFetcher($this->container)
80
        );
81
82 14
        return array_merge($integrations, $userIntegrations);
83
    }
84
85
    /**
86
     * Resolve the integrations from the user configuration with the container.
87
     *
88
     * @return SdkIntegration\IntegrationInterface[]
89
     */
90 14
    private function resolveIntegrationsFromUserConfig(): array
91
    {
92
        // Default Sentry SDK integrations
93 14
        $integrations = [
94 14
            new Integration(),
95
        ];
96
97 14
        $integrationsToResolve = $this->configuration->getIntegrations();
98
99 14
        $enableDefaultTracingIntegrations = array_key_exists('default_integrations', $this->configuration->getTracing())
100 12
            && (bool)$this->configuration->getTracing()['default_integrations'];
101
102 14
        if ($enableDefaultTracingIntegrations && $this->configuration->couldHavePerformanceTracingEnabled()) {
103 2
            $integrationsToResolve = array_merge(
104
                $integrationsToResolve,
105
                self::DEFAULT_INTEGRATIONS
106
            );
107
        }
108
        /**
109
         * @psalm-suppress MixedAssignment
110
         */
111 14
        foreach ($integrationsToResolve as $userIntegration) {
112 2
            if ($userIntegration instanceof SdkIntegration\IntegrationInterface
113
            ) {
114
                $integrations[] = $userIntegration;
115 2
            } elseif (is_string($userIntegration)) {
116
                /** @psalm-suppress MixedAssignment */
117 2
                $resolvedIntegration = $this->container->get($userIntegration);
118 2
                if (!$resolvedIntegration instanceof SdkIntegration\IntegrationInterface) {
119
                    throw new RuntimeException(
120
                        sprintf(
121
                            'Sentry integration must be an instance of `%s` got `%s`.',
122
                            SdkIntegration\IntegrationInterface::class,
123
                            get_debug_type($resolvedIntegration)
124
                        )
125
                    );
126
                }
127
128 2
                $integrations[] = $resolvedIntegration;
129
            } else {
130
                throw new RuntimeException(
131
                    sprintf(
132
                        'Sentry integration must either be a valid container reference or an instance of `%s`.',
133
                        SdkIntegration\IntegrationInterface::class
134
                    )
135
                );
136
            }
137
        }
138
139 14
        return $integrations;
140
    }
141
}
142