Test Setup Failed
Push — master ( 013fb3...ad1945 )
by Alexey
13:09
created

UpdateUsersPrivacyCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 11
Ratio 100 %

Importance

Changes 0
Metric Value
dl 11
loc 11
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 8
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\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
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 ContainerAwareCommand
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
        foreach ($usersForUpdate as $idx => $user) {
97
            usleep($this->apiDelay);
98
99
            $this->progress->advance();
100
            $this->logger->info('Processing @'.$user->getLogin());
101
102
            $this->updateUser($user);
103
104
            // Flushing each 10 users
105
            if (0 === $idx % 10) {
106
                $this->em->flush();
107
            }
108
        }
109
110
        $this->progress->finish();
111
112
        $this->em->flush();
113
114
        $this->logger->debug('Finished');
115
116
        return 0;
117
    }
118
119
    private function updateUser(User $user): void
120
    {
121
        try {
122
            $remoteUser = $this->api->getUserById($user->getId());
123
124
            if ($remoteUser !== $user) {
125
                $this->logger->error('Remote user is not equal with local.', ['user_id' => $user->getId(), 'user_login' => $user->getLogin()]);
126
            }
127
        } catch (UserNotFoundException $e) {
128
            $this->logger->info('User not found. Marking as removed.', ['user_id' => $user->getId(), 'user_login' => $user->getLogin()]);
129
130
            $user->markAsRemoved();
131
        } catch (ForbiddenException $e) {
132
            $this->logger->info('User profile access forbidden', ['user_id' => $user->getId(), 'user_login' => $user->getLogin()]);
133
134
            $user->updatePrivacy(false, true);
135
        } catch (\Exception $e) {
136
            $this->logger->error(
137
                'Error while updating user privacy',
138
                [
139
                    'user_login' => $user->getLogin(),
140
                    'user_id' => $user->getId(),
141
                    'message' => $e->getMessage(),
142
                    'file' => $e->getFile(),
143
                    'line' => $e->getLine(),
144
                ]
145
            );
146
        }
147
    }
148
149
    private function getUsersForUpdate(): array
150
    {
151
        if ($this->input->getOption('all-users')) {
152
            return $this->userRepo->findBy(['removed' => false]);
153
        }
154
155
        /** @var User $serviceUser */
156
        try {
157
            $serviceUser = $this->userRepo->findActiveUserWithSubscribers($this->appUserId);
158
        } catch (\Exception $e) {
159
            $this->logger->error('Error while getting active user with subscribers', ['app_user_id' => $this->appUserId]);
160
161
            throw $e;
162
        }
163
164
        if (!$serviceUser) {
165
            $this->logger->critical('Service user not found or marked as removed');
166
167
            throw new \RuntimeException('Service user not found in the database');
168
        }
169
170
        $this->logger->info('Getting service subscribers');
171
172
        try {
173
            return $this->api->getUserSubscribersById($this->appUserId);
174
        } catch (UserNotFoundException $e) {
175
            $this->logger->critical('Service user deleted or API response is invalid');
176
177
            throw $e;
178
        } catch (\Exception $e) {
179
            $this->logger->warning(
180
                'Error while getting service subscribers. Fallback to local list.',
181
                [
182
                    'user_login' => $serviceUser->getLogin(),
183
                    'user_id' => $serviceUser->getId(),
184
                    'message' => $e->getMessage(),
185
                    'file' => $e->getFile(),
186
                    'line' => $e->getLine(),
187
                ]
188
            );
189
190
            $localSubscribers = [];
191
192
            /** @var Subscription $subscription */
193
            foreach ($serviceUser->getSubscribers() as $subscription) {
194
                $localSubscribers[] = $subscription->getSubscriber();
195
            }
196
197
            return $localSubscribers;
198
        }
199
    }
200
}
201