From 6751eb659192e20dca10855fd5bd9226663bea34 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sat, 7 Jul 2018 15:01:18 +0300 Subject: [PATCH 01/13] Implemented webhooks database structure and console command register webhooks --- common/models/WebHook.php | 42 +++++++++++ common/models/WebHookEvent.php | 27 +++++++ console/controllers/WebhooksController.php | 57 +++++++++++++++ console/db/Migration.php | 24 ++----- .../migrations/m180706_230451_webhooks.php | 28 ++++++++ console/models/.gitkeep | 1 - console/models/WebHookForm.php | 70 +++++++++++++++++++ 7 files changed, 228 insertions(+), 21 deletions(-) create mode 100644 common/models/WebHook.php create mode 100644 common/models/WebHookEvent.php create mode 100644 console/controllers/WebhooksController.php create mode 100644 console/migrations/m180706_230451_webhooks.php delete mode 100644 console/models/.gitkeep create mode 100644 console/models/WebHookForm.php diff --git a/common/models/WebHook.php b/common/models/WebHook.php new file mode 100644 index 0000000..586fa9e --- /dev/null +++ b/common/models/WebHook.php @@ -0,0 +1,42 @@ + TimestampBehavior::class, + 'updatedAtAttribute' => false, + ], + ]; + } + + public function getEvents(): ActiveQueryInterface { + return $this->hasMany(WebHookEvent::class, ['webhook_id' => 'id']); + } + +} diff --git a/common/models/WebHookEvent.php b/common/models/WebHookEvent.php new file mode 100644 index 0000000..280d4ab --- /dev/null +++ b/common/models/WebHookEvent.php @@ -0,0 +1,27 @@ +hasOne(WebHook::class, ['id' => 'webhook_id']); + } + +} diff --git a/console/controllers/WebhooksController.php b/console/controllers/WebhooksController.php new file mode 100644 index 0000000..6f77acc --- /dev/null +++ b/console/controllers/WebhooksController.php @@ -0,0 +1,57 @@ + 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; + } + +} diff --git a/console/db/Migration.php b/console/db/Migration.php index e05b6aa..3ec8284 100644 --- a/console/db/Migration.php +++ b/console/db/Migration.php @@ -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) . ') '; } } diff --git a/console/migrations/m180706_230451_webhooks.php b/console/migrations/m180706_230451_webhooks.php new file mode 100644 index 0000000..df6a620 --- /dev/null +++ b/console/migrations/m180706_230451_webhooks.php @@ -0,0 +1,28 @@ +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}}'); + } + +} diff --git a/console/models/.gitkeep b/console/models/.gitkeep deleted file mode 100644 index 72e8ffc..0000000 --- a/console/models/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/console/models/WebHookForm.php b/console/models/WebHookForm.php new file mode 100644 index 0000000..4f9cfcd --- /dev/null +++ b/console/models/WebHookForm.php @@ -0,0 +1,70 @@ +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', + ]; + } + +} From c0aa78d1561fbd24b7cba7b270f5f643af86d2aa Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sun, 8 Jul 2018 18:20:19 +0300 Subject: [PATCH 02/13] Implemented WebHooks delivery queue. Completely removed usage of the RabbitMQ. Queue now based on Redis channels. Worker process now extracted as separate docker container. Base image upgraded to the 1.8.0 version (PHP 7.2.7 and pcntl extension). --- .env-dist | 12 - Dockerfile | 2 +- Dockerfile-dev | 2 +- .../authentication/ConfirmEmailForm.php | 6 +- .../accounts/models/BanAccountForm.php | 25 +- .../accounts/models/ChangeEmailForm.php | 23 +- .../accounts/models/ChangeUsernameForm.php | 32 +- .../accounts/models/PardonAccountForm.php | 20 +- api/modules/session/filters/RateLimiter.php | 12 +- api/modules/session/models/SessionModel.php | 6 +- autocompletion.php | 19 +- common/components/RabbitMQ/Component.php | 177 -------- common/components/RabbitMQ/Helper.php | 26 -- common/components/Redis/Cache.php | 13 - common/components/Redis/Connection.php | 415 ----------------- .../components/Redis/ConnectionInterface.php | 19 - common/components/Redis/Key.php | 20 +- common/components/Redis/Set.php | 13 +- common/config/config.php | 23 +- common/models/Account.php | 21 + common/tasks/ClearAccountSessions.php | 64 +++ common/tasks/CreateWebHooksDeliveries.php | 76 ++++ common/tasks/DeliveryWebHook.php | 110 +++++ common/tasks/PullMojangUsername.php | 72 +++ composer.json | 13 +- composer.lock | 426 +++--------------- .../controllers/AccountQueueController.php | 98 ---- console/controllers/AmqpController.php | 72 --- docker-compose.dev.yml | 23 +- docker-compose.prod.yml | 18 +- docker/phpmyadmin/Dockerfile | 2 +- .../supervisor/.gitkeep | 0 docker/supervisor/account-queue-worker.conf | 6 - docker/supervisor/worker-queue.conf | 6 - tests/codeception/api/functional.suite.yml | 1 - tests/codeception/api/unit.suite.yml | 1 - .../authentication/ConfirmEmailFormTest.php | 6 - .../accounts/models/ChangeEmailFormTest.php | 13 - .../models/ChangeUsernameFormTest.php | 23 +- .../modules/internal/models/BanFormTest.php | 27 +- .../internal/models/PardonFormTest.php | 11 - .../common/_support/amqp/Helper.php | 91 ---- .../common/_support/amqp/TestComponent.php | 58 --- .../_support/queue/CodeceptionQueueHelper.php | 7 +- .../common/fixtures/WebHooksEventsFixture.php | 19 + .../common/fixtures/WebHooksFixture.php | 15 + .../common/fixtures/data/webhooks-events.php | 11 + .../common/fixtures/data/webhooks.php | 21 + tests/codeception/common/unit.suite.yml | 1 + .../common/unit/models/AccountTest.php | 39 ++ .../unit/tasks/ClearAccountSessionsTest.php | 44 ++ .../tasks/CreateWebHooksDeliveriesTest.php | 91 ++++ .../common/unit/tasks/DeliveryWebHookTest.php | 132 ++++++ .../unit/tasks/PullMojangUsernameTest.php} | 131 +++--- tests/codeception/config/config.php | 3 - 55 files changed, 933 insertions(+), 1684 deletions(-) delete mode 100644 common/components/RabbitMQ/Component.php delete mode 100644 common/components/RabbitMQ/Helper.php delete mode 100644 common/components/Redis/Cache.php delete mode 100644 common/components/Redis/Connection.php delete mode 100644 common/components/Redis/ConnectionInterface.php create mode 100644 common/tasks/ClearAccountSessions.php create mode 100644 common/tasks/CreateWebHooksDeliveries.php create mode 100644 common/tasks/DeliveryWebHook.php create mode 100644 common/tasks/PullMojangUsername.php delete mode 100644 console/controllers/AccountQueueController.php delete mode 100644 console/controllers/AmqpController.php rename api/models/profile/TwoFactorAuthForm.php => docker/supervisor/.gitkeep (100%) delete mode 100644 docker/supervisor/account-queue-worker.conf delete mode 100644 docker/supervisor/worker-queue.conf delete mode 100644 tests/codeception/common/_support/amqp/Helper.php delete mode 100644 tests/codeception/common/_support/amqp/TestComponent.php create mode 100644 tests/codeception/common/fixtures/WebHooksEventsFixture.php create mode 100644 tests/codeception/common/fixtures/WebHooksFixture.php create mode 100644 tests/codeception/common/fixtures/data/webhooks-events.php create mode 100644 tests/codeception/common/fixtures/data/webhooks.php create mode 100644 tests/codeception/common/unit/tasks/ClearAccountSessionsTest.php create mode 100644 tests/codeception/common/unit/tasks/CreateWebHooksDeliveriesTest.php create mode 100644 tests/codeception/common/unit/tasks/DeliveryWebHookTest.php rename tests/codeception/{console/unit/controllers/AccountQueueControllerTest.php => common/unit/tasks/PullMojangUsernameTest.php} (50%) diff --git a/.env-dist b/.env-dist index a4fcd1c..8357fea 100644 --- a/.env-dist +++ b/.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 diff --git a/Dockerfile b/Dockerfile index a6f7285..54d8000 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/Dockerfile-dev b/Dockerfile-dev index 07ec96a..dec0312 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -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 diff --git a/api/models/authentication/ConfirmEmailForm.php b/api/models/authentication/ConfirmEmailForm.php index c813ac0..5d6b7cc 100644 --- a/api/models/authentication/ConfirmEmailForm.php +++ b/api/models/authentication/ConfirmEmailForm.php @@ -1,9 +1,10 @@ createEventTask($account->id, $account->username, null); - $transaction->commit(); return Yii::$app->user->createJwtAuthenticationToken($account, true); diff --git a/api/modules/accounts/models/BanAccountForm.php b/api/modules/accounts/models/BanAccountForm.php index 35f0d30..d35fec4 100644 --- a/api/modules/accounts/models/BanAccountForm.php +++ b/api/modules/accounts/models/BanAccountForm.php @@ -1,13 +1,11 @@ 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); - } - } diff --git a/api/modules/accounts/models/ChangeEmailForm.php b/api/modules/accounts/models/ChangeEmailForm.php index 1f03b67..3de16c6 100644 --- a/api/modules/accounts/models/ChangeEmailForm.php +++ b/api/modules/accounts/models/ChangeEmailForm.php @@ -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); - } - } diff --git a/api/modules/accounts/models/ChangeUsernameForm.php b/api/modules/accounts/models/ChangeUsernameForm.php index 86fa961..53ad10d 100644 --- a/api/modules/accounts/models/ChangeUsernameForm.php +++ b/api/modules/accounts/models/ChangeUsernameForm.php @@ -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); - } - } diff --git a/api/modules/accounts/models/PardonAccountForm.php b/api/modules/accounts/models/PardonAccountForm.php index 2c337d7..fe9c073 100644 --- a/api/modules/accounts/models/PardonAccountForm.php +++ b/api/modules/accounts/models/PardonAccountForm.php @@ -1,13 +1,10 @@ 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); - } - } diff --git a/api/modules/session/filters/RateLimiter.php b/api/modules/session/filters/RateLimiter.php index 72081bc..96be790 100644 --- a/api/modules/session/filters/RateLimiter.php +++ b/api/modules/session/filters/RateLimiter.php @@ -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 diff --git a/api/modules/session/models/SessionModel.php b/api/modules/session/models/SessionModel.php index ed5e626..7d37728 100644 --- a/api/modules/session/models/SessionModel.php +++ b/api/modules/session/models/SessionModel.php @@ -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 { diff --git a/autocompletion.php b/autocompletion.php index 07b6667..a70d827 100644 --- a/autocompletion.php +++ b/autocompletion.php @@ -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 { } diff --git a/common/components/RabbitMQ/Component.php b/common/components/RabbitMQ/Component.php deleted file mode 100644 index b7e6177..0000000 --- a/common/components/RabbitMQ/Component.php +++ /dev/null @@ -1,177 +0,0 @@ - - * - * @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); - } - -} diff --git a/common/components/RabbitMQ/Helper.php b/common/components/RabbitMQ/Helper.php deleted file mode 100644 index e3b5f7a..0000000 --- a/common/components/RabbitMQ/Helper.php +++ /dev/null @@ -1,26 +0,0 @@ -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 - ]); - } - -} diff --git a/common/components/Redis/Cache.php b/common/components/Redis/Cache.php deleted file mode 100644 index 6a120a1..0000000 --- a/common/components/Redis/Cache.php +++ /dev/null @@ -1,13 +0,0 @@ -redis = Instance::ensure($this->redis, ConnectionInterface::class); - } - -} diff --git a/common/components/Redis/Connection.php b/common/components/Redis/Connection.php deleted file mode 100644 index 28d62c4..0000000 --- a/common/components/Redis/Connection.php +++ /dev/null @@ -1,415 +0,0 @@ -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, - ]); - } - -} diff --git a/common/components/Redis/ConnectionInterface.php b/common/components/Redis/ConnectionInterface.php deleted file mode 100644 index f4195fe..0000000 --- a/common/components/Redis/ConnectionInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -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; } diff --git a/common/components/Redis/Set.php b/common/components/Redis/Set.php index b106304..b6a07ec 100644 --- a/common/components/Redis/Set.php +++ b/common/components/Redis/Set.php @@ -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)]); } /** diff --git a/common/config/config.php b/common/config/config.php index 36b1266..cd71980 100644 --- a/common/config/config.php +++ b/common/config/config.php @@ -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' => [ diff --git a/common/models/Account.php b/common/models/Account.php index 06d1961..dfc8dfc 100644 --- a/common/models/Account.php +++ b/common/models/Account.php @@ -1,7 +1,10 @@ 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)); + } + } diff --git a/common/tasks/ClearAccountSessions.php b/common/tasks/ClearAccountSessions.php new file mode 100644 index 0000000..86a5043 --- /dev/null +++ b/common/tasks/ClearAccountSessions.php @@ -0,0 +1,64 @@ +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(); + } + } + +} diff --git a/common/tasks/CreateWebHooksDeliveries.php b/common/tasks/CreateWebHooksDeliveries.php new file mode 100644 index 0000000..8ba6fef --- /dev/null +++ b/common/tasks/CreateWebHooksDeliveries.php @@ -0,0 +1,76 @@ +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); + } + } + +} diff --git a/common/tasks/DeliveryWebHook.php b/common/tasks/DeliveryWebHook.php new file mode 100644 index 0000000..7561d2a --- /dev/null +++ b/common/tasks/DeliveryWebHook.php @@ -0,0 +1,110 @@ += 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; + } + +} diff --git a/common/tasks/PullMojangUsername.php b/common/tasks/PullMojangUsername.php new file mode 100644 index 0000000..169ddfb --- /dev/null +++ b/common/tasks/PullMojangUsername.php @@ -0,0 +1,72 @@ +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(); + } + +} diff --git a/composer.json b/composer.json index 0b2d338..8afc205 100644 --- a/composer.json +++ b/composer.json @@ -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" diff --git a/composer.lock b/composer.lock index 19ec143..44bd2a0 100644 --- a/composer.lock +++ b/composer.lock @@ -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, diff --git a/console/controllers/AccountQueueController.php b/console/controllers/AccountQueueController.php deleted file mode 100644 index 04ee523..0000000 --- a/console/controllers/AccountQueueController.php +++ /dev/null @@ -1,98 +0,0 @@ -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(); - } - -} diff --git a/console/controllers/AmqpController.php b/console/controllers/AmqpController.php deleted file mode 100644 index 1c6360c..0000000 --- a/console/controllers/AmqpController.php +++ /dev/null @@ -1,72 +0,0 @@ -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; - } - -} diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index f93f1f7..7082ef4 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -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: diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 47b6e78..539c878 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -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: diff --git a/docker/phpmyadmin/Dockerfile b/docker/phpmyadmin/Dockerfile index 62e24d4..6b03cfe 100644 --- a/docker/phpmyadmin/Dockerfile +++ b/docker/phpmyadmin/Dockerfile @@ -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 diff --git a/api/models/profile/TwoFactorAuthForm.php b/docker/supervisor/.gitkeep similarity index 100% rename from api/models/profile/TwoFactorAuthForm.php rename to docker/supervisor/.gitkeep diff --git a/docker/supervisor/account-queue-worker.conf b/docker/supervisor/account-queue-worker.conf deleted file mode 100644 index aed1af3..0000000 --- a/docker/supervisor/account-queue-worker.conf +++ /dev/null @@ -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 diff --git a/docker/supervisor/worker-queue.conf b/docker/supervisor/worker-queue.conf deleted file mode 100644 index 397c104..0000000 --- a/docker/supervisor/worker-queue.conf +++ /dev/null @@ -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 diff --git a/tests/codeception/api/functional.suite.yml b/tests/codeception/api/functional.suite.yml index 9a6ac97..570a025 100644 --- a/tests/codeception/api/functional.suite.yml +++ b/tests/codeception/api/functional.suite.yml @@ -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 diff --git a/tests/codeception/api/unit.suite.yml b/tests/codeception/api/unit.suite.yml index 7de7236..beea248 100644 --- a/tests/codeception/api/unit.suite.yml +++ b/tests/codeception/api/unit.suite.yml @@ -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: diff --git a/tests/codeception/api/unit/models/authentication/ConfirmEmailFormTest.php b/tests/codeception/api/unit/models/authentication/ConfirmEmailFormTest.php index 404b994..c392830 100644 --- a/tests/codeception/api/unit/models/authentication/ConfirmEmailFormTest.php +++ b/tests/codeception/api/unit/models/authentication/ConfirmEmailFormTest.php @@ -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) { diff --git a/tests/codeception/api/unit/modules/accounts/models/ChangeEmailFormTest.php b/tests/codeception/api/unit/modules/accounts/models/ChangeEmailFormTest.php index eb7f8f5..abf041d 100644 --- a/tests/codeception/api/unit/modules/accounts/models/ChangeEmailFormTest.php +++ b/tests/codeception/api/unit/modules/accounts/models/ChangeEmailFormTest.php @@ -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() { diff --git a/tests/codeception/api/unit/modules/accounts/models/ChangeUsernameFormTest.php b/tests/codeception/api/unit/modules/accounts/models/ChangeUsernameFormTest.php index 1931272..67d6db5 100644 --- a/tests/codeception/api/unit/modules/accounts/models/ChangeUsernameFormTest.php +++ b/tests/codeception/api/unit/modules/accounts/models/ChangeUsernameFormTest.php @@ -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 { diff --git a/tests/codeception/api/unit/modules/internal/models/BanFormTest.php b/tests/codeception/api/unit/modules/internal/models/BanFormTest.php index 4cd3c02..3894f7f 100644 --- a/tests/codeception/api/unit/modules/internal/models/BanFormTest.php +++ b/tests/codeception/api/unit/modules/internal/models/BanFormTest.php @@ -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); } } diff --git a/tests/codeception/api/unit/modules/internal/models/PardonFormTest.php b/tests/codeception/api/unit/modules/internal/models/PardonFormTest.php index 1d6e2a7..362271f 100644 --- a/tests/codeception/api/unit/modules/internal/models/PardonFormTest.php +++ b/tests/codeception/api/unit/modules/internal/models/PardonFormTest.php @@ -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']); } } diff --git a/tests/codeception/common/_support/amqp/Helper.php b/tests/codeception/common/_support/amqp/Helper.php deleted file mode 100644 index e623646..0000000 --- a/tests/codeception/common/_support/amqp/Helper.php +++ /dev/null @@ -1,91 +0,0 @@ -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; - } - -} diff --git a/tests/codeception/common/_support/amqp/TestComponent.php b/tests/codeception/common/_support/amqp/TestComponent.php deleted file mode 100644 index ed90730..0000000 --- a/tests/codeception/common/_support/amqp/TestComponent.php +++ /dev/null @@ -1,58 +0,0 @@ -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; - } - -} diff --git a/tests/codeception/common/_support/queue/CodeceptionQueueHelper.php b/tests/codeception/common/_support/queue/CodeceptionQueueHelper.php index 8878ea3..326e7f3 100644 --- a/tests/codeception/common/_support/queue/CodeceptionQueueHelper.php +++ b/tests/codeception/common/_support/queue/CodeceptionQueueHelper.php @@ -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; } /** diff --git a/tests/codeception/common/fixtures/WebHooksEventsFixture.php b/tests/codeception/common/fixtures/WebHooksEventsFixture.php new file mode 100644 index 0000000..300a7ea --- /dev/null +++ b/tests/codeception/common/fixtures/WebHooksEventsFixture.php @@ -0,0 +1,19 @@ + 1, + 'event_type' => 'account.edit', + ], + [ + 'webhook_id' => 2, + 'event_type' => 'account.edit', + ], +]; diff --git a/tests/codeception/common/fixtures/data/webhooks.php b/tests/codeception/common/fixtures/data/webhooks.php new file mode 100644 index 0000000..238c90a --- /dev/null +++ b/tests/codeception/common/fixtures/data/webhooks.php @@ -0,0 +1,21 @@ + [ + '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, + ], +]; diff --git a/tests/codeception/common/unit.suite.yml b/tests/codeception/common/unit.suite.yml index 11d4bb4..ac95de3 100644 --- a/tests/codeception/common/unit.suite.yml +++ b/tests/codeception/common/unit.suite.yml @@ -3,6 +3,7 @@ modules: enabled: - Yii2: part: [orm, email, fixtures] + - tests\codeception\common\_support\queue\CodeceptionQueueHelper - tests\codeception\common\_support\Mockery config: Yii2: diff --git a/tests/codeception/common/unit/models/AccountTest.php b/tests/codeception/common/unit/models/AccountTest.php index afb5de9..ab9b477 100644 --- a/tests/codeception/common/unit/models/AccountTest.php +++ b/tests/codeception/common/unit/models/AccountTest.php @@ -1,14 +1,20 @@ 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); + } + } diff --git a/tests/codeception/common/unit/tasks/ClearAccountSessionsTest.php b/tests/codeception/common/unit/tasks/ClearAccountSessionsTest.php new file mode 100644 index 0000000..af0e554 --- /dev/null +++ b/tests/codeception/common/unit/tasks/ClearAccountSessionsTest.php @@ -0,0 +1,44 @@ + 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); + } + +} diff --git a/tests/codeception/common/unit/tasks/CreateWebHooksDeliveriesTest.php b/tests/codeception/common/unit/tasks/CreateWebHooksDeliveriesTest.php new file mode 100644 index 0000000..4596a51 --- /dev/null +++ b/tests/codeception/common/unit/tasks/CreateWebHooksDeliveriesTest.php @@ -0,0 +1,91 @@ + 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); + } + +} diff --git a/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php b/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php new file mode 100644 index 0000000..af74384 --- /dev/null +++ b/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php @@ -0,0 +1,132 @@ +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; + } + }; + } + +} diff --git a/tests/codeception/console/unit/controllers/AccountQueueControllerTest.php b/tests/codeception/common/unit/tasks/PullMojangUsernameTest.php similarity index 50% rename from tests/codeception/console/unit/controllers/AccountQueueControllerTest.php rename to tests/codeception/common/unit/tasks/PullMojangUsernameTest.php index 0fbe3fe..cac50b4 100644 --- a/tests/codeception/console/unit/controllers/AccountQueueControllerTest.php +++ b/tests/codeception/common/unit/tasks/PullMojangUsernameTest.php @@ -1,30 +1,32 @@ 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); - } - } diff --git a/tests/codeception/config/config.php b/tests/codeception/config/config.php index 78b097c..72c5fa3 100644 --- a/tests/codeception/config/config.php +++ b/tests/codeception/config/config.php @@ -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, ], From e3ade6a0cbf5f6663300a394616ba4eb726e2464 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sun, 8 Jul 2018 18:38:47 +0300 Subject: [PATCH 03/13] Update outdated dependencies --- composer.json | 8 +- composer.lock | 553 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 336 insertions(+), 225 deletions(-) diff --git a/composer.json b/composer.json index 8afc205..9bf9e12 100644 --- a/composer.json +++ b/composer.json @@ -6,8 +6,7 @@ "minimum-stability": "stable", "require": { "php": "^7.1", - "roave/security-advisories": "dev-master", - "yiisoft/yii2": "2.0.14", + "yiisoft/yii2": "2.0.15.1", "yiisoft/yii2-swiftmailer": "~2.1.0", "ramsey/uuid": "^3.5", "league/oauth2-server": "^4.1", @@ -28,7 +27,7 @@ "require-dev": { "yiisoft/yii2-debug": "*", "yiisoft/yii2-faker": "*", - "flow/jsonpath": "^0.3.1", + "flow/jsonpath": "^0.4.0", "phpunit/phpunit": "^6.0", "codeception/codeception": "2.3.8", "codeception/specify": "^1.0.0", @@ -37,7 +36,8 @@ "php-mock/php-mock-mockery": "^1.2.0", "friendsofphp/php-cs-fixer": "^2.11", "ely/php-code-style": "^0.1.0", - "predis/predis": "^1.1" + "predis/predis": "^1.1", + "roave/security-advisories": "dev-master" }, "repositories": [ { diff --git a/composer.lock b/composer.lock index 44bd2a0..60f1fee 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b576f6f9babd8e00a7fa6768d56c37e9", + "content-hash": "4334ca4dd8b377a9c40afd844527a850", "packages": [ { "name": "bacon/bacon-qr-code", @@ -508,16 +508,16 @@ }, { "name": "domnikl/statsd", - "version": "2.6.0", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/domnikl/statsd-php.git", - "reference": "ca9daa049fd9f353c0551384612bb4f17615b14a" + "reference": "529578b05e455280fbb2748bb0080026d86452bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/domnikl/statsd-php/zipball/ca9daa049fd9f353c0551384612bb4f17615b14a", - "reference": "ca9daa049fd9f353c0551384612bb4f17615b14a", + "url": "https://api.github.com/repos/domnikl/statsd-php/zipball/529578b05e455280fbb2748bb0080026d86452bb", + "reference": "529578b05e455280fbb2748bb0080026d86452bb", "shasum": "" }, "require": { @@ -551,7 +551,7 @@ "statsd", "udp" ], - "time": "2017-07-30T17:40:00+00:00" + "time": "2018-04-29T18:04:03+00:00" }, { "name": "egulias/email-validator", @@ -909,16 +909,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.3.0", + "version": "6.3.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", - "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", "shasum": "" }, "require": { @@ -928,7 +928,7 @@ }, "require-dev": { "ext-curl": "*", - "phpunit/phpunit": "^4.0 || ^5.0", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", "psr/log": "^1.0" }, "suggest": { @@ -937,7 +937,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.2-dev" + "dev-master": "6.3-dev" } }, "autoload": { @@ -970,7 +970,7 @@ "rest", "web service" ], - "time": "2017-06-22T18:50:49+00:00" + "time": "2018-04-22T15:46:56+00:00" }, { "name": "guzzlehttp/promises", @@ -1364,24 +1364,24 @@ }, { "name": "paragonie/constant_time_encoding", - "version": "v2.2.1", + "version": "v2.2.2", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "7c74c5d08761ead7b5e89d07c854bc28eb0b2186" + "reference": "eccf915f45f911bfb189d1d1638d940ec6ee6e33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/7c74c5d08761ead7b5e89d07c854bc28eb0b2186", - "reference": "7c74c5d08761ead7b5e89d07c854bc28eb0b2186", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/eccf915f45f911bfb189d1d1638d940ec6ee6e33", + "reference": "eccf915f45f911bfb189d1d1638d940ec6ee6e33", "shasum": "" }, "require": { "php": "^7" }, "require-dev": { - "phpunit/phpunit": "^6", - "vimeo/psalm": "^0.3|^1" + "phpunit/phpunit": "^6|^7", + "vimeo/psalm": "^1" }, "type": "library", "autoload": { @@ -1422,7 +1422,7 @@ "hex2bin", "rfc4648" ], - "time": "2018-01-23T00:54:57+00:00" + "time": "2018-03-10T19:47:49+00:00" }, { "name": "paragonie/random_compat", @@ -1602,153 +1602,6 @@ ], "time": "2018-01-20T00:28:24+00:00" }, - { - "name": "roave/security-advisories", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "94230db36bded9d164ffccabcb38c67eedd63595" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/94230db36bded9d164ffccabcb38c67eedd63595", - "reference": "94230db36bded9d164ffccabcb38c67eedd63595", - "shasum": "" - }, - "conflict": { - "adodb/adodb-php": "<5.20.6", - "amphp/artax": "<1.0.6|>=2,<2.0.6", - "aws/aws-sdk-php": ">=3,<3.2.1", - "bugsnag/bugsnag-laravel": ">=2,<2.0.2", - "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4", - "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", - "cartalyst/sentry": "<=2.1.6", - "codeigniter/framework": "<=3.0.6", - "composer/composer": "<=1.0.0-alpha11", - "contao-components/mediaelement": ">=2.14.2,<2.21.1", - "contao/core": ">=2,<3.5.32", - "contao/core-bundle": ">=4,<4.4.8", - "contao/listing-bundle": ">=4,<4.4.8", - "contao/newsletter-bundle": ">=4,<4.1", - "doctrine/annotations": ">=1,<1.2.7", - "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", - "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", - "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2", - "doctrine/doctrine-bundle": "<1.5.2", - "doctrine/doctrine-module": "<=0.7.1", - "doctrine/mongodb-odm": ">=1,<1.0.2", - "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", - "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", - "dompdf/dompdf": ">=0.6,<0.6.2", - "drupal/core": ">=8,<8.3.7", - "drupal/drupal": ">=8,<8.3.7", - "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.2|>=5.4,<5.4.10.1|>=2017.8,<2017.8.1.1", - "firebase/php-jwt": "<2", - "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", - "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", - "gree/jose": "<=2.2", - "gregwar/rst": "<1.0.3", - "guzzlehttp/guzzle": ">=6,<6.2.1|>=4.0.0-rc2,<4.2.4|>=5,<5.3.1", - "illuminate/auth": ">=4,<4.0.99|>=4.1,<4.1.26", - "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", - "joomla/session": "<1.3.1", - "laravel/framework": ">=4,<4.0.99|>=4.1,<4.1.29", - "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", - "magento/magento1ce": ">=1.5.0.1,<1.9.3.2", - "magento/magento1ee": ">=1.9,<1.14.3.2", - "magento/magento2ce": ">=2,<2.2", - "monolog/monolog": ">=1.8,<1.12", - "namshi/jose": "<2.2", - "onelogin/php-saml": "<2.10.4", - "oro/crm": ">=1.7,<1.7.4", - "oro/platform": ">=1.7,<1.7.4", - "padraic/humbug_get_contents": "<1.1.2", - "phpmailer/phpmailer": ">=5,<5.2.24", - "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", - "phpxmlrpc/extras": "<0.6.1", - "pusher/pusher-php-server": "<2.2.1", - "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", - "shopware/shopware": "<5.3.7", - "silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11", - "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", - "silverstripe/framework": ">=3,<3.3", - "silverstripe/userforms": "<3", - "simplesamlphp/saml2": "<1.10.4|>=2,<2.3.5|>=3,<3.1.1", - "simplesamlphp/simplesamlphp": "<1.15.2", - "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", - "socalnick/scn-social-auth": "<1.15.2", - "squizlabs/php_codesniffer": ">=1,<2.8.1", - "swiftmailer/swiftmailer": ">=4,<5.4.5", - "symfony/dependency-injection": ">=2,<2.0.17", - "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2", - "symfony/http-foundation": ">=2,<2.3.27|>=2.4,<2.5.11|>=2.6,<2.6.6", - "symfony/http-kernel": ">=2,<2.3.29|>=2.4,<2.5.12|>=2.6,<2.6.8", - "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/routing": ">=2,<2.0.19", - "symfony/security": ">=2,<2.0.25|>=2.1,<2.1.13|>=2.2,<2.2.9|>=2.3,<2.3.37|>=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8.23,<2.8.25|>=3.2.10,<3.2.12|>=3.3.3,<3.3.5", - "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.6|>=2.8.23,<2.8.25|>=3,<3.0.6|>=3.2.10,<3.2.12|>=3.3.3,<3.3.5", - "symfony/security-csrf": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/serializer": ">=2,<2.0.11", - "symfony/symfony": ">=2,<2.3.41|>=2.4,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", - "symfony/translation": ">=2,<2.0.17", - "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", - "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", - "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", - "thelia/backoffice-default-template": ">=2.1,<2.1.2", - "thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2", - "twig/twig": "<1.20", - "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.22|>=8,<8.7.5", - "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5", - "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", - "willdurand/js-translation-bundle": "<2.1.1", - "yiisoft/yii": ">=1.1.14,<1.1.15", - "yiisoft/yii2": "<2.0.14", - "yiisoft/yii2-bootstrap": "<2.0.4", - "yiisoft/yii2-dev": "<2.0.14", - "yiisoft/yii2-gii": "<2.0.4", - "yiisoft/yii2-jui": "<2.0.4", - "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", - "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", - "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", - "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5", - "zendframework/zend-diactoros": ">=1,<1.0.4", - "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-http": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.3,<2.3.8|>=2.4,<2.4.1", - "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", - "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", - "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2", - "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-session": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.9|>=2.3,<2.3.4", - "zendframework/zend-validator": ">=2.3,<2.3.6", - "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", - "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", - "zendframework/zendframework": ">=2,<2.4.11|>=2.5,<2.5.1", - "zendframework/zendframework1": "<1.12.20", - "zendframework/zendopenid": ">=2,<2.0.2", - "zendframework/zendxml": ">=1,<1.0.1", - "zetacomponents/mail": "<1.8.2", - "zf-commons/zfc-user": "<1.2.2", - "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3", - "zfr/zfr-oauth2-server-module": "<0.1.2" - }, - "type": "metapackage", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "role": "maintainer" - } - ], - "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2018-02-19T09:31:21+00:00" - }, { "name": "sentry/sentry", "version": "1.8.3", @@ -1815,16 +1668,16 @@ }, { "name": "spomky-labs/otphp", - "version": "v9.0.3", + "version": "v9.1.0", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/otphp.git", - "reference": "26d19c0baff675fb64193e22b777aa9be63b8deb" + "reference": "4b303e33972f6c886f32b38fea60f41be6d74ec1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/26d19c0baff675fb64193e22b777aa9be63b8deb", - "reference": "26d19c0baff675fb64193e22b777aa9be63b8deb", + "url": "https://api.github.com/repos/Spomky-Labs/otphp/zipball/4b303e33972f6c886f32b38fea60f41be6d74ec1", + "reference": "4b303e33972f6c886f32b38fea60f41be6d74ec1", "shasum": "" }, "require": { @@ -1872,7 +1725,7 @@ "otp", "totp" ], - "time": "2017-11-23T08:44:21+00:00" + "time": "2018-02-26T13:45:15+00:00" }, { "name": "swiftmailer/swiftmailer", @@ -1931,16 +1784,16 @@ }, { "name": "symfony/http-foundation", - "version": "v3.4.4", + "version": "v3.4.12", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "8c39071ac9cc7e6d8dab1d556c990dc0d2cc3d30" + "reference": "1c28679fcbb0d9b35e4fd49fbb74d2ca4ea17bce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/8c39071ac9cc7e6d8dab1d556c990dc0d2cc3d30", - "reference": "8c39071ac9cc7e6d8dab1d556c990dc0d2cc3d30", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/1c28679fcbb0d9b35e4fd49fbb74d2ca4ea17bce", + "reference": "1c28679fcbb0d9b35e4fd49fbb74d2ca4ea17bce", "shasum": "" }, "require": { @@ -1981,7 +1834,7 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2018-01-29T09:03:43+00:00" + "time": "2018-06-21T11:10:19+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -2202,16 +2055,16 @@ }, { "name": "yiisoft/yii2", - "version": "2.0.14", + "version": "2.0.15.1", "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-framework.git", - "reference": "1c9cf916b1394681c7d043e79e1522c33e5bc6c1" + "reference": "ed3a9e1c4abe206e1c3ce48a6b3624119b79850d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/1c9cf916b1394681c7d043e79e1522c33e5bc6c1", - "reference": "1c9cf916b1394681c7d043e79e1522c33e5bc6c1", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/ed3a9e1c4abe206e1c3ce48a6b3624119b79850d", + "reference": "ed3a9e1c4abe206e1c3ce48a6b3624119b79850d", "shasum": "" }, "require": { @@ -2298,7 +2151,7 @@ "framework", "yii2" ], - "time": "2018-02-18T22:52:12+00:00" + "time": "2018-03-21T18:36:53+00:00" }, { "name": "yiisoft/yii2-composer", @@ -2436,20 +2289,23 @@ }, { "name": "yiisoft/yii2-redis", - "version": "2.0.7", + "version": "2.0.8", "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-redis.git", - "reference": "3891bb19f3ddc7ad744b439fe1d656ebb5b60a99" + "reference": "ffe6bff8dc6be4bb84c9495cd3ef7ef1161c1314" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-redis/zipball/3891bb19f3ddc7ad744b439fe1d656ebb5b60a99", - "reference": "3891bb19f3ddc7ad744b439fe1d656ebb5b60a99", + "url": "https://api.github.com/repos/yiisoft/yii2-redis/zipball/ffe6bff8dc6be4bb84c9495cd3ef7ef1161c1314", + "reference": "ffe6bff8dc6be4bb84c9495cd3ef7ef1161c1314", "shasum": "" }, "require": { - "yiisoft/yii2": "~2.0.13" + "yiisoft/yii2": "~2.0.14" + }, + "require-dev": { + "yiisoft/yii2-dev": "~2.0.14" }, "type": "yii2-extension", "extra": { @@ -2459,7 +2315,7 @@ }, "autoload": { "psr-4": { - "yii\\redis\\": "" + "yii\\redis\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2480,20 +2336,20 @@ "session", "yii2" ], - "time": "2017-12-11T21:17:34+00:00" + "time": "2018-03-20T11:01:04+00:00" }, { "name": "yiisoft/yii2-swiftmailer", - "version": "2.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-swiftmailer.git", - "reference": "563570c9aa19ca47c1b22e3032983229378e9274" + "reference": "fd917fbe63b7ea796c52902143b83b98e65bfb73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-swiftmailer/zipball/563570c9aa19ca47c1b22e3032983229378e9274", - "reference": "563570c9aa19ca47c1b22e3032983229378e9274", + "url": "https://api.github.com/repos/yiisoft/yii2-swiftmailer/zipball/fd917fbe63b7ea796c52902143b83b98e65bfb73", + "reference": "fd917fbe63b7ea796c52902143b83b98e65bfb73", "shasum": "" }, "require": { @@ -2503,12 +2359,12 @@ "type": "yii2-extension", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "2.1.x-dev" } }, "autoload": { "psr-4": { - "yii\\swiftmailer\\": "" + "yii\\swiftmailer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2530,7 +2386,7 @@ "swiftmailer", "yii2" ], - "time": "2017-08-04T10:48:17+00:00" + "time": "2018-04-24T23:17:42+00:00" } ], "packages-dev": [ @@ -2882,6 +2738,50 @@ ], "time": "2016-08-30T16:08:34+00:00" }, + { + "name": "composer/xdebug-handler", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "c919dc6c62e221fc6406f861ea13433c0aa24f08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/c919dc6c62e221fc6406f861ea13433c0aa24f08", + "reference": "c919dc6c62e221fc6406f861ea13433c0aa24f08", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0", + "psr/log": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "time": "2018-04-11T15:42:36+00:00" + }, { "name": "doctrine/instantiator", "version": "1.1.0", @@ -3042,16 +2942,16 @@ }, { "name": "flow/jsonpath", - "version": "0.3.4", + "version": "0.4.0", "source": { "type": "git", "url": "https://github.com/FlowCommunications/JSONPath.git", - "reference": "00aa9c361e4d0a210dd95f3c917a1e0dde3a957f" + "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FlowCommunications/JSONPath/zipball/00aa9c361e4d0a210dd95f3c917a1e0dde3a957f", - "reference": "00aa9c361e4d0a210dd95f3c917a1e0dde3a957f", + "url": "https://api.github.com/repos/FlowCommunications/JSONPath/zipball/f0222818d5c938e4ab668ab2e2c079bd51a27112", + "reference": "f0222818d5c938e4ab668ab2e2c079bd51a27112", "shasum": "" }, "require": { @@ -3079,24 +2979,25 @@ } ], "description": "JSONPath implementation for parsing, searching and flattening arrays", - "time": "2016-09-06T17:43:18+00:00" + "time": "2018-03-04T16:39:47+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.11.1", + "version": "v2.12.2", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "ad94441c17b8ef096e517acccdbf3238af8a2da8" + "reference": "dcc87d5414e9d0bd316fce81a5bedb9ce720b183" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/ad94441c17b8ef096e517acccdbf3238af8a2da8", - "reference": "ad94441c17b8ef096e517acccdbf3238af8a2da8", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/dcc87d5414e9d0bd316fce81a5bedb9ce720b183", + "reference": "dcc87d5414e9d0bd316fce81a5bedb9ce720b183", "shasum": "" }, "require": { "composer/semver": "^1.4", + "composer/xdebug-handler": "^1.0", "doctrine/annotations": "^1.2", "ext-json": "*", "ext-tokenizer": "*", @@ -3118,27 +3019,26 @@ "require-dev": { "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", "justinrainbow/json-schema": "^5.0", - "keradus/cli-executor": "^1.0", + "keradus/cli-executor": "^1.1", "mikey179/vfsstream": "^1.6", - "php-coveralls/php-coveralls": "^2.0", + "php-coveralls/php-coveralls": "^2.1", "php-cs-fixer/accessible-object": "^1.0", - "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", - "phpunitgoodpractices/traits": "^1.3.1", - "symfony/phpunit-bridge": "^3.2.2 || ^4.0" + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.0.1", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.0.1", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1", + "phpunitgoodpractices/traits": "^1.5.1", + "symfony/phpunit-bridge": "^4.0" }, "suggest": { "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "For IsIdenticalString constraint.", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "For XmlMatchesXsd constraint.", "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." }, "bin": [ "php-cs-fixer" ], "type": "application", - "extra": { - "branch-alias": { - "dev-master": "2.11-dev" - } - }, "autoload": { "psr-4": { "PhpCsFixer\\": "src/" @@ -3148,9 +3048,6 @@ "tests/Test/AbstractIntegrationCaseFactory.php", "tests/Test/AbstractIntegrationTestCase.php", "tests/Test/Assert/AssertTokensTrait.php", - "tests/Test/Constraint/SameStringsConstraint.php", - "tests/Test/Constraint/SameStringsConstraintForV5.php", - "tests/Test/Constraint/SameStringsConstraintForV7.php", "tests/Test/IntegrationCase.php", "tests/Test/IntegrationCaseFactory.php", "tests/Test/IntegrationCaseFactoryInterface.php", @@ -3173,7 +3070,7 @@ } ], "description": "A tool to automatically fix PHP code style", - "time": "2018-03-21T17:41:26+00:00" + "time": "2018-07-06T10:37:40+00:00" }, { "name": "fzaninotto/faker", @@ -4362,6 +4259,220 @@ ], "time": "2016-06-16T16:22:20+00:00" }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10T12:19:37+00:00" + }, + { + "name": "roave/security-advisories", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Roave/SecurityAdvisories.git", + "reference": "0253937ef2720f45fbe421e3ba486587b3080c35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0253937ef2720f45fbe421e3ba486587b3080c35", + "reference": "0253937ef2720f45fbe421e3ba486587b3080c35", + "shasum": "" + }, + "conflict": { + "3f/pygmentize": "<1.2", + "adodb/adodb-php": "<5.20.12", + "amphp/artax": "<1.0.6|>=2,<2.0.6", + "amphp/http": "<1.0.1", + "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99", + "aws/aws-sdk-php": ">=3,<3.2.1", + "bugsnag/bugsnag-laravel": ">=2,<2.0.2", + "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4|>=3.4,<3.4.14|>=3.5,<3.5.17|>=3.6,<3.6.4", + "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4", + "cartalyst/sentry": "<=2.1.6", + "codeigniter/framework": "<=3.0.6", + "composer/composer": "<=1.0.0-alpha11", + "contao-components/mediaelement": ">=2.14.2,<2.21.1", + "contao/core": ">=2,<3.5.35", + "contao/core-bundle": ">=4,<4.4.18|>=4.5,<4.5.8", + "contao/listing-bundle": ">=4,<4.4.8", + "contao/newsletter-bundle": ">=4,<4.1", + "doctrine/annotations": ">=1,<1.2.7", + "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2", + "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1", + "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2", + "doctrine/doctrine-bundle": "<1.5.2", + "doctrine/doctrine-module": "<=0.7.1", + "doctrine/mongodb-odm": ">=1,<1.0.2", + "doctrine/mongodb-odm-bundle": ">=2,<3.0.1", + "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1", + "dompdf/dompdf": ">=0.6,<0.6.2", + "drupal/core": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3", + "drupal/drupal": ">=7,<7.59|>=8,<8.4.8|>=8.5,<8.5.3", + "erusev/parsedown": "<1.7", + "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.3|>=5.4,<5.4.11.3|>=2017.8,<2017.8.1.1|>=2017.12,<2017.12.2.1", + "firebase/php-jwt": "<2", + "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2", + "friendsofsymfony/user-bundle": ">=1.2,<1.3.5", + "fuel/core": "<1.8.1", + "gree/jose": "<=2.2", + "gregwar/rst": "<1.0.3", + "guzzlehttp/guzzle": ">=6,<6.2.1|>=4.0.0-rc2,<4.2.4|>=5,<5.3.1", + "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10", + "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29", + "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", + "joomla/session": "<1.3.1", + "kreait/firebase-php": ">=3.2,<3.8.1", + "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15", + "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", + "magento/magento1ce": ">=1.5.0.1,<1.9.3.2", + "magento/magento1ee": ">=1.9,<1.14.3.2", + "magento/product-community-edition": ">=2,<2.2.5", + "monolog/monolog": ">=1.8,<1.12", + "namshi/jose": "<2.2", + "onelogin/php-saml": "<2.10.4", + "oro/crm": ">=1.7,<1.7.4", + "oro/platform": ">=1.7,<1.7.4", + "padraic/humbug_get_contents": "<1.1.2", + "pagarme/pagarme-php": ">=0,<3", + "paragonie/random_compat": "<2", + "paypal/merchant-sdk-php": "<3.12", + "phpmailer/phpmailer": ">=5,<5.2.24", + "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3", + "phpxmlrpc/extras": "<0.6.1", + "propel/propel": ">=2.0.0-alpha1,<=2.0.0-alpha7", + "propel/propel1": ">=1,<=1.7.1", + "pusher/pusher-php-server": "<2.2.1", + "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", + "sensiolabs/connect": "<4.2.3", + "shopware/shopware": "<5.3.7", + "silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11", + "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", + "silverstripe/framework": ">=3,<3.3", + "silverstripe/userforms": "<3", + "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4", + "simplesamlphp/simplesamlphp": "<1.15.2", + "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", + "slim/slim": "<2.6", + "socalnick/scn-social-auth": "<1.15.2", + "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", + "stormpath/sdk": ">=0,<9.9.99", + "swiftmailer/swiftmailer": ">=4,<5.4.5", + "symfony/dependency-injection": ">=2,<2.0.17", + "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", + "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2", + "symfony/http-foundation": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/http-kernel": ">=2,<2.3.29|>=2.4,<2.5.12|>=2.6,<2.6.8", + "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", + "symfony/routing": ">=2,<2.0.19", + "symfony/security": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.37|>=3,<3.3.17|>=3.4,<3.4.7|>=4,<4.0.7", + "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/serializer": ">=2,<2.0.11", + "symfony/symfony": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", + "symfony/translation": ">=2,<2.0.17", + "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3", + "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4", + "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7", + "thelia/backoffice-default-template": ">=2.1,<2.1.2", + "thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2", + "titon/framework": ">=0,<9.9.99", + "twig/twig": "<1.20", + "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.22|>=8,<8.7.5", + "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5", + "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", + "willdurand/js-translation-bundle": "<2.1.1", + "yiisoft/yii": ">=1.1.14,<1.1.15", + "yiisoft/yii2": "<2.0.15", + "yiisoft/yii2-bootstrap": "<2.0.4", + "yiisoft/yii2-dev": "<2.0.15", + "yiisoft/yii2-elasticsearch": "<2.0.5", + "yiisoft/yii2-gii": "<2.0.4", + "yiisoft/yii2-jui": "<2.0.4", + "yiisoft/yii2-redis": "<2.0.8", + "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3", + "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2", + "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2", + "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5", + "zendframework/zend-diactoros": ">=1,<1.0.4", + "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-http": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.3,<2.3.8|>=2.4,<2.4.1", + "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", + "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", + "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2", + "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-session": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.9|>=2.3,<2.3.4", + "zendframework/zend-validator": ">=2.3,<2.3.6", + "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", + "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", + "zendframework/zendframework": ">=2,<2.4.11|>=2.5,<2.5.1", + "zendframework/zendframework1": "<1.12.20", + "zendframework/zendopenid": ">=2,<2.0.2", + "zendframework/zendxml": ">=1,<1.0.1", + "zetacomponents/mail": "<1.8.2", + "zf-commons/zfc-user": "<1.2.2", + "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3", + "zfr/zfr-oauth2-server-module": "<0.1.2" + }, + "type": "metapackage", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "role": "maintainer" + } + ], + "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", + "time": "2018-07-04T05:48:21+00:00" + }, { "name": "sebastian/code-unit-reverse-lookup", "version": "1.0.1", @@ -5730,8 +5841,8 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "roave/security-advisories": 20, - "ely/email-renderer": 20 + "ely/email-renderer": 20, + "roave/security-advisories": 20 }, "prefer-stable": false, "prefer-lowest": false, From 7f42aadfcfd987870a31f3f0f5479be0b6b5e431 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sun, 8 Jul 2018 18:55:53 +0300 Subject: [PATCH 04/13] Fix code style --- common/tasks/DeliveryWebHook.php | 1 + console/controllers/WebhooksController.php | 2 +- tests/codeception/common/unit/tasks/DeliveryWebHookTest.php | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/common/tasks/DeliveryWebHook.php b/common/tasks/DeliveryWebHook.php index 7561d2a..a3d639b 100644 --- a/common/tasks/DeliveryWebHook.php +++ b/common/tasks/DeliveryWebHook.php @@ -78,6 +78,7 @@ class DeliveryWebHook implements RetryableJobInterface { ]); } catch (ClientException $e) { Yii::info("Delivery for {$this->url} has failed with {$e->getResponse()->getStatusCode()} status."); + return; } } diff --git a/console/controllers/WebhooksController.php b/console/controllers/WebhooksController.php index 6f77acc..1dcd731 100644 --- a/console/controllers/WebhooksController.php +++ b/console/controllers/WebhooksController.php @@ -38,7 +38,7 @@ class WebhooksController extends Controller { if ($eventIndex !== '') { $events[] = $options[$eventIndex]; } - } while($eventIndex !== '' || empty($events)); // User must choose at least one event + } while ($eventIndex !== '' || empty($events)); $form->url = $url; $form->events = $events; diff --git a/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php b/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php index af74384..f798b60 100644 --- a/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php +++ b/tests/codeception/common/unit/tasks/DeliveryWebHookTest.php @@ -109,7 +109,8 @@ class DeliveryWebHookTest extends TestCase { private function createMockedTask(): DeliveryWebHook { $container = &$this->historyContainer; $response = $this->response; - return new class ($container, $response) extends DeliveryWebHook { + + return new class($container, $response) extends DeliveryWebHook { private $historyContainer; private $response; From a0d88e8a8fd2f4509e303ef3d8c43399fb3df866 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Sun, 8 Jul 2018 22:21:33 +0300 Subject: [PATCH 05/13] Allow any valid locale for account lang --- common/config/bootstrap.php | 5 +- common/validators/LanguageValidator.php | 38 ++++++----- .../m180708_155425_extends_locale_field.php | 15 +++++ .../codeception/common/unit/fixtures/.gitkeep | 0 .../common/unit/fixtures/data/i18n/en.json | 3 - .../common/unit/fixtures/data/i18n/ru.json | 3 - .../unit/validators/LanguageValidatorTest.php | 63 +++++++++++-------- 7 files changed, 72 insertions(+), 55 deletions(-) create mode 100644 console/migrations/m180708_155425_extends_locale_field.php create mode 100644 tests/codeception/common/unit/fixtures/.gitkeep delete mode 100644 tests/codeception/common/unit/fixtures/data/i18n/en.json delete mode 100644 tests/codeception/common/unit/fixtures/data/i18n/ru.json diff --git a/common/config/bootstrap.php b/common/config/bootstrap.php index 64528ff..bce9d52 100644 --- a/common/config/bootstrap.php +++ b/common/config/bootstrap.php @@ -1,5 +1,4 @@ getFilesNames(); - if (in_array($value, $files)) { - return null; + $primary = Locale::getPrimaryLanguage($value); + $region = Locale::getRegion($value); + $locales = ResourceBundle::getLocales(''); // http://php.net/manual/ru/resourcebundle.locales.php#115965 + if (($region !== '' && strtolower($primary) !== strtolower($region)) && !in_array($value, $locales)) { + return [$this->message, []]; } - return [$this->message, []]; - } - - protected function getFilesNames() { - $files = array_values(array_filter(scandir($this->getFolderPath()), function(&$value) { - return $value !== '..' && $value !== '.'; - })); - - return array_map(function($value) { - return basename($value, '.json'); - }, $files); - } - - protected function getFolderPath() { - return Yii::getAlias('@frontend/src/i18n'); + return null; } } diff --git a/console/migrations/m180708_155425_extends_locale_field.php b/console/migrations/m180708_155425_extends_locale_field.php new file mode 100644 index 0000000..600442b --- /dev/null +++ b/console/migrations/m180708_155425_extends_locale_field.php @@ -0,0 +1,15 @@ +alterColumn('{{%accounts}}', 'lang', $this->string()->notNull()->defaultValue('en')); + } + + public function safeDown() { + $this->alterColumn('{{%accounts}}', 'lang', $this->string(5)->notNull()->defaultValue('en')); + } + +} diff --git a/tests/codeception/common/unit/fixtures/.gitkeep b/tests/codeception/common/unit/fixtures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/codeception/common/unit/fixtures/data/i18n/en.json b/tests/codeception/common/unit/fixtures/data/i18n/en.json deleted file mode 100644 index cacf5c2..0000000 --- a/tests/codeception/common/unit/fixtures/data/i18n/en.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "testString": "testValue" -} diff --git a/tests/codeception/common/unit/fixtures/data/i18n/ru.json b/tests/codeception/common/unit/fixtures/data/i18n/ru.json deleted file mode 100644 index 9734d39..0000000 --- a/tests/codeception/common/unit/fixtures/data/i18n/ru.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "testString": "тестовоеЗначение" -} diff --git a/tests/codeception/common/unit/validators/LanguageValidatorTest.php b/tests/codeception/common/unit/validators/LanguageValidatorTest.php index e40e72c..e6d077e 100644 --- a/tests/codeception/common/unit/validators/LanguageValidatorTest.php +++ b/tests/codeception/common/unit/validators/LanguageValidatorTest.php @@ -1,39 +1,50 @@ createModelWithFixturePath(); - $this->assertEquals(['en', 'ru'], $this->callProtected($model, 'getFilesNames')); - } - - public function testValidateValueSupportedLanguage() { - $model = $this->createModelWithFixturePath(); - $this->assertNull($this->callProtected($model, 'validateValue', 'ru')); - } - - public function testValidateNotSupportedLanguage() { - $model = $this->createModelWithFixturePath(); - $this->assertEquals([$model->message, []], $this->callProtected($model, 'validateValue', 'by')); - } /** - * @return LanguageValidator + * @param string $locale + * @param bool $shouldBeValid + * + * @dataProvider getTestCases */ - private function createModelWithFixturePath() { - return new class extends LanguageValidator { - public function getFolderPath() { - return __DIR__ . '/../fixtures/data/i18n'; - } - }; + public function testValidate(string $locale, bool $shouldBeValid): void { + $validator = new LanguageValidator(); + $result = $validator->validate($locale, $error); + $this->assertSame($shouldBeValid, $result, $locale); + if (!$shouldBeValid) { + $this->assertSame($validator->message, $error); + } + } + + public function getTestCases(): array { + return [ + // valid + ['de', true], + ['de_DE', true], + ['deu', true], + ['en', true], + ['en_US', true], + ['fil', true], + ['fil_PH', true], + ['zh', true], + ['zh_Hans_CN', true], + ['zh_Hant_HK', true], + // invalid + ['de_FR', false], + ['fr_US', false], + ['foo_bar', false], + ['foo_bar_baz', false], + ]; } } From 49d612daa1c9993f494b6233a07bf47b93311ec4 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Tue, 10 Jul 2018 20:02:19 +0300 Subject: [PATCH 06/13] Replace debian-based image with alpine-based. Remove supervisor usage. Cron now runs as separate service. CI for backend now performs without Docker. --- .gitlab-ci.yml | 45 ++++-------- Dockerfile | 119 ++++++++++++++++++++------------ Dockerfile-dev | 52 -------------- composer.json | 2 +- composer.lock | 4 +- docker-compose.dev.yml | 33 +++++---- docker-compose.prod.yml | 9 +++ docker/php/bootstrap.sh | 41 ----------- docker/php/docker-entrypoint.sh | 62 +++++++++++++++++ docker/php/php.dev.ini | 5 ++ docker/php/php.prod.ini | 14 ++++ docker/{ => php}/wait-for-it.sh | 0 12 files changed, 201 insertions(+), 185 deletions(-) delete mode 100644 Dockerfile-dev delete mode 100755 docker/php/bootstrap.sh create mode 100755 docker/php/docker-entrypoint.sh create mode 100644 docker/php/php.dev.ini create mode 100644 docker/php/php.prod.ini rename docker/{ => php}/wait-for-it.sh (100%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8deb81b..3a5aacb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,50 +6,31 @@ stages: variables: CONTAINER_IMAGE: "registry.ely.by/elyby/accounts" -test:backend: - image: docker:18.02 +check backend codestyle: + image: edbizarro/gitlab-ci-pipeline-php:7.2-alpine + stage: test + script: + - vendor/bin/php-cs-fixer fix -v --dry-run + +test backend: + image: edbizarro/gitlab-ci-pipeline-php:7.2-alpine services: - mariadb:10.2.11 - redis:3.0-alpine variables: + # app config + YII_ENV: "test" + YII_DEBUG: "true" # mariadb config MYSQL_RANDOM_ROOT_PASSWORD: "true" MYSQL_DATABASE: "ely_accounts_test" MYSQL_USER: "ely_accounts_tester" MYSQL_PASSWORD: "ely_accounts_tester_password" stage: test - before_script: - - docker login -u gitlab-ci -p $CI_BUILD_TOKEN registry.ely.by - - echo "$SSH_PRIVATE_KEY" > id_rsa script: - - export TEMP_DEV_IMAGE="${CONTAINER_IMAGE}:ci-${CI_BUILD_ID}" - - docker build --pull -f Dockerfile-dev -t $TEMP_DEV_IMAGE . - - > - docker run --rm - $TEMP_DEV_IMAGE - bash -c " - rm /usr/local/etc/php/conf.d/xdebug.ini && - cp -r /var/www/vendor /var/www/html/vendor && - vendor/bin/php-cs-fixer fix -v --dry-run - " - - > - docker run --rm - --add-host=mariadb:`getent hosts mariadb | awk '{ print $1 ; exit }'` - --add-host=redis:`getent hosts redis | awk '{ print $1 ; exit }'` - -e YII_DEBUG="true" - -e YII_ENV="test" - -e DB_HOST="mariadb" - -e DB_DATABASE="ely_accounts_test" - -e DB_USER="ely_accounts_tester" - -e DB_PASSWORD="ely_accounts_tester_password" - -e REDIS_HOST="redis" - $TEMP_DEV_IMAGE - bash -c " - rm /usr/local/etc/php/conf.d/xdebug.ini && - docker-entrypoint.sh php vendor/bin/codecept run -c tests - " + - docker-entrypoint.sh vendor/bin/codecept run -c tests -test:frontend: +test frontend: image: node:9.2.1-alpine stage: test cache: diff --git a/Dockerfile b/Dockerfile index 54d8000..652ab21 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,55 +1,86 @@ -FROM registry.ely.by/elyby/accounts-php:1.8.0 +FROM node:9.11.2-alpine as frontend -# bootstrap скрипт для проекта -COPY docker/php/bootstrap.sh /bootstrap.sh -# Вносим конфигурации для крона и воркеров -COPY docker/cron/* /etc/cron.d/ -COPY docker/supervisor/* /etc/supervisor/conf.d/ +WORKDIR /app -COPY id_rsa /root/.ssh/id_rsa +COPY ./frontend/package.json ./ +COPY ./frontend/scripts ./scripts +COPY ./frontend/webpack-utils ./webpack-utils +COPY ./frontend/yarn.lock ./ +RUN yarn build:install -# Включаем поддержку ssh -RUN chmod 400 ~/.ssh/id_rsa \ - && eval $(ssh-agent -s) \ - && ssh-add /root/.ssh/id_rsa \ - && touch /root/.ssh/known_hosts \ - && ssh-keyscan github.com gitlab.ely.by >> /root/.ssh/known_hosts +COPY ./frontend . +RUN yarn build:quiet -# Копируем списки зависимостей composer в родительскую директорию, которая не будет синкаться -# с хостом через volume на dev окружении. В entrypoint эта папка будет скопирована обратно -COPY ./composer.json /var/www/composer.json -COPY ./composer.lock /var/www/composer.lock -# Устанавливаем зависимости PHP -RUN cd .. \ - && composer install --no-interaction --no-suggest --no-dev --optimize-autoloader \ - && cd - +FROM php:7.2.7-fpm-alpine3.7 -# Устанавливаем зависимости для Node.js -# Делаем это отдельно, чтобы можно было воспользоваться кэшем, если от предыдущего билда -# ничего не менялось в зависимостях -RUN mkdir -p /var/www/frontend +# bash needed to support wait-for-it script +RUN apk add --update --no-cache \ + git \ + bash \ + openssh \ + dcron \ + zlib-dev \ + icu-dev \ + libintl \ + imagemagick-dev \ + imagemagick \ + && docker-php-ext-install \ + zip \ + pdo_mysql \ + intl \ + pcntl \ + opcache \ + && apk add --no-cache --virtual ".phpize-deps" $PHPIZE_DEPS \ + && yes | pecl install xdebug-2.6.0 \ + && yes | pecl install imagick \ + && docker-php-ext-enable imagick \ + && apk del ".phpize-deps" \ + && rm -rf /usr/share/man \ + && rm -rf /tmp/* \ + && mkdir /etc/cron.d -COPY ./frontend/package.json /var/www/frontend/ -COPY ./frontend/yarn.lock /var/www/frontend/ -COPY ./frontend/scripts /var/www/frontend/scripts -COPY ./frontend/webpack-utils /var/www/frontend/webpack-utils +COPY --from=composer:1.6.5 /usr/bin/composer /usr/bin/composer +COPY --from=node:9.11.2-alpine /usr/local/bin/node /usr/bin/ +COPY --from=node:9.11.2-alpine /usr/lib/libgcc* /usr/lib/libstdc* /usr/lib/* /usr/lib/ -RUN cd /var/www/frontend \ - && yarn run build:install \ - && cd - +# ENV variables for composer +ENV COMPOSER_NO_INTERACTION 1 +ENV COMPOSER_ALLOW_SUPERUSER 1 -# Удаляем ключи из production контейнера на всякий случай -RUN rm -rf /root/.ssh +RUN mkdir /root/.composer \ + && echo '{"github-oauth": {"github.com": "***REMOVED***"}}' > ~/.composer/auth.json \ + && composer global require --no-progress "hirak/prestissimo:^0.3.7" \ + && composer clear-cache -# Наконец переносим все сорцы внутрь контейнера -COPY . /var/www/html +COPY ./docker/php/wait-for-it.sh /usr/local/bin/wait-for-it -# Билдим фронт -RUN cd frontend \ - && ln -s /var/www/frontend/node_modules $PWD/node_modules \ - && yarn run build:quiet \ - && rm node_modules \ - # Копируем билд наружу, чтобы его не затёрло volume в dev режиме - && cp -r ./dist /var/www/dist \ - && cd - +COPY ./composer.* /var/www/html/ + +ARG build_env=prod +ENV YII_ENV=$build_env + +RUN if [ "$build_env" = "prod" ] ; then \ + composer install --no-interaction --no-suggest --no-dev --optimize-autoloader; \ + else \ + composer install --no-interaction --no-suggest; \ + fi \ + && composer clear-cache + +COPY ./docker/php/*.ini /usr/local/etc/php/conf.d/ +COPY ./docker/php/docker-entrypoint.sh /usr/local/bin/ +COPY ./docker/cron/* /etc/cron.d/ + +COPY --from=frontend /app/dist /var/www/html/frontend/dist + +COPY ./api /var/www/html/api/ +COPY ./common /var/www/html/common/ +COPY ./console /var/www/html/console/ +COPY ./yii /var/www/html/yii + +# Expose everything under /var/www/html to share it with nginx +VOLUME ["/var/www/html"] + +WORKDIR /var/www/html +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["php-fpm"] diff --git a/Dockerfile-dev b/Dockerfile-dev deleted file mode 100644 index dec0312..0000000 --- a/Dockerfile-dev +++ /dev/null @@ -1,52 +0,0 @@ -FROM registry.ely.by/elyby/accounts-php:1.8.0-dev - -# bootstrap скрипт для проекта -COPY docker/php/bootstrap.sh /bootstrap.sh -# Вносим конфигурации для крона и воркеров -COPY docker/cron/* /etc/cron.d/ -COPY docker/supervisor/* /etc/supervisor/conf.d/ - -COPY id_rsa /root/.ssh/id_rsa - -# Включаем поддержку ssh -RUN chmod 400 ~/.ssh/id_rsa \ - && eval $(ssh-agent -s) \ - && ssh-add /root/.ssh/id_rsa \ - && touch /root/.ssh/known_hosts \ - && ssh-keyscan github.com gitlab.ely.by >> /root/.ssh/known_hosts - -# Копируем списки зависимостей composer в родительскую директорию, которая не будет синкаться -# с хостом через volume на dev окружении. В entrypoint эта папка будет скопирована обратно -COPY ./composer.json /var/www/composer.json -COPY ./composer.lock /var/www/composer.lock - -# Устанавливаем зависимости PHP -RUN cd .. \ - && composer install --no-interaction --no-suggest \ - && cd - - -# Устанавливаем зависимости для Node.js -# Делаем это отдельно, чтобы можно было воспользоваться кэшем, если от предыдущего билда -# ничего не менялось в зависимостях -RUN mkdir -p /var/www/frontend - -COPY ./frontend/package.json /var/www/frontend/ -COPY ./frontend/yarn.lock /var/www/frontend/ -COPY ./frontend/scripts /var/www/frontend/scripts -COPY ./frontend/webpack-utils /var/www/frontend/webpack-utils - -RUN cd /var/www/frontend \ - && yarn run build:install \ - && cd - - -# Наконец переносим все сорцы внутрь контейнера -COPY . /var/www/html - -# Билдим фронт -RUN cd frontend \ - && ln -s /var/www/frontend/node_modules $PWD/node_modules \ - && yarn run build:quiet \ - && rm node_modules \ - # Копируем билд наружу, чтобы его не затёрло volume в dev режиме - && cp -r ./dist /var/www/dist \ - && cd - diff --git a/composer.json b/composer.json index 9bf9e12..d6448ff 100644 --- a/composer.json +++ b/composer.json @@ -46,7 +46,7 @@ }, { "type": "git", - "url": "git@gitlab.ely.by:elyby/email-renderer.git" + "url": "https://gitlab+deploy-token-1:FDGgmcnLdykcsyJJ_8Uv@gitlab.ely.by/elyby/email-renderer.git" } ], "autoload": { diff --git a/composer.lock b/composer.lock index 60f1fee..cba9321 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4334ca4dd8b377a9c40afd844527a850", + "content-hash": "7368afb90e5f3ed26a7b6d98551da170", "packages": [ { "name": "bacon/bacon-qr-code", @@ -615,7 +615,7 @@ "version": "dev-master", "source": { "type": "git", - "url": "git@gitlab.ely.by:elyby/email-renderer.git", + "url": "https://gitlab+deploy-token-1:FDGgmcnLdykcsyJJ_8Uv@gitlab.ely.by/elyby/email-renderer.git", "reference": "8aa2e71c5b3b8e4a726c3c090b2997030ba29f73" }, "require": { diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 7082ef4..7854586 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,9 +1,11 @@ version: '2' services: app: + image: registry.ely.by/elyby/accounts:dev build: - dockerfile: Dockerfile-dev context: . + args: + build_env: dev depends_on: - db - redis @@ -12,9 +14,11 @@ services: env_file: .env worker: + image: registry.ely.by/elyby/accounts:dev build: - dockerfile: Dockerfile-dev context: . + args: + build_env: dev command: ['php', 'yii', 'queue/listen', '-v'] depends_on: - db @@ -23,6 +27,20 @@ services: - ./:/var/www/html/ env_file: .env + cron: + image: registry.ely.by/elyby/accounts:dev + build: + context: . + args: + build_env: dev + command: ['crond', '-s', '/etc/cron.d', '-f', '-L', '/var/log/cron.log'] + depends_on: + - db + - redis + volumes: + - ./:/var/www/html/ + env_file: .env + web: image: registry.ely.by/elyby/accounts-nginx:latest volumes_from: @@ -58,17 +76,6 @@ services: - default - nginx-proxy - # Эта штука работает дико медленно, грузит процессор и т.д. и т.п. - # Раскоментировать только в случае лютой надобности - #node-dev-server: - # build: ./frontend - # ports: - # - "8080:8080" - # volumes: - # - ./frontend/:/usr/src/app/ - # environment: - # DOCKERIZED: "true" - networks: nginx-proxy: external: diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 539c878..687baab 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -17,6 +17,15 @@ services: - redis env_file: .env + worker: + image: registry.ely.by/elyby/accounts:latest + restart: always + command: ['crond', '-s', '/etc/cron.d', '-f', '-L', '/var/log/cron.log'] + depends_on: + - db + - redis + env_file: .env + web: image: registry.ely.by/elyby/accounts-nginx:1.0.3 restart: always diff --git a/docker/php/bootstrap.sh b/docker/php/bootstrap.sh deleted file mode 100755 index 65617dd..0000000 --- a/docker/php/bootstrap.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -mkdir -p api/runtime api/web/assets console/runtime -chown www-data:www-data api/runtime api/web/assets console/runtime - -if [ "$YII_ENV" = "test" ] -then - YII_EXEC="/var/www/html/tests/codeception/bin/yii" -else - YII_EXEC="/var/www/html/yii" -fi - -if ! cmp -s ./../vendor/autoload.php ./vendor/autoload.php -then - echo "Vendor have diffs" - echo "Removing not bundled vendor..." - rm -rf ./vendor - echo "Copying new one..." - cp -r ./../vendor ./vendor -fi - -# Переносим dist, если его нету или он изменился (или затёрся силами volume) -if ! cmp -s ./../dist/index.html ./frontend/dist/index.html -then - echo "Frontend dist have diffs" - echo "Removing not bundled dist..." - rm -rf ./frontend/dist - echo "Copying new one..." - cp -r ./../dist ./frontend/dist -fi - -# Генерируем правила RBAC -echo "Generating RBAC rules" -php $YII_EXEC rbac/generate - -if [ "$YII_ENV" != "test" ] -then - wait-for-it "${DB_HOST:-db}:3306" -s -t 0 -- "php $YII_EXEC migrate/up --interactive=0" -else - wait-for-it "${DB_HOST:-testdb}:3306" -s -t 0 -- "php $YII_EXEC migrate/up --interactive=0" -fi diff --git a/docker/php/docker-entrypoint.sh b/docker/php/docker-entrypoint.sh new file mode 100755 index 0000000..c34f9fa --- /dev/null +++ b/docker/php/docker-entrypoint.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +set -e + +XDEBUG_EXTENSION_FILE="/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini" +PHP_PROD_INI="/usr/local/etc/php/conf.d/php.prod.ini" +PHP_DEV_INI="/usr/local/etc/php/conf.d/php.dev.ini" + +if [ "$YII_DEBUG" = "true" ] || [ "$YII_DEBUG" = "1" ] ; then + echo "zend_extension=$(find /usr/local/lib/php/extensions/ -name xdebug.so)" > $XDEBUG_EXTENSION_FILE + mv ${PHP_PROD_INI}{,.disabled} 2> /dev/null || true + mv ${PHP_DEV_INI}{.disabled,} 2> /dev/null || true +else + rm -f $XDEBUG_EXTENSION_FILE + mv ${PHP_DEV_INI}{,.disabled} 2> /dev/null || true + mv ${PHP_PROD_INI}{.disabled,} 2> /dev/null || true +fi + +cd /var/www/html + +# Create all necessary folders +mkdir -p api/runtime api/web/assets console/runtime +chown -R www-data:www-data api/runtime api/web/assets console/runtime + +if [ "$YII_ENV" = "test" ] +then + YII_EXEC="/var/www/html/tests/codeception/bin/yii" +else + YII_EXEC="/var/www/html/yii" +fi + +# Fix permissions for cron tasks +chmod 644 /etc/cron.d/* + +if [ "$1" = "crond" ] ; then + # see: https://github.com/dubiousjim/dcron/issues/13 + # ignore using `exec` for `dcron` to get another pid instead of `1` + # exec "$@" + "$@" +fi + +if [ "$1" = "yii" ] ; then + shift + php $YII_EXEC "$@" + exit 0 +fi + +if [ "$1" = "sh" ] || [ "$1" = "bash" ] || [ "$1" = "composer" ] || [ "$1" = "php" ] ; then + exec "$@" + exit 0 +fi + +echo "Generating RBAC rules" +php $YII_EXEC rbac/generate + +if [ "$YII_ENV" != "test" ] +then + wait-for-it "${DB_HOST:-db}:3306" -s -t 0 -- "php $YII_EXEC migrate/up --interactive=0" +else + wait-for-it "${DB_HOST:-testdb}:3306" -s -t 0 -- "php $YII_EXEC migrate/up --interactive=0" +fi + +exec "$@" diff --git a/docker/php/php.dev.ini b/docker/php/php.dev.ini new file mode 100644 index 0000000..7114082 --- /dev/null +++ b/docker/php/php.dev.ini @@ -0,0 +1,5 @@ +error_reporting = E_ALL; +display_errors = On; + +# Disable Opcache caching +opcache.validate_timestamps = 1; diff --git a/docker/php/php.prod.ini b/docker/php/php.prod.ini new file mode 100644 index 0000000..f0cc3e5 --- /dev/null +++ b/docker/php/php.prod.ini @@ -0,0 +1,14 @@ +error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT +display_errors = Off +display_startup_errors = Off +log_errors = On +html_errors = Off +expose_php = Off + +# Opcache tuning +opcache.revalidate_freq = 0 +opcache.validate_timestamps = 0 +opcache.max_accelerated_files = 7963 +opcache.memory_consumption = 192 +opcache.interned_strings_buffer = 16 +opcache.fast_shutdown = 1 diff --git a/docker/wait-for-it.sh b/docker/php/wait-for-it.sh similarity index 100% rename from docker/wait-for-it.sh rename to docker/php/wait-for-it.sh From 40eea7864e8e15d99ecb846b50ef737f6498233f Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Tue, 10 Jul 2018 20:08:43 +0300 Subject: [PATCH 07/13] Fix gitlab-ci --- .gitlab-ci.yml | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3a5aacb..5328d39 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,26 +9,45 @@ variables: check backend codestyle: image: edbizarro/gitlab-ci-pipeline-php:7.2-alpine stage: test + cache: + paths: + - vendor script: + - composer install - vendor/bin/php-cs-fixer fix -v --dry-run test backend: image: edbizarro/gitlab-ci-pipeline-php:7.2-alpine services: - - mariadb:10.2.11 - - redis:3.0-alpine + - name: redis:4.0.10-alpine + alias: redis + - name: mariadb:10.2.11 + alias: db variables: - # app config - YII_ENV: "test" - YII_DEBUG: "true" - # mariadb config + # App config + DB_HOST: "db" + DB_DATABASE: "ely_accounts_test" + DB_USER: "ely_accounts_tester" + DB_PASSWORD: "ely_accounts_tester_password" + REDIS_HOST: "redis" + REDIS_PORT: "6379" + # MariaDB config MYSQL_RANDOM_ROOT_PASSWORD: "true" MYSQL_DATABASE: "ely_accounts_test" MYSQL_USER: "ely_accounts_tester" MYSQL_PASSWORD: "ely_accounts_tester_password" stage: test + cache: + paths: + - vendor + before_script: + # While we not counting coverage, xdebug only slow down tests + - sudo rm /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini script: - - docker-entrypoint.sh vendor/bin/codecept run -c tests + - composer install + - php tests/codeception/bin/yii rbac/generate + - ./docker/php/wait-for-it.sh "${DB_HOST}:3306" -s -t 0 -- "php tests/codeception/bin/yii migrate/up --interactive=0" + - vendor/bin/codecept run -c tests test frontend: image: node:9.2.1-alpine @@ -71,7 +90,7 @@ build:production: - sed -i"" -e "s/{{PLACE_VERSION_HERE}}/$VERSION/g" common/config/config.php script: - export IMAGE_NAME="$CONTAINER_IMAGE:latest" - - docker build --pull -t $IMAGE_NAME . + - docker build --pull --build-arg build_env=prod -t $IMAGE_NAME . only: - develop - tags From 9ed449d144eed624bfef124c1dba25eab46407ee Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 11 Jul 2018 01:00:47 +0300 Subject: [PATCH 08/13] Use same cache for all backend builds --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5328d39..ce5a2be 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,6 +10,7 @@ check backend codestyle: image: edbizarro/gitlab-ci-pipeline-php:7.2-alpine stage: test cache: + key: backend-vendor paths: - vendor script: @@ -38,6 +39,7 @@ test backend: MYSQL_PASSWORD: "ely_accounts_tester_password" stage: test cache: + key: backend-vendor paths: - vendor before_script: From 262142a835f057de8a3fdf2c2bb8c451a8926c9a Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 11 Jul 2018 01:38:48 +0300 Subject: [PATCH 09/13] Disable csrf cookies for api --- api/config/config.php | 1 + 1 file changed, 1 insertion(+) diff --git a/api/config/config.php b/api/config/config.php index 5637f01..ffe0ee2 100644 --- a/api/config/config.php +++ b/api/config/config.php @@ -58,6 +58,7 @@ return [ ], 'request' => [ 'baseUrl' => '/api', + 'enableCsrfCookie' => false, 'parsers' => [ '*' => api\request\RequestParser::class, ], From 069e4876dde245d432b6e4fb00fdc700c21a37a9 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 11 Jul 2018 02:23:46 +0300 Subject: [PATCH 10/13] Restore xdebug configuration --- docker/php/xdebug.ini | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 docker/php/xdebug.ini diff --git a/docker/php/xdebug.ini b/docker/php/xdebug.ini new file mode 100644 index 0000000..431323d --- /dev/null +++ b/docker/php/xdebug.ini @@ -0,0 +1,11 @@ +xdebug.default_enable=1 +xdebug.remote_enable=1 +xdebug.remote_handler=dbgp +xdebug.remote_mode=req +xdebug.remote_autostart=1 +xdebug.remote_port=9000 +xdebug.remote_connect_back=0 +xdebug.cli_color=1 +xdebug.var_display_max_depth=10 +xdebug.profiler_enable_trigger=1 +xdebug.profiler_output_dir=/tmp/xdebug-profiler From 92ee2830393314c8fa97c4593bef323ed81ffea7 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 11 Jul 2018 02:24:05 +0300 Subject: [PATCH 11/13] Upgrade tests environment --- tests/.gitignore | 1 + tests/docker-compose.yml | 5 +++-- tests/php.sh | 6 ------ tests/run-tests.sh | 6 ------ 4 files changed, 4 insertions(+), 14 deletions(-) create mode 100644 tests/.gitignore delete mode 100755 tests/php.sh delete mode 100755 tests/run-tests.sh diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..c40b483 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +.bash_history diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index 42c486d..5a8fb5b 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -2,10 +2,11 @@ version: '2' services: testphp: container_name: accountelyby_testphp - image: account_testphp + image: registry.ely.by/elyby/accounts:dev build: context: ../ - dockerfile: Dockerfile-dev + args: + build_env: dev depends_on: - testdb - testredis diff --git a/tests/php.sh b/tests/php.sh deleted file mode 100755 index c6a53a2..0000000 --- a/tests/php.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -cd "$(dirname "$0")" - -./../vendor/bin/codecept build -./../vendor/bin/codecept run $* diff --git a/tests/run-tests.sh b/tests/run-tests.sh deleted file mode 100755 index 7ff68f2..0000000 --- a/tests/run-tests.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -cd "$(dirname "$0")" - -docker-compose run --rm testphp ./tests/php.sh $* -docker-compose stop # docker не останавливает зависимые контейнеры после завершения работы главного процесса From 903f453afa3786a4a96ac461a394df42bdb89483 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Wed, 11 Jul 2018 02:24:15 +0300 Subject: [PATCH 12/13] Fix RegisterCest --- .../api/functional/RegisterCest.php | 523 ++++++++++-------- 1 file changed, 282 insertions(+), 241 deletions(-) diff --git a/tests/codeception/api/functional/RegisterCest.php b/tests/codeception/api/functional/RegisterCest.php index 4730d8f..c4ee698 100644 --- a/tests/codeception/api/functional/RegisterCest.php +++ b/tests/codeception/api/functional/RegisterCest.php @@ -1,258 +1,299 @@ 'erickskrauch@ely.by', - 'username' => 'ErickSkrauch', - ]); + /** + * @var SignupRoute + */ + private $route; + + public function _before(FunctionalTester $I) { + $this->route = new SignupRoute($I); } - public function testIncorrectRegistration(FunctionalTester $I) { - $route = new SignupRoute($I); - - $I->wantTo('get error.rulesAgreement_required if we don\'t accept rules'); - $route->register([ - 'username' => 'ErickSkrauch', - 'email' => 'erickskrauch@ely.by', - 'password' => 'some_password', - 'rePassword' => 'some_password', - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'rulesAgreement' => 'error.rulesAgreement_required', - ], - ]); - - $I->wantTo('don\'t see error.rulesAgreement_requireds if we accept rules'); - $route->register([ - 'rulesAgreement' => true, - ]); - $I->cantSeeResponseContainsJson([ - 'errors' => [ - 'rulesAgreement' => 'error.rulesAgreement_required', - ], - ]); - - $I->wantTo('see error.username_required if username is not set'); - $route->register([ - 'username' => '', - 'email' => '', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'username' => 'error.username_required', - ], - ]); - - $I->wantTo('don\'t see error.username_required if username is not set'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => '', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->cantSeeResponseContainsJson([ - 'errors' => [ - 'username' => 'error.username_required', - ], - ]); - - $I->wantTo('see error.email_required if email is not set'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => '', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'email' => 'error.email_required', - ], - ]); - - $I->wantTo('see error.email_invalid if email is set, but invalid'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'invalid@email', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'email' => 'error.email_invalid', - ], - ]); - - $I->wantTo('see error.email_invalid if email is set, valid, but domain doesn\'t exist or don\'t have mx record'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'invalid@govnomail.com', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'email' => 'error.email_invalid', - ], - ]); - - $I->wantTo('see error.email_not_available if email is set, fully valid, but not available for registration'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'admin@ely.by', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'email' => 'error.email_not_available', - ], - ]); - - $I->wantTo('don\'t see errors on email if all valid'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'erickskrauch@ely.by', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->cantSeeResponseJsonMatchesJsonPath('$.errors.email'); - - $I->wantTo('see error.password_required if password is not set'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'erickskrauch@ely.by', - 'password' => '', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'password' => 'error.password_required', - ], - ]); - - $I->wantTo('see error.password_too_short before it will be compared with rePassword'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'correct-email@ely.by', - 'password' => 'short', - 'rePassword' => 'password', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'password' => 'error.password_too_short', - ], - ]); - $I->cantSeeResponseJsonMatchesJsonPath('$.errors.rePassword'); - - $I->wantTo('see error.rePassword_required if password valid and rePassword not set'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'correct-email@ely.by', - 'password' => 'valid-password', - 'rePassword' => '', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'rePassword' => 'error.rePassword_required', - ], - ]); - - $I->wantTo('see error.rePassword_does_not_match if password valid and rePassword donen\'t match it'); - $route->register([ - 'username' => 'valid_nickname', - 'email' => 'correct-email@ely.by', - 'password' => 'valid-password', - 'rePassword' => 'password', - 'rulesAgreement' => true, - ]); - $I->canSeeResponseContainsJson([ - 'success' => false, - 'errors' => [ - 'rePassword' => 'error.rePassword_does_not_match', - ], - ]); - $I->cantSeeResponseJsonMatchesJsonPath('$.errors.password'); - } - - public function testUserCorrectRegistration(FunctionalTester $I) { - $route = new SignupRoute($I); - - $I->wantTo('ensure that signup works'); - $route->register([ - 'username' => 'some_username', - 'email' => 'some_email@example.com', - 'password' => 'some_password', - 'rePassword' => 'some_password', - 'rulesAgreement' => true, - 'lang' => 'ru', - ]); - $this->assertSuccessRegistration($I); - } - - public function testUserCorrectRegistrationWithReassignUsername(FunctionalTester $I) { - $route = new SignupRoute($I); - - $I->wantTo('ensure that signup allow reassign not finished registration username'); - $route->register([ - 'username' => 'howe.garnett', - 'email' => 'custom-email@gmail.com', - 'password' => 'some_password', - 'rePassword' => 'some_password', - 'rulesAgreement' => true, - 'lang' => 'ru', - ]); - $this->assertSuccessRegistration($I); - } - - public function testUserCorrectRegistrationWithReassignEmail(FunctionalTester $I) { - $route = new SignupRoute($I); - - $I->wantTo('ensure that signup allow reassign not finished registration email'); - $route->register([ - 'username' => 'CustomUsername', - 'email' => 'achristiansen@gmail.com', - 'password' => 'some_password', - 'rePassword' => 'some_password', - 'rulesAgreement' => true, - 'lang' => 'ru', - ]); - $this->assertSuccessRegistration($I); - } - - private function assertSuccessRegistration(FunctionalTester $I) { + /** + * @dataProvider getSuccessInputExamples + */ + public function testUserCorrectRegistration(FunctionalTester $I, Example $example) { + $I->wantTo($example->offsetGet('case')); + $this->route->register($example->offsetGet('request')); $I->canSeeResponseCodeIs(200); $I->canSeeResponseIsJson(); $I->canSeeResponseContainsJson(['success' => true]); $I->cantSeeResponseJsonMatchesJsonPath('$.errors'); } + /** + * @dataProvider getInvalidInputExamples + */ + public function testIncorrectRegistration(FunctionalTester $I, Example $example) { + $I->wantTo($example->offsetGet('case')); + $this->route->register($example->offsetGet('request')); + if ($example->offsetExists('canSee')) { + $I->canSeeResponseContainsJson($example->offsetGet('canSee')); + } + + if ($example->offsetExists('cantSee')) { + $I->cantSeeResponseContainsJson($example->offsetGet('cantSee')); + } + + if ($example->offsetExists('shouldNotMatch')) { + foreach ((array)$example->offsetGet('shouldNotMatch') as $jsonPath) { + $I->cantSeeResponseJsonMatchesJsonPath($jsonPath); + } + } + } + + protected function getSuccessInputExamples(): array { + return [ + [ + 'case' => 'ensure that signup works', + 'request' => [ + 'username' => 'some_username', + 'email' => 'some_email@example.com', + 'password' => 'some_password', + 'rePassword' => 'some_password', + 'rulesAgreement' => true, + 'lang' => 'ru', + ], + ], + [ + 'case' => 'ensure that signup allow reassign not finished registration username', + 'request' => [ + 'username' => 'howe.garnett', + 'email' => 'custom-email@gmail.com', + 'password' => 'some_password', + 'rePassword' => 'some_password', + 'rulesAgreement' => true, + 'lang' => 'ru', + ], + ], + [ + 'case' => 'ensure that signup allow reassign not finished registration email', + 'request' => [ + 'username' => 'CustomUsername', + 'email' => 'achristiansen@gmail.com', + 'password' => 'some_password', + 'rePassword' => 'some_password', + 'rulesAgreement' => true, + 'lang' => 'ru', + ], + ], + ]; + } + + protected function getInvalidInputExamples(): array { + return [ + [ + 'case' => 'get error.rulesAgreement_required if we don\'t accept rules', + 'request' => [ + 'username' => 'ErickSkrauch', + 'email' => 'erickskrauch@ely.by', + 'password' => 'some_password', + 'rePassword' => 'some_password', + ], + 'canSee' => [ + 'success' => false, + 'errors' => [ + 'rulesAgreement' => 'error.rulesAgreement_required', + ], + ], + ], + [ + 'case' => 'don\'t see error.rulesAgreement_requireds if we accept rules', + 'request' => [ + 'rulesAgreement' => true, + ], + 'cantSee' => [ + 'errors' => [ + 'rulesAgreement' => 'error.rulesAgreement_required', + ], + ], + ], + [ + 'case' => 'see error.username_required if username is not set', + 'request' => [ + 'username' => '', + 'email' => '', + 'password' => '', + 'rePassword' => '', + 'rulesAgreement' => true, + ], + 'canSee' => [ + 'success' => false, + 'errors' => [ + 'username' => 'error.username_required', + ], + ], + ], + [ + 'case' => 'don\'t see error.username_required if username is not set', + 'request' => [ + 'username' => 'valid_nickname', + 'email' => '', + 'password' => '', + 'rePassword' => '', + 'rulesAgreement' => true, + ], + 'cantSee' => [ + 'errors' => [ + 'username' => 'error.username_required', + ], + ], + ], + [ + 'case' => 'see error.email_required if email is not set', + 'request' => [ + 'username' => 'valid_nickname', + 'email' => '', + 'password' => '', + 'rePassword' => '', + 'rulesAgreement' => true, + ], + 'canSee' => [ + 'success' => false, + 'errors' => [ + 'email' => 'error.email_required', + ], + ], + ], + [ + 'case' => 'see error.email_invalid if email is set, but invalid', + 'request' => [ + 'username' => 'valid_nickname', + 'email' => 'invalid@email', + 'password' => '', + 'rePassword' => '', + 'rulesAgreement' => true, + ], + 'canSee' => [ + 'success' => false, + 'errors' => [ + 'email' => 'error.email_invalid', + ], + ], + ], + [ + 'case' => 'see error.email_invalid if email is set, valid, but domain doesn\'t exist or don\'t have mx record', + 'request' => [ + 'username' => 'valid_nickname', + 'email' => 'invalid@this-should-be-really-no-exists-domain-63efd7ab-1529-46d5-9426-fa5ed9f710e6.com', + 'password' => '', + 'rePassword' => '', + 'rulesAgreement' => true, + ], + 'canSee' => [ + 'success' => false, + 'errors' => [ + 'email' => 'error.email_invalid', + ], + ], + ], + [ + 'case' => 'see error.email_not_available if email is set, fully valid, but not available for registration', + 'request' => [ + 'username' => 'valid_nickname', + 'email' => 'admin@ely.by', + 'password' => '', + 'rePassword' => '', + 'rulesAgreement' => true, + ], + 'canSee' => [ + 'success' => false, + 'errors' => [ + 'email' => 'error.email_not_available', + ], + ], + ], + [ + 'case' => 'don\'t see errors on email if email valid', + 'request' => [ + 'username' => 'valid_nickname', + 'email' => 'erickskrauch@ely.by', + 'password' => '', + 'rePassword' => '', + 'rulesAgreement' => true, + ], + 'shouldNotMatch' => [ + '$.errors.email', + ], + ], + [ + 'case' => 'see error.password_required if password is not set', + 'request' => [ + 'username' => 'valid_nickname', + 'email' => 'erickskrauch@ely.by', + 'password' => '', + 'rePassword' => '', + 'rulesAgreement' => true, + ], + 'canSee' => [ + 'success' => false, + 'errors' => [ + 'password' => 'error.password_required', + ], + ], + ], + [ + 'case' => 'see error.password_too_short before it will be compared with rePassword', + 'request' => [ + 'username' => 'valid_nickname', + 'email' => 'correct-email@ely.by', + 'password' => 'short', + 'rePassword' => 'password', + 'rulesAgreement' => true, + ], + 'canSee' => [ + 'success' => false, + 'errors' => [ + 'password' => 'error.password_too_short', + ], + ], + 'shouldNotMatch' => [ + '$.errors.rePassword', + ], + ], + [ + 'case' => 'see error.rePassword_required if password valid and rePassword not set', + 'request' => [ + 'username' => 'valid_nickname', + 'email' => 'correct-email@ely.by', + 'password' => 'valid-password', + 'rePassword' => '', + 'rulesAgreement' => true, + ], + 'canSee' => [ + 'success' => false, + 'errors' => [ + 'rePassword' => 'error.rePassword_required', + ], + ], + ], + [ + 'case' => 'see error.rePassword_does_not_match if password valid and rePassword doesn\'t match it', + 'request' => [ + 'username' => 'valid_nickname', + 'email' => 'correct-email@ely.by', + 'password' => 'valid-password', + 'rePassword' => 'password', + 'rulesAgreement' => true, + ], + 'canSee' => [ + 'success' => false, + 'errors' => [ + 'rePassword' => 'error.rePassword_does_not_match', + ], + ], + 'shouldNotMatch' => [ + '$.errors.password', + ], + ], + ]; + } + } From 9620ff75eb67fb69fcaa31c3c7d9054f19d3e467 Mon Sep 17 00:00:00 2001 From: ErickSkrauch Date: Fri, 26 Oct 2018 16:45:05 +0300 Subject: [PATCH 13/13] Update glibc installation in gitlab-ci --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ce5a2be..6fda076 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -60,10 +60,10 @@ test frontend: before_script: # Enable SSL support for wget - apk add --update openssl - # https://github.com/facebook/flow/issues/3649#issuecomment-308070179 - - wget -O /etc/apk/keys/sgerrand.rsa.pub https://raw.githubusercontent.com/sgerrand/alpine-pkg-glibc/master/sgerrand.rsa.pub - - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.25-r0/glibc-2.25-r0.apk - - apk add glibc-2.25-r0.apk + # https://github.com/facebook/flow/issues/3649#issuecomment-414691014 + - wget -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub + - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk + - apk add glibc-2.28-r0.apk script: - cd frontend - yarn run build:install