예제 #1
0
 /**
  * Check if the project id or identifier is valid.
  *
  * @param int|string $project_id
  * @return int|bool
  */
 private function checkProject($project_id)
 {
     $project = $this->client->api('project')->show($project_id);
     if (!isset($project['project']['id'])) {
         return false;
     }
     return $project['project']['id'];
 }
예제 #2
0
 /**
  * Load user data from API.
  *
  * @param $id
  */
 public function loadUser($id, $reset = FALSE)
 {
     if (!isset($this->users[$id]) || $reset) {
         $user = $this->client->api('user')->show($id);
         if (!empty($user['user'])) {
             $this->users[$id] = new User($this, $user['user']);
         }
     }
     return $this->users[$id];
 }
예제 #3
0
 /**
  * Just return the data from runRequest().
  *
  * @param type $path
  * @param type $decode
  *
  * @return array
  */
 public function get($path, $decode = true)
 {
     if ($this->useOriginalGetMethod) {
         return parent::get($path, $decode);
     }
     return $this->runRequest($path, 'GET');
 }
예제 #4
0
 /**
  * iterates over a list of issues and deletes them using their Ids.
  * 
  * @param array              $issues   The Issue List
  * @param \Redmine\Api\Issue $issueApi The Issue Api object, optional.
  * 
  * @return void 
  */
 private function deleteIssueList(array $issues, \Redmine\Api\Issue $issueApi = null)
 {
     $issueApi = $issueApi ?: $this->client->api('issue');
     /* @var $issueApi \Redmine\Api\Issue */
     foreach ($issues['issues'] as $issue) {
         $issueApi->remove($issue['id']);
     }
 }
예제 #5
0
 /**
  * Sync a single harvest time entry.
  *
  * @param \Harvest\Model\DayEntry $harvest_entry
  *
  * @return bool
  */
 protected function syncEntry(DayEntry $harvest_entry)
 {
     // Check spelling.
     $words = explode(' ', preg_replace('/[^a-z]+/i', ' ', $harvest_entry->get('notes')));
     $spelling_errors = array();
     foreach ($words as $word) {
         if (!pspell_check($this->pspellLink, $word)) {
             $spelling_errors[] = $word;
         }
     }
     if ($spelling_errors) {
         $this->userTimeEntryErrors[$harvest_entry->get('user-id')]['spelling'][] = ['entry' => $harvest_entry, 'spelling-errors' => $spelling_errors];
     }
     $redmine_issue = $this->getRedmineIssue($harvest_entry);
     if (!$redmine_issue) {
         return false;
     }
     $existing_redmine_time_entries = $this->getExistingRedmineIssueTimeEntries($redmine_issue, $harvest_entry);
     // If there are existing Redmine time entries matching this harvest entry and we are not updating, skip.
     if (count($existing_redmine_time_entries) > 0 && !$this->input->getOption('update')) {
         return false;
     }
     // Or if there is more than one matching redmine time entry, throw an error and continue.
     if (count($existing_redmine_time_entries) > 1) {
         $this->output->writeln(sprintf('<error>Multiple Redmine time entries matching harvest time entry %d. See entries %s</error>', $harvest_entry->get('id'), json_encode($existing_redmine_time_entries)));
         $this->errors = true;
         return false;
     }
     // If Harvest user is not mapped to a redmine user, throw an error and continue.
     if (!isset($this->userMap[$harvest_entry->get('user-id')])) {
         $this->output->writeln(sprintf('<error>No mapping is defined for user %d</error>', $harvest_entry->get('user-id')));
         $this->errors = true;
         return false;
     }
     // Log the entry.
     $redmine_entry_params = $this->populateRedmineTimeEntry($redmine_issue, $harvest_entry);
     // Check rounding.
     if ($redmine_entry_params['hours'] != $harvest_entry->get('hours')) {
         $this->userTimeEntryErrors[$harvest_entry->get('user-id')]['rounding'][] = ['entry' => $harvest_entry, 'rounded-hours' => $redmine_entry_params['hours']];
     }
     $save_entry_result = false;
     $this->setRedmineClient();
     if (!$this->input->getOption('dry-run')) {
         try {
             $this->redmineClient->setImpersonateUser($this->userMap[$harvest_entry->get('user-id')]);
             $save_entry_result = $this->saveHarvestTimeEntryToRedmine($redmine_entry_params, $existing_redmine_time_entries);
         } catch (\Exception $e) {
             $this->output->writeln(sprintf('<error>Failed to create time entry for redmine issue #%d, harvest id %d, exception %s</error>', $redmine_issue['issue']['id'], $harvest_entry->get('id'), $e->getMessage()));
         } finally {
             $this->redmineClient->setImpersonateUser(null);
         }
     }
     if ($save_entry_result || $this->input->getOption('dry-run')) {
         $this->output->writeln(sprintf('<comment>%s time entry for issue #%d with %s hours (Harvest hours: %s)</comment>', count($existing_redmine_time_entries) > 0 ? 'Updated' : 'Created', $redmine_issue['issue']['id'], $redmine_entry_params['hours'], $harvest_entry->get('hours')));
     }
     return $save_entry_result;
 }
