Skip to content

Commit

Permalink
CLI-1325: [ide:delete] Accept IDE uuid as option (#1728)
Browse files Browse the repository at this point in the history
* CLI-1325: [ide:delete] Accept IDE uuid as option

* fix tests

* protected vis

* private viz

* test coverage
  • Loading branch information
danepowell authored Apr 17, 2024
1 parent 7a9469f commit cf14e26
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/mutation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
run: |
git fetch --depth=1 origin $GITHUB_BASE_REF
# Explicitly specify GitHub logger since our Stryker reporting will otherwise disable it.
php vendor/bin/infection --threads=max --git-diff-lines --git-diff-base=origin/$GITHUB_BASE_REF --logger-github --only-covered
php vendor/bin/infection --threads=max --git-diff-lines --git-diff-base=origin/$GITHUB_BASE_REF --logger-github --only-covered --min-covered-msi=100
- name: Run Infection for all files
if: github.event_name == 'push'
Expand Down
8 changes: 5 additions & 3 deletions .phpstorm.meta.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@
use AcquiaCloudApi\Response\DatabasesResponse;
use AcquiaCloudApi\Response\EnvironmentResponse;
use AcquiaCloudApi\Response\EnvironmentsResponse;
use AcquiaCloudApi\Response\IdeResponse;

override(\Acquia\Cli\Tests\TestBase::mockRequest(), map([
'getAccount' => AccountResponse::class,
'getApplications' => ApplicationsResponse::class,
'getApplicationByUuid' => ApplicationResponse::class,
'getApplicationEnvironments' => EnvironmentsResponse::class,
'getEnvironmentsDatabases' => DatabasesResponse::class,
'getEnvironment' => EnvironmentResponse::class,
'getCron' => CronResponse::class,
'getCronJobsByEnvironmentId' => CronsResponse::class
'getCronJobsByEnvironmentId' => CronsResponse::class,
'getEnvironment' => EnvironmentResponse::class,
'getEnvironmentsDatabases' => DatabasesResponse::class,
'getIde' => IdeResponse::class
]));

}
4 changes: 2 additions & 2 deletions src/Command/CommandBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -1085,10 +1085,10 @@ protected function requireCloudIdeEnvironment(): void {
/**
* @return \stdClass|null
*/
protected function findIdeSshKeyOnCloud(string $ideUuid): ?stdClass {
protected function findIdeSshKeyOnCloud(string $ideLabel, string $ideUuid): ?stdClass {
$acquiaCloudClient = $this->cloudApiClientService->getClient();
$cloudKeys = $acquiaCloudClient->request('get', '/account/ssh-keys');
$sshKeyLabel = SshKeyCommandBase::getIdeSshKeyLabel();
$sshKeyLabel = SshKeyCommandBase::getIdeSshKeyLabel($ideLabel, $ideUuid);
foreach ($cloudKeys as $cloudKey) {
if ($cloudKey->label === $sshKeyLabel) {
return $cloudKey;
Expand Down
2 changes: 1 addition & 1 deletion src/Command/Ide/IdeCommandBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ protected function promptIdeChoice(
string $questionText,
Ides $idesResource,
string $cloudApplicationUuid
): ?IdeResponse {
): IdeResponse {
$ides = iterator_to_array($idesResource->getAll($cloudApplicationUuid));
if (empty($ides)) {
throw new AcquiaCliException('No IDEs exist for this application.');
Expand Down
26 changes: 16 additions & 10 deletions src/Command/Ide/IdeDeleteCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

#[RequireAuth]
Expand All @@ -20,27 +21,32 @@ final class IdeDeleteCommand extends IdeCommandBase {

protected function configure(): void {
$this->acceptApplicationUuid();
// @todo Add option to accept an ide UUID.
// @todo make this an argument
$this->addOption('uuid', NULL, InputOption::VALUE_OPTIONAL, 'UUID of the IDE to delete');
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$acquiaCloudClient = $this->cloudApiClientService->getClient();
$idesResource = new Ides($acquiaCloudClient);

$cloudApplicationUuid = $this->determineCloudApplication();
$ide = $this->promptIdeChoice("Select the IDE you'd like to delete:", $idesResource, $cloudApplicationUuid);
$answer = $this->io->confirm("Are you sure you want to delete <options=bold>{$ide->label}</>");
if (!$answer) {
$this->io->writeln('Ok, nevermind.');
return 1;
$ideUuid = $input->getOption('uuid');
if ($ideUuid) {
$ide = $idesResource->get($ideUuid);
}
else {
$cloudApplicationUuid = $this->determineCloudApplication();
$ide = $this->promptIdeChoice("Select the IDE you'd like to delete:", $idesResource, $cloudApplicationUuid);
$answer = $this->io->confirm("Are you sure you want to delete <options=bold>$ide->label</>");
if (!$answer) {
$this->io->writeln('Ok, never mind.');
return Command::FAILURE;
}
}
$response = $idesResource->delete($ide->uuid);
$this->io->writeln($response->message);
// @todo Remove after CXAPI-8261 is closed.
$this->io->writeln("This process usually takes a few minutes.");

// Check to see if an SSH key for this IDE exists on Cloud.
$cloudKey = $this->findIdeSshKeyOnCloud($ide->uuid);
$cloudKey = $this->findIdeSshKeyOnCloud($ide->label, $ide->uuid);
if ($cloudKey) {
$answer = $this->io->confirm('Would you like to delete the SSH key associated with this IDE from your Cloud Platform account?');
if ($answer) {
Expand Down
4 changes: 2 additions & 2 deletions src/Command/Ide/Wizard/IdeWizardCommandBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ protected function validateEnvironment(): void {
}

protected function getSshKeyLabel(): string {
return $this::getIdeSshKeyLabel();
return $this::getIdeSshKeyLabel(self::getThisCloudIdeLabel(), self::getThisCloudIdeUuid());
}

protected function deleteThisSshKeyFromCloud(mixed $output): void {
if ($cloudKey = $this->findIdeSshKeyOnCloud($this::getThisCloudIdeUuid())) {
if ($cloudKey = $this->findIdeSshKeyOnCloud($this::getThisCloudIdeLabel(), $this::getThisCloudIdeUuid())) {
$this->deleteSshKeyFromCloud($output, $cloudKey);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Command/Ide/Wizard/IdeWizardDeleteSshKeyCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ protected function configure(): void {
protected function execute(InputInterface $input, OutputInterface $output): int {
$this->requireCloudIdeEnvironment();

$cloudKey = $this->findIdeSshKeyOnCloud($this::getThisCloudIdeUuid());
$cloudKey = $this->findIdeSshKeyOnCloud($this::getThisCloudIdeLabel(), $this::getThisCloudIdeUuid());
if (!$cloudKey) {
throw new AcquiaCliException('Could not find an SSH key on the Cloud Platform matching any local key in this IDE.');
}
Expand Down
8 changes: 4 additions & 4 deletions src/Command/Ssh/SshKeyCommandBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ protected function setSshKeyFilepath(string $privateSshKeyFilename): void {
$this->publicSshKeyFilepath = $this->privateSshKeyFilepath . '.pub';
}

public static function getIdeSshKeyLabel(): string {
return self::normalizeSshKeyLabel('IDE_' . self::getThisCloudIdeLabel() . '_' . self::getThisCloudIdeUuid());
protected static function getIdeSshKeyLabel(string $ideLabel, string $ideUuid): string {
return self::normalizeSshKeyLabel('IDE_' . $ideLabel . '_' . $ideUuid);
}

public static function normalizeSshKeyLabel(?string $label): string|null {
private static function normalizeSshKeyLabel(?string $label): string|null {
if (is_null($label)) {
throw new RuntimeException('The label cannot be empty');
}
// It may only contain letters, numbers and underscores.
return preg_replace('/[^A-Za-z0-9_]/', '', $label);
return preg_replace('/\W/', '', $label);
}

/**
Expand Down
50 changes: 45 additions & 5 deletions tests/phpunit/src/Commands/Ide/IdeDeleteCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,12 @@ protected function createCommand(): CommandBase {
return $this->injectCommand(IdeDeleteCommand::class);
}

/**
* @group brokenProphecy
*/
public function testIdeDeleteCommand(): void {
$applications = $this->mockRequest('getApplications');
$this->mockRequest('getApplicationByUuid', $applications[0]->uuid);
$ides = $this->mockRequest('getApplicationIdes', $applications[0]->uuid);
$this->mockRequest('deleteIde', $ides[0]->uuid, NULL, 'De-provisioning IDE');
$sshKeyGetResponse = $this->mockListSshKeysRequestWithIdeKey();
$sshKeyGetResponse = $this->mockListSshKeysRequestWithIdeKey($ides[0]->label, $ides[0]->uuid);

$this->mockDeleteSshKeyRequest($sshKeyGetResponse->{'_embedded'}->items[0]->uuid);

Expand All @@ -51,7 +48,7 @@ public function testIdeDeleteCommand(): void {
'y',
// Select the IDE you'd like to delete:
0,
// Would you like to delete the SSH key associated with this IDE from your Cloud Platform account?
// Are you sure you want to delete ExampleIDE?
'y',
];

Expand All @@ -62,4 +59,47 @@ public function testIdeDeleteCommand(): void {
$this->assertStringContainsString('The Cloud IDE is being deleted.', $output);
}

public function testIdeDeleteByUuid(): void {
$this->mockRequest('getIde', IdeHelper::$remoteIdeUuid);
$this->mockRequest('deleteIde', IdeHelper::$remoteIdeUuid, NULL, 'De-provisioning IDE');
$sshKeyGetResponse = $this->mockListSshKeysRequestWithIdeKey(IdeHelper::$remoteIdeLabel, IdeHelper::$remoteIdeUuid);

$this->mockDeleteSshKeyRequest($sshKeyGetResponse->{'_embedded'}->items[0]->uuid);

$inputs = [
// Would you like to delete the SSH key associated with this IDE from your Cloud Platform account?
'y',
];

$this->executeCommand(['--uuid' => IdeHelper::$remoteIdeUuid], $inputs);

// Assert.
$output = $this->getDisplay();
$this->assertStringContainsString('The Cloud IDE is being deleted.', $output);
}

public function testIdeDeleteNeverMind(): void {
$applications = $this->mockRequest('getApplications');
$this->mockRequest('getApplicationByUuid', $applications[0]->uuid);
$this->mockRequest('getApplicationIdes', $applications[0]->uuid);
$inputs = [
// Would you like Acquia CLI to search for a Cloud application that matches your local git config?
'n',
// Select the application for which you'd like to create a new IDE.
0,
// Would you like to link the project at ... ?
'y',
// Select the IDE you'd like to delete:
0,
// Are you sure you want to delete ExampleIDE?
'n',
];

$this->executeCommand([], $inputs);

// Assert.
$output = $this->getDisplay();
$this->assertStringContainsString('Ok, never mind.', $output);
}

}
3 changes: 2 additions & 1 deletion tests/phpunit/src/Commands/Ide/IdeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
class IdeHelper {

public static string $remoteIdeUuid = '215824ff-272a-4a8c-9027-df32ed1d68a9';
public static string $remoteIdeLabel = 'ExampleIDE';

public static function setCloudIdeEnvVars(): void {
TestBase::setEnvVars(self::getEnvVars());
Expand All @@ -25,7 +26,7 @@ public static function getEnvVars(): array {
return [
'ACQUIA_USER_UUID' => '4acf8956-45df-3cf4-5106-065b62cf1ac8',
'AH_SITE_ENVIRONMENT' => 'IDE',
'REMOTEIDE_LABEL' => 'ExampleIDE',
'REMOTEIDE_LABEL' => self::$remoteIdeLabel,
'REMOTEIDE_UUID' => self::$remoteIdeUuid,
];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
class IdeWizardDeleteSshKeyCommandTest extends IdeWizardTestBase {

public function testDelete(): void {
$mockBody = $this->mockListSshKeysRequestWithIdeKey();
$mockBody = $this->mockListSshKeysRequestWithIdeKey(IdeHelper::$remoteIdeLabel, IdeHelper::$remoteIdeUuid);

$this->mockDeleteSshKeyRequest($mockBody->{'_embedded'}->items[0]->uuid);

Expand Down
5 changes: 2 additions & 3 deletions tests/phpunit/src/TestBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Acquia\Cli\Application;
use Acquia\Cli\CloudApi\ClientService;
use Acquia\Cli\CloudApi\CloudCredentials;
use Acquia\Cli\Command\Ssh\SshKeyCommandBase;
use Acquia\Cli\Config\AcquiaCliConfig;
use Acquia\Cli\Config\CloudDataConfig;
use Acquia\Cli\DataStore\AcquiaCliDatastore;
Expand Down Expand Up @@ -575,9 +574,9 @@ protected function mockListSshKeysRequest(): array {
return $this->mockRequest('getAccountSshKeys');
}

protected function mockListSshKeysRequestWithIdeKey(): object {
protected function mockListSshKeysRequestWithIdeKey(string $ideLabel, string $ideUuid): object {
$mockBody = $this->getMockResponseFromSpec('/account/ssh-keys', 'get', '200');
$mockBody->{'_embedded'}->items[0]->label = SshKeyCommandBase::getIdeSshKeyLabel();
$mockBody->{'_embedded'}->items[0]->label = preg_replace('/\W/', '', 'IDE_' . $ideLabel . '_' . $ideUuid);
$this->clientProphecy->request('get', '/account/ssh-keys')
->willReturn($mockBody->{'_embedded'}->items)
->shouldBeCalled();
Expand Down

0 comments on commit cf14e26

Please sign in to comment.