Test Setup Failed
Push — master ( 845097...013fb3 )
by Alexey
02:53
created

UpdateSubscriptionsCommand::setDeps()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 8
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 8
loc 8
rs 9.4285
c 0
b 0
f 0
ccs 0
cts 7
cp 0
cc 1
eloc 6
nc 1
nop 5
crap 2
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;
8
use Skobkin\Bundle\PointToolsBundle\Entity\User;
9
use Skobkin\Bundle\PointToolsBundle\Exception\Api\UserNotFoundException;
10
use Skobkin\Bundle\PointToolsBundle\Repository\UserRepository;
11
use Skobkin\Bundle\PointToolsBundle\Service\SubscriptionsManager;
12
use Skobkin\Bundle\PointToolsBundle\Service\Api\UserApi;
13
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
14
use Symfony\Component\Console\Helper\ProgressBar;
15
use Symfony\Component\Console\Input\InputInterface;
16
use Symfony\Component\Console\Input\InputOption;
17
use Symfony\Component\Console\Output\OutputInterface;
18
19
/**
20
 * @todo https://symfony.com/doc/current/console/lockable_trait.html
21
 */
22
class UpdateSubscriptionsCommand extends ContainerAwareCommand
23
{
24
    /**
25
     * @var EntityManagerInterface
26
     */
27
    private $em;
28
29
    /**
30
     * @var LoggerInterface
31
     */
32
    private $logger;
33
34
    /**
35
     * @var UserRepository
36
     */
37
    private $userRepo;
38
39
    /**
40
     * @var InputInterface
41
     */
42
    private $input;
43
44
    /**
45
     * @var UserApi
46
     */
47
    private $api;
48
49
    /**
50
     * @var int
51
     */
52
    private $apiDelay = 500000;
53
54
    /**
55
     * @var SubscriptionsManager
56
     */
57
    private $subscriptionManager;
58
59
    /**
60
     * @var ProgressBar
61
     */
62
    private $progress;
63
64
65 View Code Duplication
    public function setDeps(LoggerInterface $logger, EntityManagerInterface $em, UserRepository $userRepo, UserApi $userApi, SubscriptionsManager $subscriptionsManager): void
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...
66
    {
67
        $this->logger = $logger;
68
        $this->em = $em;
69
        $this->userRepo = $userRepo;
70
        $this->api = $userApi;
71
        $this->subscriptionManager = $subscriptionsManager;
72
    }
73
74
    public function setApiDelay(int $microSecs): void
75
    {
76
        $this->apiDelay = $microSecs;
77
    }
78
79
    protected function configure()
80
    {
81
        $this
82
            ->setName('point:update:subscriptions')
83
            ->setDescription('Update subscriptions of users subscribed to service')
84
            ->addOption(
85
                'all-users',
86
                null,
87
                InputOption::VALUE_NONE,
88
                'If set, command will check subscribers of all service users instead of service subscribers only'
89
            )
90
            ->addOption(
91
                'check-only',
92
                null,
93
                InputOption::VALUE_NONE,
94
                'If set, command will not perform write operations in the database'
95
            )
96
        ;
97
    }
98
99
    /**
100
     * @param InputInterface $input
101
     * @param OutputInterface $output
102
     *
103
     * @return int
104
     */
105
    protected function execute(InputInterface $input, OutputInterface $output)
106
    {
107
        $this->input = $input;
108
109
        $this->logger->debug('UpdateSubscriptionsCommand started.');
110
111
        try {
112
            $appUserId = $this->getContainer()->getParameter('point_id');
113
        } catch (\InvalidArgumentException $e) {
114
            $this->logger->alert('Could not get point_id parameter from config file', ['exception_message' => $e->getMessage()]);
115
            return 1;
116
        }
117
118
        $this->progress = new ProgressBar($output);
119
        $this->progress->setFormat('debug');
120
121
        // Beginning transaction for all changes
122
        $this->em->beginTransaction();
123
124
        $this->progress->setMessage('Getting service subscribers');
125
126
        try {
127
            $usersForUpdate = $this->getUsersForUpdate($appUserId);
128
        } catch (\Exception $e) {
129
            $this->logger->error('Error while getting service subscribers', ['exception' => get_class($e), 'message' => $e->getMessage()]);
130
131
            return 1;
132
        }
133
134
        if (0 === count($usersForUpdate)) {
135
            $this->logger->info('No local subscribers. Finishing.');
136
137
            return 0;
138
        }
139
140
        $this->logger->info('Processing users subscribers');
141
        $this->progress->setMessage('Processing users subscribers');
142
        $this->progress->start(count($usersForUpdate));
143
144
        $this->updateUsersSubscribers($usersForUpdate);
145
146
        $this->progress->finish();
147
148
        // Flushing all changes at once to database
149
        $this->em->flush();
150
        $this->em->commit();
151
152
        $this->logger->debug('Finished');
153
154
        return 0;
155
    }
156
157
    /**
158
     * @param User[] $users
159
     */
160
    private function updateUsersSubscribers(array $users): void
161
    {
162
        // Updating users subscribers
163
        foreach ($users as $user) {
164
            usleep($this->apiDelay);
165
166
            $this->progress->advance();
167
168
            $this->logger->info('Processing @'.$user->getLogin());
169
170
            try {
171
                $userCurrentSubscribers = $this->api->getUserSubscribersById($user->getId());
172
            } catch (UserNotFoundException $e) {
173
                $this->logger->warning('User not found. Marking as removed', ['login' => $user->getLogin(), 'user_id' => $user->getId()]);
174
                $user->markAsRemoved();
175
176
                continue;
177
            } catch (\Exception $e) {
178
                $this->logger->error(
179
                    'Error while getting subscribers. Skipping.',
180
                    [
181
                        'user_login' => $user->getLogin(),
182
                        'user_id' => $user->getId(),
183
                        'message' => $e->getMessage(),
184
                        'file' => $e->getFile(),
185
                        'line' => $e->getLine(),
186
                    ]
187
                );
188
189
                continue;
190
            }
191
192
            $this->logger->debug('Updating user subscribers');
193
194
            try {
195
                // Updating user subscribers
196
                $this->subscriptionManager->updateUserSubscribers($user, $userCurrentSubscribers);
197
            } catch (\Exception $e) {
198
                $this->logger->error(
199
                    'Error while updating user subscribers',
200
                    [
201
                        'user_login' => $user->getLogin(),
202
                        'user_id' => $user->getId(),
203
                        'message' => $e->getMessage(),
204
                        'file' => $e->getFile(),
205
                        'line' => $e->getLine(),
206
                    ]
207
                );
208
            }
209
        }
210
    }
211
212
    private function getUsersForUpdate(int $appUserId): array
213
    {
214
        $usersForUpdate = [];
215
216
        if ($this->input->getOption('all-users')) {
217
            $usersForUpdate = $this->userRepo->findBy(['removed' => false]);
218
        } else {
219
            /** @var User $serviceUser */
220
            try {
221
                $serviceUser = $this->userRepo->findActiveUserWithSubscribers($appUserId);
222
            } catch (\Exception $e) {
223
                $this->logger->error('Error while getting active user with subscribers', ['app_user_id' => $appUserId]);
224
225
                throw $e;
226
            }
227
228
            if (!$serviceUser) {
229
                $this->logger->critical('Service user not found or marked as removed');
230
                // @todo Retrieving user
231
232
                throw new \RuntimeException('Service user not found in the database');
233
            }
234
235
            $this->logger->info('Getting service subscribers');
236
237
            try {
238
                $usersForUpdate = $this->api->getUserSubscribersById($appUserId);
239
            } catch (UserNotFoundException $e) {
240
                $this->logger->critical('Service user deleted or API response is invalid');
241
242
                throw $e;
243
            } catch (\Exception $e) {
244
                $this->logger->warning(
245
                    'Error while getting service subscribers. Fallback to local list.',
246
                    [
247
                        'user_login' => $serviceUser->getLogin(),
248
                        'user_id' => $serviceUser->getId(),
249
                        'message' => $e->getMessage(),
250
                        'file' => $e->getFile(),
251
                        'line' => $e->getLine(),
252
                    ]
253
                );
254
255
                /** @var Subscription $subscription */
256
                foreach ((array) $serviceUser->getSubscribers() as $subscription) {
257
                    $usersForUpdate[] = $subscription->getSubscriber();
258
                }
259
            }
260
261
            $this->logger->debug('Updating service subscribers');
262
263
            // Updating service subscribers
264
            try {
265
                $this->subscriptionManager->updateUserSubscribers($serviceUser, $usersForUpdate);
266
            } catch (\Exception $e) {
267
                $this->logger->error(
268
                    'Error while updating service subscribers',
269
                    [
270
                        'user_login' => $serviceUser->getLogin(),
271
                        'user_id' => $serviceUser->getId(),
272
                        'message' => $e->getMessage(),
273
                        'file' => $e->getFile(),
274
                        'line' => $e->getLine(),
275
                    ]
276
                );
277
278
                throw $e;
279
            }
280
        }
281
282
        return $usersForUpdate;
283
    }
284
}