* kanban service and stuff

This commit is contained in:
Dávid Danyi
2018-04-21 15:23:06 +02:00
parent b0295227bc
commit a43b5e9d09
25 changed files with 2071 additions and 1 deletions

View File

@@ -40,9 +40,16 @@ class ConfigProvider
Handler\TeamHandler::class => Handler\TeamHandlerFactory::class,
Handler\SlideHandler::class => Handler\SlideHandlerFactory::class,
Handler\SlidePositionHandler::class => Handler\SlidePositionHandlerFactory::class,
Handler\AvatarHandler::class => Handler\AvatarHandlerFactory::class,
Handler\KanbanHandler::class =>Handler\KanbanHandlerFactory::class,
Service\TeamService::class => Service\TeamServiceFactory::class,
Service\SlideManager::class => Service\SlideManagerFactory::class,
Service\AvatarService::class => Service\AvatarServiceFactory::class,
Service\JiraCollectorService::class => Service\JiraCollectorServiceFactory::class,
\Zend\Http\Client::class => Service\HttpClientFactory::class,
'service.cache' => Service\CacheServiceFactory::class,
],
];
}

View File

@@ -0,0 +1,135 @@
<?php
namespace App\Entity;
class JiraAssignee implements \JsonSerializable
{
/**
* @var string
*/
private $signum;
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $email;
/**
* @var string
*/
private $avatar;
/**
* @var bool
*/
private $active = false;
/**
* @return string
*/
public function getSignum(): string
{
return $this->signum;
}
/**
* @param string $signum
* @return JiraAssignee
*/
public function setSignum(string $signum): JiraAssignee
{
$this->signum = $signum;
return $this;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @param string $name
* @return JiraAssignee
*/
public function setName(string $name): JiraAssignee
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getEmail(): string
{
return $this->email;
}
/**
* @param string $email
* @return JiraAssignee
*/
public function setEmail(string $email): JiraAssignee
{
$this->email = $email;
return $this;
}
/**
* @return string
*/
public function getAvatar(): string
{
return $this->avatar;
}
/**
* @param string $avatar
* @return JiraAssignee
*/
public function setAvatar(string $avatar): JiraAssignee
{
$this->avatar = $avatar;
return $this;
}
/**
* @return bool
*/
public function isActive(): bool
{
return $this->active;
}
/**
* @param bool $active
* @return JiraAssignee
*/
public function setActive(bool $active): JiraAssignee
{
$this->active = $active;
return $this;
}
/**
* @return array
*/
function jsonSerialize()
{
return [
'signum' => $this->getSignum(),
'name' => $this->getName(),
'email' => $this->getEmail(),
'avatar' => $this->getAvatar(),
'active' => $this->isActive(),
];
}
}

View File

@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace App\Entity;
class JiraIssueType implements \JsonSerializable
{
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $description;
/**
* @var string
*/
private $icon;
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @param string $name
* @return JiraIssueType
*/
public function setName(string $name): JiraIssueType
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getDescription(): string
{
return $this->description;
}
/**
* @param string $description
* @return JiraIssueType
*/
public function setDescription(string $description): JiraIssueType
{
$this->description = $description;
return $this;
}
/**
* @return string
*/
public function getIcon(): string
{
return $this->icon;
}
/**
* @param string $icon
* @return JiraIssueType
*/
public function setIcon(string $icon): JiraIssueType
{
$this->icon = $icon;
return $this;
}
/**
* @return array
*/
function jsonSerialize()
{
return [
'name' => $this->getName(),
'description' => $this->getDescription(),
'icon' => $this->getIcon(),
];
}
}

View File

@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace App\Entity;
class JiraStatus implements \JsonSerializable
{
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $color;
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @param string $name
* @return JiraStatus
*/
public function setName(string $name): JiraStatus
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getColor(): string
{
return $this->color;
}
/**
* @param string $color
* @return JiraStatus
*/
public function setColor(string $color): JiraStatus
{
$this->color = $color;
return $this;
}
/**
* @return array
*/
function jsonSerialize()
{
return [
'name' => $this->getName(),
'color' => $this->getColor(),
];
}
}

View File

@@ -0,0 +1,260 @@
<?php
declare(strict_types=1);
namespace App\Entity;
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
*/
private $inbox;
/**
* @var KanbanEntry[]|ArrayCollection
*/
private $inProgress;
/**
* @var KanbanEntry[]|ArrayCollection
*/
private $verification;
/**
* @var KanbanEntry[]|ArrayCollection
*/
private $done;
public function __construct()
{
$this->inbox = new ArrayCollection();
$this->inProgress = new ArrayCollection();
$this->verification = new ArrayCollection();
$this->done = new ArrayCollection();
}
/**
* @return KanbanEntry[]|ArrayCollection
*/
public function getInbox(): ArrayCollection
{
return $this->inbox;
}
/**
* @param KanbanEntry[]|ArrayCollection $inbox
* @return KanbanBoard
*/
public function setInbox(ArrayCollection $inbox): KanbanBoard
{
$this->inbox = $inbox;
return $this;
}
/**
* @param KanbanEntry $inbox
* @return KanbanBoard
*/
public function addInbox(KanbanEntry $inbox): KanbanBoard
{
if (!$this->inbox->contains($inbox)) {
$this->inbox->add($inbox);
}
return $this;
}
/**
* @param KanbanEntry $inbox
* @return KanbanBoard
*/
public function removeInbox(KanbanEntry $inbox): KanbanBoard
{
if ($this->inbox->contains($inbox)) {
$this->inbox->removeElement($inbox);
}
return $this;
}
/**
* @return KanbanEntry[]|ArrayCollection
*/
public function getInProgress(): ArrayCollection
{
return $this->inProgress;
}
/**
* @param KanbanEntry[]|ArrayCollection $inProgress
* @return KanbanBoard
*/
public function setInProgress(ArrayCollection $inProgress): KanbanBoard
{
$this->inProgress = $inProgress;
return $this;
}
/**
* @param KanbanEntry $inProgress
* @return KanbanBoard
*/
public function addInProgress(KanbanEntry $inProgress): KanbanBoard
{
if (!$this->inProgress->contains($inProgress)) {
$this->inProgress->add($inProgress);
}
return $this;
}
/**
* @param KanbanEntry $inProgress
* @return KanbanBoard
*/
public function removeInProgress(KanbanEntry $inProgress): KanbanBoard
{
if ($this->inProgress->contains($inProgress)) {
$this->inProgress->removeElement($inProgress);
}
return $this;
}
/**
* @return KanbanEntry[]|ArrayCollection
*/
public function getVerification(): ArrayCollection
{
return $this->verification;
}
/**
* @param KanbanEntry[]|ArrayCollection $verification
* @return KanbanBoard
*/
public function setVerification(ArrayCollection $verification): KanbanBoard
{
$this->verification = $verification;
return $this;
}
/**
* @param KanbanEntry $verification
* @return KanbanBoard
*/
public function addVerification(KanbanEntry $verification): KanbanBoard
{
if (!$this->verification->contains($verification)) {
$this->verification->add($verification);
}
return $this;
}
/**
* @param KanbanEntry $verification
* @return KanbanBoard
*/
public function removeVerification(KanbanEntry $verification): KanbanBoard
{
if ($this->verification->contains($verification)) {
$this->verification->removeElement($verification);
}
return $this;
}
/**
* @return KanbanEntry[]|ArrayCollection
*/
public function getDone(): ArrayCollection
{
return $this->done;
}
/**
* @param KanbanEntry[]|ArrayCollection $verification
* @return KanbanBoard
*/
public function setDone(ArrayCollection $verification): KanbanBoard
{
$this->done = $verification;
return $this;
}
/**
* @param KanbanEntry $verification
* @return KanbanBoard
*/
public function addDone(KanbanEntry $verification): KanbanBoard
{
if (!$this->done->contains($verification)) {
$this->done->add($verification);
}
return $this;
}
/**
* @param KanbanEntry $verification
* @return KanbanBoard
*/
public function removeDone(KanbanEntry $verification): KanbanBoard
{
if ($this->done->contains($verification)) {
$this->done->removeElement($verification);
}
return $this;
}
/**
* @param KanbanEntry[] $toSort
* @return KanbanEntry[]
*/
private function prioSort(array $toSort): array
{
usort($toSort, function (KanbanEntry $a, KanbanEntry $b) {
return $a->getTaurusPrio() <=> $b->getTaurusPrio();
});
return $toSort;
}
/**
* @param KanbanEntry[] $toSort
* @return KanbanEntry[]
*/
private function updatedAtReverseSort(array $toSort): array
{
$toSort = array_filter($toSort, function (KanbanEntry $item) {
return $item->getAssignee() != null && empty($item->getFixVersions());
});
usort($toSort, function (KanbanEntry $a, KanbanEntry $b) {
return $b->getUpdatedAt() <=> $a->getUpdatedAt();
});
return $toSort;
}
/**
* @return array
*/
function jsonSerialize()
{
return [
'inbox' => $this->prioSort($this->inbox->getValues()),
'inProgress' => $this->prioSort($this->inProgress->getValues()),
'verification' => $this->prioSort($this->verification->getValues()),
'done' => $this->updatedAtReverseSort($this->done->getValues()),
];
}
}

