* initial commit
This commit is contained in:
29
src/App/Action/DbgAction.php
Normal file
29
src/App/Action/DbgAction.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Service\JiraCollectorService;
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
|
||||
|
||||
class DbgAction implements ServerMiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var JiraCollectorService
|
||||
*/
|
||||
private $jiraCollectorService;
|
||||
|
||||
public function __construct(JiraCollectorService $jiraCollectorService)
|
||||
{
|
||||
$this->jiraCollectorService = $jiraCollectorService;
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
$chartData = $this->jiraCollectorService->getChartData();
|
||||
return new JsonResponse($chartData);
|
||||
}
|
||||
}
|
||||
17
src/App/Action/DbgFactory.php
Normal file
17
src/App/Action/DbgFactory.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Service\JiraCollectorService;
|
||||
use Interop\Container\ContainerInterface;
|
||||
|
||||
class DbgFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
/** @var JiraCollectorService $jiraCollectorService */
|
||||
$jiraCollectorService = $container->get(JiraCollectorService::class);
|
||||
|
||||
return new DbgAction($jiraCollectorService);
|
||||
}
|
||||
}
|
||||
32
src/App/Action/HomePageAction.php
Normal file
32
src/App/Action/HomePageAction.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Service\JiraCollectorService;
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Zend\Diactoros\Response\HtmlResponse;
|
||||
use Zend\Expressive\Template;
|
||||
|
||||
class HomePageAction implements ServerMiddlewareInterface
|
||||
{
|
||||
private $template;
|
||||
private $jiraService;
|
||||
|
||||
public function __construct(Template\TemplateRendererInterface $template,
|
||||
JiraCollectorService $jiraService)
|
||||
{
|
||||
$this->template = $template;
|
||||
$this->jiraService = $jiraService;
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
$data = [
|
||||
'chartRows' => $this->jiraService->getChartData(),
|
||||
];
|
||||
|
||||
return new HtmlResponse($this->template->render('app::home-page', $data));
|
||||
}
|
||||
}
|
||||
17
src/App/Action/HomePageFactory.php
Normal file
17
src/App/Action/HomePageFactory.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use App\Service\JiraCollectorService;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Zend\Expressive\Template\TemplateRendererInterface;
|
||||
|
||||
class HomePageFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$jiraCollector = $container->get(JiraCollectorService::class);
|
||||
$template = $container->get(TemplateRendererInterface::class);
|
||||
return new HomePageAction($template, $jiraCollector);
|
||||
}
|
||||
}
|
||||
16
src/App/Action/PingAction.php
Normal file
16
src/App/Action/PingAction.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Action;
|
||||
|
||||
use Interop\Http\ServerMiddleware\DelegateInterface;
|
||||
use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface;
|
||||
use Zend\Diactoros\Response\JsonResponse;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
class PingAction implements ServerMiddlewareInterface
|
||||
{
|
||||
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
|
||||
{
|
||||
return new JsonResponse(['ack' => time()]);
|
||||
}
|
||||
}
|
||||
87
src/App/ConfigProvider.php
Normal file
87
src/App/ConfigProvider.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Zend\Config\Config;
|
||||
use Zend\Http\Client;
|
||||
|
||||
/**
|
||||
* The configuration provider for the App module
|
||||
*
|
||||
* @see https://docs.zendframework.com/zend-component-installer/
|
||||
*/
|
||||
class ConfigProvider
|
||||
{
|
||||
/**
|
||||
* Returns the configuration array
|
||||
*
|
||||
* To add a bit of a structure, each section is defined in a separate
|
||||
* method which returns an array with its configuration.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
return [
|
||||
'dependencies' => $this->getDependencies(),
|
||||
'templates' => $this->getTemplates(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the container dependencies
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDependencies()
|
||||
{
|
||||
return [
|
||||
'invokables' => [
|
||||
Action\PingAction::class => Action\PingAction::class,
|
||||
],
|
||||
'factories' => [
|
||||
Action\HomePageAction::class => Action\HomePageFactory::class,
|
||||
Action\DbgAction::class => Action\DbgFactory::class,
|
||||
|
||||
Service\JiraCollectorService::class => Service\JiraCollectorServiceFactory::class,
|
||||
Client::class => function(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;
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the templates configuration
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTemplates()
|
||||
{
|
||||
return [
|
||||
'paths' => [
|
||||
'app' => ['templates/app'],
|
||||
'error' => ['templates/error'],
|
||||
'layout' => ['templates/layout'],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
111
src/App/Entity/JiraIssue.php
Normal file
111
src/App/Entity/JiraIssue.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
class JiraIssue implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $key;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
private $createdAt;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
private $resolvedAt;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return JiraIssue
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getKey()
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return JiraIssue
|
||||
*/
|
||||
public function setKey($key)
|
||||
{
|
||||
$this->key = $key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getCreatedAt()
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $createdAt
|
||||
* @return JiraIssue
|
||||
*/
|
||||
public function setCreatedAt($createdAt)
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getResolvedAt()
|
||||
{
|
||||
return $this->resolvedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTime $resolvedAt
|
||||
* @return JiraIssue
|
||||
*/
|
||||
public function setResolvedAt($resolvedAt)
|
||||
{
|
||||
$this->resolvedAt = $resolvedAt;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'key' => $this->getKey(),
|
||||
'createdAt' => $this->getCreatedAt(),
|
||||
'resolvedAt' => $this->getResolvedAt(),
|
||||
];
|
||||
}
|
||||
}
|
||||
103
src/App/Entity/WeekData.php
Normal file
103
src/App/Entity/WeekData.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
class WeekData implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $in = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $out = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $open = 0;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getIn(): int
|
||||
{
|
||||
return $this->in;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $in
|
||||
* @return WeekData
|
||||
*/
|
||||
public function setIn(int $in): WeekData
|
||||
{
|
||||
$this->in = $in;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $step
|
||||
*/
|
||||
public function incrementIn(int $step = 1)
|
||||
{
|
||||
$this->in += $step;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getOut(): int
|
||||
{
|
||||
return $this->out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $out
|
||||
* @return WeekData
|
||||
*/
|
||||
public function setOut(int $out): WeekData
|
||||
{
|
||||
$this->out = $out;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $step
|
||||
*/
|
||||
public function incrementOut(int $step = 1)
|
||||
{
|
||||
$this->out += $step;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getOpen(): int
|
||||
{
|
||||
return $this->open;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $open
|
||||
* @return WeekData
|
||||
*/
|
||||
public function setOpen(int $open): WeekData
|
||||
{
|
||||
$this->open = $open;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'in' => $this->getIn(),
|
||||
'out' => $this->getOut(),
|
||||
'open' => $this->getOpen(),
|
||||
];
|
||||
}
|
||||
}
|
||||
141
src/App/Service/JiraCollectorService.php
Normal file
141
src/App/Service/JiraCollectorService.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\JiraIssue;
|
||||
use App\Entity\WeekData;
|
||||
use Zend\Config\Config;
|
||||
use Zend\Http\Client;
|
||||
use Zend\Json\Decoder;
|
||||
use Zend\Json\Json;
|
||||
|
||||
class JiraCollectorService
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
private $httpClient;
|
||||
|
||||
/**
|
||||
* JiraClientService constructor.
|
||||
* @param Client $client
|
||||
* @param Config $config
|
||||
*/
|
||||
public function __construct(Client $client, Config $config)
|
||||
{
|
||||
$this->httpClient = $client;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function getChartData()
|
||||
{
|
||||
$supportTickets = $this->getSupportJiraTickets();
|
||||
$openTickets = array_values(array_filter($supportTickets, function(JiraIssue $ticket){
|
||||
return $ticket->getResolvedAt() === null;
|
||||
}));
|
||||
$openTicketCount = count($openTickets);
|
||||
$weekHash = $this->prepareWeekHash();
|
||||
|
||||
$weekHash[date("Y_W")]->setOpen($openTicketCount);
|
||||
array_map(function (JiraIssue $jiraTicket) use ($weekHash) {
|
||||
$createdAtIdx = $jiraTicket->getCreatedAt()->format("Y_W");
|
||||
|
||||
if(array_key_exists($createdAtIdx, $weekHash)) {
|
||||
$weekHash[$createdAtIdx]->incrementIn();
|
||||
}
|
||||
|
||||
if($jiraTicket->getResolvedAt()) {
|
||||
$resolvedAtIdx = $jiraTicket->getResolvedAt()->format("Y_W");
|
||||
if(array_key_exists($resolvedAtIdx, $weekHash)) {
|
||||
$weekHash[$resolvedAtIdx]->incrementOut();
|
||||
}
|
||||
}
|
||||
}, $supportTickets);
|
||||
|
||||
$this->calculateOpenTickets($weekHash);
|
||||
return array_reverse($weekHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
public function getSupportJiraTickets(): ?array
|
||||
{
|
||||
$user = $this->config->get('jira.user');
|
||||
$password = $this->config->get('jira.password');
|
||||
$jiraResultUrl = $this->config->get('jira.query');
|
||||
|
||||
$response = $this->httpClient
|
||||
->setUri($jiraResultUrl)
|
||||
->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);
|
||||
|
||||
return $kanbanBoard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $parsedJsonData
|
||||
* @return JiraIssue[]
|
||||
*/
|
||||
private function hydrateKanbanBoard($parsedJsonData): array
|
||||
{
|
||||
$jiraIssues = [];
|
||||
|
||||
foreach ($parsedJsonData['issues'] as $jsonIssue) {
|
||||
$jiraIssue = new JiraIssue();
|
||||
$jiraIssue->setId(intval($jsonIssue['id']))
|
||||
->setKey($jsonIssue['key'])
|
||||
->setCreatedAt(new \DateTime($jsonIssue['fields']['created']));
|
||||
|
||||
if ($jsonIssue['fields']['resolutiondate']) {
|
||||
$jiraIssue->setResolvedAt(new \DateTime($jsonIssue['fields']['resolutiondate']));
|
||||
}
|
||||
$jiraIssues[] = $jiraIssue;
|
||||
}
|
||||
|
||||
return $jiraIssues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WeekData[]
|
||||
*/
|
||||
private function prepareWeekHash(): array
|
||||
{
|
||||
$hash = [];
|
||||
for ($i = 0; $i > -30; $i-- ) {
|
||||
$hashIndex = date("Y_W", strtotime("${i} week"));
|
||||
$hash[$hashIndex] = new WeekData();
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WeekData[] $hash
|
||||
* @return WeekData[]
|
||||
*/
|
||||
private function calculateOpenTickets(array &$hash)
|
||||
{
|
||||
$thisWeek = reset($hash);
|
||||
$openCount = $thisWeek->getOpen() - $thisWeek->getIn() + $thisWeek->getOut();
|
||||
while($weekData = next($hash)) {
|
||||
// going in reverse direction, so logic is also reversed here
|
||||
$weekData->setOpen($openCount);
|
||||
$openCount = $openCount - $weekData->getIn() + $weekData->getOut();
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
18
src/App/Service/JiraCollectorServiceFactory.php
Normal file
18
src/App/Service/JiraCollectorServiceFactory.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Zend\Config\Config;
|
||||
use Zend\Http\Client;
|
||||
|
||||
class JiraCollectorServiceFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$configArray = $container->get('config');
|
||||
$httpClient = $container->get(Client::class);
|
||||
$config = new Config($configArray['app.config']);
|
||||
return new JiraCollectorService($httpClient, $config);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user