* watcher api endpoint implemented

This commit is contained in:
Dávid Danyi 2018-09-12 17:20:15 +02:00
parent 9d3fa5fa9d
commit 3d42f16c38
9 changed files with 378 additions and 14 deletions

View File

@ -15,6 +15,7 @@ return [
'url.jiraAvatar' => 'https://cc-jira.rnd.ki.sw.ericsson.se/secure/useravatar?ownerId=%s',
'url.jiraIssue' => 'https://cc-jira.rnd.ki.sw.ericsson.se/rest/api/2/issue/%s?fields=%s',
'url.jiraWatchedIssues' => 'https://cc-jira.rnd.ki.sw.ericsson.se/rest/api/2/search?jql=%s&maxResults=1000&fields=%s',
'url.jiraKanbanBoard' => 'https://cc-jira.rnd.ki.sw.ericsson.se/rest/api/2/search?jql=filter=%s&maxResults=1000&fields=%s',
'jira.filterFields' => [
'summary',

View File

@ -47,4 +47,5 @@ return function (Application $app, MiddlewareFactory $factory, ContainerInterfac
$app->get('/avatars/{signum}', App\Handler\AvatarHandler::class,'avatar.image');
$app->get('/api/kanban/{teamId:\d+}', App\Handler\KanbanHandler::class,'api.team.kanban');
$app->get('/api/watched/{teamId:\d+}', App\Handler\WatchedHandler::class,'api.team.watched');
};

1
src/App/ConfigProvider.php Normal file → Executable file
View File

@ -42,6 +42,7 @@ class ConfigProvider
Handler\SlidePositionHandler::class => Handler\SlidePositionHandlerFactory::class,
Handler\AvatarHandler::class => Handler\AvatarHandlerFactory::class,
Handler\KanbanHandler::class =>Handler\KanbanHandlerFactory::class,
Handler\WatchedHandler::class =>Handler\WatchedHandlerFactory::class,
Service\TeamService::class => Service\TeamServiceFactory::class,
Service\SlideManager::class => Service\SlideManagerFactory::class,

14
src/App/Entity/KanbanBoard.php Normal file → Executable file
View File

@ -8,20 +8,6 @@ use Doctrine\Common\Collections\ArrayCollection;
class KanbanBoard implements \JsonSerializable
{
// const PRIO_TRIVIAL = 0;
// const PRIO_MINOR = 1;
// const PRIO_MAJOR = 2;
// const PRIO_CRITICAL = 3;
// const PRIO_BLOCKER = 4;
// private $priorityMap = [
// 'Trivial' => self::PRIO_TRIVIAL,
// 'Minor' => self::PRIO_MINOR,
// 'Major' => self::PRIO_MAJOR,
// 'Critical' => self::PRIO_CRITICAL,
// 'Blocker' => self::PRIO_BLOCKER,
// ];
/**
* @var KanbanEntry[]|ArrayCollection
*/

105
src/App/Entity/WatchedIssue.php Executable file
View File

@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace App\Entity;
class WatchedIssue implements \JsonSerializable
{
/** @var string */
private $issue;
/** @var string */
private $summary;
/** @var string */
private $assignee;
/** @var WatchedIssueComment */
private $comment;
/**
* @return string
*/
public function getIssue(): ?string
{
return $this->issue;
}
/**
* @param string $issue
* @return WatchedIssue
*/
public function setIssue(string $issue): WatchedIssue
{
$this->issue = $issue;
return $this;
}
/**
* @return string
*/
public function getSummary(): ?string
{
return $this->summary;
}
/**
* @param string $summary
* @return WatchedIssue
*/
public function setSummary(string $summary): WatchedIssue
{
$this->summary = $summary;
return $this;
}
/**
* @return string
*/
public function getAssignee(): ?string
{
return $this->assignee;
}
/**
* @param string $assignee
* @return WatchedIssue
*/
public function setAssignee(string $assignee): WatchedIssue
{
$this->assignee = $assignee;
return $this;
}
/**
* @return WatchedIssueComment
*/
public function getComment(): ?WatchedIssueComment
{
return $this->comment;
}
/**
* @param WatchedIssueComment $comment
* @return WatchedIssue
*/
public function setComment(WatchedIssueComment $comment): WatchedIssue
{
$this->comment = $comment;
return $this;
}
/**
* @return array
*/
function jsonSerialize()
{
return [
'issue' => $this->getIssue(),
'summary' => $this->getSummary(),
'assignee' => $this->getAssignee(),
'comment' => $this->getComment() ?? new WatchedIssueComment(),
];
}
}

View File

@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace App\Entity;
class WatchedIssueComment implements \JsonSerializable
{
/** @var string */
private $signum;
/** @var string */
private $name;
/** @var string */
private $content;
/** @var \DateTimeImmutable */
private $date;
/**
* @return string
*/
public function getSignum(): ?string
{
return $this->signum;
}
/**
* @param string $signum
* @return WatchedIssueComment
*/
public function setSignum(string $signum): WatchedIssueComment
{
$this->signum = $signum;
return $this;
}
/**
* @return string
*/
public function getName(): ?string
{
return $this->name;
}
/**
* @param string $name
* @return WatchedIssueComment
*/
public function setName(string $name): WatchedIssueComment
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getContent(): ?string
{
return $this->content;
}
/**
* @param string $content
* @return WatchedIssueComment
*/
public function setContent(string $content): WatchedIssueComment
{
$this->content = $content;
return $this;
}
/**
* @return \DateTimeImmutable
*/
public function getDate(): ?\DateTimeImmutable
{
return $this->date;
}
/**
* @param \DateTimeImmutable $date
* @return WatchedIssueComment
*/
public function setDate(\DateTimeImmutable $date): WatchedIssueComment
{
$this->date = $date;
return $this;
}
/**
* @return array
*/
function jsonSerialize()
{
return [
'signum' => $this->getSignum(),
'name' => $this->getName(),
'content' => $this->getContent(),
'date' => $this->getDate() ? $this->getDate()->format("Y-m-d H:i:s") : null,
];
}
}

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace App\Handler;
use App\Entity\KanbanBoard;
use App\Service\JiraCollectorService;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\JsonResponse;
class WatchedHandler implements RequestHandlerInterface
{
/** @var JiraCollectorService */
private $dataCollector;
/**
* KanbanAction constructor.
* @param JiraCollectorService $dataCollectorService
*/
public function __construct(JiraCollectorService $dataCollectorService)
{
$this->dataCollector = $dataCollectorService;
}
/**
* @param ServerRequestInterface $request
* @return ResponseInterface
* @todo filterId
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$teamId = (int)$request->getAttribute('teamId');
/** @var KanbanBoard $kanbanResult */
$kanbanResult = $this->dataCollector->getTeamWatchedIssues($teamId);
return new JsonResponse($kanbanResult);
}
}

View File

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace App\Handler;
use App\Service\JiraCollectorService;
use Interop\Container\ContainerInterface;
class WatchedHandlerFactory
{
/**
* @param ContainerInterface $container
* @return WatchedHandler
*/
public function __invoke(ContainerInterface $container)
{
/** @var JiraCollectorService $dataCollectorService */
$dataCollectorService = $container->get(JiraCollectorService::class);
return new WatchedHandler($dataCollectorService);
}
}

View File

@ -10,6 +10,8 @@ use App\Entity\JiraStatus;
use App\Entity\KanbanBoard;
use App\Entity\KanbanEntry;
use App\Entity\Team;
use App\Entity\WatchedIssue;
use App\Entity\WatchedIssueComment;
use Zend\Cache\Storage\StorageInterface;
use Zend\Config\Config;
use Zend\Expressive\Router\RouterInterface;
@ -25,6 +27,20 @@ class JiraCollectorService
const EPIC_TICKET_LINK = 'customfield_11711';
const EPIC_NAME_FIELD = 'customfield_11712';
const STATUS_DONE = 'done';
const STATUS_CLOSED = 'closed';
const STATUS_ANSWERED = 'answered';
const STATUS_RESOLVED = 'resolved';
const IGNORED_STATUSES = [
self::STATUS_DONE,
self::STATUS_CLOSED,
self::STATUS_ANSWERED,
self::STATUS_RESOLVED,
];
const WATCH_FILTER = 'status NOT IN (%s) AND watcher in (%s) AND "Last change" not in (%s) ORDER BY "Last Comment"';
/** @var StorageInterface */
private $cache;
@ -110,6 +126,93 @@ class JiraCollectorService
return $kanbanBoard;
}
/**
* @param int $teamId
* @return array
* @throws \Exception
*/
public function getTeamWatchedIssues(int $teamId)
{
$team = $this->teamService->getTeam($teamId);
$members = array_map(function(array $member): string {
return $member['signum'];
}, $team->getMembers());
$preparedMembers = sprintf('"%s"', implode('","', $members));
$filter = sprintf(
self::WATCH_FILTER,
sprintf('"%s"', implode('","', self::IGNORED_STATUSES)),
$preparedMembers, $preparedMembers
);
$user = $this->config->get('jira.user');
$password = $this->config->get('jira.password');
/** @var Config $kanbanBoardUriParams */
$jiraWatchedIssues = $this->config->get('url.jiraWatchedIssues');
$kanbanBoardFilterFields = [
'assignee',
'summary',
'comment',
];
$issueFields = implode(",", $kanbanBoardFilterFields);
$jiraIssueUri = sprintf($jiraWatchedIssues, $filter, $issueFields);
$response = $this->httpClient
->setUri($jiraIssueUri)
->setAuth($user, $password)
->send();
if (!$response->isSuccess()) {
throw new \UnexpectedValueException(sprintf(
"Bad JIRA result for URL:\n%s",
$jiraIssueUri
), $response->getStatusCode());
}
return $this->hydrateWatchedIssues(Decoder::decode($response->getBody(), Json::TYPE_ARRAY), $members);
}
/**
* @param array $parsedJson
* @param array $members
* @return array
* @throws \Exception
*/
private function hydrateWatchedIssues(array $parsedJson, array $members)
{
/** @var WatchedIssue[] $hydratedResult */
$hydratedResult = [];
foreach ($parsedJson['issues'] as $issueJson) {
$issueItem = new WatchedIssue();
$issueItem->setIssue($issueJson['key'])
->setAssignee($issueJson['fields']['assignee']['name'])
->setSummary(html_entity_decode($issueJson['fields']['summary']));
$issueComments = [];
foreach ($issueJson['fields']['comment']['comments'] as $commentJson) {
$issueComment = new WatchedIssueComment();
$issueComment->setSignum($commentJson['updateAuthor']['name'])
->setName($commentJson['updateAuthor']['displayName'])
->setContent(html_entity_decode($commentJson['body']))
->setDate(new \DateTimeImmutable($commentJson['updated']));
$issueComments[] = $issueComment;
}
usort($issueComments, function(WatchedIssueComment $a, WatchedIssueComment $b) {
return $a->getDate() <=> $b->getDate();
});
$lastComment = array_pop($issueComments);
unset($issueComments);
$issueItem->setComment($lastComment);
$hydratedResult[] = $issueItem;
}
/**
* sanity check, we only want items where last change was a comment, but that is not possible
* with JIRA jql at the moment.
*/
return array_filter($hydratedResult, function(WatchedIssue $issue) use ($members) {
return !in_array($issue->getComment()->getSignum(), $members);
});
}
/**
* @param string $parentKey
* @return null|string