Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
pushwatchingmanager.php
1<?php
2
4
21use Bitrix\Dav\Internals\DavConnectionTable;
24use Bitrix\Main\Entity\ReferenceField;
35use CAgent;
36use Exception;
37
39{
40 private const RENEW_LIMIT = 5;
41 private const FIX_LIMIT = 5;
42 private const RENEW_INTERVAL_CHANNEL = 14400;//60*60*4
43 private const PAUSE_INTERVAL_CHANNEL = 72000; // 60*60*20
44 private const TYPE_LINK = 'SECTION_CONNECTION';
45 private const TYPE_CONNECTION = 'CONNECTION';
46 private const GOOGLE_CONNECTION = 'google_api_oauth';
47 private const OFFICE365_CONNECTION = 'office365';
48 private const RESULT_STATUS = [
49 'done' => 'done', // nothing left to process
50 'next' => 'next', // something left to process
51 ];
53 private $mapperFactory;
54
55 private SectionConnectionFactory $linkFactory;
56
57 private static array $outgoingManagersCache = [];
58
64 public function __construct()
65 {
66 if (!Loader::includeModule('dav'))
67 {
68 throw new SystemException('Module dav not found');
69 }
70
71 $this->mapperFactory = ServiceLocator::getInstance()->get('calendar.service.mappers.factory');
72 }
73
85 public static function renewWatchChannels()
86 {
87 if (!Loader::includeModule('dav') || !Loader::includeModule('calendar'))
88 {
89 return false;
90 }
91
92 $agentName = __METHOD__ . '();';
93 $manager = new static();
94
95 $status = $manager->doRenewWatchChannels();
96
97 $manager->doFixWatchSectionChannels();
98 $manager->doFixWatchConnectionChannels();
99
100 if ($status === self::RESULT_STATUS['done'])
101 {
102 $nextAgentDate = DateTime::createFromTimestamp(
103 time() + self::PAUSE_INTERVAL_CHANNEL)->format(Date::convertFormatToPhp(FORMAT_DATETIME)
104 );
105
106 CAgent::removeAgent($agentName, "calendar");
107 CAgent::addAgent($agentName, "calendar", "N", self::RENEW_INTERVAL_CHANNEL,"", "Y", $nextAgentDate);
108
109 return false;
110 }
111
112 return $agentName;
113 }
114
124 private function doRenewWatchChannels(): string
125 {
126 $pushChannels = PushTable::getList([
127 'filter' => [
128 'ENTITY_TYPE' => [self::TYPE_LINK, self::TYPE_CONNECTION],
129 '<=EXPIRES' => (new DateTime())->add('+1 day'),
130 ],
131 'order' => [
132 'EXPIRES' => 'ASC',
133 ],
134 'limit' => self::RENEW_LIMIT,
135 ])->fetchCollection();
136
137 foreach ($pushChannels as $pushChannelEO)
138 {
139 $pushChannel = (new BuilderPushFromDM($pushChannelEO))->build();
140
141 if ($pushChannel->getEntityType() === self::TYPE_LINK)
142 {
143 $this->renewSectionPush($pushChannel);
144 }
145 elseif ($pushChannel->getEntityType() === self::TYPE_CONNECTION)
146 {
147 $this->renewConnectionPush($pushChannel);
148 }
149 }
150
151 if ($pushChannels->count() < self::RENEW_LIMIT)
152 {
153 return self::RESULT_STATUS['done'];
154 }
155
156 return self::RESULT_STATUS['next'];
157 }
158
166 private function deleteChannel(Push $pushChannel): void
167 {
168 (new PushManager())->deletePush($pushChannel);
169 }
170
178 private function savePushChannel(Push $pushChannel): void
179 {
180 (new PushManager())->updatePush($pushChannel);
181 }
182
188 private function getFactoryByConnection(Connection $connection): ?FactoryInterface
189 {
190 $context = new Context([
191 'connection' => $connection,
192 ]);
193 return FactoryBuilder::create($connection->getVendor()->getCode(), $connection, $context);
194 }
195
204 private function renewPushChannel(PushManagerInterface $vendorPushManager, Push $pushChannel): Result
205 {
206 try
207 {
208 $result = $vendorPushManager->renewPush($pushChannel);
209
210 if ($result->isSuccess())
211 {
212 $this->savePushChannel($pushChannel);
213 }
214 else
215 {
216 $result->addError(new Error('Error of renew push channel.'));
217 }
218 }
219 catch(SyncException $e)
220 {
221 $result = (new Result())->addError(new Error('Error of renew push channel.', $e->getCode()));
222 }
223
224 return $result;
225 }
226
237 private function recreateSectionPushChannel(
238 PushManagerInterface $vendorPushManager,
239 Push $pushChannel,
240 SectionConnection $sectionLink
241 ): Result
242 {
243 $result = new Result();
244 try
245 {
246 $vendorPushManager->deletePush($pushChannel);
247 $result = $vendorPushManager->addSectionPush($sectionLink);
248 if ($result->isSuccess() && !empty($result->getData()))
249 {
250 $data = $result->getData();
251 $pushChannel
252 ->setChannelId($data['CHANNEL_ID'])
253 ->setResourceId($data['RESOURCE_ID'])
254 ->setExpireDate(new \Bitrix\Calendar\Core\Base\Date($data['EXPIRES']));
255 $this->savePushChannel($pushChannel);
256 }
257 else
258 {
259 $result->addError(new Error('Error of create push channel.'));
260 }
261 }
262 catch(ApiException $e)
263 {
264 $result->addError(new Error('ApiException during creation of push channel.'));
265 if ($e->getMessage() === 'ExtensionError')
266 {
267 $this->deleteChannel($pushChannel);
268 }
269 else
270 {
271 throw $e;
272 }
273 }
274 return $result;
275 }
276
282 private function isError405(Result $result): bool
283 {
284 $errors = $result->getErrors();
285 if (empty($errors))
286 {
287 return false;
288 }
289 foreach ($errors as $error)
290 {
291 if ((int)$error->getCode() === 405)
292 {
293 return true;
294 }
295 }
296
297 return false;
298 }
299
303 private function getLinkFactory(): SectionConnectionFactory
304 {
305 if (empty($this->linkFactory))
306 {
307 $this->linkFactory = new SectionConnectionFactory();
308 }
309
310 return $this->linkFactory;
311 }
312
324 private function renewSectionPush(Push $pushChannel): void
325 {
326 $sectionLink = $this->getLinkFactory()->getSectionConnection([
327 'filter' => [
328 '=ID' => $pushChannel->getEntityId(),
329 ]
330 ]);
331 if (
332 $sectionLink !== null
333 && $sectionLink->isActive()
334 && ($sectionLink->getConnection() !== null)
335 && !$sectionLink->getConnection()->isDeleted()
336 )
337 {
339 $vendorFactory = $this->getFactoryByConnection($sectionLink->getConnection());
341 if ($vendorPushManager = $vendorFactory->getPushManager())
342 {
343 $now = new DateTime();
344 if ($pushChannel->getExpireDate()->getDate() > $now)
345 {
346 $result = $this->renewPushChannel($vendorPushManager, $pushChannel);
347 if ($result->isSuccess())
348 {
349 return;
350 }
351 elseif ($result->getErrorCollection()->getErrorByCode(405))
352 {
353 $result = $this->recreateSectionPushChannel($vendorPushManager, $pushChannel, $sectionLink);
354 if ($result->isSuccess())
355 {
356 return;
357 }
358 }
359 elseif ($result->getErrorCollection()->getErrorByCode(401))
360 {
361 return;
362 }
363 }
364 else
365 {
366 $result = $this->recreateSectionPushChannel($vendorPushManager, $pushChannel, $sectionLink);
367 if ($result->isSuccess())
368 {
369 return;
370 }
371 }
372 }
373 }
374
375 $this->deleteChannel($pushChannel);
376 }
377
389 private function renewConnectionPush(Push $pushChannel): void
390 {
392 $connection = $this->getConnectionMapper()->getById($pushChannel->getEntityId());
393 if ($connection !== null && !$connection->isDeleted())
394 {
396 $vendorFactory = $this->getFactoryByConnection($connection);
398 if ($vendorPushManager = $vendorFactory->getPushManager())
399 {
400 $result = $this->recreateConnectionPushChannel($vendorPushManager, $pushChannel, $connection);
401 if ($result->isSuccess())
402 {
403 return;
404 }
405 }
406 }
407
408 $this->deleteChannel($pushChannel);
409 }
410
414 private function getConnectionMapper(): \Bitrix\Calendar\Core\Mappers\Connection
415 {
416 return $this->mapperFactory->getConnection();
417 }
418
428 private function recreateConnectionPushChannel(
429 PushManagerInterface $vendorPushManager,
430 Push $pushChannel,
431 Connection $connection
432 ): Result
433 {
434 $vendorPushManager->deletePush($pushChannel);
435 $result = $vendorPushManager->addConnectionPush($connection);
436 if ($result->isSuccess())
437 {
438 $data = $result->getData();
439 $pushChannel
440 ->setResourceId($data['RESOURCE_ID'])
441 ->setExpireDate(new \Bitrix\Calendar\Core\Base\Date($data['EXPIRES']));
442 $this->savePushChannel($pushChannel);
443 }
444 else
445 {
446 $result->addError(new Error('Error of create push channel.'));
447 }
448 return $result;
449 }
450
459 private function doFixWatchSectionChannels(): void
460 {
461 $query = SectionConnectionTable::query()
462 ->setSelect(['ID', 'CONNECTION_ID', 'SECTION_ID'])
463 ->registerRuntimeField('CONNECTION',
464 new ReferenceField(
465 'CONNECTION',
466 DavConnectionTable::getEntity(),
467 [
468 '=this.CONNECTION_ID' => 'ref.ID',
469 ],
470 ['join_type' => Join::TYPE_INNER]
471 )
472 )
473 ->registerRuntimeField('PUSH',
474 new ReferenceField(
475 'PUSH',
476 PushTable::getEntity(),
477 [
478 '=this.ID' => 'ref.ENTITY_ID',
479 'ref.ENTITY_TYPE' => ['?', self::TYPE_LINK]
480 ],
481 ['join_type' => Join::TYPE_LEFT]
482 )
483 )
484 ->where('ACTIVE', 'Y')
485 ->where('LAST_SYNC_STATUS', 'success')
486 ->where('CONNECTION.IS_DELETED', 'N')
487 ->whereIn('CONNECTION.ACCOUNT_TYPE', [self::GOOGLE_CONNECTION, self::OFFICE365_CONNECTION])
488 ->whereNull('PUSH.ENTITY_TYPE')
489 ->setLimit(self::FIX_LIMIT)
490 ->exec()
491 ;
492
493 while ($row = $query->Fetch())
494 {
495 $manager = $this->getOutgoingManager($row['CONNECTION_ID']);
497 $link = $this->mapperFactory->getSectionConnection()->getById($row['ID']);
498 try
499 {
500 $manager->subscribeSection($link);
501 }
502 catch (Exception $e)
503 {
504 $link->setLastSyncStatus(Dictionary::SYNC_STATUS['failed']);
505 $this->mapperFactory->getSectionConnection()->update($link);
506 }
507 }
508 }
509
516 private function doFixWatchConnectionChannels(): void
517 {
518 $query = DavConnectionTable::query()
519 ->setSelect(['ID'])
520 ->registerRuntimeField('PUSH',
521 new ReferenceField(
522 'PUSH',
523 PushTable::getEntity(),
524 [
525 '=this.ID' => 'ref.ENTITY_ID',
526 'ref.ENTITY_TYPE' => ['?', self::TYPE_CONNECTION]
527 ],
528 ['join_type' => Join::TYPE_LEFT]
529 )
530 )
531 ->where('IS_DELETED', 'N')
532 ->where('ACCOUNT_TYPE', self::GOOGLE_CONNECTION)
533 ->whereIn('LAST_RESULT', ['success', '[200] OK'])
534 ->whereNull('PUSH.ENTITY_TYPE')
535 ->setLimit(self::FIX_LIMIT)
536 ->exec()
537 ;
538 while ($row = $query->fetch())
539 {
540 try
541 {
542 $manager = $this->getOutgoingManager($row['ID']);
543 $manager->subscribeConnection();
544 }
545 catch (Exception $e)
546 {
547 DavConnectionTable::update($row['ID'], [
548 'LAST_RESULT' => '['. $e->getCode() .'] ERR'
549 ]);
550 }
551 }
552 }
553
562 private function getOutgoingManager($connectionId): OutgoingManager
563 {
564 if (empty(static::$outgoingManagersCache[$connectionId]))
565 {
566 $connection = $this->mapperFactory->getConnection()->getById($connectionId);
567 static::$outgoingManagersCache[$connectionId] = new OutgoingManager($connection);
568 }
569
570 return static::$outgoingManagersCache[$connectionId];
571 }
572}
static createFromTimestamp($timestamp)
Definition datetime.php:246
addError(Main\Error $error)
Definition error.php:22