Skip to content

Commit

Permalink
CLI-1103: Fix #1558: Notification arg formats (#1567)
Browse files Browse the repository at this point in the history
* CLI-1103: Fix #1558: Notification arg formats

* refactor

* make static

* kill mutants, refactor tests

* kill mutants

* kill mutants

* kill mutants
  • Loading branch information
danepowell committed Jul 14, 2023
1 parent 17d93cd commit 4dc1a18
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 78 deletions.
25 changes: 3 additions & 22 deletions src/Command/App/TaskWaitCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Acquia\Cli\Command\App;

use Acquia\Cli\Command\CommandBase;
use Acquia\Cli\Exception\AcquiaCliException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
Expand All @@ -23,31 +22,13 @@ protected function configure(): void {
$this->setDescription('Wait for a task to complete')
->addArgument('notification-uuid', InputArgument::REQUIRED, 'The task notification UUID or Cloud Platform API response containing a linked notification')
->setHelp('Accepts either a notification UUID or Cloud Platform API response as JSON string. The JSON string must contain the _links->notification->href property.')
->addUsage('"$(api:environments:domain-clear-caches [environmentId] [domain])"');
->addUsage('"$(acli api:environments:domain-clear-caches [environmentId] [domain])"');
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$notificationUuid = $this->getNotificationUuid($input);
$success = $this->waitForNotificationToComplete($this->cloudApiClientService->getClient(), $notificationUuid, "Waiting for task $notificationUuid to complete");
if ($success) {
return Command::SUCCESS;
}
else {
return Command::FAILURE;
}
}

private function getNotificationUuid(InputInterface $input): string {
$notificationUuid = $input->getArgument('notification-uuid');
$json = json_decode($notificationUuid, FALSE);
if (json_last_error() === JSON_ERROR_NONE) {
if (is_object($json) && property_exists($json, '_links') && property_exists($json->_links, 'notification') && property_exists($json->_links->notification, 'href')) {
return $this->getNotificationUuidFromResponse($json);
}
throw new AcquiaCliException("Input JSON must contain the _links.notification.href property.");
}

return self::validateUuid($input->getArgument('notification-uuid'));
$success = $this->waitForNotificationToComplete($this->cloudApiClientService->getClient(), $notificationUuid, "Waiting for task $notificationUuid to complete");
return $success ? Command::SUCCESS : Command::FAILURE;
}

}
66 changes: 59 additions & 7 deletions src/Command/CommandBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
use Composer\Semver\VersionParser;
use Exception;
use GuzzleHttp\HandlerStack;
use JsonException;
use Kevinrob\GuzzleCache\CacheMiddleware;
use Kevinrob\GuzzleCache\Storage\Psr6CacheStorage;
use Kevinrob\GuzzleCache\Strategy\PrivateCacheStrategy;
Expand Down Expand Up @@ -206,6 +207,8 @@ protected function initialize(InputInterface $input, OutputInterface $output): v
$this->convertEnvironmentAliasToUuid($input, 'source-environment');
$this->convertEnvironmentAliasToUuid($input, 'destination-environment');
$this->convertEnvironmentAliasToUuid($input, 'source');
$this->convertNotificationToUuid($input, 'notificationUuid');
$this->convertNotificationToUuid($input, 'notification-uuid');

