mirror of
https://github.com/elyby/accounts.git
synced 2024-11-23 05:33:20 +05:30
Merge branch 'webhooks' into develop
This commit is contained in:
commit
de17617be3
12
.env-dist
12
.env-dist
@ -29,13 +29,6 @@ REDIS_PORT=6379
|
||||
REDIS_DATABASE=0
|
||||
REDIS_PASSWORD=
|
||||
|
||||
## Параметры подключения к rabbitmq
|
||||
RABBITMQ_HOST=rabbitmq
|
||||
RABBITMQ_PORT=5672
|
||||
RABBITMQ_USER=ely-accounts-app
|
||||
RABBITMQ_PASS=ely-accounts-app-password
|
||||
RABBITMQ_VHOST=/ely.by
|
||||
|
||||
## Параметры Statsd
|
||||
STATSD_HOST=statsd.ely.by
|
||||
STATSD_PORT=8125
|
||||
@ -59,8 +52,3 @@ MYSQL_ROOT_PASSWORD=
|
||||
MYSQL_DATABASE=ely_accounts
|
||||
MYSQL_USER=ely_accounts_user
|
||||
MYSQL_PASSWORD=ely_accounts_password
|
||||
|
||||
# RabbitMQ
|
||||
RABBITMQ_DEFAULT_USER=ely-accounts-app
|
||||
RABBITMQ_DEFAULT_PASS=ely-accounts-app-password
|
||||
RABBITMQ_DEFAULT_VHOST=/ely.by
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM registry.ely.by/elyby/accounts-php:1.7.0
|
||||
FROM registry.ely.by/elyby/accounts-php:1.8.0
|
||||
|
||||
# bootstrap скрипт для проекта
|
||||
COPY docker/php/bootstrap.sh /bootstrap.sh
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM registry.ely.by/elyby/accounts-php:1.7.0-dev
|
||||
FROM registry.ely.by/elyby/accounts-php:1.8.0-dev
|
||||
|
||||
# bootstrap скрипт для проекта
|
||||
COPY docker/php/bootstrap.sh /bootstrap.sh
|
||||
|
@ -1,9 +1,10 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace api\models\authentication;
|
||||
|
||||
use api\aop\annotations\CollectModelMetrics;
|
||||
use api\models\base\ApiForm;
|
||||
use api\modules\accounts\models\ChangeUsernameForm;
|
||||
use api\validators\EmailActivationKeyValidator;
|
||||
use common\models\Account;
|
||||
use common\models\EmailActivation;
|
||||
@ -44,9 +45,6 @@ class ConfirmEmailForm extends ApiForm {
|
||||
throw new ErrorException('Unable activate user account.');
|
||||
}
|
||||
|
||||
$changeUsernameForm = new ChangeUsernameForm($account);
|
||||
$changeUsernameForm->createEventTask($account->id, $account->username, null);
|
||||
|
||||
$transaction->commit();
|
||||
|
||||
return Yii::$app->user->createJwtAuthenticationToken($account, true);
|
||||
|
@ -1,13 +1,11 @@
|
||||
<?php
|
||||
namespace api\modules\accounts\models;
|
||||
|
||||
use api\exceptions\ThisShouldNotHappenException;
|
||||
use api\modules\internal\helpers\Error as E;
|
||||
use common\helpers\Amqp;
|
||||
use common\models\Account;
|
||||
use common\models\amqp\AccountBanned;
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
use common\tasks\ClearAccountSessions;
|
||||
use Yii;
|
||||
use yii\base\ErrorException;
|
||||
|
||||
class BanAccountForm extends AccountActionForm {
|
||||
|
||||
@ -38,7 +36,7 @@ class BanAccountForm extends AccountActionForm {
|
||||
];
|
||||
}
|
||||
|
||||
public function validateAccountActivity() {
|
||||
public function validateAccountActivity(): void {
|
||||
if ($this->getAccount()->status === Account::STATUS_BANNED) {
|
||||
$this->addError('account', E::ACCOUNT_ALREADY_BANNED);
|
||||
}
|
||||
@ -54,27 +52,14 @@ class BanAccountForm extends AccountActionForm {
|
||||
$account = $this->getAccount();
|
||||
$account->status = Account::STATUS_BANNED;
|
||||
if (!$account->save()) {
|
||||
throw new ErrorException('Cannot ban account');
|
||||
throw new ThisShouldNotHappenException('Cannot ban account');
|
||||
}
|
||||
|
||||
$this->createTask();
|
||||
Yii::$app->queue->push(ClearAccountSessions::createFromAccount($account));
|
||||
|
||||
$transaction->commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function createTask(): void {
|
||||
$model = new AccountBanned();
|
||||
$model->accountId = $this->getAccount()->id;
|
||||
$model->duration = $this->duration;
|
||||
$model->message = $this->message;
|
||||
|
||||
$message = Amqp::getInstance()->prepareMessage($model, [
|
||||
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
|
||||
]);
|
||||
|
||||
Amqp::sendToEventsExchange('accounts.account-banned', $message);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,13 +2,10 @@
|
||||
namespace api\modules\accounts\models;
|
||||
|
||||
use api\aop\annotations\CollectModelMetrics;
|
||||
use api\exceptions\ThisShouldNotHappenException;
|
||||
use api\validators\EmailActivationKeyValidator;
|
||||
use common\helpers\Amqp;
|
||||
use common\models\amqp\EmailChanged;
|
||||
use common\models\EmailActivation;
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
use Yii;
|
||||
use yii\base\ErrorException;
|
||||
|
||||
class ChangeEmailForm extends AccountActionForm {
|
||||
|
||||
@ -35,30 +32,14 @@ class ChangeEmailForm extends AccountActionForm {
|
||||
$activation->delete();
|
||||
|
||||
$account = $this->getAccount();
|
||||
$oldEmail = $account->email;
|
||||
$account->email = $activation->newEmail;
|
||||
if (!$account->save()) {
|
||||
throw new ErrorException('Cannot save new account email value');
|
||||
throw new ThisShouldNotHappenException('Cannot save new account email value');
|
||||
}
|
||||
|
||||
$this->createTask($account->id, $account->email, $oldEmail);
|
||||
|
||||
$transaction->commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function createTask(int $accountId, string $newEmail, string $oldEmail): void {
|
||||
$model = new EmailChanged();
|
||||
$model->accountId = $accountId;
|
||||
$model->oldEmail = $oldEmail;
|
||||
$model->newEmail = $newEmail;
|
||||
|
||||
$message = Amqp::getInstance()->prepareMessage($model, [
|
||||
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
|
||||
]);
|
||||
|
||||
Amqp::sendToEventsExchange('accounts.email-changed', $message);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,13 +4,10 @@ namespace api\modules\accounts\models;
|
||||
use api\aop\annotations\CollectModelMetrics;
|
||||
use api\exceptions\ThisShouldNotHappenException;
|
||||
use api\validators\PasswordRequiredValidator;
|
||||
use common\helpers\Amqp;
|
||||
use common\models\amqp\UsernameChanged;
|
||||
use common\models\UsernameHistory;
|
||||
use common\tasks\PullMojangUsername;
|
||||
use common\validators\UsernameValidator;
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
use Yii;
|
||||
use yii\base\ErrorException;
|
||||
|
||||
class ChangeUsernameForm extends AccountActionForm {
|
||||
|
||||
@ -42,7 +39,6 @@ class ChangeUsernameForm extends AccountActionForm {
|
||||
|
||||
$transaction = Yii::$app->db->beginTransaction();
|
||||
|
||||
$oldNickname = $account->username;
|
||||
$account->username = $this->username;
|
||||
if (!$account->save()) {
|
||||
throw new ThisShouldNotHappenException('Cannot save account model with new username');
|
||||
@ -52,36 +48,14 @@ class ChangeUsernameForm extends AccountActionForm {
|
||||
$usernamesHistory->account_id = $account->id;
|
||||
$usernamesHistory->username = $account->username;
|
||||
if (!$usernamesHistory->save()) {
|
||||
throw new ErrorException('Cannot save username history record');
|
||||
throw new ThisShouldNotHappenException('Cannot save username history record');
|
||||
}
|
||||
|
||||
$this->createEventTask($account->id, $account->username, $oldNickname);
|
||||
Yii::$app->queue->push(PullMojangUsername::createFromAccount($account));
|
||||
|
||||
$transaction->commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: вынести это в отдельную сущность, т.к. эта команда используется внутри формы регистрации
|
||||
*
|
||||
* @param integer $accountId
|
||||
* @param string $newNickname
|
||||
* @param string $oldNickname
|
||||
*
|
||||
* @throws \PhpAmqpLib\Exception\AMQPExceptionInterface|\yii\base\Exception
|
||||
*/
|
||||
public function createEventTask($accountId, $newNickname, $oldNickname): void {
|
||||
$model = new UsernameChanged();
|
||||
$model->accountId = $accountId;
|
||||
$model->oldUsername = $oldNickname;
|
||||
$model->newUsername = $newNickname;
|
||||
|
||||
$message = Amqp::getInstance()->prepareMessage($model, [
|
||||
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
|
||||
]);
|
||||
|
||||
Amqp::sendToEventsExchange('accounts.username-changed', $message);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,13 +1,10 @@
|
||||
<?php
|
||||
namespace api\modules\accounts\models;
|
||||
|
||||
use api\exceptions\ThisShouldNotHappenException;
|
||||
use api\modules\internal\helpers\Error as E;
|
||||
use common\helpers\Amqp;
|
||||
use common\models\Account;
|
||||
use common\models\amqp\AccountPardoned;
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
use Yii;
|
||||
use yii\base\ErrorException;
|
||||
|
||||
class PardonAccountForm extends AccountActionForm {
|
||||
|
||||
@ -33,25 +30,12 @@ class PardonAccountForm extends AccountActionForm {
|
||||
$account = $this->getAccount();
|
||||
$account->status = Account::STATUS_ACTIVE;
|
||||
if (!$account->save()) {
|
||||
throw new ErrorException('Cannot pardon account');
|
||||
throw new ThisShouldNotHappenException('Cannot pardon account');
|
||||
}
|
||||
|
||||
$this->createTask();
|
||||
|
||||
$transaction->commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function createTask(): void {
|
||||
$model = new AccountPardoned();
|
||||
$model->accountId = $this->getAccount()->id;
|
||||
|
||||
$message = Amqp::getInstance()->prepareMessage($model, [
|
||||
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
|
||||
]);
|
||||
|
||||
Amqp::sendToEventsExchange('accounts.account-pardoned', $message);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -56,10 +56,9 @@ class RateLimiter extends \yii\filters\RateLimiter {
|
||||
$ip = $request->getUserIP();
|
||||
$key = $this->buildKey($ip);
|
||||
|
||||
$redis = $this->getRedis();
|
||||
$countRequests = (int)$redis->incr($key);
|
||||
$countRequests = (int)Yii::$app->redis->incr($key);
|
||||
if ($countRequests === 1) {
|
||||
$redis->executeCommand('EXPIRE', [$key, $this->limitTime]);
|
||||
Yii::$app->redis->expire($key, $this->limitTime);
|
||||
}
|
||||
|
||||
if ($countRequests > $this->limit) {
|
||||
@ -67,13 +66,6 @@ class RateLimiter extends \yii\filters\RateLimiter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \common\components\Redis\Connection
|
||||
*/
|
||||
public function getRedis() {
|
||||
return Yii::$app->redis;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return OauthClient|null
|
||||
|
@ -19,7 +19,7 @@ class SessionModel {
|
||||
|
||||
public static function find(string $username, string $serverId): ?self {
|
||||
$key = static::buildKey($username, $serverId);
|
||||
$result = Yii::$app->redis->executeCommand('GET', [$key]);
|
||||
$result = Yii::$app->redis->get($key);
|
||||
if (!$result) {
|
||||
return null;
|
||||
}
|
||||
@ -36,11 +36,11 @@ class SessionModel {
|
||||
'serverId' => $this->serverId,
|
||||
]);
|
||||
|
||||
return Yii::$app->redis->executeCommand('SETEX', [$key, self::KEY_TIME, $data]);
|
||||
return Yii::$app->redis->setex($key, self::KEY_TIME, $data);
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
return Yii::$app->redis->executeCommand('DEL', [static::buildKey($this->username, $this->serverId)]);
|
||||
return Yii::$app->redis->del(static::buildKey($this->username, $this->serverId));
|
||||
}
|
||||
|
||||
public function getAccount(): ?Account {
|
||||
|
@ -16,16 +16,15 @@ class Yii extends \yii\BaseYii {
|
||||
* Class BaseApplication
|
||||
* Used for properties that are identical for both WebApplication and ConsoleApplication
|
||||
*
|
||||
* @property \yii\db\Connection $unbufferedDb
|
||||
* @property \yii\swiftmailer\Mailer $mailer
|
||||
* @property \common\components\Redis\Connection $redis
|
||||
* @property \common\components\RabbitMQ\Component $amqp
|
||||
* @property \GuzzleHttp\Client $guzzle
|
||||
* @property \common\components\EmailRenderer $emailRenderer
|
||||
* @property \mito\sentry\Component $sentry
|
||||
* @property \api\components\OAuth2\Component $oauth
|
||||
* @property \common\components\StatsD $statsd
|
||||
* @property \yii\queue\Queue $queue
|
||||
* @property \yii\db\Connection $unbufferedDb
|
||||
* @property \yii\swiftmailer\Mailer $mailer
|
||||
* @property \yii\redis\Connection $redis
|
||||
* @property \GuzzleHttp\Client $guzzle
|
||||
* @property \common\components\EmailRenderer $emailRenderer
|
||||
* @property \mito\sentry\Component $sentry
|
||||
* @property \api\components\OAuth2\Component $oauth
|
||||
* @property \common\components\StatsD $statsd
|
||||
* @property \yii\queue\Queue $queue
|
||||
*/
|
||||
abstract class BaseApplication extends yii\base\Application {
|
||||
}
|
||||
|
@ -1,177 +0,0 @@
|
||||
<?php
|
||||
namespace common\components\RabbitMQ;
|
||||
|
||||
use PhpAmqpLib\Channel\AMQPChannel;
|
||||
use PhpAmqpLib\Connection\AMQPStreamConnection;
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
use yii\base\Exception;
|
||||
use yii\helpers\Json;
|
||||
|
||||
/**
|
||||
* Не гибкий компонент для работы с RabbitMQ, заточенный под нужны текущего проекта
|
||||
*
|
||||
* Компонент основан на расширении Alexey Kuznetsov <mirakuru@webtoucher.ru>
|
||||
*
|
||||
* @property AMQPStreamConnection $connection AMQP connection.
|
||||
* @property AMQPChannel $channel AMQP channel.
|
||||
*/
|
||||
class Component extends \yii\base\Component {
|
||||
|
||||
public const TYPE_TOPIC = 'topic';
|
||||
public const TYPE_DIRECT = 'direct';
|
||||
public const TYPE_HEADERS = 'headers';
|
||||
public const TYPE_FANOUT = 'fanout';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $host = '127.0.0.1';
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
public $port = 5672;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $password;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $vhost = '/';
|
||||
|
||||
/**
|
||||
* @var AMQPStreamConnection
|
||||
*/
|
||||
protected $amqpConnection;
|
||||
|
||||
/**
|
||||
* @var AMQPChannel[]
|
||||
*/
|
||||
protected $channels = [];
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function init() {
|
||||
parent::init();
|
||||
if (empty($this->user)) {
|
||||
throw new Exception("Parameter 'user' was not set for AMQP connection.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AMQPStreamConnection
|
||||
*/
|
||||
public function getConnection() {
|
||||
if (!$this->amqpConnection) {
|
||||
$this->amqpConnection = new AMQPStreamConnection(
|
||||
$this->host,
|
||||
$this->port,
|
||||
$this->user,
|
||||
$this->password,
|
||||
$this->vhost
|
||||
);
|
||||
}
|
||||
|
||||
return $this->amqpConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $channel_id
|
||||
* @return AMQPChannel
|
||||
*/
|
||||
public function getChannel($channel_id = null) {
|
||||
$index = $channel_id ?: 'default';
|
||||
if (!array_key_exists($index, $this->channels)) {
|
||||
$this->channels[$index] = $this->getConnection()->channel($channel_id);
|
||||
}
|
||||
|
||||
return $this->channels[$index];
|
||||
}
|
||||
|
||||
// TODO: метод sendToQueue
|
||||
|
||||
/**
|
||||
* Sends message to the exchange.
|
||||
*
|
||||
* @param string $exchangeName
|
||||
* @param string $routingKey
|
||||
* @param string|array $message
|
||||
* @param array $exchangeArgs
|
||||
* @param array $publishArgs
|
||||
*/
|
||||
public function sendToExchange($exchangeName, $routingKey, $message, $exchangeArgs = [], $publishArgs = []) {
|
||||
$message = $this->prepareMessage($message);
|
||||
$channel = $this->getChannel();
|
||||
$channel->exchange_declare(...$this->prepareExchangeArgs($exchangeName, $exchangeArgs));
|
||||
$channel->basic_publish(...$this->preparePublishArgs($message, $exchangeName, $routingKey, $publishArgs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns prepaired AMQP message.
|
||||
*
|
||||
* @param string|array|object $message
|
||||
* @param array $properties
|
||||
* @return AMQPMessage
|
||||
* @throws Exception If message is empty.
|
||||
*/
|
||||
public function prepareMessage($message, $properties = null) {
|
||||
if ($message instanceof AMQPMessage) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
if (empty($message)) {
|
||||
throw new Exception('AMQP message can not be empty');
|
||||
}
|
||||
|
||||
if (is_array($message) || is_object($message)) {
|
||||
$message = Json::encode($message);
|
||||
}
|
||||
|
||||
return new AMQPMessage($message, $properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Объединяет переданный набор аргументов с поведением по умолчанию
|
||||
*
|
||||
* @param string $exchangeName
|
||||
* @param array $args
|
||||
* @return array
|
||||
*/
|
||||
protected function prepareExchangeArgs($exchangeName, array $args) {
|
||||
return array_replace([
|
||||
$exchangeName,
|
||||
self::TYPE_FANOUT,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
], $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Объединяет переданный набор аргументов с поведением по умолчанию
|
||||
*
|
||||
* @param AMQPMessage $message
|
||||
* @param string $exchangeName
|
||||
* @param string $routeKey
|
||||
* @param array $args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function preparePublishArgs($message, $exchangeName, $routeKey, array $args) {
|
||||
return array_replace([
|
||||
$message,
|
||||
$exchangeName,
|
||||
$routeKey,
|
||||
], $args);
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
namespace common\components\RabbitMQ;
|
||||
|
||||
use Yii;
|
||||
|
||||
class Helper {
|
||||
|
||||
/**
|
||||
* @return Component $amqp
|
||||
*/
|
||||
public static function getInstance() {
|
||||
return Yii::$app->amqp;
|
||||
}
|
||||
|
||||
public static function sendToExchange($exchange, $routingKey, $message, $exchangeArgs = []) {
|
||||
static::getInstance()->sendToExchange($exchange, $routingKey, $message, $exchangeArgs);
|
||||
}
|
||||
|
||||
public static function sendToEventsExchange($routingKey, $message) {
|
||||
static::sendToExchange('events', $routingKey, $message, [
|
||||
1 => Component::TYPE_TOPIC, // type -> topic
|
||||
3 => true, // durable -> true
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
namespace common\components\Redis;
|
||||
|
||||
use yii\di\Instance;
|
||||
|
||||
class Cache extends \yii\redis\Cache {
|
||||
|
||||
public function init() {
|
||||
\yii\caching\Cache::init();
|
||||
$this->redis = Instance::ensure($this->redis, ConnectionInterface::class);
|
||||
}
|
||||
|
||||
}
|
@ -1,415 +0,0 @@
|
||||
<?php
|
||||
namespace common\components\Redis;
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\ClientInterface;
|
||||
use yii\base\Component;
|
||||
|
||||
/**
|
||||
* Interface defining a client able to execute commands against Redis.
|
||||
*
|
||||
* All the commands exposed by the client generally have the same signature as
|
||||
* described by the Redis documentation, but some of them offer an additional
|
||||
* and more friendly interface to ease programming which is described in the
|
||||
* following list of methods:
|
||||
*
|
||||
* @method int del(array $keys)
|
||||
* @method string dump($key)
|
||||
* @method int exists($key)
|
||||
* @method int expire($key, $seconds)
|
||||
* @method int expireat($key, $timestamp)
|
||||
* @method array keys($pattern)
|
||||
* @method int move($key, $db)
|
||||
* @method mixed object($subcommand, $key)
|
||||
* @method int persist($key)
|
||||
* @method int pexpire($key, $milliseconds)
|
||||
* @method int pexpireat($key, $timestamp)
|
||||
* @method int pttl($key)
|
||||
* @method string randomkey()
|
||||
* @method mixed rename($key, $target)
|
||||
* @method int renamenx($key, $target)
|
||||
* @method array scan($cursor, array $options = null)
|
||||
* @method array sort($key, array $options = null)
|
||||
* @method int ttl($key)
|
||||
* @method mixed type($key)
|
||||
* @method int append($key, $value)
|
||||
* @method int bitcount($key, $start = null, $end = null)
|
||||
* @method int bitop($operation, $destkey, $key)
|
||||
* @method int decr($key)
|
||||
* @method int decrby($key, $decrement)
|
||||
* @method string get($key)
|
||||
* @method int getbit($key, $offset)
|
||||
* @method string getrange($key, $start, $end)
|
||||
* @method string getset($key, $value)
|
||||
* @method int incr($key)
|
||||
* @method int incrby($key, $increment)
|
||||
* @method string incrbyfloat($key, $increment)
|
||||
* @method array mget(array $keys)
|
||||
* @method mixed mset(array $dictionary)
|
||||
* @method int msetnx(array $dictionary)
|
||||
* @method mixed psetex($key, $milliseconds, $value)
|
||||
* @method mixed set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method int setbit($key, $offset, $value)
|
||||
* @method int setex($key, $seconds, $value)
|
||||
* @method int setnx($key, $value)
|
||||
* @method int setrange($key, $offset, $value)
|
||||
* @method int strlen($key)
|
||||
* @method int hdel($key, array $fields)
|
||||
* @method int hexists($key, $field)
|
||||
* @method string hget($key, $field)
|
||||
* @method array hgetall($key)
|
||||
* @method int hincrby($key, $field, $increment)
|
||||
* @method string hincrbyfloat($key, $field, $increment)
|
||||
* @method array hkeys($key)
|
||||
* @method int hlen($key)
|
||||
* @method array hmget($key, array $fields)
|
||||
* @method mixed hmset($key, array $dictionary)
|
||||
* @method array hscan($key, $cursor, array $options = null)
|
||||
* @method int hset($key, $field, $value)
|
||||
* @method int hsetnx($key, $field, $value)
|
||||
* @method array hvals($key)
|
||||
* @method array blpop(array $keys, $timeout)
|
||||
* @method array brpop(array $keys, $timeout)
|
||||
* @method array brpoplpush($source, $destination, $timeout)
|
||||
* @method string lindex($key, $index)
|
||||
* @method int linsert($key, $whence, $pivot, $value)
|
||||
* @method int llen($key)
|
||||
* @method string lpop($key)
|
||||
* @method int lpush($key, array $values)
|
||||
* @method int lpushx($key, $value)
|
||||
* @method array lrange($key, $start, $stop)
|
||||
* @method int lrem($key, $count, $value)
|
||||
* @method mixed lset($key, $index, $value)
|
||||
* @method mixed ltrim($key, $start, $stop)
|
||||
* @method string rpop($key)
|
||||
* @method string rpoplpush($source, $destination)
|
||||
* @method int rpush($key, array $values)
|
||||
* @method int rpushx($key, $value)
|
||||
* @method int sadd($key, array $members)
|
||||
* @method int scard($key)
|
||||
* @method array sdiff(array $keys)
|
||||
* @method int sdiffstore($destination, array $keys)
|
||||
* @method array sinter(array $keys)
|
||||
* @method int sinterstore($destination, array $keys)
|
||||
* @method int sismember($key, $member)
|
||||
* @method array smembers($key)
|
||||
* @method int smove($source, $destination, $member)
|
||||
* @method string spop($key)
|
||||
* @method string srandmember($key, $count = null)
|
||||
* @method int srem($key, $member)
|
||||
* @method array sscan($key, $cursor, array $options = null)
|
||||
* @method array sunion(array $keys)
|
||||
* @method int sunionstore($destination, array $keys)
|
||||
* @method int zadd($key, array $membersAndScoresDictionary)
|
||||
* @method int zcard($key)
|
||||
* @method string zcount($key, $min, $max)
|
||||
* @method string zincrby($key, $increment, $member)
|
||||
* @method int zinterstore($destination, array $keys, array $options = null)
|
||||
* @method array zrange($key, $start, $stop, array $options = null)
|
||||
* @method array zrangebyscore($key, $min, $max, array $options = null)
|
||||
* @method int zrank($key, $member)
|
||||
* @method int zrem($key, $member)
|
||||
* @method int zremrangebyrank($key, $start, $stop)
|
||||
* @method int zremrangebyscore($key, $min, $max)
|
||||
* @method array zrevrange($key, $start, $stop, array $options = null)
|
||||
* @method array zrevrangebyscore($key, $min, $max, array $options = null)
|
||||
* @method int zrevrank($key, $member)
|
||||
* @method int zunionstore($destination, array $keys, array $options = null)
|
||||
* @method string zscore($key, $member)
|
||||
* @method array zscan($key, $cursor, array $options = null)
|
||||
* @method array zrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method int zremrangebylex($key, $min, $max)
|
||||
* @method int zlexcount($key, $min, $max)
|
||||
* @method int pfadd($key, array $elements)
|
||||
* @method mixed pfmerge($destinationKey, array $sourceKeys)
|
||||
* @method int pfcount(array $keys)
|
||||
* @method mixed pubsub($subcommand, $argument)
|
||||
* @method int publish($channel, $message)
|
||||
* @method mixed discard()
|
||||
* @method array exec()
|
||||
* @method mixed multi()
|
||||
* @method mixed unwatch()
|
||||
* @method mixed watch($key)
|
||||
* @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method mixed script($subcommand, $argument = null)
|
||||
* @method mixed auth($password)
|
||||
* @method string echo($message)
|
||||
* @method mixed ping($message = null)
|
||||
* @method mixed select($database)
|
||||
* @method mixed bgrewriteaof()
|
||||
* @method mixed bgsave()
|
||||
* @method mixed client($subcommand, $argument = null)
|
||||
* @method mixed config($subcommand, $argument = null)
|
||||
* @method int dbsize()
|
||||
* @method mixed flushall()
|
||||
* @method mixed flushdb()
|
||||
* @method array info($section = null)
|
||||
* @method int lastsave()
|
||||
* @method mixed save()
|
||||
* @method mixed slaveof($host, $port)
|
||||
* @method mixed slowlog($subcommand, $argument = null)
|
||||
* @method array time()
|
||||
* @method array command()
|
||||
*/
|
||||
class Connection extends Component implements ConnectionInterface {
|
||||
|
||||
/**
|
||||
* @var array List of available redis commands http://redis.io/commands
|
||||
*/
|
||||
public const REDIS_COMMANDS = [
|
||||
'BLPOP', // key [key ...] timeout Remove and get the first element in a list, or block until one is available
|
||||
'BRPOP', // key [key ...] timeout Remove and get the last element in a list, or block until one is available
|
||||
'BRPOPLPUSH', // source destination timeout Pop a value from a list, push it to another list and return it; or block until one is available
|
||||
'CLIENT KILL', // ip:port Kill the connection of a client
|
||||
'CLIENT LIST', // Get the list of client connections
|
||||
'CLIENT GETNAME', // Get the current connection name
|
||||
'CLIENT SETNAME', // connection-name Set the current connection name
|
||||
'CONFIG GET', // parameter Get the value of a configuration parameter
|
||||
'CONFIG SET', // parameter value Set a configuration parameter to the given value
|
||||
'CONFIG RESETSTAT', // Reset the stats returned by INFO
|
||||
'DBSIZE', // Return the number of keys in the selected database
|
||||
'DEBUG OBJECT', // key Get debugging information about a key
|
||||
'DEBUG SEGFAULT', // Make the server crash
|
||||
'DECR', // key Decrement the integer value of a key by one
|
||||
'DECRBY', // key decrement Decrement the integer value of a key by the given number
|
||||
'DEL', // key [key ...] Delete a key
|
||||
'DISCARD', // Discard all commands issued after MULTI
|
||||
'DUMP', // key Return a serialized version of the value stored at the specified key.
|
||||
'ECHO', // message Echo the given string
|
||||
'EVAL', // script numkeys key [key ...] arg [arg ...] Execute a Lua script server side
|
||||
'EVALSHA', // sha1 numkeys key [key ...] arg [arg ...] Execute a Lua script server side
|
||||
'EXEC', // Execute all commands issued after MULTI
|
||||
'EXISTS', // key Determine if a key exists
|
||||
'EXPIRE', // key seconds Set a key's time to live in seconds
|
||||
'EXPIREAT', // key timestamp Set the expiration for a key as a UNIX timestamp
|
||||
'FLUSHALL', // Remove all keys from all databases
|
||||
'FLUSHDB', // Remove all keys from the current database
|
||||
'GET', // key Get the value of a key
|
||||
'GETBIT', // key offset Returns the bit value at offset in the string value stored at key
|
||||
'GETRANGE', // key start end Get a substring of the string stored at a key
|
||||
'GETSET', // key value Set the string value of a key and return its old value
|
||||
'HDEL', // key field [field ...] Delete one or more hash fields
|
||||
'HEXISTS', // key field Determine if a hash field exists
|
||||
'HGET', // key field Get the value of a hash field
|
||||
'HGETALL', // key Get all the fields and values in a hash
|
||||
'HINCRBY', // key field increment Increment the integer value of a hash field by the given number
|
||||
'HINCRBYFLOAT', // key field increment Increment the float value of a hash field by the given amount
|
||||
'HKEYS', // key Get all the fields in a hash
|
||||
'HLEN', // key Get the number of fields in a hash
|
||||
'HMGET', // key field [field ...] Get the values of all the given hash fields
|
||||
'HMSET', // key field value [field value ...] Set multiple hash fields to multiple values
|
||||
'HSET', // key field value Set the string value of a hash field
|
||||
'HSETNX', // key field value Set the value of a hash field, only if the field does not exist
|
||||
'HVALS', // key Get all the values in a hash
|
||||
'INCR', // key Increment the integer value of a key by one
|
||||
'INCRBY', // key increment Increment the integer value of a key by the given amount
|
||||
'INCRBYFLOAT', // key increment Increment the float value of a key by the given amount
|
||||
'INFO', // [section] Get information and statistics about the server
|
||||
'KEYS', // pattern Find all keys matching the given pattern
|
||||
'LASTSAVE', // Get the UNIX time stamp of the last successful save to disk
|
||||
'LINDEX', // key index Get an element from a list by its index
|
||||
'LINSERT', // key BEFORE|AFTER pivot value Insert an element before or after another element in a list
|
||||
'LLEN', // key Get the length of a list
|
||||
'LPOP', // key Remove and get the first element in a list
|
||||
'LPUSH', // key value [value ...] Prepend one or multiple values to a list
|
||||
'LPUSHX', // key value Prepend a value to a list, only if the list exists
|
||||
'LRANGE', // key start stop Get a range of elements from a list
|
||||
'LREM', // key count value Remove elements from a list
|
||||
'LSET', // key index value Set the value of an element in a list by its index
|
||||
'LTRIM', // key start stop Trim a list to the specified range
|
||||
'MGET', // key [key ...] Get the values of all the given keys
|
||||
'MIGRATE', // host port key destination-db timeout Atomically transfer a key from a Redis instance to another one.
|
||||
'MONITOR', // Listen for all requests received by the server in real time
|
||||
'MOVE', // key db Move a key to another database
|
||||
'MSET', // key value [key value ...] Set multiple keys to multiple values
|
||||
'MSETNX', // key value [key value ...] Set multiple keys to multiple values, only if none of the keys exist
|
||||
'MULTI', // Mark the start of a transaction block
|
||||
'OBJECT', // subcommand [arguments [arguments ...]] Inspect the internals of Redis objects
|
||||
'PERSIST', // key Remove the expiration from a key
|
||||
'PEXPIRE', // key milliseconds Set a key's time to live in milliseconds
|
||||
'PEXPIREAT', // key milliseconds-timestamp Set the expiration for a key as a UNIX timestamp specified in milliseconds
|
||||
'PING', // Ping the server
|
||||
'PSETEX', // key milliseconds value Set the value and expiration in milliseconds of a key
|
||||
'PSUBSCRIBE', // pattern [pattern ...] Listen for messages published to channels matching the given patterns
|
||||
'PTTL', // key Get the time to live for a key in milliseconds
|
||||
'PUBLISH', // channel message Post a message to a channel
|
||||
'PUNSUBSCRIBE', // [pattern [pattern ...]] Stop listening for messages posted to channels matching the given patterns
|
||||
'QUIT', // Close the connection
|
||||
'RANDOMKEY', // Return a random key from the keyspace
|
||||
'RENAME', // key newkey Rename a key
|
||||
'RENAMENX', // key newkey Rename a key, only if the new key does not exist
|
||||
'RESTORE', // key ttl serialized-value Create a key using the provided serialized value, previously obtained using DUMP.
|
||||
'RPOP', // key Remove and get the last element in a list
|
||||
'RPOPLPUSH', // source destination Remove the last element in a list, append it to another list and return it
|
||||
'RPUSH', // key value [value ...] Append one or multiple values to a list
|
||||
'RPUSHX', // key value Append a value to a list, only if the list exists
|
||||
'SADD', // key member [member ...] Add one or more members to a set
|
||||
'SAVE', // Synchronously save the dataset to disk
|
||||
'SCARD', // key Get the number of members in a set
|
||||
'SCRIPT EXISTS', // script [script ...] Check existence of scripts in the script cache.
|
||||
'SCRIPT FLUSH', // Remove all the scripts from the script cache.
|
||||
'SCRIPT KILL', // Kill the script currently in execution.
|
||||
'SCRIPT LOAD', // script Load the specified Lua script into the script cache.
|
||||
'SDIFF', // key [key ...] Subtract multiple sets
|
||||
'SDIFFSTORE', // destination key [key ...] Subtract multiple sets and store the resulting set in a key
|
||||
'SELECT', // index Change the selected database for the current connection
|
||||
'SET', // key value Set the string value of a key
|
||||
'SETBIT', // key offset value Sets or clears the bit at offset in the string value stored at key
|
||||
'SETEX', // key seconds value Set the value and expiration of a key
|
||||
'SETNX', // key value Set the value of a key, only if the key does not exist
|
||||
'SETRANGE', // key offset value Overwrite part of a string at key starting at the specified offset
|
||||
'SHUTDOWN', // [NOSAVE] [SAVE] Synchronously save the dataset to disk and then shut down the server
|
||||
'SINTER', // key [key ...] Intersect multiple sets
|
||||
'SINTERSTORE', // destination key [key ...] Intersect multiple sets and store the resulting set in a key
|
||||
'SISMEMBER', // key member Determine if a given value is a member of a set
|
||||
'SLAVEOF', // host port Make the server a slave of another instance, or promote it as master
|
||||
'SLOWLOG', // subcommand [argument] Manages the Redis slow queries log
|
||||
'SMEMBERS', // key Get all the members in a set
|
||||
'SMOVE', // source destination member Move a member from one set to another
|
||||
'SORT', // key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination] Sort the elements in a list, set or sorted set
|
||||
'SPOP', // key Remove and return a random member from a set
|
||||
'SRANDMEMBER', // key [count] Get one or multiple random members from a set
|
||||
'SREM', // key member [member ...] Remove one or more members from a set
|
||||
'STRLEN', // key Get the length of the value stored in a key
|
||||
'SUBSCRIBE', // channel [channel ...] Listen for messages published to the given channels
|
||||
'SUNION', // key [key ...] Add multiple sets
|
||||
'SUNIONSTORE', // destination key [key ...] Add multiple sets and store the resulting set in a key
|
||||
'SYNC', // Internal command used for replication
|
||||
'TIME', // Return the current server time
|
||||
'TTL', // key Get the time to live for a key
|
||||
'TYPE', // key Determine the type stored at key
|
||||
'UNSUBSCRIBE', // [channel [channel ...]] Stop listening for messages posted to the given channels
|
||||
'UNWATCH', // Forget about all watched keys
|
||||
'WATCH', // key [key ...] Watch the given keys to determine execution of the MULTI/EXEC block
|
||||
'ZADD', // key score member [score member ...] Add one or more members to a sorted set, or update its score if it already exists
|
||||
'ZCARD', // key Get the number of members in a sorted set
|
||||
'ZCOUNT', // key min max Count the members in a sorted set with scores within the given values
|
||||
'ZINCRBY', // key increment member Increment the score of a member in a sorted set
|
||||
'ZINTERSTORE', // destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] Intersect multiple sorted sets and store the resulting sorted set in a new key
|
||||
'ZRANGE', // key start stop [WITHSCORES] Return a range of members in a sorted set, by index
|
||||
'ZRANGEBYSCORE', // key min max [WITHSCORES] [LIMIT offset count] Return a range of members in a sorted set, by score
|
||||
'ZRANK', // key member Determine the index of a member in a sorted set
|
||||
'ZREM', // key member [member ...] Remove one or more members from a sorted set
|
||||
'ZREMRANGEBYRANK', // key start stop Remove all members in a sorted set within the given indexes
|
||||
'ZREMRANGEBYSCORE', // key min max Remove all members in a sorted set within the given scores
|
||||
'ZREVRANGE', // key start stop [WITHSCORES] Return a range of members in a sorted set, by index, with scores ordered from high to low
|
||||
'ZREVRANGEBYSCORE', // key max min [WITHSCORES] [LIMIT offset count] Return a range of members in a sorted set, by score, with scores ordered from high to low
|
||||
'ZREVRANK', // key member Determine the index of a member in a sorted set, with scores ordered from high to low
|
||||
'ZSCORE', // key member Get the score associated with the given member in a sorted set
|
||||
'ZUNIONSTORE', // destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] Add multiple sorted sets and store the resulting sorted set in a new key
|
||||
'GEOADD', // key longitude latitude member [longitude latitude member ...] Add point
|
||||
'GEODIST', // key member1 member2 [unit] Return the distance between two members
|
||||
'GEOHASH', // key member [member ...] Return valid Geohash strings
|
||||
'GEOPOS', // key member [member ...] Return the positions (longitude,latitude)
|
||||
'GEORADIUS', // key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] Return the members
|
||||
'GEORADIUSBYMEMBER', // key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string the hostname or ip address to use for connecting to the redis server. Defaults to 'localhost'.
|
||||
* If [[unixSocket]] is specified, hostname and port will be ignored.
|
||||
*/
|
||||
public $hostname = 'localhost';
|
||||
|
||||
/**
|
||||
* @var integer the port to use for connecting to the redis server. Default port is 6379.
|
||||
* If [[unixSocket]] is specified, hostname and port will be ignored.
|
||||
*/
|
||||
public $port = 6379;
|
||||
|
||||
/**
|
||||
* @var string the unix socket path (e.g. `/var/run/redis/redis.sock`) to use for connecting to the redis server.
|
||||
* This can be used instead of [[hostname]] and [[port]] to connect to the server using a unix socket.
|
||||
* If a unix socket path is specified, [[hostname]] and [[port]] will be ignored.
|
||||
*/
|
||||
public $unixSocket;
|
||||
|
||||
/**
|
||||
* @var string the password for establishing DB connection. Defaults to null meaning no AUTH command is send.
|
||||
* See http://redis.io/commands/auth
|
||||
*/
|
||||
public $password;
|
||||
|
||||
/**
|
||||
* @var integer the redis database to use. This is an integer value starting from 0. Defaults to 0.
|
||||
*/
|
||||
public $database = 0;
|
||||
|
||||
/**
|
||||
* @var float timeout to use for connection to redis. If not set the timeout set in php.ini will be used: ini_get("default_socket_timeout")
|
||||
*/
|
||||
public $connectionTimeout;
|
||||
|
||||
/**
|
||||
* @var float timeout to use for redis socket when reading and writing data. If not set the php default value will be used.
|
||||
*/
|
||||
public $dataTimeout;
|
||||
|
||||
/**
|
||||
* @var integer Bitmask field which may be set to any combination of connection flags passed to [stream_socket_client()](http://php.net/manual/en/function.stream-socket-client.php).
|
||||
* Currently the select of connection flags is limited to `STREAM_CLIENT_CONNECT` (default), `STREAM_CLIENT_ASYNC_CONNECT` and `STREAM_CLIENT_PERSISTENT`.
|
||||
* @see http://php.net/manual/en/function.stream-socket-client.php
|
||||
*/
|
||||
public $socketClientFlags = STREAM_CLIENT_CONNECT;
|
||||
|
||||
/**
|
||||
* @var array|null
|
||||
*/
|
||||
public $parameters;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $options = [];
|
||||
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $_client;
|
||||
|
||||
public function __call($name, $params) {
|
||||
$redisCommand = mb_strtoupper($name);
|
||||
if (in_array($redisCommand, self::REDIS_COMMANDS)) {
|
||||
return $this->executeCommand($name, $params);
|
||||
}
|
||||
|
||||
return parent::__call($name, $params);
|
||||
}
|
||||
|
||||
public function getConnection(): ClientInterface {
|
||||
if ($this->_client === null) {
|
||||
$this->_client = new Client($this->prepareParams(), $this->options);
|
||||
}
|
||||
|
||||
return $this->_client;
|
||||
}
|
||||
|
||||
public function executeCommand(string $name, array $params = []) {
|
||||
return $this->getConnection()->$name(...$params);
|
||||
}
|
||||
|
||||
private function prepareParams() {
|
||||
if ($this->parameters !== null) {
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
if ($this->unixSocket) {
|
||||
$parameters = [
|
||||
'scheme' => 'unix',
|
||||
'path' => $this->unixSocket,
|
||||
];
|
||||
} else {
|
||||
$parameters = [
|
||||
'scheme' => 'tcp',
|
||||
'host' => $this->hostname,
|
||||
'port' => $this->port,
|
||||
];
|
||||
}
|
||||
|
||||
return array_merge($parameters, [
|
||||
'database' => $this->database,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
namespace common\components\Redis;
|
||||
|
||||
interface ConnectionInterface {
|
||||
|
||||
/**
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
public function getConnection();
|
||||
|
||||
/**
|
||||
* @param string $name Command, that should be executed
|
||||
* @param array $params Arguments for this command
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeCommand(string $name, array $params = []);
|
||||
|
||||
}
|
@ -16,43 +16,35 @@ class Key {
|
||||
$this->key = $this->buildKey($key);
|
||||
}
|
||||
|
||||
public function getRedis(): Connection {
|
||||
return Yii::$app->redis;
|
||||
}
|
||||
|
||||
public function getKey(): string {
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function getValue() {
|
||||
return $this->getRedis()->get($this->key);
|
||||
return Yii::$app->redis->get($this->key);
|
||||
}
|
||||
|
||||
public function setValue($value): self {
|
||||
$this->getRedis()->set($this->key, $value);
|
||||
|
||||
Yii::$app->redis->set($this->key, $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function delete(): self {
|
||||
$this->getRedis()->del([$this->getKey()]);
|
||||
|
||||
Yii::$app->redis->del($this->getKey());
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function exists(): bool {
|
||||
return (bool)$this->getRedis()->exists($this->key);
|
||||
return (bool)Yii::$app->redis->exists($this->key);
|
||||
}
|
||||
|
||||
public function expire(int $ttl): self {
|
||||
$this->getRedis()->expire($this->key, $ttl);
|
||||
|
||||
Yii::$app->redis->expire($this->key, $ttl);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function expireAt(int $unixTimestamp): self {
|
||||
$this->getRedis()->expireat($this->key, $unixTimestamp);
|
||||
|
||||
Yii::$app->redis->expireat($this->key, $unixTimestamp);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -3,23 +3,22 @@ namespace common\components\Redis;
|
||||
|
||||
use ArrayIterator;
|
||||
use IteratorAggregate;
|
||||
use Yii;
|
||||
|
||||
class Set extends Key implements IteratorAggregate {
|
||||
|
||||
public function add($value): self {
|
||||
$this->getRedis()->sadd($this->getKey(), $value);
|
||||
|
||||
Yii::$app->redis->sadd($this->getKey(), $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function remove($value): self {
|
||||
$this->getRedis()->srem($this->getKey(), $value);
|
||||
|
||||
Yii::$app->redis->srem($this->getKey(), $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function members(): array {
|
||||
return $this->getRedis()->smembers($this->getKey());
|
||||
return Yii::$app->redis->smembers($this->getKey());
|
||||
}
|
||||
|
||||
public function getValue(): array {
|
||||
@ -31,11 +30,11 @@ class Set extends Key implements IteratorAggregate {
|
||||
return parent::exists();
|
||||
}
|
||||
|
||||
return (bool)$this->getRedis()->sismember($this->getKey(), $value);
|
||||
return (bool)Yii::$app->redis->sismember($this->getKey(), $value);
|
||||
}
|
||||
|
||||
public function diff(array $sets): array {
|
||||
return $this->getRedis()->sdiff([$this->getKey(), implode(' ', $sets)]);
|
||||
return Yii::$app->redis->sdiff([$this->getKey(), implode(' ', $sets)]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,8 +4,7 @@ return [
|
||||
'vendorPath' => dirname(__DIR__, 2) . '/vendor',
|
||||
'components' => [
|
||||
'cache' => [
|
||||
'class' => common\components\Redis\Cache::class,
|
||||
'redis' => 'redis',
|
||||
'class' => yii\redis\Cache::class,
|
||||
],
|
||||
'db' => [
|
||||
'class' => yii\db\Connection::class,
|
||||
@ -61,20 +60,12 @@ return [
|
||||
'passwordHashStrategy' => 'password_hash',
|
||||
],
|
||||
'redis' => [
|
||||
'class' => common\components\Redis\Connection::class,
|
||||
'class' => yii\redis\Connection::class,
|
||||
'hostname' => getenv('REDIS_HOST') ?: 'redis',
|
||||
'password' => getenv('REDIS_PASS') ?: null,
|
||||
'port' => getenv('REDIS_PORT') ?: 6379,
|
||||
'database' => getenv('REDIS_DATABASE') ?: 0,
|
||||
],
|
||||
'amqp' => [
|
||||
'class' => common\components\RabbitMQ\Component::class,
|
||||
'host' => getenv('RABBITMQ_HOST') ?: 'rabbitmq',
|
||||
'port' => getenv('RABBITMQ_PORT') ?: 5672,
|
||||
'user' => getenv('RABBITMQ_USER'),
|
||||
'password' => getenv('RABBITMQ_PASS'),
|
||||
'vhost' => getenv('RABBITMQ_VHOST'),
|
||||
],
|
||||
'guzzle' => [
|
||||
'class' => GuzzleHttp\Client::class,
|
||||
],
|
||||
@ -97,15 +88,7 @@ return [
|
||||
'namespace' => getenv('STATSD_NAMESPACE') ?: 'ely.accounts.' . gethostname() . '.app',
|
||||
],
|
||||
'queue' => [
|
||||
'class' => yii\queue\amqp_interop\Queue::class,
|
||||
'driver' => yii\queue\amqp_interop\Queue::ENQUEUE_AMQP_LIB,
|
||||
'host' => getenv('RABBITMQ_HOST') ?: 'rabbitmq',
|
||||
'port' => getenv('RABBITMQ_PORT') ?: 5672,
|
||||
'user' => getenv('RABBITMQ_USER'),
|
||||
'password' => getenv('RABBITMQ_PASS'),
|
||||
'vhost' => getenv('RABBITMQ_VHOST'),
|
||||
'queueName' => 'worker',
|
||||
'exchangeName' => 'tasks',
|
||||
'class' => yii\queue\redis\Queue::class,
|
||||
],
|
||||
],
|
||||
'container' => [
|
||||
|
@ -1,7 +1,10 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace common\models;
|
||||
|
||||
use common\components\UserPass;
|
||||
use common\tasks\CreateWebHooksDeliveries;
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\behaviors\TimestampBehavior;
|
||||
@ -152,4 +155,22 @@ class Account extends ActiveRecord {
|
||||
return $this->registration_ip === null ? null : inet_ntop($this->registration_ip);
|
||||
}
|
||||
|
||||
public function afterSave($insert, $changedAttributes) {
|
||||
parent::afterSave($insert, $changedAttributes);
|
||||
|
||||
if ($insert) {
|
||||
return;
|
||||
}
|
||||
|
||||
$meaningfulFields = ['username', 'email', 'uuid', 'status', 'lang'];
|
||||
$meaningfulChangedAttributes = array_filter($changedAttributes, function(string $key) use ($meaningfulFields) {
|
||||
return in_array($key, $meaningfulFields, true);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
if (empty($meaningfulChangedAttributes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Yii::$app->queue->push(CreateWebHooksDeliveries::createAccountEdit($this, $meaningfulChangedAttributes));
|
||||
}
|
||||
|
||||
}
|
||||
|
42
common/models/WebHook.php
Normal file
42
common/models/WebHook.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace common\models;
|
||||
|
||||
use yii\behaviors\TimestampBehavior;
|
||||
use yii\db\ActiveQueryInterface;
|
||||
use yii\db\ActiveRecord;
|
||||
|
||||
/**
|
||||
* Fields:
|
||||
* @property int $id
|
||||
* @property string $url
|
||||
* @property string|null $secret
|
||||
* @property int $created_at
|
||||
*
|
||||
* Relations:
|
||||
* @property WebHookEvent[] $events
|
||||
*
|
||||
* Behaviors:
|
||||
* @mixin TimestampBehavior
|
||||
*/
|
||||
class WebHook extends ActiveRecord {
|
||||
|
||||
public static function tableName(): string {
|
||||
return '{{%webhooks}}';
|
||||
}
|
||||
|
||||
public function behaviors(): array {
|
||||
return [
|
||||
[
|
||||
'class' => TimestampBehavior::class,
|
||||
'updatedAtAttribute' => false,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getEvents(): ActiveQueryInterface {
|
||||
return $this->hasMany(WebHookEvent::class, ['webhook_id' => 'id']);
|
||||
}
|
||||
|
||||
}
|
27
common/models/WebHookEvent.php
Normal file
27
common/models/WebHookEvent.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace common\models;
|
||||
|
||||
use yii\db\ActiveQueryInterface;
|
||||
use yii\db\ActiveRecord;
|
||||
|
||||
/**
|
||||
* Fields:
|
||||
* @property int $webhook_id
|
||||
* @property string $event_type
|
||||
*
|
||||
* Relations:
|
||||
* @property WebHook $webhook
|
||||
*/
|
||||
class WebHookEvent extends ActiveRecord {
|
||||
|
||||
public static function tableName(): string {
|
||||
return '{{%webhooks_events}}';
|
||||
}
|
||||
|
||||
public function getWebhook(): ActiveQueryInterface {
|
||||
return $this->hasOne(WebHook::class, ['id' => 'webhook_id']);
|
||||
}
|
||||
|
||||
}
|
64
common/tasks/ClearAccountSessions.php
Normal file
64
common/tasks/ClearAccountSessions.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace common\tasks;
|
||||
|
||||
use common\models\Account;
|
||||
use Yii;
|
||||
use yii\queue\RetryableJobInterface;
|
||||
|
||||
class ClearAccountSessions implements RetryableJobInterface {
|
||||
|
||||
public $accountId;
|
||||
|
||||
public static function createFromAccount(Account $account): self {
|
||||
$result = new static();
|
||||
$result->accountId = $account->id;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int time to reserve in seconds
|
||||
*/
|
||||
public function getTtr(): int {
|
||||
return 5 * 60;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $attempt number
|
||||
* @param \Exception|\Throwable $error from last execute of the job
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRetry($attempt, $error): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \yii\queue\Queue $queue which pushed and is handling the job
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function execute($queue): void {
|
||||
$account = Account::findOne($this->accountId);
|
||||
if ($account === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($account->getSessions()->each(100, Yii::$app->unbufferedDb) as $authSession) {
|
||||
/** @var \common\models\AccountSession $authSession */
|
||||
$authSession->delete();
|
||||
}
|
||||
|
||||
foreach ($account->getMinecraftAccessKeys()->each(100, Yii::$app->unbufferedDb) as $key) {
|
||||
/** @var \common\models\MinecraftAccessKey $key */
|
||||
$key->delete();
|
||||
}
|
||||
|
||||
foreach ($account->getOauthSessions()->each(100, Yii::$app->unbufferedDb) as $oauthSession) {
|
||||
/** @var \common\models\OauthSession $oauthSession */
|
||||
$oauthSession->delete();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
76
common/tasks/CreateWebHooksDeliveries.php
Normal file
76
common/tasks/CreateWebHooksDeliveries.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace common\tasks;
|
||||
|
||||
use common\models\Account;
|
||||
use common\models\WebHook;
|
||||
use Yii;
|
||||
use yii\queue\RetryableJobInterface;
|
||||
|
||||
class CreateWebHooksDeliveries implements RetryableJobInterface {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $payloads;
|
||||
|
||||
public static function createAccountEdit(Account $account, array $changedAttributes): self {
|
||||
$result = new static();
|
||||
$result->type = 'account.edit';
|
||||
$result->payloads = [
|
||||
'id' => $account->id,
|
||||
'uuid' => $account->uuid,
|
||||
'username' => $account->username,
|
||||
'email' => $account->email,
|
||||
'lang' => $account->lang,
|
||||
'isActive' => $account->status === Account::STATUS_ACTIVE,
|
||||
'registered' => date('c', (int)$account->created_at),
|
||||
'changedAttributes' => $changedAttributes,
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int time to reserve in seconds
|
||||
*/
|
||||
public function getTtr() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $attempt number
|
||||
* @param \Exception|\Throwable $error from last execute of the job
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRetry($attempt, $error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \yii\queue\Queue $queue which pushed and is handling the job
|
||||
*/
|
||||
public function execute($queue) {
|
||||
/** @var WebHook[] $targets */
|
||||
$targets = WebHook::find()
|
||||
->joinWith('events e', false)
|
||||
->andWhere(['e.event_type' => $this->type])
|
||||
->all();
|
||||
foreach ($targets as $target) {
|
||||
$job = new DeliveryWebHook();
|
||||
$job->type = $this->type;
|
||||
$job->url = $target->url;
|
||||
$job->secret = $target->secret;
|
||||
$job->payloads = $this->payloads;
|
||||
Yii::$app->queue->push($job);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
110
common/tasks/DeliveryWebHook.php
Normal file
110
common/tasks/DeliveryWebHook.php
Normal file
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace common\tasks;
|
||||
|
||||
use GuzzleHttp\Client as GuzzleClient;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Middleware;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Yii;
|
||||
use yii\queue\RetryableJobInterface;
|
||||
|
||||
class DeliveryWebHook implements RetryableJobInterface {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $url;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public $secret;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $payloads;
|
||||
|
||||
/**
|
||||
* @return int time to reserve in seconds
|
||||
*/
|
||||
public function getTtr(): int {
|
||||
return 65;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $attempt number
|
||||
* @param \Exception|\Throwable $error from last execute of the job
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRetry($attempt, $error): bool {
|
||||
if ($attempt >= 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($error instanceof ServerException || $error instanceof ConnectException) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \yii\queue\Queue $queue which pushed and is handling the job
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function execute($queue): void {
|
||||
$client = $this->createClient();
|
||||
try {
|
||||
$client->request('POST', $this->url, [
|
||||
'headers' => [
|
||||
'User-Agent' => 'Account-Ely-Hookshot/' . Yii::$app->version,
|
||||
'X-Ely-Accounts-Event' => $this->type,
|
||||
],
|
||||
'form_params' => $this->payloads,
|
||||
]);
|
||||
} catch (ClientException $e) {
|
||||
Yii::info("Delivery for {$this->url} has failed with {$e->getResponse()->getStatusCode()} status.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected function createClient(): ClientInterface {
|
||||
return new GuzzleClient([
|
||||
'handler' => $this->createStack(),
|
||||
'timeout' => 60,
|
||||
'connect_timeout' => 10,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function createStack(): HandlerStack {
|
||||
$stack = HandlerStack::create();
|
||||
$stack->push(Middleware::mapRequest(function(RequestInterface $request): RequestInterface {
|
||||
if (empty($this->secret)) {
|
||||
return $request;
|
||||
}
|
||||
|
||||
$payload = (string)$request->getBody();
|
||||
$signature = hash_hmac('sha1', $payload, $this->secret);
|
||||
|
||||
/** @noinspection ExceptionsAnnotatingAndHandlingInspection */
|
||||
return $request->withHeader('X-Hub-Signature', 'sha1=' . $signature);
|
||||
}));
|
||||
|
||||
return $stack;
|
||||
}
|
||||
|
||||
}
|
72
common/tasks/PullMojangUsername.php
Normal file
72
common/tasks/PullMojangUsername.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace common\tasks;
|
||||
|
||||
use api\exceptions\ThisShouldNotHappenException;
|
||||
use common\components\Mojang\Api as MojangApi;
|
||||
use common\components\Mojang\exceptions\MojangApiException;
|
||||
use common\components\Mojang\exceptions\NoContentException;
|
||||
use common\models\Account;
|
||||
use common\models\MojangUsername;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Yii;
|
||||
use yii\queue\JobInterface;
|
||||
|
||||
class PullMojangUsername implements JobInterface {
|
||||
|
||||
public $username;
|
||||
|
||||
public static function createFromAccount(Account $account): self {
|
||||
$result = new static();
|
||||
$result->username = $account->username;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \yii\queue\Queue $queue which pushed and is handling the job
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function execute($queue) {
|
||||
Yii::$app->statsd->inc('queue.pullMojangUsername.attempt');
|
||||
$mojangApi = $this->createMojangApi();
|
||||
try {
|
||||
$response = $mojangApi->usernameToUUID($this->username);
|
||||
Yii::$app->statsd->inc('queue.pullMojangUsername.found');
|
||||
} catch (NoContentException $e) {
|
||||
$response = false;
|
||||
Yii::$app->statsd->inc('queue.pullMojangUsername.not_found');
|
||||
} catch (RequestException | MojangApiException $e) {
|
||||
Yii::$app->statsd->inc('queue.pullMojangUsername.error');
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var MojangUsername|null $mojangUsername */
|
||||
$mojangUsername = MojangUsername::findOne($this->username);
|
||||
if ($response === false) {
|
||||
if ($mojangUsername !== null) {
|
||||
$mojangUsername->delete();
|
||||
}
|
||||
} else {
|
||||
if ($mojangUsername === null) {
|
||||
$mojangUsername = new MojangUsername();
|
||||
$mojangUsername->username = $response->name;
|
||||
$mojangUsername->uuid = $response->id;
|
||||
} else {
|
||||
$mojangUsername->uuid = $response->id;
|
||||
$mojangUsername->touch('last_pulled_at');
|
||||
}
|
||||
|
||||
if (!$mojangUsername->save()) {
|
||||
throw new ThisShouldNotHappenException('Cannot save mojang username');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function createMojangApi(): MojangApi {
|
||||
return new MojangApi();
|
||||
}
|
||||
|
||||
}
|
@ -13,12 +13,9 @@
|
||||
"league/oauth2-server": "^4.1",
|
||||
"yiisoft/yii2-redis": "~2.0.0",
|
||||
"guzzlehttp/guzzle": "^6.0.0",
|
||||
"php-amqplib/php-amqplib": "^2.6.2",
|
||||
"ely/yii2-tempmail-validator": "^2.0",
|
||||
"emarref/jwt": "~1.0.3",
|
||||
"ely/amqp-controller": "dev-master#d7f8cdbc66c45e477c9c7d5d509bc0c1b11fd3ec",
|
||||
"ely/email-renderer": "dev-master#8aa2e71c5b3b8e4a726c3c090b2997030ba29f73",
|
||||
"predis/predis": "^1.0",
|
||||
"mito/yii2-sentry": "^1.0",
|
||||
"spomky-labs/otphp": "^9.0.2",
|
||||
"bacon/bacon-qr-code": "^1.0",
|
||||
@ -26,8 +23,7 @@
|
||||
"webmozart/assert": "^1.2.0",
|
||||
"goaop/framework": "~2.2.0",
|
||||
"domnikl/statsd": "^2.6",
|
||||
"yiisoft/yii2-queue": "~2.0.2",
|
||||
"enqueue/amqp-lib": "^0.8.11"
|
||||
"yiisoft/yii2-queue": "~2.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"yiisoft/yii2-debug": "*",
|
||||
@ -40,17 +36,14 @@
|
||||
"mockery/mockery": "^1.0.0",
|
||||
"php-mock/php-mock-mockery": "^1.2.0",
|
||||
"friendsofphp/php-cs-fixer": "^2.11",
|
||||
"ely/php-code-style": "^0.1.0"
|
||||
"ely/php-code-style": "^0.1.0",
|
||||
"predis/predis": "^1.1"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "composer",
|
||||
"url": "https://asset-packagist.org"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"url": "git@gitlab.ely.by:elyby/amqp-controller.git"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"url": "git@gitlab.ely.by:elyby/email-renderer.git"
|
||||
|
426
composer.lock
generated
426
composer.lock
generated
@ -1,10 +1,10 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "63497214afa6b50f50d3bc80fe9eb269",
|
||||
"content-hash": "b576f6f9babd8e00a7fa6768d56c37e9",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
@ -153,7 +153,7 @@
|
||||
"version": "v1.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bestiejs/punycode.js.git",
|
||||
"url": "git@github.com:bestiejs/punycode.js.git",
|
||||
"reference": "38c8d3131a82567bfef18da09f7f4db68c84f8a3"
|
||||
},
|
||||
"dist": {
|
||||
@ -610,42 +610,6 @@
|
||||
],
|
||||
"time": "2017-11-15T23:40:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ely/amqp-controller",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "git@gitlab.ely.by:elyby/amqp-controller.git",
|
||||
"reference": "d7f8cdbc66c45e477c9c7d5d509bc0c1b11fd3ec"
|
||||
},
|
||||
"require": {
|
||||
"php-amqplib/php-amqplib": "^2.6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Ely\\Amqp\\": "src/"
|
||||
}
|
||||
},
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ely.by team",
|
||||
"email": "team@ely.by"
|
||||
},
|
||||
{
|
||||
"name": "ErickSkrauch",
|
||||
"email": "erickskrauch@ely.by"
|
||||
}
|
||||
],
|
||||
"homepage": "http://ely.by",
|
||||
"keywords": [
|
||||
""
|
||||
],
|
||||
"time": "2016-11-15T19:40:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ely/email-renderer",
|
||||
"version": "dev-master",
|
||||
@ -779,117 +743,6 @@
|
||||
"description": "A JWT implementation",
|
||||
"time": "2016-09-05T20:33:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "enqueue/amqp-lib",
|
||||
"version": "0.8.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-enqueue/amqp-lib.git",
|
||||
"reference": "5a0da2f2eccb2ebda4d0b2526e1753c96cf0ef75"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-enqueue/amqp-lib/zipball/5a0da2f2eccb2ebda4d0b2526e1753c96cf0ef75",
|
||||
"reference": "5a0da2f2eccb2ebda4d0b2526e1753c96cf0ef75",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"enqueue/amqp-tools": "^0.8.5@dev",
|
||||
"php": ">=5.6",
|
||||
"php-amqplib/php-amqplib": "^2.7@dev",
|
||||
"queue-interop/amqp-interop": "^0.7@dev",
|
||||
"queue-interop/queue-interop": "^0.6@dev"
|
||||
},
|
||||
"require-dev": {
|
||||
"enqueue/enqueue": "^0.8@dev",
|
||||
"enqueue/null": "^0.8@dev",
|
||||
"enqueue/test": "^0.8@dev",
|
||||
"phpunit/phpunit": "~5.4.0",
|
||||
"queue-interop/queue-spec": "^0.5.3@dev",
|
||||
"symfony/config": "^2.8|^3|^4",
|
||||
"symfony/dependency-injection": "^2.8|^3|^4"
|
||||
},
|
||||
"suggest": {
|
||||
"enqueue/enqueue": "If you'd like to use advanced features like Client abstract layer or Symfony integration features"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.8.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Enqueue\\AmqpLib\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Message Queue Amqp Transport",
|
||||
"homepage": "https://enqueue.forma-pro.com/",
|
||||
"keywords": [
|
||||
"AMQP",
|
||||
"messaging",
|
||||
"queue"
|
||||
],
|
||||
"time": "2018-02-16T11:05:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "enqueue/amqp-tools",
|
||||
"version": "0.8.14",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-enqueue/amqp-tools.git",
|
||||
"reference": "f375dee4d8609fca565a80df1c0f238bf0fe774f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-enqueue/amqp-tools/zipball/f375dee4d8609fca565a80df1c0f238bf0fe774f",
|
||||
"reference": "f375dee4d8609fca565a80df1c0f238bf0fe774f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6",
|
||||
"queue-interop/amqp-interop": "^0.7@dev",
|
||||
"queue-interop/queue-interop": "^0.6@dev"
|
||||
},
|
||||
"require-dev": {
|
||||
"enqueue/null": "^0.8@dev",
|
||||
"enqueue/test": "^0.8@dev",
|
||||
"phpunit/phpunit": "~5.4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.8.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Enqueue\\AmqpTools\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Message Queue Amqp Tools",
|
||||
"homepage": "https://enqueue.forma-pro.com/",
|
||||
"keywords": [
|
||||
"AMQP",
|
||||
"messaging",
|
||||
"queue"
|
||||
],
|
||||
"time": "2018-01-10T12:00:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ezyang/htmlpurifier",
|
||||
"version": "v4.9.3",
|
||||
@ -1619,127 +1472,6 @@
|
||||
],
|
||||
"time": "2017-09-27T21:40:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-amqplib/php-amqplib",
|
||||
"version": "v2.7.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-amqplib/php-amqplib.git",
|
||||
"reference": "dfd3694a86f1a7394d3693485259d4074a6ec79b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/dfd3694a86f1a7394d3693485259d4074a6ec79b",
|
||||
"reference": "dfd3694a86f1a7394d3693485259d4074a6ec79b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-bcmath": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"replace": {
|
||||
"videlalvaro/php-amqplib": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpdocumentor/phpdocumentor": "^2.9",
|
||||
"phpunit/phpunit": "^4.8",
|
||||
"scrutinizer/ocular": "^1.1",
|
||||
"squizlabs/php_codesniffer": "^2.5"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-sockets": "Use AMQPSocketConnection"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.7-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpAmqpLib\\": "PhpAmqpLib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alvaro Videla",
|
||||
"role": "Original Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "John Kelly",
|
||||
"email": "johnmkelly86@gmail.com",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Raúl Araya",
|
||||
"email": "nubeiro@gmail.com",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.",
|
||||
"homepage": "https://github.com/php-amqplib/php-amqplib/",
|
||||
"keywords": [
|
||||
"message",
|
||||
"queue",
|
||||
"rabbitmq"
|
||||
],
|
||||
"time": "2018-02-11T19:28:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "predis/predis",
|
||||
"version": "v1.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nrk/predis.git",
|
||||
"reference": "f0210e38881631afeafb56ab43405a92cafd9fd1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nrk/predis/zipball/f0210e38881631afeafb56ab43405a92cafd9fd1",
|
||||
"reference": "f0210e38881631afeafb56ab43405a92cafd9fd1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.8"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Allows access to Webdis when paired with phpiredis",
|
||||
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Predis\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Daniele Alessandri",
|
||||
"email": "suppakilla@gmail.com",
|
||||
"homepage": "http://clorophilla.net"
|
||||
}
|
||||
],
|
||||
"description": "Flexible and feature-complete Redis client for PHP and HHVM",
|
||||
"homepage": "http://github.com/nrk/predis",
|
||||
"keywords": [
|
||||
"nosql",
|
||||
"predis",
|
||||
"redis"
|
||||
],
|
||||
"time": "2016-06-16T16:22:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "1.0.1",
|
||||
@ -1790,87 +1522,6 @@
|
||||
],
|
||||
"time": "2016-08-06T14:39:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "queue-interop/amqp-interop",
|
||||
"version": "0.7.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/queue-interop/amqp-interop.git",
|
||||
"reference": "03cfac42483d07ab45d1896a6a2e1d873a216bba"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/queue-interop/amqp-interop/zipball/03cfac42483d07ab45d1896a6a2e1d873a216bba",
|
||||
"reference": "03cfac42483d07ab45d1896a6a2e1d873a216bba",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5",
|
||||
"queue-interop/queue-interop": "^0.6@dev"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~5.4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.7.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Interop\\Amqp\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"time": "2018-01-04T09:52:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "queue-interop/queue-interop",
|
||||
"version": "0.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/queue-interop/queue-interop.git",
|
||||
"reference": "38579005c0492c0275bbae31170edf30a7e740fa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/queue-interop/queue-interop/zipball/38579005c0492c0275bbae31170edf30a7e740fa",
|
||||
"reference": "38579005c0492c0275bbae31170edf30a7e740fa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.6.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Interop\\Queue\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Promoting the interoperability of MQs objects. Based on Java JMS",
|
||||
"homepage": "https://github.com/queue-interop/queue-interop",
|
||||
"keywords": [
|
||||
"MQ",
|
||||
"jms",
|
||||
"message queue",
|
||||
"messaging",
|
||||
"queue"
|
||||
],
|
||||
"time": "2017-08-10T11:24:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ramsey/uuid",
|
||||
"version": "3.7.3",
|
||||
@ -2701,24 +2352,25 @@
|
||||
},
|
||||
{
|
||||
"name": "yiisoft/yii2-queue",
|
||||
"version": "2.0.2",
|
||||
"version": "2.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/yiisoft/yii2-queue.git",
|
||||
"reference": "8c2b337f7d9ea934c2affdfc21c9fb387d0a0773"
|
||||
"reference": "d04b4b3c932081200876a351cc6c3502e89e11b8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/yiisoft/yii2-queue/zipball/8c2b337f7d9ea934c2affdfc21c9fb387d0a0773",
|
||||
"reference": "8c2b337f7d9ea934c2affdfc21c9fb387d0a0773",
|
||||
"url": "https://api.github.com/repos/yiisoft/yii2-queue/zipball/d04b4b3c932081200876a351cc6c3502e89e11b8",
|
||||
"reference": "d04b4b3c932081200876a351cc6c3502e89e11b8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5.0",
|
||||
"symfony/process": "*",
|
||||
"yiisoft/yii2": "~2.0.13"
|
||||
"yiisoft/yii2": "~2.0.14"
|
||||
},
|
||||
"require-dev": {
|
||||
"aws/aws-sdk-php": ">=2.4",
|
||||
"enqueue/amqp-lib": "^0.8",
|
||||
"jeremeamia/superclosure": "*",
|
||||
"pda/pheanstalk": "*",
|
||||
@ -2729,6 +2381,7 @@
|
||||
"yiisoft/yii2-redis": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"aws/aws-sdk-php": "Need for aws SQS.",
|
||||
"enqueue/amqp-lib": "Need for AMQP interop queue.",
|
||||
"ext-gearman": "Need for Gearman queue.",
|
||||
"ext-pcntl": "Need for process signals.",
|
||||
@ -2752,7 +2405,8 @@
|
||||
"yii\\queue\\file\\": "src/drivers/file",
|
||||
"yii\\queue\\gearman\\": "src/drivers/gearman",
|
||||
"yii\\queue\\redis\\": "src/drivers/redis",
|
||||
"yii\\queue\\sync\\": "src/drivers/sync"
|
||||
"yii\\queue\\sync\\": "src/drivers/sync",
|
||||
"yii\\queue\\sqs\\": "src/drivers/sqs"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@ -2765,7 +2419,7 @@
|
||||
"email": "zhuravljov@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Yii2 Queue Extension which supported DB, Redis, RabbitMQ, Beanstalk and Gearman",
|
||||
"description": "Yii2 Queue Extension which supported DB, Redis, RabbitMQ, Beanstalk, SQS and Gearman",
|
||||
"keywords": [
|
||||
"async",
|
||||
"beanstalk",
|
||||
@ -2775,9 +2429,10 @@
|
||||
"queue",
|
||||
"rabbitmq",
|
||||
"redis",
|
||||
"sqs",
|
||||
"yii"
|
||||
],
|
||||
"time": "2017-12-26T17:16:14+00:00"
|
||||
"time": "2018-05-23T21:04:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "yiisoft/yii2-redis",
|
||||
@ -4657,6 +4312,56 @@
|
||||
],
|
||||
"time": "2018-01-06T05:45:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "predis/predis",
|
||||
"version": "v1.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nrk/predis.git",
|
||||
"reference": "f0210e38881631afeafb56ab43405a92cafd9fd1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nrk/predis/zipball/f0210e38881631afeafb56ab43405a92cafd9fd1",
|
||||
"reference": "f0210e38881631afeafb56ab43405a92cafd9fd1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.8"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Allows access to Webdis when paired with phpiredis",
|
||||
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Predis\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Daniele Alessandri",
|
||||
"email": "suppakilla@gmail.com",
|
||||
"homepage": "http://clorophilla.net"
|
||||
}
|
||||
],
|
||||
"description": "Flexible and feature-complete Redis client for PHP and HHVM",
|
||||
"homepage": "http://github.com/nrk/predis",
|
||||
"keywords": [
|
||||
"nosql",
|
||||
"predis",
|
||||
"redis"
|
||||
],
|
||||
"time": "2016-06-16T16:22:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/code-unit-reverse-lookup",
|
||||
"version": "1.0.1",
|
||||
@ -6026,7 +5731,6 @@
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"roave/security-advisories": 20,
|
||||
"ely/amqp-controller": 20,
|
||||
"ely/email-renderer": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
|
@ -1,98 +0,0 @@
|
||||
<?php
|
||||
namespace console\controllers;
|
||||
|
||||
use common\components\Mojang\Api as MojangApi;
|
||||
use common\components\Mojang\exceptions\NoContentException;
|
||||
use common\models\Account;
|
||||
use common\models\amqp\AccountBanned;
|
||||
use common\models\amqp\UsernameChanged;
|
||||
use common\models\MojangUsername;
|
||||
use Ely\Amqp\Builder\Configurator;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Yii;
|
||||
|
||||
class AccountQueueController extends AmqpController {
|
||||
|
||||
public function getExchangeName() {
|
||||
return 'events';
|
||||
}
|
||||
|
||||
public function configure(Configurator $configurator) {
|
||||
$configurator->exchange->topic()->durable();
|
||||
$configurator->queue->name('accounts-accounts-events')->durable();
|
||||
$configurator->bind->routingKey('accounts.username-changed')
|
||||
->add()->routingKey('account.account-banned');
|
||||
}
|
||||
|
||||
public function getRoutesMap() {
|
||||
return [
|
||||
'accounts.username-changed' => 'routeUsernameChanged',
|
||||
'accounts.account-banned' => 'routeAccountBanned',
|
||||
];
|
||||
}
|
||||
|
||||
public function routeUsernameChanged(UsernameChanged $body): bool {
|
||||
Yii::$app->statsd->inc('worker.account.usernameChanged.attempt');
|
||||
$mojangApi = $this->createMojangApi();
|
||||
try {
|
||||
$response = $mojangApi->usernameToUUID($body->newUsername);
|
||||
Yii::$app->statsd->inc('worker.account.usernameChanged.found');
|
||||
} catch (NoContentException $e) {
|
||||
$response = false;
|
||||
Yii::$app->statsd->inc('worker.account.usernameChanged.not_found');
|
||||
} catch (RequestException $e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var MojangUsername|null $mojangUsername */
|
||||
$mojangUsername = MojangUsername::findOne($body->newUsername);
|
||||
if ($response === false) {
|
||||
if ($mojangUsername !== null) {
|
||||
$mojangUsername->delete();
|
||||
}
|
||||
} else {
|
||||
if ($mojangUsername === null) {
|
||||
$mojangUsername = new MojangUsername();
|
||||
$mojangUsername->username = $response->name;
|
||||
$mojangUsername->uuid = $response->id;
|
||||
} else {
|
||||
$mojangUsername->uuid = $response->id;
|
||||
$mojangUsername->touch('last_pulled_at');
|
||||
}
|
||||
|
||||
$mojangUsername->save();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function routeAccountBanned(AccountBanned $body): bool {
|
||||
$account = Account::findOne($body->accountId);
|
||||
if ($account === null) {
|
||||
Yii::warning('Cannot find banned account ' . $body->accountId . '. Skipping.');
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($account->sessions as $authSession) {
|
||||
$authSession->delete();
|
||||
}
|
||||
|
||||
foreach ($account->minecraftAccessKeys as $key) {
|
||||
$key->delete();
|
||||
}
|
||||
|
||||
foreach ($account->oauthSessions as $oauthSession) {
|
||||
$oauthSession->delete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MojangApi
|
||||
*/
|
||||
protected function createMojangApi(): MojangApi {
|
||||
return new MojangApi();
|
||||
}
|
||||
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
<?php
|
||||
namespace console\controllers;
|
||||
|
||||
use Ely\Amqp\ControllerTrait;
|
||||
use Exception;
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
use Yii;
|
||||
use yii\console\Controller;
|
||||
use yii\db\Exception as YiiDbException;
|
||||
use yii\helpers\ArrayHelper;
|
||||
use yii\helpers\Inflector;
|
||||
|
||||
abstract class AmqpController extends Controller {
|
||||
use ControllerTrait {
|
||||
callback as _callback;
|
||||
}
|
||||
|
||||
private $reconnected = false;
|
||||
|
||||
final public function actionIndex() {
|
||||
$this->start();
|
||||
}
|
||||
|
||||
public function getRoutesMap() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Переопределяем метод callback, чтобы избержать логгирования в консоль ошибок,
|
||||
* связанных с обвалом того или иного соединения. Это нормально, PHP рождён умирать,
|
||||
* а не работать 24/7 в качестве демона.
|
||||
*
|
||||
* @param AMQPMessage $msg
|
||||
* @throws YiiDbException
|
||||
*/
|
||||
public function callback(AMQPMessage $msg) {
|
||||
try {
|
||||
$this->_callback($msg);
|
||||
} catch (YiiDbException $e) {
|
||||
if ($this->reconnected || !$this->isRestorableException($e)) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->reconnected = true;
|
||||
Yii::$app->db->close();
|
||||
Yii::$app->db->open();
|
||||
$this->callback($msg);
|
||||
}
|
||||
|
||||
$this->reconnected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function getConnection() {
|
||||
return Yii::$app->amqp->getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected function buildRouteActionName($route) {
|
||||
return ArrayHelper::getValue($this->getRoutesMap(), $route, 'route' . Inflector::camelize($route));
|
||||
}
|
||||
|
||||
private function isRestorableException(Exception $e): bool {
|
||||
return strpos($e->getMessage(), 'MySQL server has gone away') !== false
|
||||
|| strcmp($e->getMessage(), 'Error while sending QUERY packet') !== false;
|
||||
}
|
||||
|
||||
}
|
57
console/controllers/WebhooksController.php
Normal file
57
console/controllers/WebhooksController.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace console\controllers;
|
||||
|
||||
use common\models\WebHook;
|
||||
use console\models\WebHookForm;
|
||||
use yii\console\Controller;
|
||||
use yii\console\ExitCode;
|
||||
use yii\helpers\Console;
|
||||
|
||||
class WebhooksController extends Controller {
|
||||
|
||||
public function actionCreate(): int {
|
||||
$form = new WebHookForm(new WebHook());
|
||||
|
||||
$url = Console::prompt('Enter webhook url:', [
|
||||
'required' => true,
|
||||
'validator' => function(string $input, ?string &$error) use ($form): bool {
|
||||
$form->url = $input;
|
||||
if (!$form->validate('url')) {
|
||||
$error = $form->getFirstError('url');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
]);
|
||||
$secret = Console::prompt('Enter webhook secret (empty to no secret):');
|
||||
|
||||
$options = $form::getEvents();
|
||||
$options[''] = 'Finish input'; // It's needed to allow finish input cycle
|
||||
$events = [];
|
||||
|
||||
do {
|
||||
$availableOptions = array_diff($options, $events);
|
||||
$eventIndex = Console::select('Choose wanted events (submit no input to finish):', $availableOptions);
|
||||
if ($eventIndex !== '') {
|
||||
$events[] = $options[$eventIndex];
|
||||
}
|
||||
} while($eventIndex !== '' || empty($events)); // User must choose at least one event
|
||||
|
||||
$form->url = $url;
|
||||
$form->events = $events;
|
||||
if ($secret !== '') {
|
||||
$form->secret = $secret;
|
||||
}
|
||||
|
||||
if (!$form->save()) {
|
||||
Console::error('Unable to create new webhook. Check errors list below' . PHP_EOL . Console::errorSummary($form));
|
||||
return ExitCode::UNSPECIFIED_ERROR;
|
||||
}
|
||||
|
||||
return ExitCode::OK;
|
||||
}
|
||||
|
||||
}
|
@ -25,28 +25,12 @@ class Migration extends YiiMigration {
|
||||
parent::createTable($table, $columns, $options);
|
||||
}
|
||||
|
||||
protected function primary(...$columns) {
|
||||
switch (count($columns)) {
|
||||
case 0:
|
||||
$key = '';
|
||||
break;
|
||||
case 1:
|
||||
$key = $columns[0];
|
||||
break;
|
||||
default:
|
||||
$key = $this->buildKey($columns);
|
||||
protected function primary(string ...$columns): string {
|
||||
foreach ($columns as &$column) {
|
||||
$column = $this->db->quoteColumnName($column);
|
||||
}
|
||||
|
||||
return " PRIMARY KEY ($key) ";
|
||||
}
|
||||
|
||||
private function buildKey(array $columns) {
|
||||
$key = '';
|
||||
foreach ($columns as $i => $column) {
|
||||
$key .= $i === count($columns) ? $column : "$column,";
|
||||
}
|
||||
|
||||
return $key;
|
||||
return ' PRIMARY KEY (' . implode(', ', $columns) . ') ';
|
||||
}
|
||||
|
||||
}
|
||||
|
28
console/migrations/m180706_230451_webhooks.php
Normal file
28
console/migrations/m180706_230451_webhooks.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use console\db\Migration;
|
||||
|
||||
class m180706_230451_webhooks extends Migration {
|
||||
|
||||
public function safeUp() {
|
||||
$this->createTable('{{%webhooks}}', [
|
||||
'id' => $this->primaryKey(11)->unsigned(),
|
||||
'url' => $this->string()->notNull(),
|
||||
'secret' => $this->string(),
|
||||
'created_at' => $this->integer(11)->unsigned()->notNull(),
|
||||
]);
|
||||
|
||||
$this->createTable('{{%webhooks_events}}', [
|
||||
'webhook_id' => $this->db->getTableSchema('{{%webhooks}}')->getColumn('id')->dbType . ' NOT NULL',
|
||||
'event_type' => $this->string()->notNull(),
|
||||
$this->primary('webhook_id', 'event_type'),
|
||||
]);
|
||||
$this->addForeignKey('FK_webhook_event_to_webhook', '{{%webhooks_events}}', 'webhook_id', 'webhooks', 'id', 'CASCADE', 'CASCADE');
|
||||
}
|
||||
|
||||
public function safeDown() {
|
||||
$this->dropTable('{{%webhooks_events}}');
|
||||
$this->dropTable('{{%webhooks}}');
|
||||
}
|
||||
|
||||
}
|
@ -1 +0,0 @@
|
||||
*
|
70
console/models/WebHookForm.php
Normal file
70
console/models/WebHookForm.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace console\models;
|
||||
|
||||
use api\exceptions\ThisShouldNotHappenException;
|
||||
use common\models\WebHook;
|
||||
use common\models\WebHookEvent;
|
||||
use Yii;
|
||||
use yii\base\Model;
|
||||
|
||||
class WebHookForm extends Model {
|
||||
|
||||
public $url;
|
||||
|
||||
public $secret;
|
||||
|
||||
public $events = [];
|
||||
|
||||
private $webHook;
|
||||
|
||||
public function __construct(WebHook $webHook, array $config = []) {
|
||||
parent::__construct($config);
|
||||
$this->webHook = $webHook;
|
||||
}
|
||||
|
||||
public function rules(): array {
|
||||
return [
|
||||
[['url'], 'required'],
|
||||
[['url'], 'url'],
|
||||
[['secret'], 'string'],
|
||||
[['events'], 'in', 'range' => static::getEvents(), 'allowArray' => true],
|
||||
];
|
||||
}
|
||||
|
||||
public function save(): bool {
|
||||
if (!$this->validate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$transaction = Yii::$app->db->beginTransaction();
|
||||
|
||||
$webHook = $this->webHook;
|
||||
$webHook->url = $this->url;
|
||||
$webHook->secret = $this->secret;
|
||||
if (!$webHook->save()) {
|
||||
throw new ThisShouldNotHappenException('Cannot save webhook.');
|
||||
}
|
||||
|
||||
foreach ($this->events as $event) {
|
||||
$eventModel = new WebHookEvent();
|
||||
$eventModel->webhook_id = $webHook->id;
|
||||
$eventModel->event_type = $event;
|
||||
if (!$eventModel->save()) {
|
||||
throw new ThisShouldNotHappenException('Cannot save webhook event.');
|
||||
}
|
||||
}
|
||||
|
||||
$transaction->commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function getEvents(): array {
|
||||
return [
|
||||
'account.edit',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -7,7 +7,18 @@ services:
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
- rabbitmq
|
||||
volumes:
|
||||
- ./:/var/www/html/
|
||||
env_file: .env
|
||||
|
||||
worker:
|
||||
build:
|
||||
dockerfile: Dockerfile-dev
|
||||
context: .
|
||||
command: ['php', 'yii', 'queue/listen', '-v']
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
volumes:
|
||||
- ./:/var/www/html/
|
||||
env_file: .env
|
||||
@ -34,16 +45,6 @@ services:
|
||||
volumes:
|
||||
- ./data/redis:/data
|
||||
|
||||
rabbitmq:
|
||||
image: rabbitmq:3.6-management
|
||||
env_file: .env
|
||||
environment:
|
||||
- VIRTUAL_HOST=rabbitmq.account.ely.by.local
|
||||
- VIRTUAL_PORT=15672
|
||||
networks:
|
||||
- default
|
||||
- nginx-proxy
|
||||
|
||||
phpmyadmin:
|
||||
build: ./docker/phpmyadmin
|
||||
environment:
|
||||
|
@ -2,14 +2,24 @@ version: '2'
|
||||
services:
|
||||
app:
|
||||
image: registry.ely.by/elyby/accounts:latest
|
||||
restart: always
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
env_file: .env
|
||||
|
||||
worker:
|
||||
image: registry.ely.by/elyby/accounts:latest
|
||||
restart: always
|
||||
command: ['php', 'yii', 'queue/listen', '-v']
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
- rabbitmq
|
||||
env_file: .env
|
||||
|
||||
web:
|
||||
image: registry.ely.by/elyby/accounts-nginx:1.0.3
|
||||
restart: always
|
||||
volumes_from:
|
||||
- app
|
||||
links:
|
||||
@ -21,19 +31,17 @@ services:
|
||||
|
||||
db:
|
||||
build: ./docker/mariadb
|
||||
restart: always
|
||||
env_file: .env
|
||||
volumes:
|
||||
- ./data/mysql:/var/lib/mysql
|
||||
|
||||
redis:
|
||||
image: redis:3.0-alpine
|
||||
restart: always
|
||||
volumes:
|
||||
- ./data/redis:/data
|
||||
|
||||
rabbitmq:
|
||||
image: rabbitmq:3.6
|
||||
env_file: .env
|
||||
|
||||
networks:
|
||||
nginx-proxy:
|
||||
external:
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM phpmyadmin/phpmyadmin
|
||||
FROM phpmyadmin/phpmyadmin:4.7.9-1
|
||||
|
||||
RUN printf "\n\nrequire('./config.local.php');\n" >> /www/config.inc.php
|
||||
|
||||
|
@ -1,6 +0,0 @@
|
||||
[program:account-queue-worker]
|
||||
directory=/var/www/html
|
||||
command=wait-for-it rabbitmq:5672 -- php yii account-queue
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=10
|
@ -1,6 +0,0 @@
|
||||
[program:queue-worker]
|
||||
directory=/var/www/html
|
||||
command=wait-for-it rabbitmq:5672 -- php yii queue/listen -v
|
||||
autostart=true
|
||||
autorestart=true
|
||||
priority=10
|
@ -4,7 +4,6 @@ modules:
|
||||
- Filesystem
|
||||
- Yii2
|
||||
- tests\codeception\common\_support\FixtureHelper
|
||||
- tests\codeception\common\_support\amqp\Helper
|
||||
- tests\codeception\common\_support\Mockery
|
||||
- Redis
|
||||
- Asserts
|
||||
|
@ -3,7 +3,6 @@ modules:
|
||||
enabled:
|
||||
- Yii2:
|
||||
part: [orm, email, fixtures]
|
||||
- tests\codeception\common\_support\amqp\Helper
|
||||
- tests\codeception\common\_support\queue\CodeceptionQueueHelper
|
||||
- tests\codeception\common\_support\Mockery
|
||||
config:
|
||||
|
@ -28,12 +28,6 @@ class ConfirmEmailFormTest extends TestCase {
|
||||
/** @var Account $account */
|
||||
$account = Account::findOne($fixture['account_id']);
|
||||
$this->assertEquals(Account::STATUS_ACTIVE, $account->status, 'user status changed to active');
|
||||
|
||||
$message = $this->tester->grabLastSentAmqpMessage('events');
|
||||
$body = json_decode($message->getBody(), true);
|
||||
$this->assertEquals($account->id, $body['accountId']);
|
||||
$this->assertEquals($account->username, $body['newUsername']);
|
||||
$this->assertNull($body['oldUsername']);
|
||||
}
|
||||
|
||||
private function createModel($key) {
|
||||
|
@ -32,19 +32,6 @@ class ChangeEmailFormTest extends TestCase {
|
||||
/** @noinspection UnserializeExploitsInspection */
|
||||
$data = unserialize($newEmailConfirmationFixture['_data']);
|
||||
$this->assertEquals($data['newEmail'], $account->email);
|
||||
$this->tester->canSeeAmqpMessageIsCreated('events');
|
||||
}
|
||||
|
||||
public function testCreateTask() {
|
||||
/** @var Account $account */
|
||||
$account = Account::findOne($this->getAccountId());
|
||||
$model = new ChangeEmailForm($account);
|
||||
$model->createTask(1, 'test1@ely.by', 'test@ely.by');
|
||||
$message = $this->tester->grabLastSentAmqpMessage('events');
|
||||
$body = json_decode($message->getBody(), true);
|
||||
$this->assertEquals(1, $body['accountId']);
|
||||
$this->assertEquals('test1@ely.by', $body['newEmail']);
|
||||
$this->assertEquals('test@ely.by', $body['oldEmail']);
|
||||
}
|
||||
|
||||
private function getAccountId() {
|
||||
|
@ -4,6 +4,7 @@ namespace tests\codeception\api\unit\modules\accounts\models;
|
||||
use api\modules\accounts\models\ChangeUsernameForm;
|
||||
use common\models\Account;
|
||||
use common\models\UsernameHistory;
|
||||
use common\tasks\PullMojangUsername;
|
||||
use tests\codeception\api\unit\TestCase;
|
||||
use tests\codeception\common\fixtures\AccountFixture;
|
||||
use tests\codeception\common\fixtures\UsernameHistoryFixture;
|
||||
@ -25,7 +26,10 @@ class ChangeUsernameFormTest extends TestCase {
|
||||
$this->assertTrue($model->performAction());
|
||||
$this->assertEquals('my_new_nickname', Account::findOne($this->getAccountId())->username);
|
||||
$this->assertInstanceOf(UsernameHistory::class, UsernameHistory::findOne(['username' => 'my_new_nickname']));
|
||||
$this->tester->canSeeAmqpMessageIsCreated('events');
|
||||
/** @var PullMojangUsername $job */
|
||||
$job = $this->tester->grabLastQueuedJob();
|
||||
$this->assertInstanceOf(PullMojangUsername::class, $job);
|
||||
$this->assertSame($job->username, 'my_new_nickname');
|
||||
}
|
||||
|
||||
public function testPerformActionWithTheSameUsername() {
|
||||
@ -42,7 +46,7 @@ class ChangeUsernameFormTest extends TestCase {
|
||||
'username' => $username,
|
||||
['>=', 'applied_in', $callTime],
|
||||
]), 'no new UsernameHistory record, if we don\'t change username');
|
||||
$this->tester->cantSeeAmqpMessageIsCreated('events');
|
||||
$this->assertNull($this->tester->grabLastQueuedJob());
|
||||
}
|
||||
|
||||
public function testPerformActionWithChangeCase() {
|
||||
@ -58,17 +62,10 @@ class ChangeUsernameFormTest extends TestCase {
|
||||
UsernameHistory::findOne(['username' => $newUsername]),
|
||||
'username should change, if we change case of some letters'
|
||||
);
|
||||
$this->tester->canSeeAmqpMessageIsCreated('events');
|
||||
}
|
||||
|
||||
public function testCreateTask() {
|
||||
$model = new ChangeUsernameForm($this->getAccount());
|
||||
$model->createEventTask(1, 'test1', 'test');
|
||||
$message = $this->tester->grabLastSentAmqpMessage('events');
|
||||
$body = json_decode($message->getBody(), true);
|
||||
$this->assertEquals(1, $body['accountId']);
|
||||
$this->assertEquals('test1', $body['newUsername']);
|
||||
$this->assertEquals('test', $body['oldUsername']);
|
||||
/** @var PullMojangUsername $job */
|
||||
$job = $this->tester->grabLastQueuedJob();
|
||||
$this->assertInstanceOf(PullMojangUsername::class, $job);
|
||||
$this->assertSame($job->username, $newUsername);
|
||||
}
|
||||
|
||||
private function getAccount(): Account {
|
||||
|
@ -4,6 +4,7 @@ namespace tests\codeception\api\unit\modules\internal\models;
|
||||
use api\modules\accounts\models\BanAccountForm;
|
||||
use api\modules\internal\helpers\Error as E;
|
||||
use common\models\Account;
|
||||
use common\tasks\ClearAccountSessions;
|
||||
use tests\codeception\api\unit\TestCase;
|
||||
|
||||
class BanFormTest extends TestCase {
|
||||
@ -35,28 +36,10 @@ class BanFormTest extends TestCase {
|
||||
$model = new BanAccountForm($account);
|
||||
$this->assertTrue($model->performAction());
|
||||
$this->assertEquals(Account::STATUS_BANNED, $account->status);
|
||||
$this->tester->canSeeAmqpMessageIsCreated('events');
|
||||
}
|
||||
|
||||
public function testCreateTask() {
|
||||
$account = new Account();
|
||||
$account->id = 3;
|
||||
|
||||
$model = new BanAccountForm($account);
|
||||
$model->createTask();
|
||||
$message = json_decode($this->tester->grabLastSentAmqpMessage('events')->body, true);
|
||||
$this->assertSame(3, $message['accountId']);
|
||||
$this->assertSame(-1, $message['duration']);
|
||||
$this->assertSame('', $message['message']);
|
||||
|
||||
$model = new BanAccountForm($account);
|
||||
$model->duration = 123;
|
||||
$model->message = 'test';
|
||||
$model->createTask();
|
||||
$message = json_decode($this->tester->grabLastSentAmqpMessage('events')->body, true);
|
||||
$this->assertSame(3, $message['accountId']);
|
||||
$this->assertSame(123, $message['duration']);
|
||||
$this->assertSame('test', $message['message']);
|
||||
/** @var ClearAccountSessions $job */
|
||||
$job = $this->tester->grabLastQueuedJob();
|
||||
$this->assertInstanceOf(ClearAccountSessions::class, $job);
|
||||
$this->assertSame($job->accountId, $account->id);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,17 +36,6 @@ class PardonFormTest extends TestCase {
|
||||
$model = new PardonAccountForm($account);
|
||||
$this->assertTrue($model->performAction());
|
||||
$this->assertEquals(Account::STATUS_ACTIVE, $account->status);
|
||||
$this->tester->canSeeAmqpMessageIsCreated('events');
|
||||
}
|
||||
|
||||
public function testCreateTask() {
|
||||
$account = new Account();
|
||||
$account->id = 3;
|
||||
|
||||
$model = new PardonAccountForm($account);
|
||||
$model->createTask();
|
||||
$message = json_decode($this->tester->grabLastSentAmqpMessage('events')->body, true);
|
||||
$this->assertSame(3, $message['accountId']);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,91 +0,0 @@
|
||||
<?php
|
||||
namespace tests\codeception\common\_support\amqp;
|
||||
|
||||
use Codeception\Exception\ModuleException;
|
||||
use Codeception\Module;
|
||||
use Codeception\Module\Yii2;
|
||||
|
||||
class Helper extends Module {
|
||||
|
||||
/**
|
||||
* Checks that message is created.
|
||||
*
|
||||
* ```php
|
||||
* <?php
|
||||
* // check that at least 1 message was created
|
||||
* $I->seeAmqpMessageIsCreated();
|
||||
*
|
||||
* // check that only 3 messages were created
|
||||
* $I->seeAmqpMessageIsCreated(3);
|
||||
* ```
|
||||
*
|
||||
* @param string|null $exchange
|
||||
* @param int|null $num
|
||||
*/
|
||||
public function seeAmqpMessageIsCreated($exchange = null, $num = null) {
|
||||
if ($num === null) {
|
||||
$this->assertNotEmpty($this->grabSentAmqpMessages($exchange), 'message were created');
|
||||
return;
|
||||
}
|
||||
|
||||
$this->assertCount(
|
||||
$num,
|
||||
$this->grabSentAmqpMessages($exchange),
|
||||
'number of created messages is equal to ' . $num
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that no messages was created
|
||||
*
|
||||
* @param string|null $exchange
|
||||
*/
|
||||
public function dontSeeAmqpMessageIsCreated($exchange = null) {
|
||||
$this->seeAmqpMessageIsCreated($exchange, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns last sent message
|
||||
*
|
||||
* @param string|null $exchange
|
||||
* @return \PhpAmqpLib\Message\AMQPMessage
|
||||
*/
|
||||
public function grabLastSentAmqpMessage($exchange = null) {
|
||||
$this->seeAmqpMessageIsCreated();
|
||||
$messages = $this->grabSentAmqpMessages($exchange);
|
||||
|
||||
return end($messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of all sent amqp messages.
|
||||
* Each message is `\PhpAmqpLib\Message\AMQPMessage` instance.
|
||||
* Useful to perform additional checks using `Asserts` module.
|
||||
*
|
||||
* @param string|null $exchange
|
||||
* @return \PhpAmqpLib\Message\AMQPMessage[]
|
||||
* @throws ModuleException
|
||||
*/
|
||||
public function grabSentAmqpMessages($exchange = null) {
|
||||
$amqp = $this->grabComponent('amqp');
|
||||
if (!$amqp instanceof TestComponent) {
|
||||
throw new ModuleException($this, 'AMQP module is not mocked, can\'t test messages');
|
||||
}
|
||||
|
||||
return $amqp->getSentMessages($exchange);
|
||||
}
|
||||
|
||||
private function grabComponent(string $component) {
|
||||
return $this->getYii2()->grabComponent($component);
|
||||
}
|
||||
|
||||
private function getYii2(): Yii2 {
|
||||
$yii2 = $this->getModule('Yii2');
|
||||
if (!$yii2 instanceof Yii2) {
|
||||
throw new ModuleException($this, 'Yii2 module must be configured');
|
||||
}
|
||||
|
||||
return $yii2;
|
||||
}
|
||||
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
<?php
|
||||
namespace tests\codeception\common\_support\amqp;
|
||||
|
||||
use common\components\RabbitMQ\Component;
|
||||
use PhpAmqpLib\Connection\AbstractConnection;
|
||||
|
||||
class TestComponent extends Component {
|
||||
|
||||
private $sentMessages = [];
|
||||
|
||||
public function init() {
|
||||
\yii\base\Component::init();
|
||||
}
|
||||
|
||||
public function getConnection() {
|
||||
/** @noinspection MagicMethodsValidityInspection */
|
||||
/** @noinspection PhpMissingParentConstructorInspection */
|
||||
return new class extends AbstractConnection {
|
||||
public function __construct(
|
||||
$user,
|
||||
$password,
|
||||
$vhost,
|
||||
$insist,
|
||||
$login_method,
|
||||
$login_response,
|
||||
$locale,
|
||||
\PhpAmqpLib\Wire\IO\AbstractIO $io,
|
||||
$heartbeat
|
||||
) {
|
||||
// ничего не делаем
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public function sendToExchange($exchangeName, $routingKey, $message, $exchangeArgs = [], $publishArgs = []) {
|
||||
$this->sentMessages[$exchangeName][] = $this->prepareMessage($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $exchangeName
|
||||
* @return \PhpAmqpLib\Message\AMQPMessage[]
|
||||
*/
|
||||
public function getSentMessages(string $exchangeName = null): array {
|
||||
if ($exchangeName !== null) {
|
||||
return $this->sentMessages[$exchangeName] ?? [];
|
||||
}
|
||||
|
||||
$messages = [];
|
||||
foreach ($this->sentMessages as $exchangeGroup) {
|
||||
foreach ($exchangeGroup as $message) {
|
||||
$messages[] = $message;
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
}
|
@ -14,7 +14,12 @@ class CodeceptionQueueHelper extends Module {
|
||||
*/
|
||||
public function grabLastQueuedJob() {
|
||||
$messages = $this->grabQueueJobs();
|
||||
return end($messages);
|
||||
$last = end($messages);
|
||||
if ($last === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $last;
|
||||
}
|
||||
|
||||
/**
|
||||
|
19
tests/codeception/common/fixtures/WebHooksEventsFixture.php
Normal file
19
tests/codeception/common/fixtures/WebHooksEventsFixture.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests\codeception\common\fixtures;
|
||||
|
||||
use common\models\WebHookEvent;
|
||||
use yii\test\ActiveFixture;
|
||||
|
||||
class WebHooksEventsFixture extends ActiveFixture {
|
||||
|
||||
public $modelClass = WebHookEvent::class;
|
||||
|
||||
public $dataFile = '@tests/codeception/common/fixtures/data/webhooks-events.php';
|
||||
|
||||
public $depends = [
|
||||
WebHooksFixture::class,
|
||||
];
|
||||
|
||||
}
|
15
tests/codeception/common/fixtures/WebHooksFixture.php
Normal file
15
tests/codeception/common/fixtures/WebHooksFixture.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests\codeception\common\fixtures;
|
||||
|
||||
use common\models\WebHook;
|
||||
use yii\test\ActiveFixture;
|
||||
|
||||
class WebHooksFixture extends ActiveFixture {
|
||||
|
||||
public $modelClass = WebHook::class;
|
||||
|
||||
public $dataFile = '@tests/codeception/common/fixtures/data/webhooks.php';
|
||||
|
||||
}
|
11
tests/codeception/common/fixtures/data/webhooks-events.php
Normal file
11
tests/codeception/common/fixtures/data/webhooks-events.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
return [
|
||||
[
|
||||
'webhook_id' => 1,
|
||||
'event_type' => 'account.edit',
|
||||
],
|
||||
[
|
||||
'webhook_id' => 2,
|
||||
'event_type' => 'account.edit',
|
||||
],
|
||||
];
|
21
tests/codeception/common/fixtures/data/webhooks.php
Normal file
21
tests/codeception/common/fixtures/data/webhooks.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
return [
|
||||
'webhook-with-secret' => [
|
||||
'id' => 1,
|
||||
'url' => 'http://localhost:80/webhooks/ely',
|
||||
'secret' => 'my-secret',
|
||||
'created_at' => 1531054333,
|
||||
],
|
||||
'webhook-without-secret' => [
|
||||
'id' => 2,
|
||||
'url' => 'http://localhost:81/webhooks/ely',
|
||||
'secret' => null,
|
||||
'created_at' => 1531054837,
|
||||
],
|
||||
'webhook-without-events' => [
|
||||
'id' => 3,
|
||||
'url' => 'http://localhost:82/webhooks/ely',
|
||||
'secret' => null,
|
||||
'created_at' => 1531054990,
|
||||
],
|
||||
];
|
@ -3,6 +3,7 @@ modules:
|
||||
enabled:
|
||||
- Yii2:
|
||||
part: [orm, email, fixtures]
|
||||
- tests\codeception\common\_support\queue\CodeceptionQueueHelper
|
||||
- tests\codeception\common\_support\Mockery
|
||||
config:
|
||||
Yii2:
|
||||
|
@ -1,14 +1,20 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests\codeception\common\unit\models;
|
||||
|
||||
use Codeception\Specify;
|
||||
use common\components\UserPass;
|
||||
use common\models\Account;
|
||||
use common\tasks\CreateWebHooksDeliveries;
|
||||
use tests\codeception\common\fixtures\MojangUsernameFixture;
|
||||
use tests\codeception\common\unit\TestCase;
|
||||
use Yii;
|
||||
use const common\LATEST_RULES_VERSION;
|
||||
|
||||
/**
|
||||
* @covers \common\models\Account
|
||||
*/
|
||||
class AccountTest extends TestCase {
|
||||
use Specify;
|
||||
|
||||
@ -119,4 +125,37 @@ class AccountTest extends TestCase {
|
||||
$this->assertNull($account->getRegistrationIp());
|
||||
}
|
||||
|
||||
public function testAfterSaveInsertEvent() {
|
||||
$account = new Account();
|
||||
$account->afterSave(true, [
|
||||
'username' => 'old-username',
|
||||
]);
|
||||
$this->assertNull($this->tester->grabLastQueuedJob());
|
||||
}
|
||||
|
||||
public function testAfterSaveNotMeaningfulAttributes() {
|
||||
$account = new Account();
|
||||
$account->afterSave(false, [
|
||||
'updatedAt' => time(),
|
||||
]);
|
||||
$this->assertNull($this->tester->grabLastQueuedJob());
|
||||
}
|
||||
|
||||
public function testAfterSavePushEvent() {
|
||||
$changedAttributes = [
|
||||
'username' => 'old-username',
|
||||
'email' => 'old-email@ely.by',
|
||||
'uuid' => 'c3cc0121-fa87-4818-9c0e-4acb7f9a28c5',
|
||||
'status' => 10,
|
||||
'lang' => 'en',
|
||||
];
|
||||
|
||||
$account = new Account();
|
||||
$account->afterSave(false, $changedAttributes);
|
||||
/** @var CreateWebHooksDeliveries $job */
|
||||
$job = $this->tester->grabLastQueuedJob();
|
||||
$this->assertInstanceOf(CreateWebHooksDeliveries::class, $job);
|
||||
$this->assertSame($job->payloads['changedAttributes'], $changedAttributes);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests\codeception\common\unit\tasks;
|
||||
|
||||
use common\models\Account;
|
||||
use common\tasks\ClearAccountSessions;
|
||||
use tests\codeception\common\fixtures;
|
||||
use tests\codeception\common\unit\TestCase;
|
||||
use yii\queue\Queue;
|
||||
|
||||
/**
|
||||
* @covers \common\tasks\ClearAccountSessions
|
||||
*/
|
||||
class ClearAccountSessionsTest extends TestCase {
|
||||
|
||||
public function _fixtures() {
|
||||
return [
|
||||
'accounts' => fixtures\AccountFixture::class,
|
||||
'oauthSessions' => fixtures\OauthSessionFixture::class,
|
||||
'minecraftAccessKeys' => fixtures\MinecraftAccessKeyFixture::class,
|
||||
'authSessions' => fixtures\AccountSessionFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function testCreateFromAccount() {
|
||||
$account = new Account();
|
||||
$account->id = 123;
|
||||
$task = ClearAccountSessions::createFromAccount($account);
|
||||
$this->assertSame(123, $task->accountId);
|
||||
}
|
||||
|
||||
public function testExecute() {
|
||||
/** @var \common\models\Account $bannedAccount */
|
||||
$bannedAccount = $this->tester->grabFixture('accounts', 'banned-account');
|
||||
$task = new ClearAccountSessions();
|
||||
$task->accountId = $bannedAccount->id;
|
||||
$task->execute(mock(Queue::class));
|
||||
$this->assertEmpty($bannedAccount->sessions);
|
||||
$this->assertEmpty($bannedAccount->minecraftAccessKeys);
|
||||
$this->assertEmpty($bannedAccount->oauthSessions);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests\codeception\common\unit\tasks;
|
||||
|
||||
use common\models\Account;
|
||||
use common\tasks\CreateWebHooksDeliveries;
|
||||
use common\tasks\DeliveryWebHook;
|
||||
use tests\codeception\common\fixtures;
|
||||
use tests\codeception\common\unit\TestCase;
|
||||
use yii\queue\Queue;
|
||||
|
||||
/**
|
||||
* @covers \common\tasks\CreateWebHooksDeliveries
|
||||
*/
|
||||
class CreateWebHooksDeliveriesTest extends TestCase {
|
||||
|
||||
public function _fixtures(): array {
|
||||
return [
|
||||
'webhooks' => fixtures\WebHooksFixture::class,
|
||||
'webhooksEvents' => fixtures\WebHooksEventsFixture::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function testCreateAccountEdit() {
|
||||
$account = new Account();
|
||||
$account->id = 123;
|
||||
$account->username = 'mock-username';
|
||||
$account->uuid = 'afc8dc7a-4bbf-4d3a-8699-68890088cf84';
|
||||
$account->email = 'mock@ely.by';
|
||||
$account->lang = 'en';
|
||||
$account->status = Account::STATUS_ACTIVE;
|
||||
$account->created_at = 1531008814;
|
||||
$changedAttributes = [
|
||||
'username' => 'old-username',
|
||||
'uuid' => 'e05d33e9-ff91-4d26-9f5c-8250f802a87a',
|
||||
'email' => 'old-email@ely.by',
|
||||
'status' => 0,
|
||||
];
|
||||
$result = CreateWebHooksDeliveries::createAccountEdit($account, $changedAttributes);
|
||||
$this->assertInstanceOf(CreateWebHooksDeliveries::class, $result);
|
||||
$this->assertSame('account.edit', $result->type);
|
||||
$this->assertArraySubset([
|
||||
'id' => 123,
|
||||
'uuid' => 'afc8dc7a-4bbf-4d3a-8699-68890088cf84',
|
||||
'username' => 'mock-username',
|
||||
'email' => 'mock@ely.by',
|
||||
'lang' => 'en',
|
||||
'isActive' => true,
|
||||
'registered' => '2018-07-08T00:13:34+00:00',
|
||||
'changedAttributes' => $changedAttributes,
|
||||
], $result->payloads);
|
||||
}
|
||||
|
||||
public function testExecute() {
|
||||
$task = new CreateWebHooksDeliveries();
|
||||
$task->type = 'account.edit';
|
||||
$task->payloads = [
|
||||
'id' => 123,
|
||||
'uuid' => 'afc8dc7a-4bbf-4d3a-8699-68890088cf84',
|
||||
'username' => 'mock-username',
|
||||
'email' => 'mock@ely.by',
|
||||
'lang' => 'en',
|
||||
'isActive' => true,
|
||||
'registered' => '2018-07-08T00:13:34+00:00',
|
||||
'changedAttributes' => [
|
||||
'username' => 'old-username',
|
||||
'uuid' => 'e05d33e9-ff91-4d26-9f5c-8250f802a87a',
|
||||
'email' => 'old-email@ely.by',
|
||||
'status' => 0,
|
||||
],
|
||||
];
|
||||
$task->execute(mock(Queue::class));
|
||||
/** @var DeliveryWebHook[] $tasks */
|
||||
$tasks = $this->tester->grabQueueJobs();
|
||||
$this->assertCount(2, $tasks);
|
||||
|
||||
$this->assertInstanceOf(DeliveryWebHook::class, $tasks[0]);
|
||||
$this->assertSame($task->type, $tasks[0]->type);
|
||||
$this->assertSame($task->payloads, $tasks[0]->payloads);
|
||||
$this->assertSame('http://localhost:80/webhooks/ely', $tasks[0]->url);
|
||||
$this->assertSame('my-secret', $tasks[0]->secret);
|
||||
|
||||
$this->assertInstanceOf(DeliveryWebHook::class, $tasks[1]);
|
||||
$this->assertSame($task->type, $tasks[1]->type);
|
||||
$this->assertSame($task->payloads, $tasks[1]->payloads);
|
||||
$this->assertSame('http://localhost:81/webhooks/ely', $tasks[1]->url);
|
||||
$this->assertNull($tasks[1]->secret);
|
||||
}
|
||||
|
||||
}
|
132
tests/codeception/common/unit/tasks/DeliveryWebHookTest.php
Normal file
132
tests/codeception/common/unit/tasks/DeliveryWebHookTest.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests\codeception\common\unit\tasks;
|
||||
|
||||
use common\tasks\DeliveryWebHook;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Middleware;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use tests\codeception\common\unit\TestCase;
|
||||
use yii\queue\Queue;
|
||||
|
||||
/**
|
||||
* @covers \common\tasks\DeliveryWebHook
|
||||
*/
|
||||
class DeliveryWebHookTest extends TestCase {
|
||||
|
||||
private $historyContainer = [];
|
||||
|
||||
/**
|
||||
* @var Response|\GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
private $response;
|
||||
|
||||
public function testCanRetry() {
|
||||
$task = new DeliveryWebHook();
|
||||
$this->assertFalse($task->canRetry(1, new \Exception()));
|
||||
$request = new Request('POST', 'http://localhost');
|
||||
$this->assertTrue($task->canRetry(4, new ConnectException('', $request)));
|
||||
$this->assertTrue($task->canRetry(4, new ServerException('', $request)));
|
||||
$this->assertFalse($task->canRetry(5, new ConnectException('', $request)));
|
||||
$this->assertFalse($task->canRetry(5, new ServerException('', $request)));
|
||||
}
|
||||
|
||||
public function testExecuteSuccessDelivery() {
|
||||
$this->response = new Response();
|
||||
$task = $this->createMockedTask();
|
||||
$task->type = 'account.edit';
|
||||
$task->url = 'http://localhost:81/webhooks/ely';
|
||||
$task->payloads = [
|
||||
'key' => 'value',
|
||||
'another' => 'value',
|
||||
];
|
||||
$task->execute(mock(Queue::class));
|
||||
/** @var Request $request */
|
||||
$request = $this->historyContainer[0]['request'];
|
||||
$this->assertSame('http://localhost:81/webhooks/ely', (string)$request->getUri());
|
||||
$this->assertStringStartsWith('Account-Ely-Hookshot/', $request->getHeaders()['User-Agent'][0]);
|
||||
$this->assertSame('account.edit', $request->getHeaders()['X-Ely-Accounts-Event'][0]);
|
||||
$this->assertSame('application/x-www-form-urlencoded', $request->getHeaders()['Content-Type'][0]);
|
||||
$this->assertArrayNotHasKey('X-Hub-Signature', $request->getHeaders());
|
||||
$this->assertEquals('key=value&another=value', (string)$request->getBody());
|
||||
}
|
||||
|
||||
public function testExecuteSuccessDeliveryWithSignature() {
|
||||
$this->response = new Response();
|
||||
$task = $this->createMockedTask();
|
||||
$task->type = 'account.edit';
|
||||
$task->url = 'http://localhost:81/webhooks/ely';
|
||||
$task->secret = 'secret';
|
||||
$task->payloads = [
|
||||
'key' => 'value',
|
||||
'another' => 'value',
|
||||
];
|
||||
$task->execute(mock(Queue::class));
|
||||
/** @var Request $request */
|
||||
$request = $this->historyContainer[0]['request'];
|
||||
$this->assertSame('http://localhost:81/webhooks/ely', (string)$request->getUri());
|
||||
$this->assertStringStartsWith('Account-Ely-Hookshot/', $request->getHeaders()['User-Agent'][0]);
|
||||
$this->assertSame('account.edit', $request->getHeaders()['X-Ely-Accounts-Event'][0]);
|
||||
$this->assertSame('application/x-www-form-urlencoded', $request->getHeaders()['Content-Type'][0]);
|
||||
$this->assertSame('sha1=3c0b1eef564b2d3a5e9c0f2a8302b1b42b3d4784', $request->getHeaders()['X-Hub-Signature'][0]);
|
||||
$this->assertEquals('key=value&another=value', (string)$request->getBody());
|
||||
}
|
||||
|
||||
public function testExecuteHandleClientException() {
|
||||
$this->response = new Response(403);
|
||||
$task = $this->createMockedTask();
|
||||
$task->type = 'account.edit';
|
||||
$task->url = 'http://localhost:81/webhooks/ely';
|
||||
$task->secret = 'secret';
|
||||
$task->payloads = [
|
||||
'key' => 'value',
|
||||
'another' => 'value',
|
||||
];
|
||||
$task->execute(mock(Queue::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \GuzzleHttp\Exception\ServerException
|
||||
*/
|
||||
public function testExecuteUnhandledException() {
|
||||
$this->response = new Response(502);
|
||||
$task = $this->createMockedTask();
|
||||
$task->type = 'account.edit';
|
||||
$task->url = 'http://localhost:81/webhooks/ely';
|
||||
$task->secret = 'secret';
|
||||
$task->payloads = [
|
||||
'key' => 'value',
|
||||
'another' => 'value',
|
||||
];
|
||||
$task->execute(mock(Queue::class));
|
||||
}
|
||||
|
||||
private function createMockedTask(): DeliveryWebHook {
|
||||
$container = &$this->historyContainer;
|
||||
$response = $this->response;
|
||||
return new class ($container, $response) extends DeliveryWebHook {
|
||||
private $historyContainer;
|
||||
|
||||
private $response;
|
||||
|
||||
public function __construct(array &$historyContainer, $response) {
|
||||
$this->historyContainer = &$historyContainer;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
protected function createStack(): HandlerStack {
|
||||
$stack = parent::createStack();
|
||||
$stack->setHandler(new MockHandler([$this->response]));
|
||||
$stack->push(Middleware::history($this->historyContainer));
|
||||
|
||||
return $stack;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -1,30 +1,32 @@
|
||||
<?php
|
||||
namespace codeception\console\unit\controllers;
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace tests\codeception\common\unit\tasks;
|
||||
|
||||
use common\components\Mojang\Api;
|
||||
use common\components\Mojang\exceptions\NoContentException;
|
||||
use common\components\Mojang\response\UsernameToUUIDResponse;
|
||||
use common\models\amqp\AccountBanned;
|
||||
use common\models\amqp\UsernameChanged;
|
||||
use common\models\Account;
|
||||
use common\models\MojangUsername;
|
||||
use console\controllers\AccountQueueController;
|
||||
use tests\codeception\common\fixtures\AccountFixture;
|
||||
use common\tasks\PullMojangUsername;
|
||||
use tests\codeception\common\fixtures\MojangUsernameFixture;
|
||||
use tests\codeception\console\unit\TestCase;
|
||||
use Yii;
|
||||
use tests\codeception\common\unit\TestCase;
|
||||
use yii\queue\Queue;
|
||||
|
||||
class AccountQueueControllerTest extends TestCase {
|
||||
|
||||
/**
|
||||
* @var AccountQueueController
|
||||
*/
|
||||
private $controller;
|
||||
/**
|
||||
* @covers \common\tasks\PullMojangUsername
|
||||
*/
|
||||
class PullMojangUsernameTest extends TestCase {
|
||||
|
||||
private $expectedResponse;
|
||||
|
||||
/**
|
||||
* @var PullMojangUsername
|
||||
*/
|
||||
private $task;
|
||||
|
||||
public function _fixtures() {
|
||||
return [
|
||||
'accounts' => AccountFixture::class,
|
||||
'mojangUsernames' => MojangUsernameFixture::class,
|
||||
];
|
||||
}
|
||||
@ -32,10 +34,9 @@ class AccountQueueControllerTest extends TestCase {
|
||||
public function _before() {
|
||||
parent::_before();
|
||||
|
||||
/** @var AccountQueueController|\PHPUnit_Framework_MockObject_MockObject $controller */
|
||||
$controller = $this->getMockBuilder(AccountQueueController::class)
|
||||
/** @var PullMojangUsername|\PHPUnit_Framework_MockObject_MockObject $task */
|
||||
$task = $this->getMockBuilder(PullMojangUsername::class)
|
||||
->setMethods(['createMojangApi'])
|
||||
->setConstructorArgs(['account-queue', Yii::$app])
|
||||
->getMock();
|
||||
|
||||
/** @var Api|\PHPUnit_Framework_MockObject_MockObject $apiMock */
|
||||
@ -54,30 +55,31 @@ class AccountQueueControllerTest extends TestCase {
|
||||
return $this->expectedResponse;
|
||||
});
|
||||
|
||||
$controller
|
||||
$task
|
||||
->expects($this->any())
|
||||
->method('createMojangApi')
|
||||
->willReturn($apiMock);
|
||||
|
||||
$this->controller = $controller;
|
||||
$this->task = $task;
|
||||
}
|
||||
|
||||
public function testRouteUsernameChangedUsernameExists() {
|
||||
public function testCreateFromAccount() {
|
||||
$account = new Account();
|
||||
$account->username = 'find-me';
|
||||
$result = PullMojangUsername::createFromAccount($account);
|
||||
$this->assertSame('find-me', $result->username);
|
||||
}
|
||||
|
||||
public function testExecuteUsernameExists() {
|
||||
$expectedResponse = new UsernameToUUIDResponse();
|
||||
$expectedResponse->id = '069a79f444e94726a5befca90e38aaf5';
|
||||
$expectedResponse->name = 'Notch';
|
||||
$this->expectedResponse = $expectedResponse;
|
||||
|
||||
/** @var \common\models\Account $accountInfo */
|
||||
$accountInfo = $this->tester->grabFixture('accounts', 'admin');
|
||||
/** @var MojangUsername $mojangUsernameFixture */
|
||||
/** @var \common\models\MojangUsername $mojangUsernameFixture */
|
||||
$mojangUsernameFixture = $this->tester->grabFixture('mojangUsernames', 'Notch');
|
||||
$body = new UsernameChanged([
|
||||
'accountId' => $accountInfo->id,
|
||||
'oldUsername' => $accountInfo->username,
|
||||
'newUsername' => 'Notch',
|
||||
]);
|
||||
$this->controller->routeUsernameChanged($body);
|
||||
$this->task->username = 'Notch';
|
||||
$this->task->execute(mock(Queue::class));
|
||||
/** @var MojangUsername|null $mojangUsername */
|
||||
$mojangUsername = MojangUsername::findOne('Notch');
|
||||
$this->assertInstanceOf(MojangUsername::class, $mojangUsername);
|
||||
@ -85,81 +87,62 @@ class AccountQueueControllerTest extends TestCase {
|
||||
$this->assertLessThanOrEqual(time(), $mojangUsername->last_pulled_at);
|
||||
}
|
||||
|
||||
public function testRouteUsernameChangedUsernameNotExists() {
|
||||
public function testExecuteChangedUsernameExists() {
|
||||
$expectedResponse = new UsernameToUUIDResponse();
|
||||
$expectedResponse->id = '069a79f444e94726a5befca90e38aaf5';
|
||||
$expectedResponse->name = 'Notch';
|
||||
$this->expectedResponse = $expectedResponse;
|
||||
|
||||
/** @var MojangUsername $mojangUsernameFixture */
|
||||
$mojangUsernameFixture = $this->tester->grabFixture('mojangUsernames', 'Notch');
|
||||
$this->task->username = 'Notch';
|
||||
$this->task->execute(mock(Queue::class));
|
||||
/** @var MojangUsername|null $mojangUsername */
|
||||
$mojangUsername = MojangUsername::findOne('Notch');
|
||||
$this->assertInstanceOf(MojangUsername::class, $mojangUsername);
|
||||
$this->assertGreaterThan($mojangUsernameFixture->last_pulled_at, $mojangUsername->last_pulled_at);
|
||||
$this->assertLessThanOrEqual(time(), $mojangUsername->last_pulled_at);
|
||||
}
|
||||
|
||||
public function testExecuteChangedUsernameNotExists() {
|
||||
$expectedResponse = new UsernameToUUIDResponse();
|
||||
$expectedResponse->id = '607153852b8c4909811f507ed8ee737f';
|
||||
$expectedResponse->name = 'Chest';
|
||||
$this->expectedResponse = $expectedResponse;
|
||||
|
||||
/** @var \common\models\Account $accountInfo */
|
||||
$accountInfo = $this->tester->grabFixture('accounts', 'admin');
|
||||
$body = new UsernameChanged([
|
||||
'accountId' => $accountInfo['id'],
|
||||
'oldUsername' => $accountInfo['username'],
|
||||
'newUsername' => 'Chest',
|
||||
]);
|
||||
$this->controller->routeUsernameChanged($body);
|
||||
$this->task->username = 'Chest';
|
||||
$this->task->execute(mock(Queue::class));
|
||||
/** @var MojangUsername|null $mojangUsername */
|
||||
$mojangUsername = MojangUsername::findOne('Chest');
|
||||
$this->assertInstanceOf(MojangUsername::class, $mojangUsername);
|
||||
}
|
||||
|
||||
public function testRouteUsernameChangedRemoveIfExistsNoMore() {
|
||||
public function testExecuteRemoveIfExistsNoMore() {
|
||||
$this->expectedResponse = false;
|
||||
|
||||
/** @var \common\models\Account $accountInfo */
|
||||
$accountInfo = $this->tester->grabFixture('accounts', 'admin');
|
||||
$username = $this->tester->grabFixture('mojangUsernames', 'not-exists')['username'];
|
||||
$body = new UsernameChanged([
|
||||
'accountId' => $accountInfo['id'],
|
||||
'oldUsername' => $accountInfo['username'],
|
||||
'newUsername' => $username,
|
||||
]);
|
||||
$this->controller->routeUsernameChanged($body);
|
||||
$this->task->username = $username;
|
||||
$this->task->execute(mock(Queue::class));
|
||||
/** @var MojangUsername|null $mojangUsername */
|
||||
$mojangUsername = MojangUsername::findOne($username);
|
||||
$this->assertNull($mojangUsername);
|
||||
}
|
||||
|
||||
public function testRouteUsernameChangedUuidUpdated() {
|
||||
public function testExecuteUuidUpdated() {
|
||||
$expectedResponse = new UsernameToUUIDResponse();
|
||||
$expectedResponse->id = 'f498513ce8c84773be26ecfc7ed5185d';
|
||||
$expectedResponse->name = 'jeb';
|
||||
$this->expectedResponse = $expectedResponse;
|
||||
|
||||
/** @var \common\models\Account $accountInfo */
|
||||
$accountInfo = $this->tester->grabFixture('accounts', 'admin');
|
||||
/** @var MojangUsername $mojangInfo */
|
||||
$mojangInfo = $this->tester->grabFixture('mojangUsernames', 'uuid-changed');
|
||||
$username = $mojangInfo['username'];
|
||||
$body = new UsernameChanged([
|
||||
'accountId' => $accountInfo['id'],
|
||||
'oldUsername' => $accountInfo['username'],
|
||||
'newUsername' => $username,
|
||||
]);
|
||||
$this->controller->routeUsernameChanged($body);
|
||||
$this->task->username = $username;
|
||||
$this->task->execute(mock(Queue::class));
|
||||
/** @var MojangUsername|null $mojangUsername */
|
||||
$mojangUsername = MojangUsername::findOne($username);
|
||||
$this->assertInstanceOf(MojangUsername::class, $mojangUsername);
|
||||
$this->assertNotEquals($mojangInfo->uuid, $mojangUsername->uuid);
|
||||
}
|
||||
|
||||
public function testRouteAccountBanned() {
|
||||
/** @var \common\models\Account $bannedAccount */
|
||||
$bannedAccount = $this->tester->grabFixture('accounts', 'banned-account');
|
||||
$this->tester->haveFixtures([
|
||||
'oauthSessions' => \tests\codeception\common\fixtures\OauthSessionFixture::class,
|
||||
'minecraftAccessKeys' => \tests\codeception\common\fixtures\MinecraftAccessKeyFixture::class,
|
||||
'authSessions' => \tests\codeception\common\fixtures\AccountSessionFixture::class,
|
||||
]);
|
||||
|
||||
$body = new AccountBanned();
|
||||
$body->accountId = $bannedAccount->id;
|
||||
|
||||
$this->controller->routeAccountBanned($body);
|
||||
$this->assertEmpty($bannedAccount->sessions);
|
||||
$this->assertEmpty($bannedAccount->minecraftAccessKeys);
|
||||
$this->assertEmpty($bannedAccount->oauthSessions);
|
||||
}
|
||||
|
||||
}
|
@ -20,9 +20,6 @@ return [
|
||||
// Для тестов нам не сильно важна безопасность, а вот время прохождения тестов значительно сокращается
|
||||
'passwordHashCost' => 4,
|
||||
],
|
||||
'amqp' => [
|
||||
'class' => tests\codeception\common\_support\amqp\TestComponent::class,
|
||||
],
|
||||
'queue' => [
|
||||
'class' => tests\codeception\common\_support\queue\Queue::class,
|
||||
],
|
||||
|
Loading…
Reference in New Issue
Block a user