Completed
Push — 1.4 ( 460714...084e89 )
by Paweł
24:24 queued 03:14
created

WebhookEventsSubscriber::getSubscribedEvents()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Superdesk Web Publisher Core Bundle.
7
 *
8
 * Copyright 2016 Sourcefabric z.ú. and contributors.
9
 *
10
 * For the full copyright and license information, please see the
11
 * AUTHORS and LICENSE files distributed with this source code.
12
 *
13
 * @copyright 2016 Sourcefabric z.ú
14
 * @license http://www.superdesk.org/license
15
 */
16
17
namespace SWP\Bundle\CoreBundle\EventSubscriber;
18
19
use JMS\Serializer\SerializerInterface;
20
use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface;
21
use SWP\Bundle\ContentBundle\Event\ArticleEvent;
22
use SWP\Bundle\ContentBundle\Event\RouteEvent;
23
use SWP\Bundle\CoreBundle\Model\WebhookInterface;
24
use SWP\Bundle\CoreBundle\Repository\WebhookRepositoryInterface;
25
use SWP\Bundle\CoreBundle\Webhook\WebhookEvents;
26
use SWP\Bundle\MultiTenancyBundle\Context\TenantContext;
27
use SWP\Component\MultiTenancy\Model\TenantAwareInterface;
28
use SWP\Component\MultiTenancy\Repository\TenantRepositoryInterface;
29
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
30
use Symfony\Component\EventDispatcher\Event;
31
use Symfony\Component\EventDispatcher\GenericEvent;
32
33
final class WebhookEventsSubscriber implements EventSubscriberInterface
34
{
35
    private $producer;
36
37
    private $serializer;
38
39
    private $webhooksRepository;
40
41
    private $tenantContext;
42
43
    private $tenantRepository;
44
45
    public function __construct(
46
        ProducerInterface $producer,
47
        SerializerInterface $serializer,
48
        WebhookRepositoryInterface $webhooksRepository,
49
        TenantContext $tenantContext,
50
        TenantRepositoryInterface $tenantRepository
51
    ) {
52
        $this->producer = $producer;
53
        $this->serializer = $serializer;
54
        $this->webhooksRepository = $webhooksRepository;
55
        $this->tenantContext = $tenantContext;
56
        $this->tenantRepository = $tenantRepository;
57
    }
58
59
    public static function getSubscribedEvents(): array
60
    {
61
        $subscribedEvents = [];
62
        foreach (WebhookEvents::EVENTS as $webhookEvent) {
63
            $subscribedEvents[$webhookEvent] = 'handleEvent';
64
        }
65
66
        return $subscribedEvents;
67
    }
68
69
    public function handleEvent(Event $event): void
70
    {
71
        $eventName = $this->getEventName($event);
72
        if (!is_string($eventName)) {
73
            return;
74
        }
75
76
        $subject = $this->getSubject($event);
77
        $webhooks = $this->getWebhooks($event, $subject);
78
79
        /** @var WebhookInterface $webhook */
80
        foreach ($webhooks as $webhook) {
81
            $this->producer->publish($this->serializer->serialize([
82
                'url' => $webhook->getUrl(),
83
                'metadata' => [
84
                    'event' => $this->getEventName($event),
85
                    'tenant' => $webhook->getTenantCode(),
86
                ],
87
                'subject' => $subject,
88
            ], 'json'));
89
        }
90
    }
91
92
    private function getWebhooks(Event $event, $subject): array
93
    {
94
        $originalTenant = null;
95
96
        if (
97
            $subject instanceof TenantAwareInterface
98
            && $subject->getTenantCode() !== $this->tenantContext->getTenant()->getCode()
99
            && null !== $subject->getTenantCode()
100
            && null !== ($subjectTenant = $this->tenantRepository->findOneByCode($subject->getTenantCode()))
101
        ) {
102
            $originalTenant = $this->tenantContext->getTenant();
103
            $this->tenantContext->setTenant($subjectTenant);
104
        }
105
106
        $webhooks = $this->webhooksRepository->getEnabledForEvent($this->getEventName($event))->getResult();
107
108
        if (null !== $originalTenant) {
109
            $this->tenantContext->setTenant($originalTenant);
110
        }
111
112
        return $webhooks;
113
    }
114
115
    private function getEventName(Event $event): ?string
116
    {
117
        if ($event instanceof GenericEvent) {
118
            $arguments = $event->getArguments();
119
            if (array_key_exists('eventName', $arguments)) {
120
                return array_search($arguments['eventName'], WebhookEvents::EVENTS);
121
            }
122
        } elseif (method_exists($event, 'getEventName')) {
123
            return array_search($event->getEventName(), WebhookEvents::EVENTS);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\EventDispatcher\Event as the method getEventName() does only exist in the following sub-classes of Symfony\Component\EventDispatcher\Event: SWP\Bundle\ContentBundle\Event\ArticleEvent, SWP\Bundle\ContentBundle\Event\RouteEvent. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
124
        }
125
126
        return null;
127
    }
128
129
    private function getSubject(Event $event)
130
    {
131
        switch ($event) {
132
            case $event instanceof GenericEvent:
133
                return $event->getSubject();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\EventDispatcher\Event as the method getSubject() does only exist in the following sub-classes of Symfony\Component\EventDispatcher\Event: SWP\Component\Common\Event\HttpCacheEvent, Symfony\Component\EventDispatcher\GenericEvent, Symfony\Component\Workflow\Event\Event, Symfony\Component\Workflow\Event\GuardEvent. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
134
            case $event instanceof ArticleEvent:
135
                return $event->getArticle();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\EventDispatcher\Event as the method getArticle() does only exist in the following sub-classes of Symfony\Component\EventDispatcher\Event: SWP\Bundle\ContentBundle\Event\ArticleEvent. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
136
            case $event instanceof RouteEvent:
137
                return $event->getRoute();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\EventDispatcher\Event as the method getRoute() does only exist in the following sub-classes of Symfony\Component\EventDispatcher\Event: SWP\Bundle\ContentBundle\Event\RouteEvent. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
138
            default:
139
                return null;
140
        }
141
    }
142
}
143