diff --git a/CHANGELOG.md b/CHANGELOG.md index a5465f7..18510ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ## [0.0.3] - implement date arguments for weekly-report #5 +- add `close` command to close issues, #4 [0.0.3]: https://github.com/eventum/cli/compare/0.0.2...master diff --git a/README.md b/README.md index 2ca3830..14865b4 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Available commands: | Command | | | ------------- | ------------- | | **add-attachment** | Add attachment to issue | +| **close** | Marks an issue as closed | | **open-issues** | List open issues | | **set-status**, **ss** | Set Issue status | | **view-issue** | Display Issue details | diff --git a/src/Application.php b/src/Application.php index 56e8106..b620cc4 100644 --- a/src/Application.php +++ b/src/Application.php @@ -57,6 +57,7 @@ protected function getDefaultCommands() $commands[] = new Command\ConfigCommand(); $commands[] = new Command\AddTimeEntryCommand(); $commands[] = new Command\SetIssueStatusCommand(); + $commands[] = new Command\CloseIssueCommand(); if (strpos(__FILE__, 'phar:') === 0) { $commands[] = new Command\SelfUpdateCommand('self-update'); diff --git a/src/Command/CloseIssueCommand.php b/src/Command/CloseIssueCommand.php new file mode 100644 index 0000000..ad89c69 --- /dev/null +++ b/src/Command/CloseIssueCommand.php @@ -0,0 +1,197 @@ +setName('close') + ->setDescription('Marks an issue as closed') + ->addArgument( + 'issue_id', + InputArgument::REQUIRED, + 'Issue id' + ) + ->addOption( + 'status', + 's', + InputOption::VALUE_REQUIRED, + 'New status for issue' + ) + ->addOption( + 'resolution', + 'r', + InputOption::VALUE_REQUIRED, + 'Resolution for issue' + ) + ->addOption( + 'notify', + null, + InputOption::VALUE_NONE, + 'Send notification about issue close', + null + ) + ->addOption( + 'no-notify', + null, + InputOption::VALUE_NONE, + 'Do not send notification about issue close', + null + ) + ->addOption( + 'message', + 'm', + InputOption::VALUE_REQUIRED, + 'Note message' + ) + // project_id required when retrieving statuses + // should really figure that out by asking via issue_id instead + ->addOption( + 'project', + null, + InputOption::VALUE_REQUIRED, + 'Project Id' + ) + ->setHelp( + <<%command.full_name% 123 + +Marks an issue as closed. +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $issue_id = (int )$input->getArgument('issue_id'); + $status = $this->getStatus(); + $resolution_id = $this->getResolutionId(); + $send_notification = $this->getSendNotification(); + $note = $this->getMessage(); + + $client = $this->getClient(); + $result = $client->closeIssue($issue_id, $status, $resolution_id, $send_notification, $note); + + $message = "OK: issue #$issue_id successfully closed."; + $output->writeln($message); + + if ($result === 'INCIDENT') { + $message = 'WARNING: This customer has incidents.'; + $message .= " Please redeem incidents by running 'eventum $issue_id redeem'"; + $output->writeln($message); + } + } + + /** + * Return issue status title. + * + * @return string + */ + private function getStatus() + { + $default = $this->input->getOption('status'); + + $list = $this->getClient()->getClosedAbbreviationAssocList($this->getProjectId()); + $prompt = 'Which status do you want to use in this action?'; + $errorMessage = "Entered status doesn't match any in the list available to you"; + + $answer = $this->askChoices($prompt, $list, $errorMessage, $default); + + return $list[$answer]; + } + + /** + * @return int + */ + private function getResolutionId() + { + $resolution = $this->input->getOption('resolution'); + if ($resolution) { + return (int)$resolution; + } + + $list = $this->getClient()->getResolutionAssocList(); + $prompt = 'Which resolution do you want to use in this action?'; + $errorMessage = "Entered resolution doesn't match any in the list available to you"; + + return (int)$this->askChoices($prompt, $list, $errorMessage); + } + + /** + * @return bool + */ + private function getSendNotification() + { + $notify = $this->input->getOption('notify'); + $no_notify = $this->input->getOption('no-notify'); + if ($notify || $no_notify) { + return $notify || !$no_notify; + } + + $question = 'Would you like to send a notification email about this issue being closed? [Y/n]: '; + + return $this->io->askConfirmation($question, 'y'); + } + + /** + * Get issue close message from commandline option or prompt from user. + * + * @return string + */ + private function getMessage() + { + $message = $this->input->getOption('message'); + if ($message) { + return $message; + } + + $question = 'Please enter a reason for closing this issue (one line only): '; + + return $this->io->ask($question); + } + + /** + * ask choices, but return the key not value from the list. + */ + private function askChoices($prompt, $list, $errorMessage, $default = null) + { + // default given and present in $list, use that + if ($default && isset($list[$default])) { + return $default; + } + + // avoid asking if answer is known + switch (count($list)) { + case 0: + return 0; + case 1: + return key($list); + } + + if ($default) { + $prompt .= " ($default)"; + } + + $answer = $this->io->askChoices($prompt, $list, $errorMessage, $default); + + return array_search($answer, $list); + } +}