369 lines
9.3 KiB
PHP
369 lines
9.3 KiB
PHP
<?php
|
|
namespace Bitrix\Rest;
|
|
|
|
use Bitrix\Main;
|
|
use Bitrix\Main\Data\Cache;
|
|
use Bitrix\Rest\Exceptions\ArgumentTypeException;
|
|
|
|
/**
|
|
* Class EventOfflineTable
|
|
*
|
|
* Fields:
|
|
* <ul>
|
|
* <li> ID int mandatory
|
|
* <li> TIMESTAMP_X datetime optional default 'CURRENT_TIMESTAMP'
|
|
* <li> MESSAGE_ID varchar(100) mandatory
|
|
* <li> APP_ID int mandatory
|
|
* <li> EVENT_NAME string(255) mandatory
|
|
* <li> EVENT_DATA string optional
|
|
* <li> PROCESS_ID string(255) optional
|
|
* <li> CONNECTOR_ID string(255) optional
|
|
* </ul>
|
|
*
|
|
* @package Bitrix\Rest
|
|
*
|
|
* DO NOT WRITE ANYTHING BELOW THIS
|
|
*
|
|
* <<< ORMENTITYANNOTATION
|
|
* @method static EO_EventOffline_Query query()
|
|
* @method static EO_EventOffline_Result getByPrimary($primary, array $parameters = [])
|
|
* @method static EO_EventOffline_Result getById($id)
|
|
* @method static EO_EventOffline_Result getList(array $parameters = [])
|
|
* @method static EO_EventOffline_Entity getEntity()
|
|
* @method static \Bitrix\Rest\EO_EventOffline createObject($setDefaultValues = true)
|
|
* @method static \Bitrix\Rest\EO_EventOffline_Collection createCollection()
|
|
* @method static \Bitrix\Rest\EO_EventOffline wakeUpObject($row)
|
|
* @method static \Bitrix\Rest\EO_EventOffline_Collection wakeUpCollection($rows)
|
|
*/
|
|
|
|
class EventOfflineTable extends Main\Entity\DataManager
|
|
{
|
|
const PROCESS_ID_LIFETIME = 2952000; // 30 days
|
|
private const OFFLINE_EVENT_DEFAULT_TIMEOUT = 1;
|
|
private const OFFLINE_EVENT_CACHE_PREFIX = 'OFFLINE_EVENT_TIMEOUT';
|
|
|
|
private static $isSendOfflineEvent = false;
|
|
|
|
/**
|
|
* Returns DB table name for entity.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getTableName()
|
|
{
|
|
return 'b_rest_event_offline';
|
|
}
|
|
|
|
/**
|
|
* Returns entity map definition.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function getMap()
|
|
{
|
|
return array(
|
|
'ID' => array(
|
|
'data_type' => 'integer',
|
|
'primary' => true,
|
|
'autocomplete' => true,
|
|
),
|
|
'TIMESTAMP_X' => array(
|
|
'data_type' => 'datetime',
|
|
),
|
|
'MESSAGE_ID' => array(
|
|
'data_type' => 'string',
|
|
'required' => true,
|
|
),
|
|
'APP_ID' => array(
|
|
'data_type' => 'integer',
|
|
'required' => true,
|
|
),
|
|
'EVENT_NAME' => array(
|
|
'data_type' => 'string',
|
|
'required' => true,
|
|
),
|
|
'EVENT_DATA' => array(
|
|
'data_type' => 'text',
|
|
'serialized' => true,
|
|
),
|
|
'EVENT_ADDITIONAL' => array(
|
|
'data_type' => 'text',
|
|
'serialized' => true,
|
|
),
|
|
'PROCESS_ID' => array(
|
|
'data_type' => 'string',
|
|
'default_value' => '',
|
|
),
|
|
'CONNECTOR_ID' => array(
|
|
'data_type' => 'string',
|
|
'default_value' => '',
|
|
),
|
|
'ERROR' => array(
|
|
'data_type' => 'integer',
|
|
'default_value' => 0,
|
|
),
|
|
);
|
|
}
|
|
|
|
public static function cleanProcessAgent()
|
|
{
|
|
$connection = Main\Application::getConnection();
|
|
|
|
$tableName = static::getTableName();
|
|
$dateTime = $connection->getSqlHelper()->addSecondsToDateTime('-' . static::PROCESS_ID_LIFETIME);
|
|
|
|
$sql = "DELETE FROM {$tableName} WHERE PROCESS_ID<>'' AND TIMESTAMP_X<{$dateTime}";
|
|
|
|
$connection->query($sql);
|
|
|
|
return "\\Bitrix\\Rest\\EventOfflineTable::cleanProcessAgent();";
|
|
}
|
|
|
|
public static function callEvent($fields)
|
|
{
|
|
if(!isset($fields['CONNECTOR_ID']))
|
|
{
|
|
$fields['CONNECTOR_ID'] = '';
|
|
}
|
|
|
|
$addFields = array(
|
|
'TIMESTAMP_X' => new Main\Type\DateTime(),
|
|
'MESSAGE_ID' => static::getMessageId($fields),
|
|
'APP_ID' => $fields['APP_ID'],
|
|
'EVENT_NAME' => $fields['EVENT_NAME'],
|
|
'EVENT_DATA' => serialize($fields['EVENT_DATA']),
|
|
'EVENT_ADDITIONAL' => serialize($fields['EVENT_ADDITIONAL']),
|
|
'CONNECTOR_ID' => $fields['CONNECTOR_ID'],
|
|
);
|
|
|
|
$updateFields = array(
|
|
'TIMESTAMP_X' => new Main\Type\DateTime(),
|
|
'EVENT_DATA' => serialize($fields['EVENT_DATA']),
|
|
'EVENT_ADDITIONAL' => serialize($fields['EVENT_ADDITIONAL']),
|
|
);
|
|
|
|
if(array_key_exists('ERROR', $fields))
|
|
{
|
|
$addFields['ERROR'] = intval($fields['ERROR']) > 0 ? 1 : 0;
|
|
$updateFields['ERROR'] = intval($fields['ERROR']) > 0 ? 1 : 0;
|
|
}
|
|
|
|
$connection = Main\Application::getConnection();
|
|
$queries = $connection->getSqlHelper()->prepareMerge(
|
|
static::getTableName(),
|
|
array('MESSAGE_ID', 'APP_ID', 'CONNECTOR_ID', 'PROCESS_ID'),
|
|
$addFields,
|
|
$updateFields
|
|
);
|
|
|
|
foreach($queries as $query)
|
|
{
|
|
$connection->queryExecute($query);
|
|
}
|
|
}
|
|
|
|
public static function markEvents($filter, $order, $limit): string
|
|
{
|
|
$processId = static::getProcessId();
|
|
|
|
$limit = intval($limit);
|
|
$query = new EventOfflineQuery(static::getEntity());
|
|
$query->setOrder($order);
|
|
$query->setLimit($limit);
|
|
|
|
if (is_array($filter))
|
|
{
|
|
foreach ($filter as $key => $value)
|
|
{
|
|
$matches = [];
|
|
if (preg_match('/^([\W]{1,2})(.+)/', $key, $matches) && $matches[0] === $key)
|
|
{
|
|
if (
|
|
!is_string($matches[2])
|
|
|| !is_string($matches[1])
|
|
)
|
|
{
|
|
throw new ArgumentTypeException('FILTER_KEYS', 'string');
|
|
}
|
|
if (is_array($value) || is_object($value))
|
|
{
|
|
throw new ArgumentTypeException($key);
|
|
}
|
|
$query->where(
|
|
$matches[2],
|
|
$matches[1],
|
|
$value
|
|
);
|
|
}
|
|
else
|
|
{
|
|
if (!is_string($key))
|
|
{
|
|
throw new ArgumentTypeException('FILTER_KEYS', 'string');
|
|
}
|
|
if (is_array($value) || is_object($value))
|
|
{
|
|
throw new ArgumentTypeException($key);
|
|
}
|
|
$query->where(
|
|
$key,
|
|
$value
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
$query->mark($processId);
|
|
|
|
return $processId;
|
|
}
|
|
|
|
public static function clearEvents($processId, $appId, $connectorId, $listIds = false)
|
|
{
|
|
$connection = Main\Application::getConnection();
|
|
|
|
$tableName = static::getTableName();
|
|
$processId = $connection->getSqlHelper()->forSql($processId);
|
|
$appId = intval($appId);
|
|
$connectorId = $connection->getSqlHelper()->forSql($connectorId);
|
|
|
|
$sql = "DELETE FROM {$tableName} WHERE PROCESS_ID='{$processId}' AND APP_ID='{$appId}' AND CONNECTOR_ID='{$connectorId}'";
|
|
|
|
if($listIds !== false)
|
|
{
|
|
array_map('intval', $listIds);
|
|
$sql .= " AND ID IN ('".implode("', '", $listIds)."')";
|
|
}
|
|
|
|
$connection->query($sql);
|
|
}
|
|
|
|
public static function clearEventsByMessageId($processId, $appId, $connectorId, $listIds = false)
|
|
{
|
|
$connection = Main\Application::getConnection();
|
|
$helper = $connection->getSqlHelper();
|
|
|
|
$tableName = static::getTableName();
|
|
$processId = $connection->getSqlHelper()->forSql($processId);
|
|
$appId = intval($appId);
|
|
$connectorId = $connection->getSqlHelper()->forSql($connectorId);
|
|
|
|
$sql = "DELETE FROM {$tableName} WHERE PROCESS_ID='{$processId}' AND APP_ID='{$appId}' AND CONNECTOR_ID='{$connectorId}'";
|
|
|
|
if($listIds !== false)
|
|
{
|
|
foreach($listIds as $key => $id)
|
|
{
|
|
$listIds[$key] = $helper->forSql($id);
|
|
}
|
|
|
|
$sql .= " AND MESSAGE_ID IN ('".implode("', '", $listIds)."')";
|
|
}
|
|
|
|
$connection->query($sql);
|
|
}
|
|
|
|
public static function markError($processId, $appId, $connectorId, array $listIds)
|
|
{
|
|
if(count($listIds) > 0)
|
|
{
|
|
$connection = Main\Application::getConnection();
|
|
$helper = $connection->getSqlHelper();
|
|
|
|
foreach($listIds as $key => $id)
|
|
{
|
|
$listIds[$key] = $helper->forSql($id);
|
|
}
|
|
|
|
$queryWhere = array(
|
|
"APP_ID='".intval($appId)."'",
|
|
"CONNECTOR_ID='".$helper->forSql($connectorId)."'",
|
|
"MESSAGE_ID IN ('".implode("', '", $listIds)."')",
|
|
);
|
|
|
|
$sqlTable = static::getTableName();
|
|
$sqlWhere = implode(" AND ", $queryWhere);
|
|
$sqlProcessId = $helper->forSql($processId);
|
|
|
|
$sql = array();
|
|
$sql[] = "DELETE FROM {$sqlTable} WHERE {$sqlWhere} AND ERROR=0 AND PROCESS_ID <> '{$sqlProcessId}'";
|
|
$sql[] = "UPDATE {$sqlTable} SET ERROR=1, PROCESS_ID=IF(PROCESS_ID='{$sqlProcessId}', '', 'fake_process_id') WHERE {$sqlWhere} AND ERROR=0 ORDER BY PROCESS_ID ASC";
|
|
$sql[] = "DELETE FROM {$sqlTable} WHERE {$sqlWhere} AND PROCESS_ID='fake_process_id'";
|
|
|
|
foreach($sql as $query)
|
|
{
|
|
$connection->query($query);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected static function getProcessId()
|
|
{
|
|
return Main\Security\Random::getString(32);
|
|
}
|
|
|
|
protected static function getMessageId($fields)
|
|
{
|
|
return isset($fields['MESSAGE_ID']) ? $fields['MESSAGE_ID'] : md5($fields['EVENT_NAME'].'|'.Main\Web\Json::encode($fields['EVENT_DATA']));
|
|
}
|
|
|
|
public static function checkSendTime($id, $timeout = null) : bool
|
|
{
|
|
$result = false;
|
|
$timeout = !is_null($timeout) ? (int)$timeout : static::OFFLINE_EVENT_DEFAULT_TIMEOUT;
|
|
|
|
if ($timeout > 0)
|
|
{
|
|
$key = static::OFFLINE_EVENT_CACHE_PREFIX. '|' . $id . '|'. $timeout;
|
|
$cache = Cache::createInstance();
|
|
if ($cache->initCache($timeout, $key))
|
|
{
|
|
$result = false;
|
|
}
|
|
elseif ($cache->startDataCache())
|
|
{
|
|
$result = true;
|
|
$data = 1;
|
|
$cache->endDataCache($data);
|
|
}
|
|
}
|
|
elseif (static::$isSendOfflineEvent === false)
|
|
{
|
|
static::$isSendOfflineEvent = true;
|
|
$result = true;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
public static function prepareOfflineEvent($params, $handler)
|
|
{
|
|
$data = reset($params);
|
|
if (!is_array($data['APP_LIST']) || !in_array((int) $handler['APP_ID'], $data['APP_LIST'], true))
|
|
{
|
|
throw new RestException('Wrong application.');
|
|
}
|
|
|
|
$timeout = $handler['OPTIONS']['minTimeout'] ?? null;
|
|
if (!static::checkSendTime($handler['ID'], $timeout))
|
|
{
|
|
throw new RestException('Time is not up.');
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Removes all application offline event handlers.
|
|
*
|
|
* @param int $appId Application ID.
|
|
*
|
|
* @return Main\DB\Result
|
|
*/
|
|
public static function deleteByApp(mixed $appId): Main\DB\Result
|
|
{
|
|
$connection = Main\Application::getConnection();
|
|
|
|
return $connection->query("DELETE FROM ".static::getTableName()." WHERE APP_ID='".$appId."'");
|
|
}
|
|
} |