From cab83b10ac04c1e4fde586edacde7b95b7d9c541 Mon Sep 17 00:00:00 2001 From: Ronan Dowling Date: Fri, 9 Sep 2016 13:28:39 -0500 Subject: [PATCH] Adds machine token commands for 1.0 --- bin/terminus.php | 9 ++ src/Commands/MachineToken/DeleteCommand.php | 22 +++- src/Commands/MachineToken/ListCommand.php | 22 +++- src/Commands/TerminusCommand.php | 5 +- src/Session/Session.php | 106 +++++++++++++++ src/Session/SessionAwareInterface.php | 25 ++++ src/Session/SessionAwareTrait.php | 38 ++++++ tests/active_features/machine-token.feature | 23 ++++ .../MachineToken/MachineTokenCommandTest.php | 51 ++++++++ .../MachineTokensDeleteCommandTest.php | 122 ++++++++++++++++++ .../MachineTokensListCommandTest.php | 71 ++++++++++ 11 files changed, 491 insertions(+), 3 deletions(-) create mode 100644 src/Session/Session.php create mode 100644 src/Session/SessionAwareInterface.php create mode 100644 src/Session/SessionAwareTrait.php create mode 100644 tests/active_features/machine-token.feature create mode 100644 tests/new_unit_tests/Commands/MachineToken/MachineTokenCommandTest.php create mode 100644 tests/new_unit_tests/Commands/MachineToken/MachineTokensDeleteCommandTest.php create mode 100644 tests/new_unit_tests/Commands/MachineToken/MachineTokensListCommandTest.php diff --git a/bin/terminus.php b/bin/terminus.php index 227f2de64..5d1eb902e 100755 --- a/bin/terminus.php +++ b/bin/terminus.php @@ -15,10 +15,13 @@ use League\Container\Container; use Pantheon\Terminus\Config; use Pantheon\Terminus\Runner; +use Pantheon\Terminus\Session\Session; +use Pantheon\Terminus\Session\SessionAwareInterface; use Pantheon\Terminus\Terminus; use Robo\Robo; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Output\ConsoleOutput; +use Terminus\Caches\FileCache; // Initializing the Terminus application $config = new Config(); @@ -31,6 +34,12 @@ $roboConfig = new \Robo\Config(); // TODO: make Terminus Config extend \Robo\Config and use $config here Robo::configureContainer($container, $roboConfig, $input, $output, $application); +$container->share('fileCache', FileCache::class); +$container->share('session', Session::class) + ->withArgument('fileCache'); +$container->inflector(SessionAwareInterface::class) + ->invokeMethod('setSession', ['session']); + // Running Terminus $runner = new Runner($container); $status_code = $runner->run($input, $output); diff --git a/src/Commands/MachineToken/DeleteCommand.php b/src/Commands/MachineToken/DeleteCommand.php index 39ac448de..ff4eac540 100644 --- a/src/Commands/MachineToken/DeleteCommand.php +++ b/src/Commands/MachineToken/DeleteCommand.php @@ -3,6 +3,7 @@ namespace Pantheon\Terminus\Commands\MachineToken; use Pantheon\Terminus\Commands\TerminusCommand; +use Terminus\Exceptions\TerminusException; class DeleteCommand extends TerminusCommand { @@ -15,14 +16,33 @@ class DeleteCommand extends TerminusCommand * Removes a machine token from the logged-in user's account * * @name machine-token:delete + * @param string $machine_token_id The ID of the machine token to be deleted + * @throws \Terminus\Exceptions\TerminusException * @aliases mt:delete * - * @param machine_token_id The ID of the machine token to be deleted * @usage terminus machine-token:delete * Removes the given machine token from the user's account */ public function delete($machine_token_id) { + $user = $this->session()->getUser(); + // Find the token. Will throw an exception if it doesn't exist. + $machine_token = $user->machine_tokens->get($machine_token_id); + if (empty($machine_token)) { + throw new TerminusException('There are no machine tokens with the id {id}.', compact($machine_token_id)); + } + $name = $machine_token->get('device_name'); + + // Confirm the delete. + $this->confirm(sprintf('Are you sure you want to delete "%s"?', $name)); + + $this->log()->info('Deleting {name} ...', ['name' => $name]); + $response = $machine_token->delete(); + if ($response['status_code'] == 200) { + $this->log()->info('Deleted {name}!', ['name' => $name]); + } else { + throw new TerminusException('There was an problem deleting the machine token.'); + } } } diff --git a/src/Commands/MachineToken/ListCommand.php b/src/Commands/MachineToken/ListCommand.php index 049b325a3..7648bfd76 100644 --- a/src/Commands/MachineToken/ListCommand.php +++ b/src/Commands/MachineToken/ListCommand.php @@ -3,6 +3,8 @@ namespace Pantheon\Terminus\Commands\MachineToken; use Pantheon\Terminus\Commands\TerminusCommand; +use Consolidation\OutputFormatters\StructuredData\RowsOfFields; + class ListCommand extends TerminusCommand { @@ -19,9 +21,27 @@ class ListCommand extends TerminusCommand * * @usage terminus machine-token:list * Lists your user's machine tokens + * + * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields */ - public function listTokens() { + public function listTokens($options = ['format' => 'table', 'fields' => '']) { + $user = $this->session()->getUser(); + + $machine_tokens = $user->machine_tokens->all(); + $data = array(); + foreach ($machine_tokens as $id => $machine_token) { + $data[] = array( + 'id' => $machine_token->id, + 'device_name' => $machine_token->get('device_name'), + ); + } + + if (count($data) == 0) { + $this->log()->warning('You have no machine tokens.'); + } + // Return the output data. + return new RowsOfFields($data); } } diff --git a/src/Commands/TerminusCommand.php b/src/Commands/TerminusCommand.php index 75fe1dd12..a21b11878 100644 --- a/src/Commands/TerminusCommand.php +++ b/src/Commands/TerminusCommand.php @@ -3,6 +3,8 @@ namespace Pantheon\Terminus\Commands; use Pantheon\Terminus\Config; +use Pantheon\Terminus\Session\SessionAwareInterface; +use Pantheon\Terminus\Session\SessionAwareTrait; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use Psr\Log\LoggerInterface; @@ -12,11 +14,12 @@ use Robo\Common\IO; use Terminus\Models\Auth; -abstract class TerminusCommand implements IOAwareInterface, LoggerAwareInterface, ConfigAwareInterface +abstract class TerminusCommand implements IOAwareInterface, LoggerAwareInterface, ConfigAwareInterface, SessionAwareInterface { use LoggerAwareTrait; use ConfigAwareTrait; use IO; + use SessionAwareTrait; /** * @var boolean True if the command requires the user to be logged in diff --git a/src/Session/Session.php b/src/Session/Session.php new file mode 100644 index 000000000..0d41e1f64 --- /dev/null +++ b/src/Session/Session.php @@ -0,0 +1,106 @@ +cache = $fileCache; + $session = $this->cache->getData('session'); + $this->data = $session; + if (empty($session)) { + $this->data = new \stdClass(); + } + } + + /** + * Removes the session from the cache + * + * @return void + */ + public function destroy() { + $this->cache->remove('session'); + } + + /** + * Returns given data property or default if DNE. + * + * @param string $key Name of property to return + * @param mixed $default Default return value in case property DNE + * @return mixed + */ + public function get($key = 'session', $default = false) { + if (isset($this->data) && isset($this->data->$key)) { + return $this->data->$key; + } + return $default; + } + + /** + * Retrieves session data + * + * @return object + */ + public function getData() { + return $this->data; + } + + /** + * Sets a keyed value to be part of the data property object + * + * @param string $key Name of data property + * @param mixed $value Value of property to set + * @return Session + */ + public function set($key, $value = null) { + $this->data->$key = $value; + return $this; + } + + /** + * Saves session data to cache + * + * @param array $data Session data to save + * @return bool + */ + public function setData($data) { + if (empty($data)) { + return false; + } + $cache = new FileCache(); + $cache->putData('session', $data); + + $this->data->set('data', $data); + foreach ($data as $k => $v) { + $this->set($k, $v); + } + return true; + } + + /** + * Returns a user with the current session user id + + * @return \Terminus\Models\User [user] $session user + */ + public function getUser() { + $user_uuid = $this->get('user_uuid'); + $user = new User((object)array('id' => $user_uuid)); + return $user; + } + +} diff --git a/src/Session/SessionAwareInterface.php b/src/Session/SessionAwareInterface.php new file mode 100644 index 000000000..932f8e868 --- /dev/null +++ b/src/Session/SessionAwareInterface.php @@ -0,0 +1,25 @@ +session = $session; + + return $this; + } + + /** + * @inheritdoc + */ + public function session() + { + return $this->session; + } + + +} diff --git a/tests/active_features/machine-token.feature b/tests/active_features/machine-token.feature new file mode 100644 index 000000000..f2bc5e352 --- /dev/null +++ b/tests/active_features/machine-token.feature @@ -0,0 +1,23 @@ +Feature: Machine tokens command + In order to manage my devices + As a user + I need to be able to view and delete my machine tokens. + + Background: I am logged in + Given I am authenticated + + @vcr machine-tokens_list + Scenario: List the machine tokens + When I run "terminus machine-token:list" + Then I should get: + """ + [[machine_token_id]] + """ + + @vcr machine-tokens_delete + Scenario: Delete machine token + When I run "terminus machine-token:delete [[machine_token_id]] --yes" + Then I should get: + """ + Deleted [[machine_token_device]]! + """ diff --git a/tests/new_unit_tests/Commands/MachineToken/MachineTokenCommandTest.php b/tests/new_unit_tests/Commands/MachineToken/MachineTokenCommandTest.php new file mode 100644 index 000000000..ea89ebf28 --- /dev/null +++ b/tests/new_unit_tests/Commands/MachineToken/MachineTokenCommandTest.php @@ -0,0 +1,51 @@ +machine_tokens = $this->getMockBuilder(MachineTokens::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->user = $this->getMockBuilder(User::class) + ->disableOriginalConstructor() + ->getMock(); + $this->user->machine_tokens = $this->machine_tokens; + + $this->session = $this->getMockBuilder(Session::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->session->method('getUser') + ->willReturn($this->user); + + $this->logger = $this->getMockBuilder(NullLogger::class) + ->setMethods(array('log')) + ->getMock(); + + + } +} diff --git a/tests/new_unit_tests/Commands/MachineToken/MachineTokensDeleteCommandTest.php b/tests/new_unit_tests/Commands/MachineToken/MachineTokensDeleteCommandTest.php new file mode 100644 index 000000000..00ed5d167 --- /dev/null +++ b/tests/new_unit_tests/Commands/MachineToken/MachineTokensDeleteCommandTest.php @@ -0,0 +1,122 @@ +command = new DeleteCommand(new Config()); + $this->command->setSession($this->session); + $this->command->setLogger($this->logger); + + // Ignore user input. + // TODO: Figure out how to test for the ask prompt. + $input = new ArgvInput(); + $input->setInteractive(false); + $this->command->setInput($input); + } + + + /** + * Tests the machine-token:delete command. + * + * @return void + */ + public function testMachineTokenDelete() + { + $token = $this->getMockBuilder(MachineToken::class) + ->disableOriginalConstructor() + ->getMock(); + + $token->expects($this->once()) + ->method('delete') + ->willReturn( + ['status_code' => 200] + ); + + + $this->machine_tokens->expects($this->once()) + ->method('get') + ->with($this->equalTo('123')) + ->willReturn( + $token + ); + + + $this->command->delete('123'); + } + + /** + * Tests the machine-token:delete command when there are no tokens. + * + * @return void + */ + public function testMachineTokenDeleteNonExistant() + { + $token = $this->getMockBuilder(MachineToken::class) + ->disableOriginalConstructor() + ->getMock(); + + $token->expects($this->never()) + ->method('delete'); + + $this->machine_tokens->expects($this->once()) + ->method('get') + ->with($this->equalTo('123')) + ->willReturn(null); + + $this->setExpectedException(\Exception::class, 'There are no machine tokens with the id {id}.'); + + $this->command->delete('123'); + } + + /** + * Tests the machine-token:delete command when the API fails. + * + * @return void + */ + public function testMachineTokenDeleteAPIFailure() + { + $token = $this->getMockBuilder(MachineToken::class) + ->disableOriginalConstructor() + ->getMock(); + + $token->expects($this->once()) + ->method('delete') + ->willReturn( + ['status_code' => 500] + ); + + $this->machine_tokens->expects($this->once()) + ->method('get') + ->with($this->equalTo('123')) + ->willReturn( + $token + ); + + $this->setExpectedException(\Exception::class, 'There was an problem deleting the machine token.'); + + $this->command->delete('123'); + } +} diff --git a/tests/new_unit_tests/Commands/MachineToken/MachineTokensListCommandTest.php b/tests/new_unit_tests/Commands/MachineToken/MachineTokensListCommandTest.php new file mode 100644 index 000000000..750570acc --- /dev/null +++ b/tests/new_unit_tests/Commands/MachineToken/MachineTokensListCommandTest.php @@ -0,0 +1,71 @@ +command = new ListCommand(new Config()); + $this->command->setSession($this->session); + $this->command->setLogger($this->logger); + } + + /** + * Tests the machine-token:list command when there are no tokens. + * + * @return void + */ + public function testMachineTokenListEmpty() + { + $this->machine_tokens->method('all') + ->willReturn([]); + + $this->logger->expects($this->once()) + ->method('log') + ->with($this->equalTo('warning'), $this->equalTo('You have no machine tokens.')); + + $out = $this->command->listTokens(); + $this->assertInstanceOf('Consolidation\OutputFormatters\StructuredData\RowsOfFields', $out); + $this->assertEquals([], $out->getArrayCopy()); + } + + /** + * Tests the machine-token:list command when there are tokens. + * + * @return void + */ + public function testMachineTokenListNotEmpty() + { + $tokens = [ + ['id' => '1', 'device_name' => 'Foo'], + ['id' => '2', 'device_name' => 'Bar'] + ]; + $collection = new MachineTokens(['user' => $this->user]); + $this->machine_tokens->method('all') + ->willReturn([ + new MachineToken((object)$tokens[0], ['collection' => $collection]), + new MachineToken((object)$tokens[1], ['collection' => $collection]) + ]); + + $this->logger->expects($this->never()) + ->method($this->anything()); + + $out = $this->command->listTokens(); + $this->assertInstanceOf('Consolidation\OutputFormatters\StructuredData\RowsOfFields', $out); + $this->assertEquals($tokens, $out->getArrayCopy()); + } + +}