UpdateUsersPrivacyCommand::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 11
Ratio 100 %

Importance

Changes 0
Metric Value
dl 11
loc 11
rs 9.9
c 0
b 0
f 0
cc 1
nc 1
nop 6
1
<?php
2
3
namespace Skobkin\Bundle\PointToolsBundle\Command;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Psr\Log\LoggerInterface;
7
use Skobkin\Bundle\PointToolsBundle\Entity\{Subscription, User};
8
use Skobkin\Bundle\PointToolsBundle\Exception\Api\{ForbiddenException, UserNotFoundException};
9
use Skobkin\Bundle\PointToolsBundle\Repository\UserRepository;
10
use Skobkin\Bundle\PointToolsBundle\Service\Api\UserApi;
11
use Symfony\Component\Console\Command\Command;
12
use Symfony\Component\Console\Helper\ProgressBar;
13
use Symfony\Component\Console\Input\{InputInterface, InputOption};
14
use Symfony\Component\Console\Output\OutputInterface;
15
16
class UpdateUsersPrivacyCommand extends Command
17
{
18
    /** @var EntityManagerInterface */
19
    private $em;
20
21
    /** @var LoggerInterface */
22
    private $logger;
23
24
    /** @var UserRepository */
25
    private $userRepo;
26
27
    /** @var InputInterface */
28
    private $input;
29
30
    /** @var UserApi */
31
    private $api;
32
33
    /** @var int */
34
    private $apiDelay = 500000;
35
36
    /** @var int */
37
    private $appUserId;
38
39
    /** @var ProgressBar */
40
    private $progress;
41
42 View Code Duplication
    public function __construct(EntityManagerInterface $em, LoggerInterface $logger, UserRepository $userRepo, UserApi $api, int $apiDelay, int $appUserId)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
43
    {
44
        parent::__construct();
45
46
        $this->em = $em;
47
        $this->logger = $logger;
48
        $this->userRepo = $userRepo;
49
        $this->api = $api;
50
        $this->apiDelay = $apiDelay;
51
        $this->appUserId = $appUserId;
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57
    protected function configure()
58
    {
59
        $this
60
            ->setName('point:update:privacy')
61
            ->setDescription('Update users privacy')
62
            ->addOption(
63
                'all-users',
64
                null,
65
                InputOption::VALUE_NONE,
66
                'If set, command will check all users instead of service subscribers only'
67
            )
68
        ;
69
    }
70
71
    /**
72
     * {@inheritdoc}
73
     */
74
    protected function execute(InputInterface $input, OutputInterface $output)
75
    {
76
        $this->input = $input;
0 ignored issues
show
Documentation Bug introduced by
It seems like $input of type object<Symfony\Component...e\Input\InputInterface> is incompatible with the declared type object<InputInterface> of property $input.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
77
78
        $this->logger->debug(static::class.' started.');
79
80
        $this->progress = new ProgressBar($output);
81
        $this->progress->setFormat('debug');
82
83
        try {
84
            /** @var User[] $usersForUpdate */
85
            $usersForUpdate = $this->getUsersForUpdate();
86
        } catch (\Exception $e) {
87
            $this->logger->error('Error while getting service subscribers', ['exception' => get_class($e), 'message' => $e->getMessage()]);
88
89
            return 1;
90
        }
91
92
        $this->logger->info('Processing users privacy.');
93
94
        $this->progress->start(count($usersForUpdate));
95
96 View Code Duplication
        foreach ($usersForUpdate as $idx => $user) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
97
            usleep($this->apiDelay);
98
99
            $this->progress->advance();
100
            $this->logger->info('Processing @'.$user->getLogin());
101
102
            $this->updateUser($user);
103
        }
104
105
        $this->progress->finish();
106
107
        $this->em->flush();
108
109
        $this->logger->debug('Finished');
110
111
        return 0;
112
    }
113
114
    private function updateUser(User $user): void
115
    {
116
        try {
117
            $remoteUser = $this->api->getUserById($user->getId());
118
119
            if ($remoteUser !== $user) {
120
                $this->logger->error('Remote user is not equal with local.', [
121
                    'local_user_id' => $user->getId(),
122
                    'local_user_login' => $user->getLogin(),
123
                    'local_user_name' => $user->getName(),
124
                    'remote_user_id' => $remoteUser->getId(),
125
                    'remote_user_login' => $remoteUser->getLogin(),
126
                    'remote_user_name' => $remoteUser->getName(),
127
                ]);
128
            }
129
        } catch (UserNotFoundException $e) {
130
            $this->logger->info('User not found. Marking as removed.', ['user_id' => $user->getId(), 'user_login' => $user->getLogin()]);
131
132
            $user->markAsRemoved();
133
        } catch (ForbiddenException $e) {
134
            $this->logger->info('User profile access forbidden', ['user_id' => $user->getId(), 'user_login' => $user->getLogin()]);
135
136
            $user->updatePrivacy(false, true);
137
        } catch (\Exception $e) {
138
            $this->logger->error(
139
                'Error while updating user privacy',
140
                [
141
                    'user_login' => $user->getLogin(),
142
                    'user_id' => $user->getId(),
143
                    'message' => $e->getMessage(),
144
                    'file' => $e->getFile(),
145
                    'line' => $e->getLine(),
146
                ]
147
            );
148
        }
149
    }
150
151
    private function getUsersForUpdate(): array
152
    {
153
        if ($this->input->getOption('all-users')) {
154
            return $this->userRepo->findBy(['removed' => false]);
155
        }
156
157
        /** @var User $serviceUser */
158
        try {
159
            $serviceUser = $this->userRepo->findActiveUserWithSubscribers($this->appUserId);
160
        } catch (\Exception $e) {
161
            $this->logger->error('Error while getting active user with subscribers', ['app_user_id' => $this->appUserId]);
162
163
            throw $e;
164
        }
165
166 View Code Duplication
        if (!$serviceUser) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
167
            $this->logger->warning('Service user not found or marked as removed. Falling back to API.');
168
169
            try {
170
                $serviceUser = $this->api->getUserById($this->appUserId);
171
            } catch (UserNotFoundException $e) {
172
                throw new \RuntimeException('Service user not found in the database and could not be retrieved from API.');
173
            }
174
        }
175
176
        $this->logger->info('Getting service subscribers');
177
178
        try {
179
            return $this->api->getUserSubscribersById($this->appUserId);
180
        } catch (UserNotFoundException $e) {
181
            $this->logger->critical('Service user deleted or API response is invalid');
182
183
            throw $e;
184
        } catch (\Exception $e) {
185
            $this->logger->warning(
186
                'Error while getting service subscribers. Fallback to local list.',
187
                [
188
                    'user_login' => $serviceUser->getLogin(),
189
                    'user_id' => $serviceUser->getId(),
190
                    'message' => $e->getMessage(),
191
                    'file' => $e->getFile(),
192
                    'line' => $e->getLine(),
193
                ]
194
            );
195
196
            $localSubscribers = [];
197
198
            /** @var Subscription $subscription */
199
            foreach ($serviceUser->getSubscribers() as $subscription) {
200
                $localSubscribers[] = $subscription->getSubscriber();
201
            }
202
203
            return $localSubscribers;
204
        }
205
    }
206
}
207