* watcher api endpoint implemented
This commit is contained in:
parent
9d3fa5fa9d
commit
3d42f16c38
@ -15,6 +15,7 @@ return [
|
|||||||
|
|
||||||
'url.jiraAvatar' => 'https://cc-jira.rnd.ki.sw.ericsson.se/secure/useravatar?ownerId=%s',
|
'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.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',
|
'url.jiraKanbanBoard' => 'https://cc-jira.rnd.ki.sw.ericsson.se/rest/api/2/search?jql=filter=%s&maxResults=1000&fields=%s',
|
||||||
'jira.filterFields' => [
|
'jira.filterFields' => [
|
||||||
'summary',
|
'summary',
|
||||||
|
|||||||
@ -47,4 +47,5 @@ return function (Application $app, MiddlewareFactory $factory, ContainerInterfac
|
|||||||
|
|
||||||
$app->get('/avatars/{signum}', App\Handler\AvatarHandler::class,'avatar.image');
|
$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/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
1
src/App/ConfigProvider.php
Normal file → Executable file
@ -42,6 +42,7 @@ class ConfigProvider
|
|||||||
Handler\SlidePositionHandler::class => Handler\SlidePositionHandlerFactory::class,
|
Handler\SlidePositionHandler::class => Handler\SlidePositionHandlerFactory::class,
|
||||||
Handler\AvatarHandler::class => Handler\AvatarHandlerFactory::class,
|
Handler\AvatarHandler::class => Handler\AvatarHandlerFactory::class,
|
||||||
Handler\KanbanHandler::class =>Handler\KanbanHandlerFactory::class,
|
Handler\KanbanHandler::class =>Handler\KanbanHandlerFactory::class,
|
||||||
|
Handler\WatchedHandler::class =>Handler\WatchedHandlerFactory::class,
|
||||||
|
|
||||||
Service\TeamService::class => Service\TeamServiceFactory::class,
|
Service\TeamService::class => Service\TeamServiceFactory::class,
|
||||||
Service\SlideManager::class => Service\SlideManagerFactory::class,
|
Service\SlideManager::class => Service\SlideManagerFactory::class,
|
||||||
|
|||||||
14
src/App/Entity/KanbanBoard.php
Normal file → Executable file
14
src/App/Entity/KanbanBoard.php
Normal file → Executable file
@ -8,20 +8,6 @@ use Doctrine\Common\Collections\ArrayCollection;
|
|||||||
|
|
||||||
class KanbanBoard implements \JsonSerializable
|
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
|
* @var KanbanEntry[]|ArrayCollection
|
||||||
*/
|
*/
|
||||||
|
|||||||
105
src/App/Entity/WatchedIssue.php
Executable file
105
src/App/Entity/WatchedIssue.php
Executable 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(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
105
src/App/Entity/WatchedIssueComment.php
Executable file
105
src/App/Entity/WatchedIssueComment.php
Executable 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,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/App/Handler/WatchedHandler.php
Executable file
40
src/App/Handler/WatchedHandler.php
Executable 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/App/Handler/WatchedHandlerFactory.php
Executable file
22
src/App/Handler/WatchedHandlerFactory.php
Executable 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,6 +10,8 @@ use App\Entity\JiraStatus;
|
|||||||
use App\Entity\KanbanBoard;
|
use App\Entity\KanbanBoard;
|
||||||
use App\Entity\KanbanEntry;
|
use App\Entity\KanbanEntry;
|
||||||
use App\Entity\Team;
|
use App\Entity\Team;
|
||||||
|
use App\Entity\WatchedIssue;
|
||||||
|
use App\Entity\WatchedIssueComment;
|
||||||
use Zend\Cache\Storage\StorageInterface;
|
use Zend\Cache\Storage\StorageInterface;
|
||||||
use Zend\Config\Config;
|
use Zend\Config\Config;
|
||||||
use Zend\Expressive\Router\RouterInterface;
|
use Zend\Expressive\Router\RouterInterface;
|
||||||
@ -25,6 +27,20 @@ class JiraCollectorService
|
|||||||
const EPIC_TICKET_LINK = 'customfield_11711';
|
const EPIC_TICKET_LINK = 'customfield_11711';
|
||||||
const EPIC_NAME_FIELD = 'customfield_11712';
|
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 */
|
/** @var StorageInterface */
|
||||||
private $cache;
|
private $cache;
|
||||||
|
|
||||||
@ -110,6 +126,93 @@ class JiraCollectorService
|
|||||||
return $kanbanBoard;
|
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
|
* @param string $parentKey
|
||||||
* @return null|string
|
* @return null|string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user