diff --git a/composer.json b/composer.json index ce9f58c..eedffa9 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,11 @@ "AcquiaCli\\":"src" } }, + "autoload-dev": { + "psr-4":{ + "AcquiaCli\\Tests\\": "tests/" + } + }, "require-dev": { "php-coveralls/php-coveralls": "^2.0.0", "squizlabs/php_codesniffer": "^3.1", diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..b37886a --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,20 @@ + + + + + ./tests/ + + + + + ./src/ + + + + + + + + + + diff --git a/src/Cli/AcquiaCli.php b/src/Cli/AcquiaCli.php index 130e924..aa1abe7 100644 --- a/src/Cli/AcquiaCli.php +++ b/src/Cli/AcquiaCli.php @@ -158,6 +158,7 @@ public function injectParameters($container) $parameterInjection->register('AcquiaCloudApi\Endpoints\Insights', new AcquiaCliInjector); $parameterInjection->register('AcquiaCloudApi\Endpoints\LogForwardingDestinations', new AcquiaCliInjector); $parameterInjection->register('AcquiaCloudApi\Endpoints\SslCertificates', new AcquiaCliInjector); + $parameterInjection->register('AcquiaCloudApi\Endpoints\Organizations', new AcquiaCliInjector); } /** diff --git a/src/Commands/AcquiaCommand.php b/src/Commands/AcquiaCommand.php index 4210003..d063b16 100644 --- a/src/Commands/AcquiaCommand.php +++ b/src/Commands/AcquiaCommand.php @@ -73,24 +73,6 @@ public function __construct() $this->setTableStyles(); } - public function getCloudApi() - { - $cloudapi = Robo::service('cloudApi')->getCloudApi(); - return $cloudapi; - } - - public function getEnvironments() - { - $environments = Robo::service('cloudApi')->getEnvironments(); - return $environments; - } - - public function getApplications() - { - $applications = Robo::service('cloudApi')->getApplications(); - return $applications; - } - /** * Override the confirm method from consolidation/Robo to allow automatic * confirmation. @@ -196,7 +178,7 @@ protected function waitForNotification($response) $progress->setMessage('Looking up notification'); $progress->start(); - $notificationAdapter = new Notifications($this->getCloudApi()); + $notificationAdapter = new Notifications($this->cloudapi); while (true) { $progress->advance($sleep); @@ -210,7 +192,7 @@ protected function waitForNotification($response) case self::TASKFAILED: // If there's one failure we should throw an exception throw new \Exception('Acquia task failed.'); - break(2); + break(2); // If tasks are started or in progress, we should continue back // to the top of the loop and wait until tasks are complete. case self::TASKSTARTED: @@ -221,7 +203,7 @@ protected function waitForNotification($response) break(2); default: throw new \Exception('Unknown notification status.'); - break(2); + break(2); } // Timeout if the command exceeds the configured timeout threshold. @@ -246,7 +228,6 @@ protected function waitForNotification($response) */ protected function backupAndMoveDbs($uuid, $environmentFrom, $environmentTo, $dbName = null) { - if (null !== $dbName) { $this->cloudapi->addQuery('filter', "name=${dbName}"); } @@ -268,7 +249,7 @@ protected function backupAndMoveDbs($uuid, $environmentFrom, $environmentTo, $db ) ); - $databaseAdapter = new Databases($this->getCloudApi()); + $databaseAdapter = new Databases($this->cloudapi); $response = $databaseAdapter->copy($environmentFrom->uuid, $database->name, $environmentTo->uuid); $this->waitForNotification($response); } @@ -280,7 +261,7 @@ protected function backupAndMoveDbs($uuid, $environmentFrom, $environmentTo, $db */ protected function backupAllEnvironmentDbs($uuid, $environment) { - $dbAdapter = new Databases($this->getCloudApi()); + $dbAdapter = new Databases($this->cloudapi); $databases = $dbAdapter->getAll($uuid); foreach ($databases as $database) { $this->backupDb($uuid, $environment, $database); @@ -296,7 +277,7 @@ protected function backupDb($uuid, $environment, $database) { // Run database backups. $this->say(sprintf('Backing up DB (%s) on %s', $database->name, $environment->label)); - $dbAdapter = new DatabaseBackups($this->getCloudApi()); + $dbAdapter = new DatabaseBackups($this->cloudapi); $response = $dbAdapter->create($environment->uuid, $database->name); $this->waitForNotification($response); } diff --git a/src/Commands/ApplicationsCommand.php b/src/Commands/ApplicationsCommand.php index dba4d1c..7505bc8 100644 --- a/src/Commands/ApplicationsCommand.php +++ b/src/Commands/ApplicationsCommand.php @@ -179,7 +179,7 @@ public function applicationTagDelete(Applications $applicationsAdapter, $uuid, $ * @param string $name * * @command application:rename - * @alias app:rename,a:rename + * @aliases app:rename,a:rename */ public function applicationRename(Applications $applicationsAdapter, $uuid, $name) { diff --git a/src/Commands/CodeCommand.php b/src/Commands/CodeCommand.php index bf9b027..68bd15d 100644 --- a/src/Commands/CodeCommand.php +++ b/src/Commands/CodeCommand.php @@ -60,9 +60,9 @@ public function code(Client $client, Code $codeAdapter, $uuid, $match = null) /** * Deploys code from one environment to another. * - * @param string $uuid - * @param string $environmentFrom - * @param string $environmentTo + * @param string $uuid + * @param string $environmentFrom + * @param string $environmentTo * * @command code:deploy * @aliases c:d diff --git a/src/Commands/CronCommand.php b/src/Commands/CronCommand.php index 792cdab..937ec73 100644 --- a/src/Commands/CronCommand.php +++ b/src/Commands/CronCommand.php @@ -18,8 +18,8 @@ class CronCommand extends AcquiaCommand /** * Shows all cron tasks associated with an environment. * - * @param string $uuid - * @param string $environment + * @param string $uuid + * @param string $environment * * @command cron:list */ @@ -55,11 +55,11 @@ public function crons(Crons $cronAdapter, $uuid, $environment) /** * Adds a new cron task for an environment. * - * @param string $uuid - * @param string $environment - * @param string $commandString The command to be run on cron wrapped in quotes. - * @param string $frequency The crontab format frequency wrapped in quotes - * @param string $label An optional label for the cron command wrapped in quotes. + * @param string $uuid + * @param string $environment + * @param string $commandString The command to be run on cron wrapped in quotes. + * @param string $frequency The crontab format frequency wrapped in quotes + * @param string $label An optional label for the cron command wrapped in quotes. * * @command cron:create * @aliases cron:add @@ -75,9 +75,9 @@ public function cronAdd(Crons $cronAdapter, $uuid, $environment, $commandString, /** * Removes a cron task for an environment. * - * @param string $uuid - * @param string $environment - * @param int $cronId + * @param string $uuid + * @param string $environment + * @param int $cronId * * @command cron:delete * @aliases cron:remove @@ -95,9 +95,9 @@ public function cronDelete(Crons $cronAdapter, $uuid, $environment, $cronId) /** * Enables a disabled cron entry. * - * @param string $uuid - * @param string $environment - * @param int $cronId + * @param string $uuid + * @param string $environment + * @param int $cronId * * @command cron:enable */ @@ -112,9 +112,9 @@ public function cronEnable(Crons $cronAdapter, $uuid, $environment, $cronId) /** * Disables an enabled cron entry. * - * @param string $uuid - * @param string $environment - * @param int $cronId + * @param string $uuid + * @param string $environment + * @param int $cronId * * @command cron:disable */ @@ -131,9 +131,9 @@ public function cronDisable(Crons $cronAdapter, $uuid, $environment, $cronId) /** * Shows detailed information about a single cron command. * - * @param string $uuid - * @param string $environment - * @param int $cronId + * @param string $uuid + * @param string $environment + * @param int $cronId * * @command cron:info */ diff --git a/src/Commands/DbBackupCommand.php b/src/Commands/DbBackupCommand.php index f94113f..c533931 100644 --- a/src/Commands/DbBackupCommand.php +++ b/src/Commands/DbBackupCommand.php @@ -27,30 +27,55 @@ class DbBackupCommand extends AcquiaCommand /** * Backs up all DBs in an environment. * - * @param string $uuid - * @param string $environment + * @param string $uuid + * @param string $environment + * + * @command database:backup:all + * @aliases db:backup:all + */ + public function dbBackupAll($uuid, $environment) + { + $environment = $this->cloudapiService->getEnvironment($uuid, $environment); + $this->backupAllEnvironmentDbs($uuid, $environment); + } + + /** + * Backs up a DB in an environment. + * + * @param string $uuid + * @param string $environment + * @param string $dbName * * @command database:backup * @aliases db:backup */ - public function dbBackup($uuid, $environment) + public function dbBackup(Databases $databaseAdapter, $uuid, $environment, $dbName) { $environment = $this->cloudapiService->getEnvironment($uuid, $environment); - $this->backupAllEnvironmentDbs($uuid, $environment); + + // @TODO replace this call with a specific API call. + // https://cloudapi-docs.acquia.com/#/Environments/getEnvironmentsDatabase + $databases = $databaseAdapter->getAll($uuid); + foreach ($databases as $database) { + if ($database->name === $dbName) { + $this->backupDb($uuid, $environment, $database); + } + } } /** * Shows a list of database backups for all databases in an environment. * - * @param string $uuid - * @param string $environment - * @param string $dbName + * @param string $uuid + * @param string $environment + * @param string $dbName * * @command database:backup:list * @aliases db:backup:list */ public function dbBackupList( Client $client, + Databases $databaseAdapter, DatabaseBackups $databaseBackupsAdapter, $uuid, $environment, @@ -61,8 +86,7 @@ public function dbBackupList( if (null !== $dbName) { $client->addQuery('filter', "name=${dbName}"); } - $dbAdapter = new Databases($this->cloudapi); - $databases = $dbAdapter->getAll($uuid); + $databases = $databaseAdapter->getAll($uuid); $client->clearQuery(); $table = new Table($this->output()); @@ -97,10 +121,10 @@ public function dbBackupList( /** * Restores a database from a saved backup. * - * @param string $uuid - * @param string $environment - * @param string $dbName - * @param int $backupId + * @param string $uuid + * @param string $environment + * @param string $dbName + * @param int $backupId * * @command database:backup:restore * @aliases db:backup:restore @@ -122,10 +146,10 @@ public function dbBackupRestore(DatabaseBackups $databaseBackupsAdapter, $uuid, /** * Provides a database backup link. * - * @param string $uuid - * @param string $environment - * @param string $dbName - * @param int $backupId + * @param string $uuid + * @param string $environment + * @param string $dbName + * @param int $backupId * * @command database:backup:link * @aliases db:backup:link @@ -148,9 +172,9 @@ public function dbBackupLink($uuid, $environment, $dbName, $backupId) /** * Downloads a database backup. * - * @param string $uuid - * @param string $environment - * @param string $dbName + * @param string $uuid + * @param string $environment + * @param string $dbName * * @throws \Exception * diff --git a/src/Commands/DbCommand.php b/src/Commands/DbCommand.php index 86afec7..a7652d4 100644 --- a/src/Commands/DbCommand.php +++ b/src/Commands/DbCommand.php @@ -19,15 +19,6 @@ class DbCommand extends AcquiaCommand { - protected $databaseAdapter; - - public function __construct() - { - parent::__construct(); - - $this->databaseAdapter = new Databases($this->getCloudApi()); - } - /** * Shows all databases. * @@ -36,9 +27,9 @@ public function __construct() * @command database:list * @aliases db:list */ - public function dbList($uuid) + public function dbList(Databases $databaseAdapter, $uuid) { - $databases = $this->databaseAdapter->getAll($uuid); + $databases = $databaseAdapter->getAll($uuid); $table = new Table($this->output()); $table->setHeaders(['Databases']); foreach ($databases as $database) { @@ -63,9 +54,9 @@ public function dbList($uuid) * @command database:create * @aliases database:add,db:create,db:add */ - public function dbCreate($uuid, $dbName) + public function dbCreate(Databases $databaseAdapter, $uuid, $dbName) { - $response = $this->databaseAdapter->create($uuid, $dbName); + $response = $databaseAdapter->create($uuid, $dbName); $this->say(sprintf('Creating database (%s)', $dbName)); $this->waitForNotification($response); } @@ -79,17 +70,17 @@ public function dbCreate($uuid, $dbName) * @command database:delete * @aliases database:remove,db:remove,db:delete */ - public function dbDelete($uuid, $dbName) + public function dbDelete(Databases $databaseAdapter, $uuid, $dbName) { if ($this->confirm('Are you sure you want to delete this database?')) { $this->say(sprintf('Deleting database (%s)', $dbName)); - $response = $this->databaseAdapter->delete($uuid, $dbName); + $response = $databaseAdapter->delete($uuid, $dbName); $this->waitForNotification($response); } } /** - * Truncaates a database. + * Truncates a database (only applicable to Acquia free tier). * * @param string $uuid * @param string $dbName @@ -97,11 +88,11 @@ public function dbDelete($uuid, $dbName) * @command database:truncate * @aliases db:truncate */ - public function dbTruncate($uuid, $dbName) + public function dbTruncate(Databases $databaseAdapter, $uuid, $dbName) { if ($this->confirm('Are you sure you want to truncate this database?')) { $this->say(sprintf('Truncate database (%s)', $dbName)); - $response = $this->databaseAdapter->truncate($uuid, $dbName); + $response = $databaseAdapter->truncate($uuid, $dbName); $this->waitForNotification($response); } } diff --git a/src/Commands/DomainCommand.php b/src/Commands/DomainCommand.php index 59de411..f0335c5 100644 --- a/src/Commands/DomainCommand.php +++ b/src/Commands/DomainCommand.php @@ -70,6 +70,8 @@ public function domainInfo(OutputInterface $output, Domains $domainAdapter, $uui $table = new Table($output); $table->setHeaders(['Hostname', 'Active', 'DNS Resolves', 'IP Addresses', 'CNAMES']); + $table->setColumnStyle(1, 'center-align'); + $table->setColumnStyle(2, 'center-align'); $table ->addRows( [ @@ -94,7 +96,7 @@ public function domainInfo(OutputInterface $output, Domains $domainAdapter, $uui * @param string $domain * * @command domain:create - * @alias domain:add + * @aliases domain:add */ public function domainCreate(Domains $domainAdapter, $uuid, $environment, $domain) { @@ -112,7 +114,7 @@ public function domainCreate(Domains $domainAdapter, $uuid, $environment, $domai * @param string $domain * * @command domain:delete - * @alias domain:remove + * @aliases domain:remove */ public function domainDelete(Domains $domainAdapter, $uuid, $environment, $domain) { diff --git a/src/Commands/EnvironmentsCommand.php b/src/Commands/EnvironmentsCommand.php index a3d7735..2a2826e 100644 --- a/src/Commands/EnvironmentsCommand.php +++ b/src/Commands/EnvironmentsCommand.php @@ -204,7 +204,7 @@ protected function renderEnvironmentInfo(EnvironmentResponse $environment, Serve * @param string $name * * @command environment:rename - * @alias env:rename,e:rename + * @aliases env:rename,e:rename */ public function environmentRename(Environments $environmentsAdapter, $uuid, $environment, $name) { @@ -220,12 +220,12 @@ public function environmentRename(Environments $environmentsAdapter, $uuid, $env * @param string $environment * * @command environment:delete - * @alias env:delete,e:d,environment:remove,env:remove + * @aliases env:delete,e:d,environment:remove,env:remove */ public function environmentDelete(Environments $environmentsAdapter, $uuid, $environment) { $environment = $this->cloudapiService->getEnvironment($uuid, $environment); - if ($this->confirm("Are you sure you want to delete this environment?")) { + if ($this->confirm(sprintf('Are you sure you want to delete the %s environment?', $environment->label))) { $this->say(sprintf('Deleting %s environment', $environment->label)); $response = $environmentsAdapter->delete($environment->uuid); $this->waitForNotification($response); diff --git a/src/Commands/LogForwardCommand.php b/src/Commands/LogForwardCommand.php index 2ca7cce..e263938 100644 --- a/src/Commands/LogForwardCommand.php +++ b/src/Commands/LogForwardCommand.php @@ -62,7 +62,7 @@ public function logforwardList( } /** - * Gets information about a Log ForwaRD. + * Gets information about a Log Forward. * * @param string $uuid * @param string $environment diff --git a/src/Commands/LogsCommand.php b/src/Commands/LogsCommand.php index 7138023..dc8efb7 100644 --- a/src/Commands/LogsCommand.php +++ b/src/Commands/LogsCommand.php @@ -24,9 +24,9 @@ class LogsCommand extends AcquiaCommand /** * Streams logs from an environment. * - * @param string $uuid - * @param string $environment - * @param array $opts + * @param string $uuid + * @param string $environment + * @param array $opts * @option $colourise Colourise the output * @option $logtypes Filter to specific log types * @option $servers Filter to specific servers diff --git a/src/Commands/NotificationsCommand.php b/src/Commands/NotificationsCommand.php index 9fbad1d..33549de 100644 --- a/src/Commands/NotificationsCommand.php +++ b/src/Commands/NotificationsCommand.php @@ -26,7 +26,7 @@ class NotificationsCommand extends AcquiaCommand * A leading "~" in the field indicates the field should be sorted in a descending order. * * @command notification:list - * @alias n:l + * @aliases n:l */ public function notificationList( Config $config, @@ -85,7 +85,7 @@ public function notificationList( * @param string $notificationUuid * * @command notification:info - * @alias n:i + * @aliases n:i * @throws \Exception */ public function notificationInfo(Config $config, Notifications $notificationsAdapter, $uuid, $notificationUuid) diff --git a/src/Commands/OrganizationsCommand.php b/src/Commands/OrganizationsCommand.php index cb0ac7d..7d40104 100644 --- a/src/Commands/OrganizationsCommand.php +++ b/src/Commands/OrganizationsCommand.php @@ -19,24 +19,15 @@ class OrganizationsCommand extends AcquiaCommand { - protected $organizationsAdapter; - - public function __construct() - { - parent::__construct(); - - $this->organizationsAdapter = new Organizations($this->getCloudApi()); - } - /** * Shows a list of all organizations. * * @command organization:list * @aliases org:list,o:l */ - public function showOrganizations() + public function showOrganizations(Organizations $organizationsAdapter) { - $organizations = $this->organizationsAdapter->getAll(); + $organizations = $organizationsAdapter->getAll(); $table = new Table($this->output()); $table->setHeaders(['UUID', 'Organization', 'Owner', 'Subs', 'Admins', 'Users', 'Teams', 'Roles']); @@ -67,19 +58,17 @@ public function showOrganizations() /** * Shows a list of all applications within an organization. * - * @param string $organization + * @param string $organization * * @command organization:applications * @aliases org:apps,o:a */ - public function organizationApplications($organization) + public function organizationApplications(Organizations $organizationsAdapter, $organization) { $organization = $this->cloudapiService->getOrganization($organization); + $applications = $organizationsAdapter->getApplications($organization->uuid); - $organizationUuid = $organization->uuid; - $applications = $this->organizationsAdapter->getApplications($organizationUuid); - - $this->say("Applications in organisation: ${organizationUuid}"); + $this->say(sprintf('Applications in organisation: %s', $organization->uuid)); $table = new Table($this->output()); $table->setHeaders(['UUID', 'Name', 'Type', 'Hosting ID']); foreach ($applications as $application) { @@ -105,19 +94,17 @@ public function organizationApplications($organization) /** * Shows teams within an organization. * - * @param string $organization + * @param string $organization * * @command organization:teams * @aliases org:teams,o:t */ - public function organizationTeams($organization) + public function organizationTeams(Organizations $organizationsAdapter, $organization) { $organization = $this->cloudapiService->getOrganization($organization); + $teams = $organizationsAdapter->getTeams($organization->uuid); - $organizationUuid = $organization->uuid; - $teams = $this->organizationsAdapter->getTeams($organizationUuid); - - $this->say("Teams in organisation: ${organizationUuid}"); + $this->say(sprintf('Teams in organisation: %s', $organization->uuid)); $table = new Table($this->output()); $table->setHeaders(['UUID', 'Name']); foreach ($teams as $team) { @@ -141,19 +128,19 @@ public function organizationTeams($organization) /** * Shows all members. * - * @param string $organization + * @param string $organization * * @command organization:members * @aliases org:members,o:m */ - public function members($organization) + public function members(Organizations $organizationsAdapter, $organization) { $organization = $this->cloudapiService->getOrganization($organization); $organizationUuid = $organization->uuid; - $admins = $this->organizationsAdapter->getAdmins($organizationUuid); - $members = $this->organizationsAdapter->getMembers($organizationUuid); + $admins = $organizationsAdapter->getAdmins($organization->uuid); + $members = $organizationsAdapter->getMembers($organization->uuid); - $this->say("Members in organisation: ${organizationUuid}"); + $this->say(sprintf('Members in organisation: %s', $organization->uuid)); $table = new Table($this->output()); $table ->setHeaders(['UUID', 'Username', 'Mail', 'Teams(s)']) diff --git a/src/Commands/SetupCommand.php b/src/Commands/SetupCommand.php index 1789ce5..4e88216 100644 --- a/src/Commands/SetupCommand.php +++ b/src/Commands/SetupCommand.php @@ -2,8 +2,8 @@ namespace AcquiaCli\Commands; -use Robo\Robo; use Robo\Tasks; +use AcquiaCli\Cli\Config; use Symfony\Component\Yaml\Yaml; /** @@ -14,28 +14,20 @@ class SetupCommand extends Tasks { - protected $configFiles; - - /** - * AcquiaCommand constructor. - */ - public function __construct() - { - $this->configFiles = [ - 'global' => Robo::config()->get('config.global'), - 'project' => Robo::config()->get('config.project'), - ]; - } - /** * Performs a check of the config files and provides a view of the parameters provided. Allows the user to create * new config files with correct parameters. * * @command setup */ - public function setup() + public function setup(Config $config) { - foreach ($this->configFiles as $type => $location) { + $configFiles = [ + 'global' => $config->get('config.global'), + 'project' => $config->get('config.project'), + ]; + + foreach ($configFiles as $type => $location) { $this->say(sprintf('Checking %s configuration at %s', $type, $location)); if (file_exists($location)) { $this->yell(sprintf('%s configuration file found', $type)); diff --git a/src/Commands/TeamsCommand.php b/src/Commands/TeamsCommand.php index 806e920..d1addb9 100644 --- a/src/Commands/TeamsCommand.php +++ b/src/Commands/TeamsCommand.php @@ -37,14 +37,14 @@ public function teamCreate(Teams $teamsAdapter, $organization, $name) * Invites a user to a team. * * @param string $teamUuid - * @param string $email The email address for the user that needs to be invited. - * @param string $roles A comma separated list of roles that a user should be invited to. + * @param string $email The email address for the user that needs to be invited. + * @param string $roleUuids A comma separated list of role UUIDs that a user should be invited to. * * @command team:invite */ - public function teamInvite(Teams $teamsAdapter, $teamUuid, $email, $roles) + public function teamInvite(Teams $teamsAdapter, $teamUuid, $email, $roleUuids) { - $rolesArray = explode(',', $roles); + $rolesArray = explode(',', $roleUuids); $this->say(sprintf('Inviting %s to team.', $email)); $teamsAdapter->invite($teamUuid, $email, $rolesArray); } @@ -56,7 +56,7 @@ public function teamInvite(Teams $teamsAdapter, $teamUuid, $email, $roles) * @param string $teamUuid * * @command team:addapplication - * @alias team:addapp + * @aliases team:addapp */ public function teamAddApplication(Teams $teamsAdapter, $uuid, $teamUuid) { @@ -150,7 +150,7 @@ public function roleUpdatePermissions(Roles $rolesAdapter, $roleUuid, $permissio /** * Shows all roles within an organization. * - * @param string $organization + * @param string $organization * * @command role:list */ diff --git a/src/Commands/VariablesCommand.php b/src/Commands/VariablesCommand.php index b68a2b2..b8eef25 100644 --- a/src/Commands/VariablesCommand.php +++ b/src/Commands/VariablesCommand.php @@ -54,7 +54,7 @@ public function variablesList(CloudApi $cloudapi, Variables $variablesAdapter, $ } /** - * Gets information about a domain. + * Gets information about a variable. * * @param string $uuid * @param string $environment diff --git a/src/Injector/AcquiaCliInjector.php b/src/Injector/AcquiaCliInjector.php index 3d25849..09ccf51 100644 --- a/src/Injector/AcquiaCliInjector.php +++ b/src/Injector/AcquiaCliInjector.php @@ -23,6 +23,7 @@ use AcquiaCloudApi\Endpoints\Insights; use AcquiaCloudApi\Endpoints\LogForwardingDestinations; use AcquiaCloudApi\Endpoints\SslCertificates; +use AcquiaCloudApi\Endpoints\Organizations; class AcquiaCliInjector implements ParameterInjector { @@ -87,6 +88,8 @@ public function get(CommandData $commandData, $interfaceName) return new LogForwardingDestinations($this->client); case 'AcquiaCloudApi\Endpoints\SslCertificates': return new SslCertificates($this->client); + case 'AcquiaCloudApi\Endpoints\Organizations': + return new Organizations($this->client); } } } diff --git a/tests/AcquiaCliApplicationTest.php b/tests/AcquiaCliApplicationTest.php index 8c577f5..35dcd72 100644 --- a/tests/AcquiaCliApplicationTest.php +++ b/tests/AcquiaCliApplicationTest.php @@ -2,7 +2,6 @@ namespace AcquiaCli\Tests; -use AcquiaCli\Tests\AcquiaCliTestCase; use AcquiaCli\Cli\Config; use AcquiaCli\Cli\CloudApi; use Symfony\Component\Console\Input\ArgvInput; @@ -111,7 +110,7 @@ public function testWaitForNotifications() { $config = new Config($this->root); - $command = ['acquiacli', '--yes', 'db:backup', 'devcloud:devcloud2', 'dev']; + $command = ['acquiacli', '--yes', 'db:backup', 'devcloud:devcloud2', 'dev', 'database1']; $input = new ArgvInput($command); $output = new BufferedOutput(); $app = new AcquiaCli($config, $this->client, $input, $output); @@ -121,7 +120,7 @@ public function testWaitForNotifications() $stopwatch->start('5s-sleep', 'notifications'); $app->run($input, $output); $sleep5 = $stopwatch->stop('5s-sleep'); - $this->assertGreaterThan(6000, $sleep5->getDuration()); + $this->assertGreaterThan(5000, $sleep5->getDuration()); $sleep5Output = $output->fetch(); // Change the task wait threshold to 2s and try again. @@ -132,7 +131,7 @@ public function testWaitForNotifications() $stopwatch->start('2s-sleep', 'notifications'); $app->run($input, $output); $sleep2 = $stopwatch->stop('2s-sleep'); - $this->assertLessThan(5000, $sleep2->getDuration()); + $this->assertLessThan(3000, $sleep2->getDuration()); $sleep2Output = $output->fetch(); \Robo\Robo::unsetContainer(); @@ -142,10 +141,6 @@ public function testWaitForNotifications() Looking up notification < 1 sec [➤⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬] 0% -> Backing up DB (database2) on Dev - Looking up notification -< 1 sec [➤⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬⚬] 0% - OUTPUT; $this->assertSame($notificationOutput . PHP_EOL, $sleep5Output, 'Testing 5s sleep output'); diff --git a/tests/AcquiaCliTestCase.php b/tests/AcquiaCliTestCase.php new file mode 100644 index 0000000..e43186b --- /dev/null +++ b/tests/AcquiaCliTestCase.php @@ -0,0 +1,435 @@ +client = $this->getMockClient(); + $this->logstream = $this->getMockLogstream(); + } + + protected function getPsr7StreamForFixture($fixture): StreamInterface + { + $path = sprintf( + '%s/vendor/typhonius/acquia-php-sdk-v2/tests/Fixtures/Endpoints/%s', + dirname(__DIR__), + $fixture + ); + $this->assertFileExists($path); + $stream = Psr7\stream_for(file_get_contents($path)); + $this->assertInstanceOf(Psr7\Stream::class, $stream); + + return $stream; + } + + /** + * Returns a PSR7 Response (JSON) for a given fixture. + * + * @param string $fixture The fixture to create the response for. + * @param integer $statusCode A HTTP Status Code for the response. + * @return Psr7\Response + */ + protected function getPsr7JsonResponseForFixture($fixture, $statusCode = 200): Psr7\Response + { + $stream = $this->getPsr7StreamForFixture($fixture); + $this->assertNotNull(json_decode($stream)); + $this->assertEquals(JSON_ERROR_NONE, json_last_error()); + + return new Psr7\Response($statusCode, ['Content-Type' => 'application/json'], $stream); + } + + /** + * Returns a PSR7 Response (Gzip) for a given fixture. + * + * @param string $fixture The fixture to create the response for. + * @param integer $statusCode A HTTP Status Code for the response. + * @return Psr7\Response + */ + protected function getPsr7GzipResponseForFixture($fixture, $statusCode = 200): Psr7\Response + { + $stream = $this->getPsr7StreamForFixture($fixture); + $this->assertEquals(JSON_ERROR_NONE, json_last_error()); + + return new Psr7\Response($statusCode, ['Content-Type' => 'application/octet-stream'], $stream); + } + + /** + * Mock client class. + * + * @return Client + */ + protected function getMockClient() + { + $connector = $this + ->getMockBuilder('AcquiaCloudApi\Connector\Connector') + ->disableOriginalConstructor() + ->setMethods(['sendRequest']) + ->getMock(); + + $connector + ->expects($this->any()) + ->method('sendRequest') + ->will($this->returnCallback(array($this, 'sendRequestCallback'))); + + return Client::factory($connector); + } + + /** + * Mock client class. + * + * @return LogstreamManager + */ + protected function getMockLogstream() + { + $logstream = $this + ->getMockBuilder('AcquiaLogstream\LogstreamManager') + ->disableOriginalConstructor() + ->setMethods(['stream']) + ->getMock(); + + $logstream + ->expects($this->any()) + ->method('stream') + ->willReturn(''); + + return $logstream; + } + + /** + * Callback for the mock client. + */ + public function sendRequestCallback($verb, $path) + { + $fixtureMap = self::getFixtureMap(); + + if ($fixture = $fixtureMap[$path][$verb]) { + // Add in overrides for fixtures which should be downloaded + // rather than responded to e.g. log:download + if ($fixture === 'Logs/downloadLog.dat' + || $fixture === 'DatabaseBackups/downloadDatabaseBackup.dat' + || $fixture === 'Account/getDrushAliases.dat' + ) { + return $this->getPsr7GzipResponseForFixture($fixture); + } + return $this->getPsr7JsonResponseForFixture($fixture); + } + } + + /** + * Run commands with a mock client. + * + * @see bin/acquia-robo.php + */ + public function execute($command) + { + // Create an instance of the application and use some default parameters. + $root = dirname(dirname(__DIR__)); + $config = new Config($root); + $loader = new YamlConfigLoader(); + $processor = new ConfigProcessor(); + $processor->extend($loader->load(dirname(__DIR__) . '/default.acquiacli.yml')); + $config->import($processor->export()); + + array_unshift($command, 'acquiacli', '--no-wait', '--yes'); + $input = new ArgvInput($command); + $output = new BufferedOutput(); + + $app = new AcquiaCli($config, $this->client, $input, $output); + + // Override the LogstreamManager with a mock in the container. + $container = Robo::getContainer(); + $container->add('logstream', $this->logstream); + $parameterInjection = $container->get('parameterInjection'); + $parameterInjection->register('AcquiaLogstream\LogstreamManager', new AcquiaCliInjector); + Robo::setContainer($container); + + $app->run($input, $output); + + // Unset the container so we're dealing with a fresh state for each command + // This mimics the behaviour expected by users interacting with the application. + Robo::unsetContainer(); + + return $output->fetch(); + } + + public static function getFixtureMap() + { + return [ + '/account' => [ + 'get' => 'Account/getAccount.json' + ], + '/account/drush-aliases/download' => [ + 'get' => 'Account/getDrushAliases.dat' + ], + '/applications' => [ + 'get' => 'Applications/getAllApplications.json', + ], + '/applications/a47ac10b-58cc-4372-a567-0e02b2c3d470' => [ + 'put' => 'Applications/renameApplication.json' + ], + '/applications/a47ac10b-58cc-4372-a567-0e02b2c3d470/environments' => [ + 'get' => 'Environments/getAllEnvironments.json' + ], + '/applications/a47ac10b-58cc-4372-a567-0e02b2c3d470/tags' => [ + 'get' => 'Applications/getAllTags.json', + 'post' => 'Applications/createTag.json', + ], + '/applications/a47ac10b-58cc-4372-a567-0e02b2c3d470/tags/name' => [ + 'delete' => 'Applications/deleteTag.json', + ], + '/applications/a47ac10b-58cc-4372-a567-0e02b2c3d470/databases' => [ + 'get' => 'Databases/getAllDatabases.json', + 'post' => 'Databases/createDatabases.json', + ], + '/applications/a47ac10b-58cc-4372-a567-0e02b2c3d470/databases/dbName/actions/erase' => [ + 'post' => 'Databases/truncateDatabases.json', + ], + '/applications/a47ac10b-58cc-4372-a567-0e02b2c3d470/databases/dbName' => [ + 'delete' => 'Databases/deleteDatabases.json' + ], + '/roles/roleUuid' => [ + 'put' => 'Roles/updateRole.json', + 'delete' => 'Roles/deleteRole.json' + ], + '/applications/a47ac10b-58cc-4372-a567-0e02b2c3d470/notifications' => [ + 'get' => 'Notifications/getAllNotifications.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470' => [ + 'delete' => 'Environments/deleteCDEnvironment.json' + ], + '/environments/32-a47ac10b-58cc-4372-a567-0e02b2c3d470/databases/database1/backups' => [ + 'get' => 'DatabaseBackups/getAllDatabaseBackups.json', + 'post' => 'DatabaseBackups/createDatabaseBackup.json' + ], + '/environments/32-a47ac10b-58cc-4372-a567-0e02b2c3d470/databases/database2/backups' => [ + 'get' => 'DatabaseBackups/getAllDatabaseBackups.json', + 'post' => 'DatabaseBackups/createDatabaseBackup.json' + ], + '/environments/15-a47ac10b-58cc-4372-a567-0e02b2c3d470/databases/database1/backups' => [ + 'get' => 'DatabaseBackups/getAllDatabaseBackups.json', + 'post' => 'DatabaseBackups/createDatabaseBackup.json' + ], + '/environments/15-a47ac10b-58cc-4372-a567-0e02b2c3d470/databases/database2/backups' => [ + 'get' => 'DatabaseBackups/getAllDatabaseBackups.json', + 'post' => 'DatabaseBackups/createDatabaseBackup.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/databases/database1/backups' => [ + 'get' => 'DatabaseBackups/getAllDatabaseBackups.json', + 'post' => 'DatabaseBackups/createDatabaseBackup.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/databases/database2/backups' => [ + 'get' => 'DatabaseBackups/getAllDatabaseBackups.json', + 'post' => 'DatabaseBackups/createDatabaseBackup.json' + ], + '/environments/32-a47ac10b-58cc-4372-a567-0e02b2c3d470/databases' => [ + 'post' => 'Databases/copyDatabases.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/databases' => [ + 'post' => 'Databases/copyDatabases.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/databases/dbName/backups/1234/actions/restore' => [ + 'post' => 'DatabaseBackups/restoreDatabaseBackup.json', + ], + '/environments/bfcc7ad1-f987-41b8-9ea5-f26f0ef3838a/databases/database2/backups/1/actions/download' => [ + 'get' => 'DatabaseBackups/downloadDatabaseBackup.dat' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/databases/database2/backups/1/actions/download' => [ + 'get' => 'DatabaseBackups/downloadDatabaseBackup.dat' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/domains' => [ + 'post' => 'Domains/createDomain.json', + 'get' => 'Domains/getAllDomains.json' + ], + '/environments/32-a47ac10b-58cc-4372-a567-0e02b2c3d470/domains' => [ + 'post' => 'Domains/createDomain.json', + 'get' => 'Domains/getAllDomains.json' + ], + '/environments/32-a47ac10b-58cc-4372-a567-0e02b2c3d470/domains/domain' => [ + 'delete' => 'Domains/deleteDomain.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/domains/domain' => [ + 'delete' => 'Domains/deleteDomain.json' + ], + '/environments/15-a47ac10b-58cc-4372-a567-0e02b2c3d470/domains/domain/status' => [ + 'get' => 'Domains/getDomainStatus.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/domains/actions/clear-varnish' => [ + 'post' => 'Domains/purgeVarnish.json' + ], + '/environments/32-a47ac10b-58cc-4372-a567-0e02b2c3d470/files' => [ + 'post' => 'Environments/copyFiles.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/files' => [ + 'post' => 'Environments/copyFiles.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/crons' => [ + 'get' => 'Crons/getAllCrons.json', + 'post' => 'Crons/createCron.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/crons/cronId' => [ + 'get' => 'Crons/getCron.json', + 'delete' => 'Crons/deleteCron.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/crons/cronId/actions/enable' => [ + 'post' => 'Crons/enableCron.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/crons/cronId/actions/disable' => [ + 'post' => 'Crons/disableCron.json' + ], + '/environments/15-a47ac10b-58cc-4372-a567-0e02b2c3d470/code/actions/switch' => [ + 'post' => 'Code/switchCode.json' + ], + '/environments/32-a47ac10b-58cc-4372-a567-0e02b2c3d470/code' => [ + 'post' => 'Code/deployCode.json' + ], + '/applications/a47ac10b-58cc-4372-a567-0e02b2c3d470/code' => [ + 'get' => 'Code/getAllCode.json' + ], + '/applications/a47ac10b-58cc-4372-a567-0e02b2c3d470/insight' => [ + 'get' => 'Insights/getAllInsights.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/livedev/actions/disable' => [ + 'post' => 'Environments/disableLiveDev.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/livedev/actions/enable' => [ + 'post' => 'Environments/enableLiveDev.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/servers' => [ + 'get' => 'Servers/getAllServers.json' + ], + '/environments/15-a47ac10b-58cc-4372-a567-0e02b2c3d470/servers' => [ + 'get' => 'Servers/getAllServers.json' + ], + '/environments/32-a47ac10b-58cc-4372-a567-0e02b2c3d470/servers' => [ + 'get' => 'Servers/getAllServers.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/actions/change-label' => [ + 'post' => 'Environments/renameEnvironment.json' + ], + '/environments/15-a47ac10b-58cc-4372-a567-0e02b2c3d470/production-mode/actions/disable' => [ + 'post' => 'Environments/disableProductionMode.json' + ], + '/environments/15-a47ac10b-58cc-4372-a567-0e02b2c3d470/production-mode/actions/enable' => [ + 'post' => 'Environments/disableProductionMode.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/variables/variable_one' => [ + 'put' => 'Variables/updateVariable.json', + 'get' => 'Variables/getVariable.json', + 'delete' => 'Variables/deleteVariable.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/variables' => [ + 'get' => 'Variables/getAllVariables.json', + 'post' => 'Variables/createVariable.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/logs' => [ + 'get' => 'Logs/getAllLogs.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/logs/apache-access' => [ + 'get' => 'Logs/downloadLog.dat', + 'post' => 'Logs/createLogSnapshot.json' + ], + '/teams/teamUuid/invites' => [ + 'post' => 'Teams/invite.json' + ], + '/teams/teamUuid/applications' => [ + 'post' => 'Teams/addApplication.json' + ], + '/permissions' => [ + 'get' => 'Permissions/getPermissions.json' + ], + '/notifications/f4b37e3c-1g96-4ed4-ad20-3081fe0f9545' => [ + 'get' => 'Notifications/getNotification.json' + ], + '/organizations' => [ + 'get' => 'Organizations/getAllOrganizations.json' + ], + '/organizations/g47ac10b-58cc-4372-a567-0e02b2c3d470/admins' => [ + 'get' => 'Organizations/getAdmins.json' + ], + '/organizations/g47ac10b-58cc-4372-a567-0e02b2c3d470/teams' => [ + 'get' => 'Organizations/getTeams.json', + 'post' => 'Teams/createTeam.json' + ], + '/organizations/g47ac10b-58cc-4372-a567-0e02b2c3d470/applications' => [ + 'get' => 'Organizations/getApplications.json' + ], + '/organizations/g47ac10b-58cc-4372-a567-0e02b2c3d470/members' => [ + 'get' => 'Organizations/getMembers.json' + ], + '/organizations/g47ac10b-58cc-4372-a567-0e02b2c3d470/roles' => [ + 'get' => 'Roles/getAllRoles.json', + 'post' => 'Roles/createRole.json' + ], + '/insight/siteId/modules' => [ + 'get' => 'Insights/getModules.json' + ], + '/insight/siteId/alerts' => [ + 'get' => 'Insights/getAllAlerts.json' + ], + '/insight/siteId/alerts/alertUuid' => [ + 'get' => 'Insights/getAlert.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/insight' => [ + 'get' => 'Insights/getEnvironment.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/logstream' => [ + 'get' => 'Logs/getLogstream.json', + ], + '/applications/foobar/environments' => [ + 'get' => 'Environments/getAllEnvironments.json' + ], + '/applications/foobar/databases' => [ + 'get' => 'Databases/getAllDatabases.json', + 'post' => 'Databases/createDatabases.json', + ], + '/notifications/42b56cff-0b55-4bdf-a949-1fd0fca61c6c' => [ + 'get' => 'Notifications/getNotification.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/log-forwarding-destinations' => [ + 'get' => 'LogForwarding/getAllLogForwarding.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/log-forwarding-destinations/1234' => [ + 'get' => 'LogForwarding/getLogForwarding.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/ssl/certificates' => [ + 'get' => 'SslCertificates/getAllSslCertificates.json' + ], + '/environments/24-a47ac10b-58cc-4372-a567-0e02b2c3d470/ssl/certificates/1234' => [ + 'get' => 'SslCertificates/getSslCertificate.json' + ] + ]; + } + + protected function getPrivateProperty($className, $propertyName) + { + $reflector = new \ReflectionClass($className); + $property = $reflector->getProperty($propertyName); + $property->setAccessible(true); + + return $property; + } +} diff --git a/tests/Commands/DbBackupCommandTest.php b/tests/Commands/DbBackupCommandTest.php index 78aa6ec..3263830 100644 --- a/tests/Commands/DbBackupCommandTest.php +++ b/tests/Commands/DbBackupCommandTest.php @@ -82,9 +82,11 @@ public function dbBackupProvider() +-----+-------+----------------------+ TABLE; - $createBackupText = '> Backing up DB (database1) on Dev + $createBackupAllText = '> Backing up DB (database1) on Dev > Backing up DB (database2) on Dev'; + $createBackupText = '> Backing up DB (database1) on Dev'; + $dbLink = sprintf( '%s/environments/%s/databases/%s/backups/%s/actions/download', '> https://cloud.acquia.com/api', @@ -99,7 +101,11 @@ public function dbBackupProvider() '> Restoring backup 1234 to dbName on Dev' . PHP_EOL ], [ - ['database:backup', 'devcloud:devcloud2', 'dev'], + ['database:backup:all', 'devcloud:devcloud2', 'dev'], + $createBackupAllText . PHP_EOL + ], + [ + ['database:backup', 'devcloud:devcloud2', 'dev', 'database1'], $createBackupText . PHP_EOL ], [ diff --git a/tests/Commands/DomainsCommandTest.php b/tests/Commands/DomainsCommandTest.php index 7c22299..4f7f8ca 100644 --- a/tests/Commands/DomainsCommandTest.php +++ b/tests/Commands/DomainsCommandTest.php @@ -23,7 +23,7 @@ public function domainsProvider() +-------------+--------+--------------+--------------+---------------------+ | Hostname | Active | DNS Resolves | IP Addresses | CNAMES | +-------------+--------+--------------+--------------+---------------------+ -| example.com | ✓ | ✓ | 12.23.34.45 | another.example.com | +| example.com | ✓ | ✓ | 12.23.34.45 | another.example.com | +-------------+--------+--------------+--------------+---------------------+ TABLE; diff --git a/tests/Commands/SshCommandTest.php b/tests/Commands/SshCommandTest.php new file mode 100644 index 0000000..60341ac --- /dev/null +++ b/tests/Commands/SshCommandTest.php @@ -0,0 +1,39 @@ +execute($command); + $this->assertSame($expected, $actualResponse); + } + + public function sshProvider() + { + + $infoResponse = << dev: ssh +> prod: ssh +> test: ssh +INFO; + + return [ + [ + ['ssh:info', 'devcloud:devcloud2'], + $infoResponse . PHP_EOL + ], + [ + ['ssh:info', 'devcloud:devcloud2', 'dev'], + $infoResponse . PHP_EOL, + ] + ]; + } +}