if ($latest = $this->checkForNewVersion()) {
$this->output->writeln("Acquia CLI $latest is available. Run <options=bold>acli self-update</> to update.");
Expand Down Expand Up @@ -1045,6 +1048,34 @@ private function validateUserUuid(string $userUuidArgument, string $orgUuidArgum
return $userUuidArgument;
}

private static function getNotificationUuid(string $notification): string {
// Greedily hope this is already a UUID.
try {
self::validateUuid($notification);
return $notification;
}
catch (ValidatorException) {
}

// Not a UUID, maybe a JSON object?
try {
$json = json_decode($notification, NULL, 4, JSON_THROW_ON_ERROR);
return CommandBase::getNotificationUuidFromResponse($json);
}
catch (JsonException | AcquiaCliException) {
}

// Last chance, maybe a URL?
try {
return self::getNotificationUuidFromUrl($notification);
}
catch (ValidatorException | AcquiaCliException) {
}

// Womp womp.
throw new AcquiaCliException('Notification format is not one of UUID, JSON response, or URL');
}

/**
* @param String $userAlias User alias like uuid or email.
* @param String $orgUuidArgument Organization uuid.
Expand Down Expand Up @@ -1078,14 +1109,22 @@ protected function convertApplicationAliasToUuid(InputInterface $input): void {
}
}

protected function convertEnvironmentAliasToUuid(InputInterface $input, mixed $argumentName): void {
protected function convertEnvironmentAliasToUuid(InputInterface $input, string $argumentName): void {
if ($input->hasArgument($argumentName) && $input->getArgument($argumentName)) {
$envUuidArgument = $input->getArgument($argumentName);
$environmentUuid = $this->validateEnvironmentUuid($envUuidArgument, $argumentName);
$input->setArgument($argumentName, $environmentUuid);
}
}

protected function convertNotificationToUuid(InputInterface $input, string $argumentName): void {
if ($input->hasArgument($argumentName) && $input->getArgument($argumentName)) {
$notificationArgument = $input->getArgument($argumentName);
$notificationUuid = CommandBase::getNotificationUuid($notificationArgument);
$input->setArgument($argumentName, $notificationUuid);
}
}

/**
* @param string $sshUrl The SSH URL to the server.
* @return string The sitegroup. E.g., eemgrasmick.
Expand Down Expand Up @@ -1235,7 +1274,7 @@ protected function getDrushDatabaseConnectionStatus(Closure $outputCallback = NU
'--no-interaction',
], $outputCallback, $this->dir, FALSE);
if ($process->isSuccessful()) {
$drushStatusReturnOutput = json_decode($process->getOutput(), TRUE, 512);
$drushStatusReturnOutput = json_decode($process->getOutput(), TRUE);
if (is_array($drushStatusReturnOutput) && array_key_exists('db-status', $drushStatusReturnOutput) && $drushStatusReturnOutput['db-status'] === 'Connected') {
$this->drushHasActiveDatabaseConnection = TRUE;
return $this->drushHasActiveDatabaseConnection;
Expand Down Expand Up @@ -1472,16 +1511,29 @@ private function writeCompletedMessage(NotificationResponse $notification): void
$this->io->writeln("Duration: $duration seconds");
}

protected function getNotificationUuidFromResponse(object $response): string {
protected static function getNotificationUuidFromResponse(object $response): string {
if (property_exists($response, 'links')) {
$links = $response->links;
}
else {
elseif (property_exists($response, '_links')) {
$links = $response->_links;
}
$notificationUrl = $links->notification->href;
$urlParts = explode('/', $notificationUrl);
return $urlParts[5];
else {
throw new AcquiaCliException('JSON object must contain the _links.notification.href property');
}
if (property_exists($links, 'notification') && property_exists($links->notification, 'href')) {
return self::getNotificationUuidFromUrl($links->notification->href);
}
throw new AcquiaCliException('JSON object must contain the _links.notification.href property');
}

private static function getNotificationUuidFromUrl(string $notificationUrl): string {
$notificationUrlPattern = '/^https:\/\/cloud.acquia.com\/api\/notifications\/([\w-]*)$/';
if (preg_match($notificationUrlPattern, $notificationUrl, $matches)) {
self::validateUuid($matches[1]);
return $matches[1];
}
throw new AcquiaCliException('Notification UUID not found in URL');
}

protected function validateRequiredCloudPermissions(Client $acquiaCloudClient, ?string $cloudApplicationUuid, AccountResponse $account, array $requiredPermissions): void {
Expand Down
2 changes: 1 addition & 1 deletion src/Command/Env/EnvCertCreateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$csrId,
$legacy
);
$notificationUuid = $this->getNotificationUuidFromResponse($response);
$notificationUuid = CommandBase::getNotificationUuidFromResponse($response);
$this->waitForNotificationToComplete($acquiaCloudClient, $notificationUuid, 'Installing certificate');
return Command::SUCCESS;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Command/Env/EnvCreateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$this->checklist->addItem("Initiating environment creation");
$response = $environmentsResource->create($cloudAppUuid, $label, $branch, $databaseNames);
$notificationUuid = $this->getNotificationUuidFromResponse($response);
$notificationUuid = CommandBase::getNotificationUuidFromResponse($response);
$this->checklist->completePreviousItem();

$success = function () use ($environmentsResource, $cloudAppUuid, $label): void {
Expand Down
8 changes: 4 additions & 4 deletions src/Command/Env/EnvMirrorCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

if (isset($codeCopyResponse)) {
$this->waitForNotificationToComplete($acquiaCloudClient, $this->getNotificationUuidFromResponse($codeCopyResponse), 'Waiting for code copy to complete');
$this->waitForNotificationToComplete($acquiaCloudClient, CommandBase::getNotificationUuidFromResponse($codeCopyResponse), 'Waiting for code copy to complete');
}
if (isset($dbCopyResponse)) {
$this->waitForNotificationToComplete($acquiaCloudClient, $this->getNotificationUuidFromResponse($dbCopyResponse), 'Waiting for database copy to complete');
$this->waitForNotificationToComplete($acquiaCloudClient, CommandBase::getNotificationUuidFromResponse($dbCopyResponse), 'Waiting for database copy to complete');
}
if (isset($filesCopyResponse)) {
$this->waitForNotificationToComplete($acquiaCloudClient, $this->getNotificationUuidFromResponse($filesCopyResponse), 'Waiting for files copy to complete');
$this->waitForNotificationToComplete($acquiaCloudClient, CommandBase::getNotificationUuidFromResponse($filesCopyResponse), 'Waiting for files copy to complete');
}
if (isset($configCopyResponse)) {
$this->waitForNotificationToComplete($acquiaCloudClient, $this->getNotificationUuidFromResponse($configCopyResponse), 'Waiting for config copy to complete');
$this->waitForNotificationToComplete($acquiaCloudClient, CommandBase::getNotificationUuidFromResponse($configCopyResponse), 'Waiting for config copy to complete');
}

$this->io->success([
Expand Down
17 changes: 3 additions & 14 deletions tests/phpunit/src/CommandTestBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,20 +344,9 @@ protected function mockDatabaseBackupCreateResponse(
return $backupCreateResponse;
}

protected function mockNotificationResponseFromObject(object $responseWithNotificationLink): mixed {
return $this->mockNotificationResponse(substr($responseWithNotificationLink->_links->notification->href, -36));
}

protected function mockNotificationResponse(string $notificationUuid, string $status = NULL): mixed {
$notificationResponse = $this->getMockResponseFromSpec('/notifications/{notificationUuid}', 'get', 200);
if ($status) {
$notificationResponse->status = $status;
}
$this->clientProphecy->request('get', "/notifications/$notificationUuid")
->willReturn($notificationResponse)
->shouldBeCalled();

return $notificationResponse;
protected function mockNotificationResponseFromObject(object $responseWithNotificationLink): array|object {
$uuid = substr($responseWithNotificationLink->_links->notification->href, -36);
return $this->mockRequest('getNotificationByUuid', $uuid);
}

protected function mockCreateMySqlDumpOnLocal(ObjectProphecy $localMachineHelper): void {
Expand Down
9 changes: 2 additions & 7 deletions tests/phpunit/src/Commands/Acsf/AcsfAuthLoginCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public function providerTestAuthLoginCommand(): array {
// Arguments.
[],
// Output to assert.
"Acquia CLI is now logged in to {$this->acsfCurrentFactoryUrl} as {$this->acsfUsername}",
"Acquia CLI is now logged in to $this->acsfCurrentFactoryUrl as $this->acsfUsername",
// $config.
$this->getAcsfCredentialsFileContents(),
],
Expand All @@ -88,14 +88,9 @@ public function providerTestAuthLoginCommand(): array {

/**
* @dataProvider providerTestAuthLoginCommand
* @param $machineIsAuthenticated
* @param $inputs
* @param $args
* @param $outputToAssert
* @param array $config
* @requires OS linux|darwin
*/
public function testAcsfAuthLoginCommand(mixed $machineIsAuthenticated, mixed $inputs, mixed $args, mixed $outputToAssert, array $config = []): void {
public function testAcsfAuthLoginCommand(bool $machineIsAuthenticated, array $inputs, array $args, string $outputToAssert, array $config = []): void {
if (!$machineIsAuthenticated) {
$this->clientServiceProphecy->isMachineAuthenticated()->willReturn(FALSE);
$this->removeMockCloudConfigFile();
Expand Down
Loading

0 comments on commit 4dc1a18

Please sign in to comment.