Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Adds machine token commands for 1.0 #1182

Merged
merged 10 commits into from Sep 19, 2016
1 change: 1 addition & 0 deletions php/Terminus/Exceptions/TerminusException.php
Expand Up @@ -30,6 +30,7 @@ public function __construct(
$code = 0
) {
$this->replacements = $replacements;

parent::__construct($message, $code);
}

Expand Down
16 changes: 15 additions & 1 deletion src/Commands/MachineToken/DeleteCommand.php
Expand Up @@ -3,6 +3,7 @@
namespace Pantheon\Terminus\Commands\MachineToken;

use Pantheon\Terminus\Commands\TerminusCommand;
use Terminus\Exceptions\TerminusException;

class DeleteCommand extends TerminusCommand
{
Expand All @@ -15,14 +16,27 @@ 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 <machine_token_id>
* 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);
Copy link
Contributor Author

@ronan ronan Sep 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I'm letting the exception thrown by get() fall through and get caught by the runner. This works fine (and saves a few lines of code) but the Robo runner isn't familiar with our translatable exceptions. That means the output when there is an error here is:

[error] Could not find {model} "{id}"

We have 3 options:

  1. Catch and rethrow here with a regular, untranslatable exception
  2. Override the runner to output a "translated" version of the exception error.
  3. Change TerminusException to do the replacement itself so that getMessage() always returns a valid error message

I favor #3. We'll have to revisit it a bit when we actually start looking at translating error messages, but it should be backwards compatible with the old Terminus runner and forwards compatible with the new one (while not prematurely interpolating error strings).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also favor approach approach 3.

$name = $machine_token->get('device_name');

$this->log()->notice('Deleting {token} ...', ['token' => $name]);
$response = $machine_token->delete();
if ($response['status_code'] == 200) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should be leaking the response status code out of the model here. I'd rather have delete() throw an exception (which the command could simply let fall through to the runner for simplicity). That change will probably break the old stuff though so I don't know if I should proceed or leave it as is and try and clean it up later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By "break the old stuff" I mean "require a change to the old stuff" I woudn't leave the existing code in a broken state obviously

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This status-code check must've persisted for some time because it's not necessary anymore. We can just remove it.

$this->log()->notice('Deleted {token}!', ['token' => $name]);
} else {
throw new TerminusException('There was an problem deleting the machine token.');
}
}

}
22 changes: 21 additions & 1 deletion src/Commands/MachineToken/ListCommand.php
Expand Up @@ -3,6 +3,8 @@
namespace Pantheon\Terminus\Commands\MachineToken;

use Pantheon\Terminus\Commands\TerminusCommand;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;


class ListCommand extends TerminusCommand
{
Expand All @@ -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' => '']) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

format and fields should be global options ala --yes. I can add that once #1179 is merged

$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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yay!

}

}
23 changes: 23 additions & 0 deletions 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]]"
Then I should get:
"""
Deleted [[machine_token_device]]!
"""
@@ -0,0 +1,51 @@
<?php
/**
* @file
* Contains Pantheon\Terminus\UnitTests\Commands\Auth\MachineTokenCommandTest
*/


namespace Pantheon\Terminus\UnitTests\Commands\Auth;


use Pantheon\Terminus\Session\Session;
use Psr\Log\NullLogger;
use Terminus\Collections\MachineTokens;
use Terminus\Models\User;

abstract class MachineTokenCommandTest extends \PHPUnit_Framework_TestCase {
protected $session;
protected $machine_tokens;
protected $user;
protected $logger;
protected $command;

/**
* Sets up the fixture, for example, open a network connection.
* This method is called before a test is executed.
*/
protected function setUp()
{
$this->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();


}
}
@@ -0,0 +1,111 @@
<?php
namespace Pantheon\Terminus\UnitTests\Commands\Auth;
use Pantheon\Terminus\Commands\MachineToken\DeleteCommand;
use Pantheon\Terminus\Config;
use Terminus\Exceptions\TerminusException;
use Terminus\Models\MachineToken;

/**
* Testing class for Pantheon\Terminus\Commands\Auth\LoginCommand
*/
class MachineTokenDeleteCommandTest extends MachineTokenCommandTest
{

/**
* Sets up the fixture, for example, open a network connection.
* This method is called before a test is executed.
*/
protected function setUp()
{
parent::setUp();

$this->command = new DeleteCommand(new Config());
$this->command->setSession($this->session);
$this->command->setLogger($this->logger);
}


/**
* 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'))
->will($this->throwException(new TerminusException));


$this->setExpectedException(TerminusException::class);

$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');
}
}
@@ -0,0 +1,71 @@
<?php
namespace Pantheon\Terminus\UnitTests\Commands\Auth;
use Pantheon\Terminus\Commands\MachineToken\ListCommand;
use Pantheon\Terminus\Config;
use Terminus\Collections\MachineTokens;
use Terminus\Models\MachineToken;

/**
* Testing class for Pantheon\Terminus\Commands\Auth\LoginCommand
*/
class MachineTokensListCommandTest extends MachineTokenCommandTest
{
/**
* Sets up the fixture, for example, open a network connection.
* This method is called before a test is executed.
*/
protected function setUp()
{
parent::setUp();

$this->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());
}

}