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 |
|
|
|
|
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
|
|
|
} |
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.