diff --git a/src/Command/Api/ApiBaseCommand.php b/src/Command/Api/ApiBaseCommand.php index 8f3fc52ea..2af1251a0 100644 --- a/src/Command/Api/ApiBaseCommand.php +++ b/src/Command/Api/ApiBaseCommand.php @@ -12,6 +12,7 @@ use Closure; use GuzzleHttp\Psr7\Utils; use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -93,6 +94,10 @@ protected function interact(InputInterface $input, OutputInterface $output): voi parent::interact($input, $output); } + /** + * @throws \Acquia\Cli\Exception\AcquiaCliException + * @throws \JsonException + */ protected function execute(InputInterface $input, OutputInterface $output): int { if ($this->getName() === 'api:base') { @@ -123,10 +128,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int $exitCode = 1; } - $contents = json_encode($response, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT); - $this->output->writeln($contents); - - return $exitCode; + if ($exitCode || !$this->getParamFromInput($input, 'task-wait')) { + $contents = json_encode($response, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT); + $this->output->writeln($contents); + return $exitCode; + } + $notificationUuid = CommandBase::getNotificationUuidFromResponse($response); + $success = $this->waitForNotificationToComplete($this->cloudApiClientService->getClient(), $notificationUuid, "Waiting for task $notificationUuid to complete"); + return $success ? Command::SUCCESS : Command::FAILURE; } public function setMethod(string $method): void diff --git a/src/Command/Api/ApiCommandHelper.php b/src/Command/Api/ApiCommandHelper.php index 70082ff0c..72312291f 100644 --- a/src/Command/Api/ApiCommandHelper.php +++ b/src/Command/Api/ApiCommandHelper.php @@ -100,6 +100,11 @@ private function addApiCommandParameters(array $schema, array $acquiaCloudSpec, $inputDefinition = array_merge($inputDefinition, $bodyInputDefinition); } + // Add --task-wait parameter for responses with notifications. + if (array_key_exists(202, $schema['responses'])) { + $inputDefinition[] = new InputOption('task-wait', null, InputOption::VALUE_NONE, 'Wait for this task to complete'); + } + $command->setDefinition(new InputDefinition($inputDefinition)); if ($usage) { $command->addUsage(rtrim($usage)); diff --git a/src/Command/CommandBase.php b/src/Command/CommandBase.php index fb1583bd9..61918edbb 100644 --- a/src/Command/CommandBase.php +++ b/src/Command/CommandBase.php @@ -1892,6 +1892,9 @@ private function writeCompletedMessage(NotificationResponse $notification): void $this->io->writeln("Duration: $duration seconds"); } + /** + * @throws \Acquia\Cli\Exception\AcquiaCliException + */ protected static function getNotificationUuidFromResponse(object $response): string { if (property_exists($response, 'links')) { diff --git a/tests/phpunit/src/Commands/Api/ApiCommandTest.php b/tests/phpunit/src/Commands/Api/ApiCommandTest.php index 804b16bc2..761e13656 100644 --- a/tests/phpunit/src/Commands/Api/ApiCommandTest.php +++ b/tests/phpunit/src/Commands/Api/ApiCommandTest.php @@ -30,11 +30,38 @@ protected function createCommand(): CommandBase return $this->injectCommand(ApiBaseCommand::class); } - /** - * @group brokenProphecy - */ + public function testTaskWait(): void + { + $environmentId = '24-a47ac10b-58cc-4372-a567-0e02b2c3d470'; + $branch = 'my-feature-branch'; + $this->mockRequest('postEnvironmentsSwitchCode', $environmentId, null, 'Switching code'); + $this->clientProphecy->addOption('json', ['branch' => $branch])->shouldBeCalled(); + $this->clientProphecy->addOption('headers', ['Accept' => 'application/hal+json, version=2']) + ->shouldBeCalled(); + $this->mockRequest('getNotificationByUuid', 'bfd9a39b-a85e-4de3-8a70-042d1c7e607a'); + $this->command = $this->getApiCommandByName('api:environments:code-switch'); + $this->executeCommand([ + '--task-wait' => true, + 'branch' => $branch, + 'environmentId' => $environmentId, + ]); + $output = $this->getDisplay(); + $this->assertStringContainsString('[OK] The task with notification uuid 1bd3487e-71d1-4fca-a2d9-5f969b3d35c1 completed', $output); + $expected = <<assertStringContainsStringIgnoringLineEndings($expected, $output); + $this->assertEquals(0, $this->getStatusCode()); + } + public function testArgumentsInteraction(): void { + $this->clientProphecy->addOption('headers', ['Accept' => 'application/hal+json, version=2']) + ->shouldBeCalled(); + $this->mockRequest('getEnvironmentsLog', ['289576-53785bca-1946-4adc-a022-e50d24686c20', 'apache-access']); $this->command = $this->getApiCommandByName('api:environments:log-download'); $this->executeCommand([], [ '289576-53785bca-1946-4adc-a022-e50d24686c20', @@ -106,7 +133,14 @@ public function testApiCommandErrorResponse(): void // Assert. $output = $this->getDisplay(); $this->assertJson($output); - $this->assertStringContainsString($mockBody->message, $output); + $expected = <<assertStringEqualsStringIgnoringLineEndings($expected, $output); $this->assertEquals(1, $this->getStatusCode()); } @@ -133,12 +167,12 @@ public function testApiCommandExecutionForHttpGet(): void $this->assertArrayHasKey('uuid', $contents[0]); } - /** - * @group brokenProphecy - */ public function testObjectParam(): void { + $this->clientProphecy->addOption('headers', ['Accept' => 'application/hal+json, version=2']) + ->shouldBeCalled(); $this->mockRequest('putEnvironmentCloudActions', '24-a47ac10b-58cc-4372-a567-0e02b2c3d470'); + $this->clientProphecy->addOption('json', ['cloud-actions' => (object)['fb4aa87a-8be2-42c6-bdf0-ef9d09a3de70' => true]]); $this->command = $this->getApiCommandByName('api:environments:cloud-actions-update'); $this->executeCommand([ 'cloud-actions' => '{"fb4aa87a-8be2-42c6-bdf0-ef9d09a3de70":true}', @@ -578,9 +612,6 @@ public function testOrganizationMemberDeleteByUserEmail(): void $this->assertStringContainsString("Organization member removed", $output); } - /** - * @group brokenProphecy - */ public function testOrganizationMemberDeleteInvalidEmail(): void { $membersResponse = self::getMockResponseFromSpec('/organizations/{organizationUuid}/members', 'get', 200); @@ -589,11 +620,6 @@ public function testOrganizationMemberDeleteInvalidEmail(): void $this->clientProphecy->request('get', '/organizations/' . $orgId . '/members') ->willReturn($membersResponse->_embedded->items)->shouldBeCalled(); - $this->mockRequest('postOrganizationMemberDelete', [ - $orgId, - $memberUuid, - ], null, 'Member removed'); - $this->command = $this->getApiCommandByName('api:organizations:member-delete'); $this->expectException(AcquiaCliException::class); $this->expectExceptionMessage('No matching user found in this organization');