TeamInvite::process()   B
last analyzed

Complexity

Conditions 11
Paths 28

Size

Total Lines 66
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 43
c 1
b 0
f 0
nc 28
nop 1
dl 0
loc 66
rs 7.3166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright (C) 2020-2025 Iain Cambridge
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
10
 * the Free Software Foundation, either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
namespace Parthenon\User\RequestProcessor;
23
24
use Parthenon\Common\Exception\GeneralException;
25
use Parthenon\Common\Exception\RequestProcessor\AlreadyInvitedException;
26
use Parthenon\Common\LoggerAwareTrait;
27
use Parthenon\Common\RequestHandler\RequestHandlerManagerInterface;
28
use Parthenon\Notification\EmailSenderInterface;
29
use Parthenon\User\Entity\MemberInterface;
30
use Parthenon\User\Entity\TeamInviteCode;
31
use Parthenon\User\Entity\UserInterface;
32
use Parthenon\User\Event\PostTeamInviteEvent;
33
use Parthenon\User\Event\PreTeamInviteEvent;
34
use Parthenon\User\Factory\EntityFactory;
35
use Parthenon\User\Form\Type\UserInviteType;
36
use Parthenon\User\Notification\MessageFactory;
37
use Parthenon\User\Repository\TeamInviteCodeRepositoryInterface;
38
use Parthenon\User\Repository\TeamRepositoryInterface;
39
use Parthenon\User\Repository\UserRepositoryInterface;
40
use Symfony\Bundle\SecurityBundle\Security;
41
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
42
use Symfony\Component\Form\FormFactoryInterface;
43
use Symfony\Component\HttpFoundation\Request;
44
use Symfony\Component\HttpFoundation\RequestStack;
45
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
46
47
class TeamInvite
48
{
49
    use LoggerAwareTrait;
50
51
    public function __construct(
52
        private FormFactoryInterface $formFactory,
53
        private Security $security,
54
        private TeamInviteCodeRepositoryInterface $inviteCodeRepository,
55
        private EmailSenderInterface $sender,
56
        private UserInviteType $userInviteType,
57
        private EventDispatcherInterface $dispatcher,
58
        private MessageFactory $messageFactory,
59
        private EntityFactory $entityFactory,
60
        private TeamRepositoryInterface $teamRepository,
61
        private RequestStack $requestStack,
62
        private RequestHandlerManagerInterface $requestHandlerManager,
63
        private AuthorizationCheckerInterface $authorizationChecker,
64
        private UserRepositoryInterface $userRepository,
65
        private string $defaultRole,
66
        private array $roles,
67
    ) {
68
    }
69
70
    public function process(Request $request)
71
    {
72
        $output = ['success' => false, 'already_invited' => false, 'already_a_member' => false, 'hit_limit' => false];
73
74
        $formType = $this->formFactory->create(get_class($this->userInviteType));
75
        $requestHander = $this->requestHandlerManager->getRequestHandler($request);
76
77
        if ($request->isMethod('POST')) {
78
            $requestHander->handleForm($formType, $request);
79
            if ($formType->isSubmitted() && $formType->isValid()) {
80
                $email = $formType->getData()['email'];
81
                $role = $formType->getData()['role'] ?? $this->defaultRole;
82
                $output['processed'] = true;
83
                try {
84
                    $user = $this->security->getUser();
85
                    if (!$user instanceof UserInterface) {
86
                        throw new \InvalidArgumentException('Not a user');
87
                    }
88
89
                    if (!$user instanceof MemberInterface) {
90
                        $this->getLogger()->critical('A user tried to send a team invite when not a member of a team');
91
                        throw new \InvalidArgumentException('Not a member of a team');
92
                    }
93
                    $team = $this->teamRepository->getByMember($user);
94
95
                    if ($this->inviteCodeRepository->hasInviteForEmailAndTeam($email, $team)) {
96
                        throw new AlreadyInvitedException();
97
                    }
98
99
                    if (!in_array($role, $this->roles) && $this->defaultRole !== $role) {
100
                        $this->getLogger()->error('A team invite was not sent due an invalid role being used', ['role' => $role]);
101
                        throw new GeneralException('Invalid role');
102
                    }
103
104
                    $inviteCode = $this->entityFactory->buildTeamInviteCode($user, $team, $email, $role);
105
                    if (!$this->authorizationChecker->isGranted(TeamInviteCode::AUTH_CHECKER_ATTRIBUTE, $inviteCode)) {
106
                        $output['success'] = false;
107
                        $output['hit_limit'] = true;
108
                        $this->getLogger()->info('A team invite was not sent due to plan limits');
109
110
                        return $requestHander->generateErrorOutput($formType, $output);
111
                    }
112
113
                    $this->dispatcher->dispatch(new PreTeamInviteEvent($user, $team, $inviteCode), PreTeamInviteEvent::NAME);
114
                    $this->inviteCodeRepository->save($inviteCode);
115
                    $message = $this->messageFactory->getTeamInviteMessage($user, $team, $inviteCode);
116
117
                    $this->sender->send($message);
118
                    $this->dispatcher->dispatch(new PostTeamInviteEvent($user, $team, $inviteCode), PostTeamInviteEvent::NAME);
119
                    $output['success'] = true;
120
                    $this->requestStack->getSession()->getFlashBag()->add('success', 'parthenon.user.team_invite.success');
121
                    $this->getLogger()->info('A team invite was successfully sent');
122
                } catch (AlreadyInvitedException $e) {
123
                    $this->getLogger()->info('A team invite failed to be created due to one already existing');
124
125
                    $this->requestStack->getSession()->getFlashBag()->add('error', 'parthenon.user.team_invite.already_invited');
126
                    $output['already_invited'] = true;
127
128
                    return $requestHander->generateErrorOutput($formType, $output);
129
                }
130
            }
131
        }
132
133
        $output['form'] = $formType->createView();
134
135
        return $requestHander->generateDefaultOutput($formType, $output);
136
    }
137
}
138