예제 #6
0
 /**
  * Create follow up issue to given issue
  *
  * @param array $issue
  */
 public function createAction($issue)
 {
     $redmineClient = new Redmine\Client($this->settings['Redmine']['url'], $this->settings['Redmine']['apiKey']);
     $id = $issue['related_id'];
     unset($issue['related_id']);
     $params = $issue;
     // get additional issue data and assign to new issue
     $res = $redmineClient->api('issue')->show($id);
     $params['project_id'] = Arrays::getValueByPath($res, 'issue.project.id');
     $params['category_id'] = Arrays::getValueByPath($res, 'issue.category.id');
     $newIssue = $redmineClient->api('issue')->create($params);
     // create relation
     $relation['issue_to_id'] = $id;
     $relation['relation_type'] = self::RELATION_TYPE;
     $json = json_encode(array('relation' => $relation));
     $redmineClient->post('/issues/' . (string) $newIssue->id . '/relations.json', $json);
     $this->view->assign('value', json_decode(json_encode((array) $newIssue), 1));
 }
예제 #7
0
 /**
  * Factory method to be implemented from \RedmineCommand\AbstractCommand .
  *
  *
  * Must return an instance of \RedmineCommand\SlackResult .
  *
  * @see \RedmineCommand\AbstractCommand::executeImpl()
  * @return \RedmineCommand\SlackResult
  */
 protected function executeImpl()
 {
     $log = $this->log;
     $result = new SlackResult();
     $log->debug("CmdShow: Issues Id: " . implode(",", $this->cmd));
     $client = new Client($this->config->redmine_url, $this->config->redmine_api_key);
     $resultText = "[requested by " . $this->post["user_name"] . "]";
     if (empty($this->cmd)) {
         $resultText .= " Issue number required!";
     } else {
         $resultText .= " Issue Details: ";
     }
     // Fetching issues and adding them as slack attachments
     $attachments = array();
     $attachmentUnknown = null;
     foreach ($this->cmd as $issueId) {
         $log->debug("CmdShow: calling Redmine api for issue id #{$issueId}");
         $issue = $client->api('issue')->show((int) $issueId);
         $attachment = new SlackResultAttachment();
         if (!is_array($issue)) {
             if (strcmp($issue, "Syntax error") == 0) {
                 if ($attachmentUnknown == null) {
                     $attachmentUnknown = new SlackResultAttachment();
                     $attachmentUnknown->setTitle("Unknown Issues:");
                     $attachmentUnknown->setText("");
                 }
                 $log->debug("CmdShow: #{$issueId} issue unknown!");
                 $attachmentUnknown->setText($attachmentUnknown->getText() . " {$issueId}");
             }
         } else {
             $log->debug("CmdShow: #{$issueId} issue found!");
             $attachment = Util::convertIssueToAttachment($this->config->getRedmineIssuesUrl(), $issueId, $issue);
             $attachments[] = $attachment;
         }
     }
     $result->setText($resultText);
     if ($attachmentUnknown != null) {
         $attachments[] = $attachmentUnknown;
     }
     $result->setAttachmentsArray($attachments);
     return $result;
 }
예제 #8
0
 /**
  * Authenticate to remote API
  * @return Redmine
  */
 protected function authenticate()
 {
     $redmine = new Redmine($this->redmineProtocol . $this->redmineUrl, $this->redmineToken);
     $redmine->setPort($this->redminePort);
     return $redmine;
 }
예제 #9
0
 /**
  * Perform the client delete() method.
  *
  * @param string $path
  *
  * @return array
  */
 protected function delete($path)
 {
     return $this->client->delete($path);
 }
예제 #10
0
 /**
  * @covers Redmine\Client
  * @test
  * @dataProvider getApiClassesProvider
  */
 public function shouldGetApiInstance($apiName, $class)
 {
     $client = new Client('http://test.local', 'asdf');
     $this->assertInstanceOf($class, $client->api($apiName));
 }
예제 #11
0
 function it_presents_a_summary_before_initiating_the_migration(Client $redmine, Project $project)
 {
     $project_detail = ['project' => ['name' => 'Redmine Project']];
     $redmine->api('project')->willReturn($project);
     $project->show(34)->willReturn($project_detail);
     $mock = PHPMockery::mock('\\Ttf\\Remaim', 'fgets')->andReturn('y');
     $phabricator_project = ['name' => 'Phabricator Project', 'id' => 78];
     $policies = ['view' => 'PHID-foobar', 'edit' => 'PHID-barbaz'];
     $tasks = ['total_count' => [10]];
     ob_start();
     $this->presentSummary(34, $phabricator_project, $tasks, $policies)->shouldReturn(true);
     $output = ob_get_clean();
     expect($output)->toBe(PHP_EOL . PHP_EOL . '####################' . PHP_EOL . '# Pre-flight check #' . PHP_EOL . '####################' . PHP_EOL . 'Redmine project named "Redmine Project" with ID 34.' . PHP_EOL . 'Target phabricator project named "Phabricator Project" with ID 78.' . PHP_EOL . 'View policy: PHID-foobar, Edit policy: PHID-barbaz' . PHP_EOL . '10 tickets to be migrated!' . PHP_EOL . PHP_EOL . 'OK to continue? [y/N]:' . PHP_EOL . '> ');
 }