View File

@@ -0,0 +1,689 @@
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
class KanbanEntry implements \JsonSerializable
{
/**
* @var int
*/
private $id;
/**
* @var string
*/
private $key;
// 'fields' below
/**
* @var string
*/
private $summary;
/**
* @var JiraIssueType
*/
private $issueType;
/**
* @var JiraStatus
*/
private $status;
/**
* @var JiraAssignee
*/
private $assignee;
/**
* JIRA: customfield_10401
* @var JiraAssignee[]
*/
private $additionalAssignees;
/**
* @var string
*/
private $issuePriority;
/**
* @var string
*/
private $issuePriorityIcon;
/**
* @var string[]
*/
private $labels;
/**
* @var array
*/
private $fixVersions;
/**
* JIRA: customfield_11226
* @var int
*/
private $prio;
/**
* JIRA: customfield_11225
* @var string[]|ArrayCollection
*/
private $functionalAreas;
/**
* JIRA: customfield_10010
* @var string
*/
private $externalId;
/**
* JIRA: customfield_10850
* @var string
*/
private $externalLink;
/**
* JIRA: customfield_10840
* @var string
*/
private $project;
/**
* JIRA: customfield_10844
* @var string
*/
private $mhwebStatus;
/**
* JIRA: customfield_10847
* @var bool
*/
private $mhwebHot;
/**
* JIRA: customfield_10849
* @var bool
*/
private $mhwebExternal = false;
/**
* JIRA: customfield_10904
* @var string
*/
private $team;
/**
* JIRA: customfield_11692
* @var string
*/
private $answerCode;
/**
* ITS OVER 9000!
* JIRA: customfield_12500
* @var int
*/
private $taurusPrio = 9001;
/**
* @var int
*/
private $worklog = 0;
/**
* @var int
*/
private $daysBlocked = 0;
/**
* @var \DateTime
*/
private $updatedAt;
/**
* KanbanEntry constructor.
*/
public function __construct()
{
$this->functionalAreas = new ArrayCollection();
$this->additionalAssignees = new ArrayCollection();
}
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @param int $id
* @return KanbanEntry
*/
public function setId(int $id): KanbanEntry
{
$this->id = $id;
return $this;
}
/**
* @return string
*/
public function getKey(): string
{
return $this->key;
}
/**
* @param string $key
* @return KanbanEntry
*/
public function setKey(string $key): KanbanEntry
{
$this->key = $key;
return $this;
}
/**
* @return string
*/
public function getSummary(): string
{
return $this->summary;
}
/**
* @param string $summary
* @return KanbanEntry
*/
public function setSummary(string $summary): KanbanEntry
{
$this->summary = $summary;
return $this;
}
/**
* @return JiraIssueType
*/
public function getIssueType(): ?JiraIssueType
{
return $this->issueType;
}
/**
* @param JiraIssueType $issueType
* @return KanbanEntry
*/
public function setIssueType(?JiraIssueType $issueType): KanbanEntry
{
$this->issueType = $issueType;
return $this;
}
/**
* @return JiraStatus
*/
public function getStatus(): ?JiraStatus
{
return $this->status;
}
/**
* @param JiraStatus $status
* @return KanbanEntry
*/
public function setStatus(?JiraStatus $status): KanbanEntry
{
$this->status = $status;
return $this;
}
/**
* @return JiraAssignee
*/
public function getAssignee(): ?JiraAssignee
{
return $this->assignee;
}
/**
* @param JiraAssignee $assignee
* @return KanbanEntry
*/
public function setAssignee(?JiraAssignee $assignee): KanbanEntry
{
$this->assignee = $assignee;
return $this;
}
/**
* @return JiraAssignee[]|ArrayCollection
*/
public function getAdditionalAssignees(): ?ArrayCollection
{
return $this->additionalAssignees;
}
/**
* @param JiraAssignee[]|ArrayCollection $additionalAssignees
* @return KanbanEntry
*/
public function setAdditionalAssignees(?ArrayCollection $additionalAssignees): KanbanEntry
{
$this->additionalAssignees = $additionalAssignees;
return $this;
}
/**
* @param JiraAssignee $assignee
* @return KanbanEntry
*/
public function addAdditionalAssignee(JiraAssignee $assignee): KanbanEntry
{
if(!$this->additionalAssignees->contains($assignee)) {
$this->additionalAssignees->add($assignee);
}
return $this;
}
/**
* @param JiraAssignee $assignee
* @return KanbanEntry
*/
public function removeAdditionalAssignee(JiraAssignee $assignee): KanbanEntry
{
if($this->additionalAssignees->contains($assignee)) {
$this->additionalAssignees->removeElement($assignee);
}
return $this;
}
/**
* @return string
*/
public function getIssuePriority(): string
{
return $this->issuePriority;
}
/**
* @param string $issuePriority
* @return KanbanEntry
*/
public function setIssuePriority(string $issuePriority): KanbanEntry
{
$this->issuePriority = $issuePriority;
return $this;
}
/**
* @return string
*/
public function getIssuePriorityIcon(): string
{
return $this->issuePriorityIcon;
}
/**
* @param string $issuePriorityIcon
* @return KanbanEntry
*/
public function setIssuePriorityIcon(string $issuePriorityIcon): KanbanEntry
{
$this->issuePriorityIcon = $issuePriorityIcon;
return $this;
}
/**
* @return int
*/
public function getPrio(): ?int
{
return $this->prio;
}
/**
* @param int $prio
* @return KanbanEntry
*/
public function setPrio(?int $prio)
{
$this->prio = $prio;
return $this;
}
/**
* @return string[]
*/
public function getLabels(): ?array
{
return $this->labels;
}
/**
* @param string[] $labels
* @return KanbanEntry
*/
public function setLabels(?array $labels): KanbanEntry
{
$this->labels = $labels;
return $this;
}
/**
* @return array|null
*/
public function getFixVersions(): ?array
{
return $this->fixVersions;
}
/**
* @param array|null $fixVersions
* @return KanbanEntry
*/
public function setFixVersions(?array $fixVersions): KanbanEntry
{
$this->fixVersions = $fixVersions;
return $this;
}
/**
* @return string[]|ArrayCollection
*/
public function getFunctionalAreas(): ?ArrayCollection
{
return $this->functionalAreas;
}
/**
* @param string[]|ArrayCollection $functionalAreas
* @return KanbanEntry
*/
public function setFunctionalAreas(ArrayCollection $functionalAreas): KanbanEntry
{
$this->functionalAreas = $functionalAreas;
return $this;
}
/**
* @param string $functionalArea
* @return KanbanEntry
*/
public function addFunctionalArea(string $functionalArea): KanbanEntry
{
if(!$this->functionalAreas->contains($functionalArea)) {
$this->functionalAreas->add($functionalArea);
}
return $this;
}
/**
* @param string $functionalArea
* @return KanbanEntry
*/
public function removeFunctionalArea(string $functionalArea): KanbanEntry
{
if($this->functionalAreas->contains($functionalArea)) {
$this->functionalAreas->removeElement($functionalArea);
}
return $this;
}
/**
* @return string
*/
public function getExternalId(): ?string
{
return $this->externalId;
}
/**
* @param string $externalId
* @return KanbanEntry
*/
public function setExternalId(?string $externalId): KanbanEntry
{
$this->externalId = $externalId;
return $this;
}
/**
* @return string
*/
public function getExternalLink(): ?string
{
return $this->externalLink;
}
/**
* @param string $externalLink
* @return KanbanEntry
*/
public function setExternalLink(?string $externalLink): KanbanEntry
{
$this->externalLink = $externalLink;
return $this;
}
/**
* @return string
*/
public function getProject(): ?string
{
return $this->project;
}
/**
* @param string $project
* @return KanbanEntry
*/
public function setProject(?string $project): KanbanEntry
{
$this->project = $project;
return $this;
}
/**
* @return string
*/
public function getMhwebStatus(): ?string
{
return $this->mhwebStatus;
}
/**
* @param string $mhwebStatus
* @return KanbanEntry
*/
public function setMhwebStatus(?string $mhwebStatus): KanbanEntry
{
$this->mhwebStatus = $mhwebStatus;
return $this;
}
/**
* @return bool
*/
public function getMhwebHot(): ?bool
{
return $this->mhwebHot;
}
/**
* @param bool $mhwebHot
* @return KanbanEntry
*/
public function setMhwebHot(?bool $mhwebHot): KanbanEntry
{
$this->mhwebHot = $mhwebHot;
return $this;
}
/**
* @return bool
*/
public function getMhwebExternal(): ?bool
{
return $this->mhwebExternal;
}
/**
* @param bool $mhwebExternal
* @return KanbanEntry
*/
public function setMhwebExternal(?bool $mhwebExternal): KanbanEntry
{
$this->mhwebExternal = $mhwebExternal;
return $this;
}
/**
* @return string
*/
public function getTeam(): ?string
{
return $this->team;
}
/**
* @param string $team
* @return KanbanEntry
*/
public function setTeam(?string $team): KanbanEntry
{
$this->team = $team;
return $this;
}
/**
* @return string
*/
public function getAnswerCode(): ?string
{
return $this->answerCode;
}
/**
* @param string $answerCode
* @return KanbanEntry
*/
public function setAnswerCode(?string $answerCode): KanbanEntry
{
$this->answerCode = $answerCode;
return $this;
}
/**
* @return int
*/
public function getTaurusPrio(): ?int
{
return $this->taurusPrio;
}
/**
* @param int $taurusPrio
* @return KanbanEntry
*/
public function setTaurusPrio(?int $taurusPrio): KanbanEntry
{
$this->taurusPrio = $taurusPrio;
return $this;
}
/**
* @return int
*/
public function getWorklog(): int
{
return $this->worklog;
}
/**
* @param int $worklog
* @return KanbanEntry
*/
public function setWorklog(int $worklog): KanbanEntry
{
$this->worklog = $worklog;
return $this;
}
/**
* @return int
*/
public function getDaysBlocked(): int
{
return $this->daysBlocked;
}
/**
* @param int $daysBlocked
* @return KanbanEntry
*/
public function setDaysBlocked(int $daysBlocked): KanbanEntry
{
$this->daysBlocked = $daysBlocked;
return $this;
}
/**
* @return \DateTime
*/
public function getUpdatedAt(): ?\DateTime
{
return $this->updatedAt;
}
/**
* @param \DateTime $updatedAt
* @return KanbanEntry
*/
public function setUpdatedAt(?\DateTime $updatedAt): KanbanEntry
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* @return array
*/
function jsonSerialize()
{
return [
'id' => $this->getId(),
'key' => $this->getKey(),
'summary' => $this->getSummary(),
'issueType' => $this->getIssueType(),
'status' => $this->getStatus(),
'assignee' => $this->getAssignee(),
'additionalAssignees' => $this->getAdditionalAssignees()->getValues(),
'issuePriority' => $this->getIssuePriority(),
'issuePriorityIcon' => $this->getIssuePriorityIcon(),
'labels' => $this->getLabels(),
'prio' => $this->getPrio(),
'functionalArea' => $this->getFunctionalAreas()->getValues(),
'externalId' => $this->getExternalId(),
'externalLink' => $this->getExternalLink(),
'project' => $this->getProject(),
'mhwebStatus' => $this->getMhwebStatus(),
'mhwebHot' => $this->getMhwebHot(),
'mhwebExternal' => $this->getMhwebExternal(),
'team' => $this->getTeam(),
'answerCode' => $this->getAnswerCode(),
'taurusPrio' => $this->getTaurusPrio(),
'worklog' => $this->getWorklog(),
'daysBlocked' => $this->getDaysBlocked(),
'updatedAt' => $this->getUpdatedAt(),
];
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Handler;
use App\Service\AvatarService;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\TextResponse;
class AvatarHandler implements RequestHandlerInterface
{
/** @var AvatarService */
private $avatarService;
/**
* AvatarHandler constructor.
* @param AvatarService $avatarService
*/
public function __construct(AvatarService $avatarService)
{
$this->avatarService = $avatarService;
}
/**
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$signum = $request->getAttribute('signum', false);
try {
$avatarImageData = $this->avatarService->getAvatarImageData($signum);
} catch (\UnexpectedValueException $e) {
return new TextResponse("Avatar not found", 404);
}
return (new TextResponse($avatarImageData, 200, [
'content-type' => 'image/png',
]))->withHeader('Expires', '0')
->withHeader('Cache-Control', 'must-revalidate');
}
}

View File

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

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 KanbanHandler 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
{
$filterId = $request->getAttribute('filterId');
/** @var KanbanBoard $kanbanResult */
$kanbanResult = $this->dataCollector->getKanbanBoard($filterId);
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 KanbanHandlerFactory
{
/**
* @param ContainerInterface $container
* @return KanbanHandler
*/
public function __invoke(ContainerInterface $container)
{
/** @var JiraCollectorService $dataCollectorService */
$dataCollectorService = $container->get(JiraCollectorService::class);
return new KanbanHandler($dataCollectorService);
}
}

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace App\Service;
use Zend\Cache\Storage\StorageInterface;
use Zend\Config\Config;
use Zend\Http\Client;
class AvatarService
{
/** @var Config */
private $config;
/** @var Client */
private $httpClient;
/** @var StorageInterface */
private $cache;
/**
* JiraClientService constructor.
* @param Client $client
* @param Config $config
* @param StorageInterface $cache
*/
public function __construct(Client $client, Config $config, StorageInterface $cache)
{
$this->httpClient = $client;
$this->config = $config;
$this->cache = $cache;
}
/**
* Returns avatar image data as string
*
* @param string $signum
* @return string
*/
public function getAvatarImageData(string $signum): string
{
if (!$this->cache->hasItem($signum)) {
$user = $this->config->get('jira.user');
$password = $this->config->get('jira.password');
$jiraAvatarUrl = $this->config->get('url.jiraAvatar');
$response = $this->httpClient
->setAuth($user, $password)
->setUri(sprintf($jiraAvatarUrl, $signum))
->send();
if (!$response->isSuccess()) {
throw new \UnexpectedValueException("Missing avatar", 404);
}
$this->cache->setItem($signum, $response->getBody());
}
return $this->cache->getItem($signum);
}
}

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Service;
use Interop\Container\ContainerInterface;
use Zend\Config\Config;
use Zend\Http\Client;
class AvatarServiceFactory
{
public function __invoke(ContainerInterface $container)
{
$httpClient = $container->get(Client::class);
$cache = $container->get('service.cache');
$configArray = $container->get('config');
$config = new Config($configArray['app.config']);
return new AvatarService($httpClient, $config, $cache);
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace App\Service;
use Interop\Container\ContainerInterface;
use Zend\Cache\Storage\Adapter\Filesystem as FilesytemCache;
use Zend\Cache\Storage\StorageInterface;
class CacheServiceFactory
{
/**
* @param ContainerInterface $container
* @return StorageInterface
*/
public function __invoke(ContainerInterface $container): StorageInterface
{
$cache = new FilesytemCache();
$cache->getOptions()
->setFromArray([
'ttl' => 28800,
'cache_dir' => 'data/cache',
]);
return $cache;
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace App\Service;
use Interop\Container\ContainerInterface;
use Zend\Config\Config;
use Zend\Http\Client;
class HttpClientFactory
{
/**
* @param ContainerInterface $container
* @return Client
*/
public function __invoke(ContainerInterface $container): Client
{
$configArray = $container->get('config');
$config = new Config($configArray['app.config']);
$httpClient = new Client();
$httpClient->setAdapter($curlAdapter = new Client\Adapter\Curl());
$curlAdapter->setOptions([
'timeout' => 300,
]);
$curlAdapter
->setCurlOption(CURLOPT_SSL_VERIFYPEER, false)
->setCurlOption(CURLOPT_SSL_VERIFYHOST, false);
if ($config->get('http.proxy.enabled', false)) {
$curlAdapter
->setCurlOption(CURLOPT_PROXYTYPE, $config->get('http.proxy.type'))
->setCurlOption(CURLOPT_PROXY, $config->get('http.proxy.url'));
}
return $httpClient;
}
}

View File

@@ -0,0 +1,245 @@
<?php
declare(strict_types=1);
namespace App\Service;
use App\Entity\JiraAssignee;
use App\Entity\JiraIssueType;
use App\Entity\JiraStatus;
use App\Entity\KanbanBoard;
use App\Entity\KanbanEntry;
use Zend\Cache\Storage\StorageInterface;
use Zend\Config\Config;
use Zend\Expressive\Router\RouterInterface;
use Zend\Http\Client;
use Zend\Json\Decoder;
use Zend\Json\Json;
class JiraCollectorService
{
const CACHE_KEY_KANBANBOARD = 'kanbanBoard';
/** @var StorageInterface */
private $cache;
/** @var Config */
private $config;
/** @var Client */
private $httpClient;
/** @var RouterInterface */
private $router;
/**
* JiraClientService constructor.
* @param StorageInterface $cache
* @param Client $client
* @param Config $config
* @param RouterInterface $router
*/
public function __construct(StorageInterface $cache, Client $client, Config $config, RouterInterface $router)
{
$this->cache = $cache;
$this->router = $router;
$this->httpClient = $client;
$this->config = $config;
}
/**
* @param int $filterId
* @param bool $forceReload
* @return KanbanBoard
*/
public function getKanbanBoard(int $filterId = null, bool $forceReload = false): KanbanBoard
{
$kanbanBoard = $this->cache->getItem('kanbanBoard');
if ($forceReload || null === $kanbanBoard) {
$user = $this->config->get('jira.user');
$password = $this->config->get('jira.password');
/** @var Config $kanbanBoardUriParams */
$kanbanBoardUriParams = $this->config->get('url.jiraKanbanBoard');
$kanbanBoardUri = sprintf(
$kanbanBoardUriParams['baseUrl'],
isset($filterId) ? $filterId : $kanbanBoardUriParams['filterId'],
implode(",", $kanbanBoardUriParams['fields']->toArray())
);
$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($parsedJsonData);
$this->cache->setItem(self::CACHE_KEY_KANBANBOARD, serialize($kanbanBoard));
} else {
$kanbanBoard = unserialize($kanbanBoard);
}
return $kanbanBoard;
}
/**
* @param $parsedJsonData
* @return KanbanBoard
* @todo check if avatar has to be locally cached
*/
private function hydrateKanbanBoard($parsedJsonData): KanbanBoard
{
$kanbanBoard = new KanbanBoard();
foreach ($parsedJsonData['issues'] as $jsonIssue) {
$kanbanEntry = new KanbanEntry();
$kanbanEntry->setId(intval($jsonIssue['id']))
->setKey($jsonIssue['key'])
->setSummary($jsonIssue['fields']['summary'])
->setExternalLink($jsonIssue['fields']['customfield_10850'])
->setMhwebStatus($jsonIssue['fields']['customfield_10844'])
->setAnswerCode($jsonIssue['fields']['customfield_11692'])
->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);
}
}
// externalId : customfield_10010
if (isset($jsonIssue['fields']['customfield_10010'])) {
$kanbanEntry->setExternalId($jsonIssue['fields']['customfield_10010']);
}
// prio : customfield_10840
if (isset($jsonIssue['fields']['customfield_11226'])) {
$kanbanEntry->setPrio($jsonIssue['fields']['customfield_11226']);
}
// functional area : customfield_11225
if (isset($jsonIssue['fields']['customfield_11225'])) {
foreach ($jsonIssue['fields']['customfield_11225'] as $functionalArea) {
$kanbanEntry->addFunctionalArea($functionalArea['value']);
}
}
// project : customfield_10840
if (isset($jsonIssue['fields']['customfield_10840'])) {
$kanbanEntry->setProject($jsonIssue['fields']['customfield_10840']['value']);
}
// mhweb hot : customfield_10847
if (isset($jsonIssue['fields']['customfield_10847'])) {
$boolVal = $jsonIssue['fields']['customfield_10847'][0]['value'] == 'yes';
$kanbanEntry->setMhwebHot($boolVal);
}
// mhweb external : customfield_10849
if (isset($jsonIssue['fields']['customfield_10849'])) {
$boolVal = $jsonIssue['fields']['customfield_10849'][0]['value'] == 'yes';
$kanbanEntry->setMhwebExternal($boolVal);
}
// team : customfield_10904
if (isset($jsonIssue['fields']['customfield_10904'])) {
$kanbanEntry->setTeam($jsonIssue['fields']['customfield_10904']['value']);
}
// team : customfield_12500
if (isset($jsonIssue['fields']['customfield_12500'])) {
$kanbanEntry->setTaurusPrio($jsonIssue['fields']['customfield_12500']);
}
// 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']));
switch ($jiraStatus->getName()) {
case "Backlog":
$kanbanBoard->addInbox($kanbanEntry);
break;
case "In Progress":
$kanbanBoard->addInProgress($kanbanEntry);
break;
case "Verification":
$kanbanBoard->addVerification($kanbanEntry);
break;
case "Done":
$kanbanBoard->addDone($kanbanEntry);
break;
}
unset($kanbanEntry);
}
return $kanbanBoard;
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace App\Service;
use Interop\Container\ContainerInterface;
use Zend\Config\Config;
use Zend\Expressive\Router\RouterInterface;
use Zend\Http\Client;
class JiraCollectorServiceFactory
{
public function __invoke(ContainerInterface $container)
{
$cache = $container->get('service.cache');
$configArray = $container->get('config');
$httpClient = $container->get(Client::class);
$config = new Config($configArray['app.config']);
$router = $container->get(RouterInterface::class);
return new JiraCollectorService($cache,$httpClient, $config, $router);
}
}