diff --git a/api/modules/internal/controllers/AccountsController.php b/api/modules/internal/controllers/AccountsController.php index e666a0c..34f4dbe 100644 --- a/api/modules/internal/controllers/AccountsController.php +++ b/api/modules/internal/controllers/AccountsController.php @@ -4,6 +4,7 @@ namespace api\modules\internal\controllers; use api\components\ApiUser\AccessControl; use api\controllers\Controller; use api\modules\internal\models\BanForm; +use api\modules\internal\models\PardonForm; use common\models\Account; use common\models\OauthScope as S; use Yii; @@ -30,8 +31,22 @@ class AccountsController extends Controller { ]); } + public function verbs() { + return [ + 'ban' => ['POST', 'DELETE'], + ]; + } + public function actionBan(int $accountId) { $account = $this->findAccount($accountId); + if (Yii::$app->request->isPost) { + return $this->banAccount($account); + } else { + return $this->pardonAccount($account); + } + } + + private function banAccount(Account $account) { $model = new BanForm($account); $model->load(Yii::$app->request->post()); if (!$model->ban()) { @@ -46,6 +61,21 @@ class AccountsController extends Controller { ]; } + private function pardonAccount(Account $account) { + $model = new PardonForm($account); + $model->load(Yii::$app->request->post()); + if (!$model->pardon()) { + return [ + 'success' => false, + 'errors' => $model->getFirstErrors(), + ]; + } + + return [ + 'success' => true, + ]; + } + private function findAccount(int $accountId): Account { $account = Account::findOne($accountId); if ($account === null) { diff --git a/api/modules/internal/helpers/Error.php b/api/modules/internal/helpers/Error.php index 86b45b5..7a7dec6 100644 --- a/api/modules/internal/helpers/Error.php +++ b/api/modules/internal/helpers/Error.php @@ -4,5 +4,6 @@ namespace api\modules\internal\helpers; final class Error { public const ACCOUNT_ALREADY_BANNED = 'error.account_already_banned'; + public const ACCOUNT_NOT_BANNED = 'error.account_not_banned'; } diff --git a/api/modules/internal/models/PardonForm.php b/api/modules/internal/models/PardonForm.php new file mode 100644 index 0000000..60c10e5 --- /dev/null +++ b/api/modules/internal/models/PardonForm.php @@ -0,0 +1,72 @@ +account; + } + + public function validateAccountBanned(): void { + if ($this->account->status !== Account::STATUS_BANNED) { + $this->addError('account', E::ACCOUNT_NOT_BANNED); + } + } + + public function pardon(): bool { + if (!$this->validate()) { + return false; + } + + $transaction = Yii::$app->db->beginTransaction(); + + $account = $this->account; + $account->status = Account::STATUS_ACTIVE; + if (!$account->save()) { + throw new ErrorException('Cannot pardon account'); + } + + $this->createTask(); + + $transaction->commit(); + + return true; + } + + public function createTask(): void { + $model = new AccountPardoned(); + $model->accountId = $this->account->id; + + $message = Amqp::getInstance()->prepareMessage($model, [ + 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT, + ]); + + Amqp::sendToEventsExchange('accounts.account-pardoned', $message); + } + + public function __construct(Account $account, array $config = []) { + $this->account = $account; + parent::__construct($config); + } + +} diff --git a/common/models/amqp/AccountPardoned.php b/common/models/amqp/AccountPardoned.php new file mode 100644 index 0000000..31599ed --- /dev/null +++ b/common/models/amqp/AccountPardoned.php @@ -0,0 +1,10 @@ +actor->sendPOST($this->getUrl()); } + public function pardon($accountId) { + $this->route = '/internal/accounts/' . $accountId . '/ban'; + $this->actor->sendDELETE($this->getUrl()); + } + } diff --git a/tests/codeception/api/functional/internal/PardonCest.php b/tests/codeception/api/functional/internal/PardonCest.php new file mode 100644 index 0000000..c7aea10 --- /dev/null +++ b/tests/codeception/api/functional/internal/PardonCest.php @@ -0,0 +1,47 @@ +route = new InternalRoute($I); + } + + public function testPardonAccount(OauthSteps $I) { + $accessToken = $I->getAccessTokenByClientCredentialsGrant([S::ACCOUNT_BLOCK]); + $I->amBearerAuthenticated($accessToken); + + $this->route->pardon(10); + $I->canSeeResponseCodeIs(200); + $I->canSeeResponseIsJson(); + $I->canSeeResponseContainsJson([ + 'success' => true, + ]); + } + + public function testPardonNotBannedAccount(OauthSteps $I) { + $accessToken = $I->getAccessTokenByClientCredentialsGrant([S::ACCOUNT_BLOCK]); + $I->amBearerAuthenticated($accessToken); + + $this->route->pardon(1); + $I->canSeeResponseCodeIs(200); + $I->canSeeResponseIsJson(); + $I->canSeeResponseContainsJson([ + 'success' => false, + 'errors' => [ + 'account' => 'error.account_not_banned', + ], + ]); + } + +} diff --git a/tests/codeception/api/unit/modules/internal/models/PardonFormTest.php b/tests/codeception/api/unit/modules/internal/models/PardonFormTest.php new file mode 100644 index 0000000..c05d9a0 --- /dev/null +++ b/tests/codeception/api/unit/modules/internal/models/PardonFormTest.php @@ -0,0 +1,52 @@ +status = Account::STATUS_BANNED; + $form = new PardonForm($account); + $form->validateAccountBanned(); + $this->assertEmpty($form->getErrors('account')); + + $account = new Account(); + $account->status = Account::STATUS_ACTIVE; + $form = new PardonForm($account); + $form->validateAccountBanned(); + $this->assertEquals([E::ACCOUNT_NOT_BANNED], $form->getErrors('account')); + } + + public function testPardon() { + /** @var Account|\PHPUnit_Framework_MockObject_MockObject $account */ + $account = $this->getMockBuilder(Account::class) + ->setMethods(['save']) + ->getMock(); + + $account->expects($this->once()) + ->method('save') + ->willReturn(true); + + $account->status = Account::STATUS_BANNED; + $model = new PardonForm($account); + $this->assertTrue($model->pardon()); + $this->assertEquals(Account::STATUS_ACTIVE, $account->status); + $this->tester->canSeeAmqpMessageIsCreated('events'); + } + + public function testCreateTask() { + $account = new Account(); + $account->id = 3; + + $model = new PardonForm($account); + $model->createTask(); + $message = json_decode($this->tester->grabLastSentAmqpMessage('events')->body, true); + $this->assertSame(3, $message['accountId']); + } + +}