1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
pushwatchingmanager.php
См. документацию.
1<?php
2
3namespace Bitrix\Calendar\Sync\Managers;
4
5use Bitrix\Calendar\Core\Base\BaseException;
6use Bitrix\Calendar\Core\Mappers\Factory;
7use Bitrix\Calendar\Internals\PushTable;
8use Bitrix\Calendar\Internals\SectionConnectionTable;
9use Bitrix\Calendar\Sync\Builders\BuilderPushFromDM;
10use Bitrix\Calendar\Sync\Connection\Connection;
11use Bitrix\Calendar\Sync\Connection\SectionConnection;
12use Bitrix\Calendar\Sync\Dictionary;
13use Bitrix\Calendar\Sync\Exceptions\ApiException;
14use Bitrix\Calendar\Sync\Exceptions\SyncException;
15use Bitrix\Calendar\Sync\Factories\FactoryBuilder;
16use Bitrix\Calendar\Sync\Factories\SectionConnectionFactory;
17use Bitrix\Calendar\Sync\Factories\FactoryInterface;
18use Bitrix\Calendar\Sync\Push\Push;
19use Bitrix\Calendar\Sync\Util\Context;
20use Bitrix\Calendar\Sync\Util\Result;
21use Bitrix\Dav\Internals\DavConnectionTable;
22use Bitrix\Main\ArgumentException;
23use Bitrix\Main\DI\ServiceLocator;
24use Bitrix\Main\Entity\ReferenceField;
25use Bitrix\Main\Error;
26use Bitrix\Main\Loader;
27use Bitrix\Main\LoaderException;
28use Bitrix\Main\ObjectException;
29use Bitrix\Main\ObjectNotFoundException;
30use Bitrix\Main\ObjectPropertyException;
31use Bitrix\Main\ORM\Query\Join;
32use Bitrix\Main\SystemException;
33use Bitrix\Main\Type\Date;
34use Bitrix\Main\Type\DateTime;
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 && $sectionLink->getConnection()?->getOwner() !== null
337 )
338 {
340 $vendorFactory = $this->getFactoryByConnection($sectionLink->getConnection());
342 if ($vendorPushManager = $vendorFactory->getPushManager())
343 {
344 $now = new DateTime();
345 if ($pushChannel->getExpireDate()->getDate() > $now)
346 {
347 $result = $this->renewPushChannel($vendorPushManager, $pushChannel);
348 if ($result->isSuccess())
349 {
350 return;
351 }
352 elseif ($result->getErrorCollection()->getErrorByCode(405))
353 {
354 $result = $this->recreateSectionPushChannel($vendorPushManager, $pushChannel, $sectionLink);
355 if ($result->isSuccess())
356 {
357 return;
358 }
359 }
360 elseif ($result->getErrorCollection()->getErrorByCode(401))
361 {
362 return;
363 }
364 }
365 else
366 {
367 $result = $this->recreateSectionPushChannel($vendorPushManager, $pushChannel, $sectionLink);
368 if ($result->isSuccess())
369 {
370 return;
371 }
372 }
373 }
374 }
375
376 $this->deleteChannel($pushChannel);
377 }
378
390 private function renewConnectionPush(Push $pushChannel): void
391 {
393 $connection = $this->getConnectionMapper()->getById($pushChannel->getEntityId());
394 if (
395 $connection !== null
396 && !$connection->isDeleted()
397 && $connection->getOwner() !== null
398 )
399 {
401 $vendorFactory = $this->getFactoryByConnection($connection);
403 if ($vendorPushManager = $vendorFactory->getPushManager())
404 {
405 $result = $this->recreateConnectionPushChannel($vendorPushManager, $pushChannel, $connection);
406 if ($result->isSuccess())
407 {
408 return;
409 }
410 }
411 }
412
413 $this->deleteChannel($pushChannel);
414 }
415
419 private function getConnectionMapper(): \Bitrix\Calendar\Core\Mappers\Connection
420 {
421 return $this->mapperFactory->getConnection();
422 }
423
433 private function recreateConnectionPushChannel(
434 PushManagerInterface $vendorPushManager,
435 Push $pushChannel,
436 Connection $connection
437 ): Result
438 {
439 $vendorPushManager->deletePush($pushChannel);
440 $result = $vendorPushManager->addConnectionPush($connection);
441 if ($result->isSuccess())
442 {
443 $data = $result->getData();
444 $pushChannel
445 ->setResourceId($data['RESOURCE_ID'])
446 ->setExpireDate(new \Bitrix\Calendar\Core\Base\Date($data['EXPIRES']));
447 $this->savePushChannel($pushChannel);
448 }
449 else
450 {
451 $result->addError(new Error('Error of create push channel.'));
452 }
453 return $result;
454 }
455
464 private function doFixWatchSectionChannels(): void
465 {
466 $query = SectionConnectionTable::query()
467 ->setSelect([
468 'ID',
469 'CONNECTION_ID',
470 'SECTION_ID',
471 'ACTIVE',
472 'LAST_SYNC_STATUS',
473 'CONNECTION.IS_DELETED',
474 'CONNECTION.ACCOUNT_TYPE',
475 'PUSH.ENTITY_TYPE'
476 ])
477 ->registerRuntimeField('PUSH',
478 new ReferenceField(
479 'PUSH',
480 PushTable::getEntity(),
481 [
482 '=this.ID' => 'ref.ENTITY_ID',
483 'ref.ENTITY_TYPE' => ['?', self::TYPE_LINK]
484 ],
485 ['join_type' => Join::TYPE_LEFT]
486 )
487 )
488 ->where('ACTIVE', 'Y')
489 ->where('LAST_SYNC_STATUS', 'success')
490 ->where('CONNECTION.IS_DELETED', 'N')
491 ->whereIn('CONNECTION.ACCOUNT_TYPE', [self::GOOGLE_CONNECTION, self::OFFICE365_CONNECTION])
492 ->whereNull('PUSH.ENTITY_TYPE')
493 ->setLimit(self::FIX_LIMIT)
494 ->exec()
495 ;
496
497 while ($row = $query->Fetch())
498 {
500 $connection = $this->mapperFactory->getConnection()->getById($row['CONNECTION_ID']);
501 if ($connection === null || $connection->getOwner()?->getId() === null)
502 {
503 SectionConnectionTable::delete($row['ID']);
504
505 continue;
506 }
507
508 $manager = $this->getOutgoingManager($connection);
510 $link = $this->mapperFactory->getSectionConnection()->getById($row['ID']);
511 try
512 {
513 if ($link !== null)
514 {
515 $manager->subscribeSection($link);
516 }
517 }
518 catch (Exception $e)
519 {
520 $link->setLastSyncStatus(Dictionary::SYNC_STATUS['failed']);
521 $this->mapperFactory->getSectionConnection()->update($link);
522 }
523 }
524 }
525
532 private function doFixWatchConnectionChannels(): void
533 {
534 $query = DavConnectionTable::query()
535 ->setSelect([
536 'ID',
537 'IS_DELETED',
538 'ACCOUNT_TYPE',
539 'LAST_RESULT',
540 'PUSH.ENTITY_TYPE',
541 ])
542 ->registerRuntimeField('PUSH',
543 new ReferenceField(
544 'PUSH',
545 PushTable::getEntity(),
546 [
547 '=this.ID' => 'ref.ENTITY_ID',
548 'ref.ENTITY_TYPE' => ['?', self::TYPE_CONNECTION]
549 ],
550 ['join_type' => Join::TYPE_LEFT]
551 )
552 )
553 ->where('IS_DELETED', 'N')
554 ->where('ACCOUNT_TYPE', self::GOOGLE_CONNECTION)
555 ->whereIn('LAST_RESULT', ['success', '[200] OK'])
556 ->whereNull('PUSH.ENTITY_TYPE')
557 ->setLimit(self::FIX_LIMIT)
558 ->exec()
559 ;
560 while ($row = $query->fetch())
561 {
562 try
563 {
565 $connection = $this->mapperFactory->getConnection()->getById($row['ID']);
566 if ($connection === null || $connection->getOwner()?->getId() === null)
567 {
568 return;
569 }
570
571 $manager = $this->getOutgoingManager($connection);
572 $manager->subscribeConnection();
573 }
574 catch (Exception $e)
575 {
576 DavConnectionTable::update($row['ID'], [
577 'LAST_RESULT' => '['. $e->getCode() .'] ERR'
578 ]);
579 }
580 }
581 }
582
589 private function getOutgoingManager(Connection $connection): OutgoingManager
590 {
591 if (empty(static::$outgoingManagersCache[$connection->getId()]))
592 {
593 static::$outgoingManagersCache[$connection->getId()] = new OutgoingManager($connection);
594 }
595
596 return static::$outgoingManagersCache[$connection->getId()];
597 }
598}
$connection
Определения actionsdefinitions.php:38
const SYNC_STATUS
Определения dictionary.php:16
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
$query
Определения get_search.php:11
$errors
Определения iblock_catalog_edit.php:74
$context
Определения csv_new_setup.php:223
const FORMAT_DATETIME
Определения include.php:64
$status
Определения session.php:10
Calendar($fieldName, $formName="")
Определения tools.php:291
addError(string $class, string $message='')
Определения AccessErrorTrait.php:18
trait Error
Определения error.php:11
$manager
Определения office365push.php:39
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$error
Определения subscription_card_product.php:20