cache = $cache; $this->router = $router; $this->httpClient = $client; $this->config = $config; $this->teamService = $teamService; } /** * @param int $teamId * @param bool $forceReload * @return KanbanBoard */ public function getKanbanBoard(int $teamId = null, bool $forceReload = false): KanbanBoard { $team = $this->teamService->getTeam($teamId); $teamName = $team->getName(); $kanbanBoard = $this->cache->getItem(sprintf("%s-%s", self::CACHE_KEY_KANBANBOARD, $teamName)); if ($forceReload || null === $kanbanBoard) { $user = $this->config->get('jira.user'); $password = $this->config->get('jira.password'); /** @var Config $kanbanBoardUriParams */ $kanbanBoardUri = $this->config->get('url.jiraKanbanBoard'); $kanbanBoardFilter = $this->config->get('jira.filterFields')->toArray(); $kanbanBoardUri = sprintf( $kanbanBoardUri, $team->getFilterId(), implode(",", $kanbanBoardFilter) ); $response = $this->httpClient ->setUri($kanbanBoardUri) ->setAuth($user, $password) ->send(); if (!$response->isSuccess()) { throw new \UnexpectedValueException("Bad JIRA result", $response->getStatusCode()); } $parsedJsonData = Decoder::decode($response->getBody(), Json::TYPE_ARRAY); $kanbanBoard = $this->hydrateKanbanBoard($team, $parsedJsonData); $this->cache->setItem(sprintf("%s-%s", self::CACHE_KEY_KANBANBOARD, $teamName), serialize($kanbanBoard)); } else { $kanbanBoard = unserialize($kanbanBoard); } return $kanbanBoard; } /** * @param string $parentKey * @return null|string */ private function getEpicNameFromParent(string $parentKey): ?string { if (array_key_exists($parentKey, $this->cachedEpics)) { return $this->cachedEpics[$parentKey]; } $user = $this->config->get('jira.user'); $password = $this->config->get('jira.password'); /** @var Config $kanbanBoardUriParams */ $jiraIssueBaseUrl = $this->config->get('url.jiraIssue'); $kanbanBoardFilter = $this->config->get('jira.filterFields')->toArray(); $kanbanBoardFilterString = implode(",", $kanbanBoardFilter); $jiraIssueUri = sprintf($jiraIssueBaseUrl, $parentKey, $kanbanBoardFilterString); $response = $this->httpClient ->setUri($jiraIssueUri) ->setAuth($user, $password) ->send(); if (!$response->isSuccess()) { throw new \UnexpectedValueException("Bad JIRA result: $jiraIssueUri", $response->getStatusCode()); } $parsedJsonParentData = Decoder::decode($response->getBody(), Json::TYPE_ARRAY); if ($parsedJsonParentData['fields'][self::EPIC_TICKET_LINK]) { $jiraIssueUri = sprintf( $jiraIssueBaseUrl, $parsedJsonParentData['fields'][self::EPIC_TICKET_LINK], $kanbanBoardFilterString ); $response = $this->httpClient ->setUri($jiraIssueUri) ->setAuth($user, $password) ->send(); if (!$response->isSuccess()) { throw new \UnexpectedValueException("Bad JIRA result", $response->getStatusCode()); } $parsedJsonEpicData = Decoder::decode($response->getBody(), Json::TYPE_ARRAY); $this->cachedEpics[$parentKey] = $parsedJsonEpicData['fields'][self::EPIC_NAME_FIELD]; return $this->cachedEpics[$parentKey]; } $this->cachedEpics[$parentKey] = null; return null; } /** * @param Team $team * @param $parsedJsonData * @return KanbanBoard * @todo check if avatar has to be locally cached */ private function hydrateKanbanBoard(Team $team, $parsedJsonData): KanbanBoard { $kanbanBoard = new KanbanBoard(); $teamBacklogColumns = explode(self::BACKLOG_FIELD_DELIMITER, $team->getBacklogColumn()["jiraStatusName"]); $teamInprogressColumns = explode(self::BACKLOG_FIELD_DELIMITER, $team->getInprogressColumn()["jiraStatusName"]); $teamVerificationColumns = explode(self::BACKLOG_FIELD_DELIMITER, $team->getVerificationColumn()["jiraStatusName"]); $teamDoneColumns = explode(self::BACKLOG_FIELD_DELIMITER, $team->getDoneColumn()["jiraStatusName"]); foreach ($parsedJsonData['issues'] as $jsonIssue) { set_time_limit(30); $kanbanEntry = new KanbanEntry(); $kanbanEntry->setId(intval($jsonIssue['id'])) ->setKey($jsonIssue['key']) ->setSummary($jsonIssue['fields']['summary']) ->setIssuePriority($jsonIssue['fields']['priority']['name']) ->setIssuePriorityIcon($jsonIssue['fields']['priority']['iconUrl']) ->setLabels($jsonIssue['fields']['labels']) ->setFixVersions($jsonIssue['fields']['fixVersions']); $spikeTimeSpent = 0; array_map(function ($worklog) use (&$spikeTimeSpent) { $spikeTimeSpent += strtoupper($worklog['comment']) == 'BLOCKED' ? 0 : $worklog['timeSpentSeconds']; }, $jsonIssue['fields']['worklog']['worklogs']); $kanbanEntry->setWorklog((int)ceil($spikeTimeSpent / 3600)); $secondsBlocked = 0; array_map(function ($worklog) use (&$secondsBlocked) { $secondsBlocked += strtoupper($worklog['comment']) == 'BLOCKED' ? $worklog['timeSpentSeconds'] : 0; }, $jsonIssue['fields']['worklog']['worklogs']); $kanbanEntry->setDaysBlocked((int)ceil($secondsBlocked / 28800)); // additional assignees : customfield_10401 if (isset($jsonIssue['fields']['customfield_10401'])) { foreach ($jsonIssue['fields']['customfield_10401'] as $assignee) { $avatarUrl = $this->router->generateUri('avatar.image', [ 'signum' => $assignee['key'], ]); $jiraAssignee = new JiraAssignee(); $jiraAssignee->setName($assignee['displayName']) ->setSignum($assignee['key']) ->setEmail(strtolower($assignee['emailAddress'])) ->setAvatar($avatarUrl) ->setActive($assignee['active']); $kanbanEntry->addAdditionalAssignee($jiraAssignee); } } // epicName: have to fetch 2 extra records if (isset($jsonIssue['fields'][self::EPIC_TICKET_LINK])) { $epicName = $this->getEpicNameFromParent($jsonIssue['key']); $kanbanEntry->setEpicName($epicName); } elseif (isset($jsonIssue['fields']['parent'])) { $epicName = $this->getEpicNameFromParent($jsonIssue['fields']['parent']['key']); $kanbanEntry->setEpicName($epicName); } // jira status $jiraStatus = new JiraStatus(); $jiraStatus->setName($jsonIssue['fields']['status']['name']) ->setColor($jsonIssue['fields']['status']['statusCategory']['colorName']); $kanbanEntry->setStatus($jiraStatus); // assignee if ($jsonIssue['fields']['assignee']) { $avatarUrl = $this->router->generateUri('avatar.image', [ 'signum' => $jsonIssue['fields']['assignee']['key'], ]); $jiraAssignee = new JiraAssignee(); $jiraAssignee->setName($jsonIssue['fields']['assignee']['displayName']) ->setSignum($jsonIssue['fields']['assignee']['key']) ->setEmail(strtolower($jsonIssue['fields']['assignee']['emailAddress'])) ->setAvatar($avatarUrl) ->setActive($jsonIssue['fields']['assignee']['active']); $kanbanEntry->setAssignee($jiraAssignee); unset($jiraAssignee); } // issue type if ($jsonIssue['fields']['issuetype']) { $jiraIssueType = new JiraIssueType(); $jiraIssueType->setName($jsonIssue['fields']['issuetype']['name']) ->setDescription($jsonIssue['fields']['issuetype']['description']) ->setIcon($jsonIssue['fields']['issuetype']['iconUrl']); $kanbanEntry->setIssueType($jiraIssueType); unset($jiraIssueType); } $kanbanEntry->setUpdatedAt(new \DateTime($jsonIssue['fields']['updated'])); if (in_array($jiraStatus->getName(), $teamBacklogColumns)) { $kanbanBoard->addInbox($kanbanEntry); } elseif (in_array($jiraStatus->getName(), $teamInprogressColumns)) { $kanbanBoard->addInProgress($kanbanEntry); } elseif (in_array($jiraStatus->getName(), $teamVerificationColumns)) { $kanbanBoard->addVerification($kanbanEntry); } elseif (in_array($jiraStatus->getName(), $teamDoneColumns)) { $kanbanBoard->addDone($kanbanEntry); } unset($kanbanEntry); } return $kanbanBoard; } }