Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e5f4c7f68 | ||
|
|
d27ff7d574 | ||
|
|
15455f648e | ||
|
|
66bc94037d | ||
|
|
25ff60b34b | ||
|
|
c7a2e68a82 | ||
|
|
bb937a664b | ||
|
|
3d42f16c38 | ||
|
|
9d3fa5fa9d | ||
|
|
8c2b7fe548 | ||
|
|
176cbd86f0 | ||
|
|
261c43086a | ||
|
|
65098ee587 | ||
|
|
9563eae0b1 | ||
|
|
cfc388aa77 | ||
|
|
c096510b3d |
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "zendframework/zend-expressive-skeleton",
|
||||
"description": "Zend expressive skeleton. Begin developing PSR-15 middleware applications in seconds!",
|
||||
"name": "mtas/mtas-tv-backend",
|
||||
"description": "MTAS-TV backend api",
|
||||
"type": "project",
|
||||
"homepage": "https://github.com/zendframework/zend-expressive-skeleton",
|
||||
"homepage": "https://gogs.ragnarok.yvan.hu/MTAS/mtas-tv-backend",
|
||||
"license": "BSD-3-Clause",
|
||||
"keywords": [
|
||||
"skeleton",
|
||||
@ -63,7 +63,8 @@
|
||||
"zendframework/zend-json": "^3.1",
|
||||
"zendframework/zend-log": "^2.10",
|
||||
"zendframework/zend-servicemanager": "^3.3",
|
||||
"zendframework/zend-stdlib": "^3.1"
|
||||
"zendframework/zend-stdlib": "^3.1",
|
||||
"ext-posix": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0.1",
|
||||
|
||||
16
config/autoload/cli.global.php
Executable file
16
config/autoload/cli.global.php
Executable file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
App\Command\UpdatePageCachesCommand::class => App\Command\UpdatePageCachesFactory::class,
|
||||
],
|
||||
],
|
||||
'console' => [
|
||||
'commands' => [
|
||||
App\Command\UpdatePageCachesCommand::class,
|
||||
],
|
||||
],
|
||||
];
|
||||
28
config/autoload/local.php.dist
Normal file → Executable file
28
config/autoload/local.php.dist
Normal file → Executable file
@ -9,4 +9,32 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'app.config' => [
|
||||
'jira.user' => '',
|
||||
'jira.password' => '',
|
||||
|
||||
'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',
|
||||
'priority',
|
||||
'issuetype',
|
||||
'labels',
|
||||
'assignee',
|
||||
'status',
|
||||
'worklog',
|
||||
'updated',
|
||||
'fixVersions',
|
||||
|
||||
'customfield_11711', // epic link field
|
||||
'customfield_11712', // epic name
|
||||
'customfield_13662', // additional jiraAssignees
|
||||
],
|
||||
|
||||
'http.proxy.enabled' => false,
|
||||
'http.proxy.type' => CURLPROXY_SOCKS5,
|
||||
'http.proxy.url' => "localhost:1080",
|
||||
],
|
||||
];
|
||||
|
||||
4
config/routes.php
Normal file → Executable file
4
config/routes.php
Normal file → Executable file
@ -46,6 +46,6 @@ return function (Application $app, MiddlewareFactory $factory, ContainerInterfac
|
||||
$app->route('/api/slide[/{id:\d+}]', App\Handler\SlideHandler::class)->setName('api.slide');
|
||||
|
||||
$app->get('/avatars/{signum}', App\Handler\AvatarHandler::class,'avatar.image');
|
||||
$app->get('/api/kanban[/{filterId:\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');
|
||||
};
|
||||
|
||||
79
deploy-local-build.php
Normal file
79
deploy-local-build.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
namespace Deployer;
|
||||
require 'recipe/common.php';
|
||||
|
||||
// Configuration
|
||||
set('ssh_type', 'native');
|
||||
set('ssh_multiplexing', true);
|
||||
|
||||
#set('repository', 'https://gogs.ragnarok.yvan.hu/MTAS/mtas-tv-backend.git');
|
||||
set('shared_files', [
|
||||
'config/autoload/local.php',
|
||||
'config/autoload/los-basepath.local.php',
|
||||
'config/autoload/doctrine.local.php',
|
||||
'data/production.db',
|
||||
]);
|
||||
/*
|
||||
set('shared_dirs', [
|
||||
'data/persistent',
|
||||
]);
|
||||
*/
|
||||
set('writable_dirs', [
|
||||
'data/cache',
|
||||
'data/logs',
|
||||
]);
|
||||
set('keep_releases', 3);
|
||||
set('default_stage', 'production');
|
||||
|
||||
|
||||
// Servers - mtas : esekivws5222a.rnd.ki.sw.ericsson.se
|
||||
host('mtas')
|
||||
->stage('production')
|
||||
->user('edvidan')
|
||||
->forwardAgent()
|
||||
->set('deploy_path', '/proj/webdocs/mtoolbox/root/mtastv-inst/backend');
|
||||
|
||||
|
||||
task('build', function() {
|
||||
run('composer install --verbose --prefer-dist --no-progress --no-interaction --no-dev --optimize-autoloader');
|
||||
run('composer archive --file=release-pkg');
|
||||
run('tar -czf vendor-pkg.tar.gz vendor');
|
||||
})->local();
|
||||
|
||||
task('upload', function() {
|
||||
upload('release-pkg.tar', '{{release_path}}');
|
||||
upload('vendor-pkg.tar.gz', '{{release_path}}');
|
||||
cd('{{release_path}}');
|
||||
run('tar -xf release-pkg.tar');
|
||||
run('tar -xzf vendor-pkg.tar.gz');
|
||||
});
|
||||
|
||||
task('clean-uploaded', function() {
|
||||
cd('{{release_path}}');
|
||||
run('rm -f release-pkg.tar vendor-pkg.tar.gz');
|
||||
});
|
||||
|
||||
task('restore-dev', function() {
|
||||
run('composer install --verbose --no-progress --no-interaction');
|
||||
})->local();
|
||||
|
||||
task('release', [
|
||||
'deploy:lock',
|
||||
'deploy:prepare',
|
||||
'deploy:release',
|
||||
'upload',
|
||||
'clean-uploaded',
|
||||
'deploy:shared',
|
||||
'deploy:writable',
|
||||
'deploy:symlink',
|
||||
'deploy:unlock',
|
||||
]);
|
||||
|
||||
desc('Deploy your project');
|
||||
task('deploy', [
|
||||
'build',
|
||||
'release',
|
||||
'cleanup',
|
||||
'restore-dev',
|
||||
'success',
|
||||
]);
|
||||
@ -23,9 +23,8 @@ set('keep_releases', 3);
|
||||
set('default_stage', 'production');
|
||||
|
||||
|
||||
// Servers
|
||||
host('vasgyuro.tsp')
|
||||
->stage('production')
|
||||
->stage('staging')
|
||||
->user('edvidan')
|
||||
->forwardAgent()
|
||||
->set('php_service_name', 'php7.1-fpm')
|
||||
@ -37,7 +36,7 @@ task('php-fpm:reload', function () {
|
||||
// The user must have rights for restart service
|
||||
// /etc/sudoers: username ALL=NOPASSWD:/bin/systemctl restart php-fpm.service
|
||||
run('sudo service {{php_service_name}} reload');
|
||||
}); //->onlyOn('alfheim');
|
||||
});
|
||||
after('deploy:symlink', 'php-fpm:reload');
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 675 B |
Binary file not shown.
|
Before Width: | Height: | Size: 288 B |
108
src/App/Command/UpdatePageCachesCommand.php
Executable file
108
src/App/Command/UpdatePageCachesCommand.php
Executable file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Service\JiraCollectorService;
|
||||
use App\Service\TeamService;
|
||||
use LosMiddleware\LosLog\StaticLogger;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class UpdatePageCachesCommand extends Command
|
||||
{
|
||||
const LOCK_FILE = 'data/update-caches-cron.lock';
|
||||
|
||||
/** @var JiraCollectorService */
|
||||
private $jiraCollectorService;
|
||||
|
||||
/** @var TeamService */
|
||||
private $teamService;
|
||||
|
||||
public function __construct(JiraCollectorService $jiraCollectorService,
|
||||
TeamService $teamService)
|
||||
{
|
||||
$this->jiraCollectorService = $jiraCollectorService;
|
||||
$this->teamService = $teamService;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the command
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('cache:update')
|
||||
->setDescription('Updates page-cache data for kanban pages');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int|null|void
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($this->isLocked()) {
|
||||
$output->writeln("Lock file exists, not starting.");
|
||||
return;
|
||||
}
|
||||
$this->createLock();
|
||||
$teams = $this->teamService->listTeams();
|
||||
foreach ($teams as $team) {
|
||||
if ($team->isKanbanEnabled() && null !== $team->getFilterId()) {
|
||||
set_time_limit(30);
|
||||
$this->jiraCollectorService->getKanbanBoard($team->getId(), true);
|
||||
}
|
||||
if ($team->isWatchedEnabled()) {
|
||||
set_time_limit(30);
|
||||
$this->jiraCollectorService->getTeamWatchedIssues($team->getId(), true);
|
||||
}
|
||||
}
|
||||
$this->releaseLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the lock file
|
||||
*/
|
||||
private function createLock(): void
|
||||
{
|
||||
file_put_contents(self::LOCK_FILE, posix_getpid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the lock file
|
||||
* @return bool
|
||||
*/
|
||||
private function releaseLock(): bool
|
||||
{
|
||||
return unlink(self::LOCK_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if lock file exists, also removes stale lock
|
||||
* @return bool
|
||||
* @throws \LosMiddleware\LosLog\Exception\InvalidArgumentException
|
||||
*/
|
||||
private function isLocked(): bool
|
||||
{
|
||||
if (!file_exists(self::LOCK_FILE)) {
|
||||
return false;
|
||||
}
|
||||
StaticLogger::save("[1] LOCK file exists.");
|
||||
|
||||
$pid = (int)file_get_contents(self::LOCK_FILE);
|
||||
if (posix_getpgid($pid)) {
|
||||
StaticLogger::save("[2] PID is running");
|
||||
$binPath = readlink("/proc/${pid}/exe");
|
||||
$binName = basename($binPath);
|
||||
return substr($binName, 0, 3) === 'php';
|
||||
}
|
||||
|
||||
StaticLogger::save("[2] No process found.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
42
src/App/Command/UpdatePageCachesFactory.php
Executable file
42
src/App/Command/UpdatePageCachesFactory.php
Executable file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Handler\AvatarHandler;
|
||||
use App\Service\JiraCollectorService;
|
||||
use App\Service\TeamService;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Zend\Expressive\MiddlewareContainer;
|
||||
use Zend\Expressive\MiddlewareFactory;
|
||||
use Zend\Expressive\Router\Route;
|
||||
use Zend\Expressive\Router\RouterInterface;
|
||||
|
||||
class UpdatePageCachesFactory
|
||||
{
|
||||
/**
|
||||
* @param ContainerInterface $container
|
||||
* @return UpdatePageCachesCommand
|
||||
*/
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$middlewareContainer = new MiddlewareContainer($container);
|
||||
$factory = new MiddlewareFactory($middlewareContainer);
|
||||
$avatarHandlerMiddleware = $factory->prepare(AvatarHandler::class);
|
||||
|
||||
$avatarRoute = new Route(
|
||||
'/avatars/{signum}',
|
||||
$avatarHandlerMiddleware,
|
||||
Route::HTTP_METHOD_ANY,
|
||||
'avatar.image'
|
||||
);
|
||||
/** @var \Zend\Expressive\Router\FastRouteRouter $router */
|
||||
$router = $container->get(RouterInterface::class);
|
||||
$router->addRoute($avatarRoute);
|
||||
|
||||
$jiraCollectorService = $container->get(JiraCollectorService::class);
|
||||
$teamService = $container->get(TeamService::class);
|
||||
return new UpdatePageCachesCommand($jiraCollectorService, $teamService);
|
||||
}
|
||||
}
|
||||
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\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,
|
||||
|
||||
25
src/App/Entity/KanbanBoard.php
Normal file → Executable file
25
src/App/Entity/KanbanBoard.php
Normal file → Executable file
@ -8,19 +8,13 @@ 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,
|
||||
// ];
|
||||
const PRIO_MAP = [
|
||||
'Trivial' => 0,
|
||||
'Minor' => 1,
|
||||
'Major' => 2,
|
||||
'Critical' => 3,
|
||||
'Blocker' => 4,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var KanbanEntry[]|ArrayCollection
|
||||
@ -225,7 +219,10 @@ class KanbanBoard implements \JsonSerializable
|
||||
private function prioSort(array $toSort): array
|
||||
{
|
||||
usort($toSort, function (KanbanEntry $a, KanbanEntry $b) {
|
||||
return $a->getTaurusPrio() <=> $b->getTaurusPrio();
|
||||
// if (null !== $a->getTaurusPrio()) {
|
||||
return $a->getTaurusPrio() <=> $b->getTaurusPrio();
|
||||
// }
|
||||
// return self::PRIO_MAP[$b->getIssuePriority()] <=> self::PRIO_MAP[$a->getIssuePriority()];
|
||||
});
|
||||
return $toSort;
|
||||
}
|
||||
|
||||
83
src/App/Entity/KanbanColumn.php
Normal file
83
src/App/Entity/KanbanColumn.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
class KanbanColumn implements \JsonSerializable
|
||||
{
|
||||
/** @var string */
|
||||
private $jiraStatusName = "";
|
||||
|
||||
/** @var string */
|
||||
private $label = "";
|
||||
|
||||
/** @var integer */
|
||||
private $wipLimit = 0;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getJiraStatusName(): ?string
|
||||
{
|
||||
return $this->jiraStatusName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $jiraStatusName
|
||||
* @return KanbanColumn
|
||||
*/
|
||||
public function setJiraStatusName(?string $jiraStatusName): KanbanColumn
|
||||
{
|
||||
$this->jiraStatusName = $jiraStatusName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $label
|
||||
* @return KanbanColumn
|
||||
*/
|
||||
public function setLabel(?string $label): KanbanColumn
|
||||
{
|
||||
$this->label = $label;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getWipLimit(): ?int
|
||||
{
|
||||
return $this->wipLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $wipLimit
|
||||
* @return KanbanColumn
|
||||
*/
|
||||
public function setWipLimit(?int $wipLimit): KanbanColumn
|
||||
{
|
||||
$this->wipLimit = $wipLimit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'jiraStatusName' => $this->getJiraStatusName(),
|
||||
'label' => $this->getLabel(),
|
||||
'wipLimit' => $this->getWipLimit(),
|
||||
];
|
||||
}
|
||||
}
|
||||
55
src/App/Entity/KanbanEntry.php
Normal file → Executable file
55
src/App/Entity/KanbanEntry.php
Normal file → Executable file
@ -29,6 +29,12 @@ class KanbanEntry implements \JsonSerializable
|
||||
*/
|
||||
private $issueType;
|
||||
|
||||
/**
|
||||
* JIRA: link to parent is in a custom field, parent has the epic name as a custom field
|
||||
* @var string
|
||||
*/
|
||||
private $epicName;
|
||||
|
||||
/**
|
||||
* @var JiraStatus
|
||||
*/
|
||||
@ -40,7 +46,6 @@ class KanbanEntry implements \JsonSerializable
|
||||
private $assignee;
|
||||
|
||||
/**
|
||||
* JIRA: customfield_10401
|
||||
* @var JiraAssignee[]
|
||||
*/
|
||||
private $additionalAssignees;
|
||||
@ -66,68 +71,57 @@ class KanbanEntry implements \JsonSerializable
|
||||
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;
|
||||
@ -228,6 +222,24 @@ class KanbanEntry implements \JsonSerializable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEpicName(): ?string
|
||||
{
|
||||
return $this->epicName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $epicName
|
||||
* @return KanbanEntry
|
||||
*/
|
||||
public function setEpicName(?string $epicName): KanbanEntry
|
||||
{
|
||||
$this->epicName = $epicName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JiraStatus
|
||||
*/
|
||||
@ -309,7 +321,7 @@ class KanbanEntry implements \JsonSerializable
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIssuePriority(): string
|
||||
public function getIssuePriority(): ?string
|
||||
{
|
||||
return $this->issuePriority;
|
||||
}
|
||||
@ -318,7 +330,7 @@ class KanbanEntry implements \JsonSerializable
|
||||
* @param string $issuePriority
|
||||
* @return KanbanEntry
|
||||
*/
|
||||
public function setIssuePriority(string $issuePriority): KanbanEntry
|
||||
public function setIssuePriority(?string $issuePriority): KanbanEntry
|
||||
{
|
||||
$this->issuePriority = $issuePriority;
|
||||
return $this;
|
||||
@ -327,7 +339,7 @@ class KanbanEntry implements \JsonSerializable
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIssuePriorityIcon(): string
|
||||
public function getIssuePriorityIcon(): ?string
|
||||
{
|
||||
return $this->issuePriorityIcon;
|
||||
}
|
||||
@ -336,7 +348,7 @@ class KanbanEntry implements \JsonSerializable
|
||||
* @param string $issuePriorityIcon
|
||||
* @return KanbanEntry
|
||||
*/
|
||||
public function setIssuePriorityIcon(string $issuePriorityIcon): KanbanEntry
|
||||
public function setIssuePriorityIcon(?string $issuePriorityIcon): KanbanEntry
|
||||
{
|
||||
$this->issuePriorityIcon = $issuePriorityIcon;
|
||||
return $this;
|
||||
@ -664,22 +676,13 @@ class KanbanEntry implements \JsonSerializable
|
||||
'key' => $this->getKey(),
|
||||
'summary' => $this->getSummary(),
|
||||
'issueType' => $this->getIssueType(),
|
||||
'epicName' => $this->getEpicName(),
|
||||
'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(),
|
||||
|
||||
153
src/App/Entity/Slide.php
Normal file → Executable file
153
src/App/Entity/Slide.php
Normal file → Executable file
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use JsonSerializable;
|
||||
@ -15,6 +17,12 @@ use JsonSerializable;
|
||||
*/
|
||||
class Slide implements JsonSerializable
|
||||
{
|
||||
const TYPE_MARKDOWN = 'markdown';
|
||||
const TYPE_IFRAME = 'iframe';
|
||||
|
||||
const VISIBILITY_PUBLIC = 'public';
|
||||
const VISIBILITY_TEAM = 'team';
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="IDENTITY")
|
||||
@ -30,18 +38,29 @@ class Slide implements JsonSerializable
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Team", inversedBy="slides")
|
||||
* @ORM\JoinColumn(name="team_id", referencedColumnName="id")
|
||||
* @var Team
|
||||
* @ORM\Column(name="type", type="string", length=50, options={"default" = "markdown"})
|
||||
* @var string
|
||||
*/
|
||||
private $team;
|
||||
private $type = self::TYPE_MARKDOWN;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="slide_data", type="text", nullable=false)
|
||||
* @ORM\Column(name="visibility", type="string", length=50, options={"default" = "public"})
|
||||
* @var string
|
||||
*/
|
||||
private $visibility = self::VISIBILITY_PUBLIC;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="slide_data", type="text", nullable=true)
|
||||
* @var string
|
||||
*/
|
||||
private $slideData;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="slide_url", type="text", nullable=true)
|
||||
* @var string
|
||||
*/
|
||||
private $slideUrl;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="is_visible", type="boolean")
|
||||
* @var bool
|
||||
@ -69,6 +88,17 @@ class Slide implements JsonSerializable
|
||||
*/
|
||||
private $position;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToMany(targetEntity="Team", inversedBy="slides", cascade={"persist", "remove"})
|
||||
* @var Team[]|ArrayCollection
|
||||
*/
|
||||
private $teams;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->teams = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
@ -106,20 +136,38 @@ class Slide implements JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Team
|
||||
* @return string
|
||||
*/
|
||||
public function getTeam(): ?Team
|
||||
public function getType(): ?string
|
||||
{
|
||||
return $this->team;
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Team $team
|
||||
* @param string $type
|
||||
* @return Slide
|
||||
*/
|
||||
public function setTeam(?Team $team): Slide
|
||||
public function setType(?string $type): Slide
|
||||
{
|
||||
$this->team = $team;
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getVisibility(): ?string
|
||||
{
|
||||
return $this->visibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $visibility
|
||||
* @return Slide
|
||||
*/
|
||||
public function setVisibility(?string $visibility): Slide
|
||||
{
|
||||
$this->visibility = $visibility;
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -135,12 +183,30 @@ class Slide implements JsonSerializable
|
||||
* @param string $slideData
|
||||
* @return Slide
|
||||
*/
|
||||
public function setSlideData(string $slideData): Slide
|
||||
public function setSlideData(?string $slideData): Slide
|
||||
{
|
||||
$this->slideData = $slideData;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSlideUrl(): ?string
|
||||
{
|
||||
return $this->slideUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $slideUrl
|
||||
* @return Slide
|
||||
*/
|
||||
public function setSlideUrl(?string $slideUrl): Slide
|
||||
{
|
||||
$this->slideUrl = $slideUrl;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@ -213,6 +279,64 @@ class Slide implements JsonSerializable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Team $team
|
||||
* @return Slide
|
||||
*/
|
||||
public function addTeam(Team $team): Slide
|
||||
{
|
||||
if (!$this->teams->contains($team)) {
|
||||
$this->teams->add($team);
|
||||
}
|
||||
//$team->addSlide($this);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Team[] $teams
|
||||
* @return Slide
|
||||
*/
|
||||
public function addTeams($teams): Slide
|
||||
{
|
||||
foreach ($teams as $team) {
|
||||
$this->addTeam($team);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Team[]|Collection
|
||||
*/
|
||||
public function getTeams(): Collection
|
||||
{
|
||||
return $this->teams;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Team $team
|
||||
* @return Slide
|
||||
*/
|
||||
public function removeTeam(Team $team): Slide
|
||||
{
|
||||
if ($this->teams->contains($team)) {
|
||||
$this->teams->removeElement($team);
|
||||
}
|
||||
//$team->removeSlide($this);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Team[] $teams
|
||||
* @return Slide
|
||||
*/
|
||||
public function removeTeams($teams): Slide
|
||||
{
|
||||
foreach ($teams as $team) {
|
||||
$this->removeTeam($team);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
@ -221,8 +345,11 @@ class Slide implements JsonSerializable
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'title' => $this->getTitle(),
|
||||
'team' => $this->getTeam(),
|
||||
'type' => $this->getType(),
|
||||
'visibility' => $this->getVisibility(),
|
||||
'teams' => $this->getTeams()->getValues(),
|
||||
'slideData' => $this->getSlideData(),
|
||||
'slideUrl' => $this->getSlideUrl(),
|
||||
'isVisible' => $this->isVisible(),
|
||||
'createdAt' => $this->getCreatedAt()
|
||||
? $this->getCreatedAt()->format("Y-m-d H:i:s")
|
||||
|
||||
334
src/App/Entity/Team.php
Normal file → Executable file
334
src/App/Entity/Team.php
Normal file → Executable file
@ -37,21 +37,97 @@ class Team implements JsonSerializable
|
||||
private $members;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(
|
||||
* targetEntity="Slide",
|
||||
* mappedBy="team",
|
||||
* cascade={"persist", "remove"},
|
||||
* orphanRemoval=true
|
||||
* @ORM\Column(name="labels", type="json", nullable=true)
|
||||
* @var array
|
||||
*/
|
||||
private $labels;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToMany(targetEntity="Slide", mappedBy="teams", cascade={"persist", "remove"})
|
||||
* @ORM\JoinTable(
|
||||
* name="team_slides",
|
||||
* joinColumns={
|
||||
* @ORM\JoinColumn(name="team_id", referencedColumnName="id")
|
||||
* },
|
||||
* inverseJoinColumns={
|
||||
* @ORM\JoinColumn(name="slide_id", referencedColumnName="id")
|
||||
* }
|
||||
* )
|
||||
* @var Slide[]|Collection
|
||||
*/
|
||||
private $slides;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="kanban_enabled", type="boolean", options={"default" = true})
|
||||
* @var bool
|
||||
*/
|
||||
private $kanbanEnabled = true;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="commit_tracker_enabled", type="boolean", options={"default" = true})
|
||||
* @var bool
|
||||
*/
|
||||
private $commitTrackerEnabled = true;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="watched_enabled", type="boolean", options={"default" = true})
|
||||
* @var bool
|
||||
*/
|
||||
private $watchedEnabled = true;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="filter_id", type="integer", nullable=true)
|
||||
* @var int
|
||||
*/
|
||||
private $filterId;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="daily_lock_enabled", type="boolean", options={"default" = false})
|
||||
* @var bool
|
||||
*/
|
||||
private $dailyLockEnabled = false;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="daily_start_time", type="time_immutable", nullable=true)
|
||||
* @var \DateTimeImmutable
|
||||
*/
|
||||
private $dailyStartTime;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="daily_end_time", type="time_immutable", nullable=true)
|
||||
* @var \DateTimeImmutable
|
||||
*/
|
||||
private $dailyEndTime;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="backlog_column", type="json", nullable=true)
|
||||
* @var KanbanColumn
|
||||
*/
|
||||
private $backlogColumn;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="inprogress_column", type="json", nullable=true)
|
||||
* @var KanbanColumn
|
||||
*/
|
||||
private $inprogressColumn;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="verification_column", type="json", nullable=true)
|
||||
* @var KanbanColumn
|
||||
*/
|
||||
private $verificationColumn;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="done_column", type="json", nullable=true)
|
||||
* @var KanbanColumn
|
||||
*/
|
||||
private $doneColumn;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="is_active", type="boolean")
|
||||
* @var bool
|
||||
*/
|
||||
private $isActive;
|
||||
private $isActive = true;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="created_at", type="datetime_immutable", nullable=true)
|
||||
@ -69,8 +145,14 @@ class Team implements JsonSerializable
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->slides = new ArrayCollection;
|
||||
$this->members = new \ArrayObject;
|
||||
$this->labels = new \ArrayObject;
|
||||
$this->slides = new ArrayCollection;
|
||||
|
||||
$this->backlogColumn = new KanbanColumn();
|
||||
$this->inprogressColumn = new KanbanColumn();
|
||||
$this->verificationColumn = new KanbanColumn();
|
||||
$this->doneColumn = new KanbanColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,15 +209,34 @@ class Team implements JsonSerializable
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getLabels()
|
||||
{
|
||||
return $this->labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $labels
|
||||
* @return Team
|
||||
*/
|
||||
public function setLabels(array $labels): Team
|
||||
{
|
||||
$this->labels = $labels;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Slide $slide
|
||||
* @return Team
|
||||
*/
|
||||
public function addSlides(Slide $slide): Team
|
||||
public function addSlide(Slide $slide): Team
|
||||
{
|
||||
if (!$this->slides->contains($slide)) {
|
||||
$this->slides->removeElement($slide);
|
||||
}
|
||||
//$slide->addTeam($this);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -156,6 +257,205 @@ class Team implements JsonSerializable
|
||||
if ($this->slides->contains($slide)) {
|
||||
$this->slides->removeElement($slide);
|
||||
}
|
||||
//$slide->removeTeam($this);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isKanbanEnabled(): bool
|
||||
{
|
||||
return $this->kanbanEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $kanbanEnabled
|
||||
* @return Team
|
||||
*/
|
||||
public function setKanbanEnabled(bool $kanbanEnabled): Team
|
||||
{
|
||||
$this->kanbanEnabled = $kanbanEnabled;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isCommitTrackerEnabled(): bool
|
||||
{
|
||||
return $this->commitTrackerEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $commitTrackerEnabled
|
||||
* @return Team
|
||||
*/
|
||||
public function setCommitTrackerEnabled(bool $commitTrackerEnabled): Team
|
||||
{
|
||||
$this->commitTrackerEnabled = $commitTrackerEnabled;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isWatchedEnabled(): bool
|
||||
{
|
||||
return $this->watchedEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $watchedEnabled
|
||||
* @return Team
|
||||
*/
|
||||
public function setWatchedEnabled(bool $watchedEnabled): Team
|
||||
{
|
||||
$this->watchedEnabled = $watchedEnabled;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getFilterId(): ?int
|
||||
{
|
||||
return $this->filterId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $filterId
|
||||
* @return Team
|
||||
*/
|
||||
public function setFilterId(?int $filterId): Team
|
||||
{
|
||||
$this->filterId = $filterId;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isDailyLockEnabled(): bool
|
||||
{
|
||||
return $this->dailyLockEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $dailyLockEnabled
|
||||
* @return Team
|
||||
*/
|
||||
public function setDailyLockEnabled(bool $dailyLockEnabled): Team
|
||||
{
|
||||
$this->dailyLockEnabled = $dailyLockEnabled;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeImmutable
|
||||
*/
|
||||
public function getDailyStartTime(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->dailyStartTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTimeInterface $dailyStartTime
|
||||
* @return Team
|
||||
*/
|
||||
public function setDailyStartTime(?\DateTimeInterface $dailyStartTime): Team
|
||||
{
|
||||
$this->dailyStartTime = $dailyStartTime;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTimeImmutable
|
||||
*/
|
||||
public function getDailyEndTime(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->dailyEndTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTimeInterface $dailyEndTime
|
||||
* @return Team
|
||||
*/
|
||||
public function setDailyEndTime(?\DateTimeInterface $dailyEndTime): Team
|
||||
{
|
||||
$this->dailyEndTime = $dailyEndTime;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|KanbanColumn
|
||||
*/
|
||||
public function getBacklogColumn()
|
||||
{
|
||||
return $this->backlogColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $backlogColumn
|
||||
* @return Team
|
||||
*/
|
||||
public function setBacklogColumn(?array $backlogColumn): Team
|
||||
{
|
||||
$this->backlogColumn = $backlogColumn;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|KanbanColumn
|
||||
*/
|
||||
public function getInprogressColumn()
|
||||
{
|
||||
return $this->inprogressColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $inprogressColumn
|
||||
* @return Team
|
||||
*/
|
||||
public function setInprogressColumn(array $inprogressColumn): Team
|
||||
{
|
||||
$this->inprogressColumn = $inprogressColumn;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|KanbanColumn
|
||||
*/
|
||||
public function getVerificationColumn()
|
||||
{
|
||||
return $this->verificationColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $verificationColumn
|
||||
* @return Team
|
||||
*/
|
||||
public function setVerificationColumn(?array $verificationColumn): Team
|
||||
{
|
||||
$this->verificationColumn = $verificationColumn;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|KanbanColumn
|
||||
*/
|
||||
public function getDoneColumn()
|
||||
{
|
||||
return $this->doneColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $doneColumn
|
||||
* @return Team
|
||||
*/
|
||||
public function setDoneColumn(?array $doneColumn): Team
|
||||
{
|
||||
$this->doneColumn = $doneColumn;
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -221,7 +521,23 @@ class Team implements JsonSerializable
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'name' => $this->getName(),
|
||||
'members' => $this->getMembers(),
|
||||
'members' => $this->getMembers() ?? [],
|
||||
'labels' => $this->getLabels() ?? [],
|
||||
'kanbanEnabled' => $this->isKanbanEnabled(),
|
||||
'commitTrackerEnabled' => $this->isCommitTrackerEnabled(),
|
||||
'watchedEnabled' => $this->isWatchedEnabled(),
|
||||
'filterId' => $this->getFilterId(),
|
||||
'dailyLockEnabled' => $this->isDailyLockEnabled(),
|
||||
'dailyStartTime' => $this->getDailyStartTime()
|
||||
? $this->getDailyStartTime()->format("H:i")
|
||||
: null,
|
||||
'dailyEndTime' => $this->getDailyEndTime()
|
||||
? $this->getDailyEndTime()->format("H:i")
|
||||
: null,
|
||||
'backlogColumn' => $this->getBacklogColumn() ?? new KanbanColumn(),
|
||||
'inprogressColumn' => $this->getInprogressColumn() ?? new KanbanColumn(),
|
||||
'verificationColumn' => $this->getVerificationColumn() ?? new KanbanColumn(),
|
||||
'doneColumn' => $this->getDoneColumn() ?? new KanbanColumn(),
|
||||
'isActive' => $this->isActive(),
|
||||
'createdAt' => $this->getCreatedAt()
|
||||
? $this->getCreatedAt()->format("Y-m-d H:i:s")
|
||||
|
||||
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,
|
||||
];
|
||||
}
|
||||
}
|
||||
53
src/App/Form/Slide.php
Normal file → Executable file
53
src/App/Form/Slide.php
Normal file → Executable file
@ -30,9 +30,34 @@ class Slide
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Required(true)
|
||||
* @Annotation\InputFilter("Zend\Filter\StringTrim")
|
||||
* @Annotation\Options({
|
||||
* "label": "Slide type"
|
||||
* })
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Required(true)
|
||||
* @Annotation\InputFilter("Zend\Filter\StringTrim")
|
||||
* @Annotation\Options({
|
||||
* "label": "Slide visibility"
|
||||
* })
|
||||
* @var string
|
||||
*/
|
||||
private $visibility;
|
||||
|
||||
/**
|
||||
* @Annotation\Type("doctrine.object_select")
|
||||
* @Annotation\Required(false)
|
||||
* @Annotation\Attributes({
|
||||
* "multiple": true
|
||||
* })
|
||||
* @Annotation\Options({
|
||||
* "property": "name",
|
||||
* "label": "Team",
|
||||
@ -48,21 +73,39 @@ class Slide
|
||||
* }
|
||||
* }
|
||||
* })
|
||||
* @var
|
||||
* @var Team[]
|
||||
*/
|
||||
private $team;
|
||||
private $teams;
|
||||
|
||||
/**
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Required(true)
|
||||
* @Annotation\Required(false)
|
||||
* @Annotation\InputFilter("Zend\Filter\StringTrim")
|
||||
* @Annotation\Options({
|
||||
* "label": "Slide contents"
|
||||
* "label": "Slide contents (type markdown)"
|
||||
* })
|
||||
* @var
|
||||
* @var string
|
||||
*/
|
||||
private $slideData;
|
||||
|
||||
/**
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Required(false)
|
||||
* @Annotation\InputFilter("Zend\Filter\StringTrim")
|
||||
* @Annotation\Options({
|
||||
* "label": "Slide url (type iframe)"
|
||||
* })
|
||||
* @Annotation\Validator({
|
||||
* "name":"Uri",
|
||||
* "options": {
|
||||
* "allowAbsolute": true,
|
||||
* "allowRelative": false,
|
||||
* }
|
||||
* })
|
||||
* @var string
|
||||
*/
|
||||
private $slideUrl;
|
||||
|
||||
/**
|
||||
* @Annotation\Type("Zend\Form\Element\Checkbox")
|
||||
* @Annotation\Options({
|
||||
|
||||
150
src/App/Form/Team.php
Normal file → Executable file
150
src/App/Form/Team.php
Normal file → Executable file
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\KanbanColumn;
|
||||
use Zend\Form\Annotation;
|
||||
|
||||
/**
|
||||
@ -41,6 +42,155 @@ class Team
|
||||
*/
|
||||
private $members;
|
||||
|
||||
/**
|
||||
* This is a dummy field, not a text actually. Only used to filter the input
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Required(false)
|
||||
* @Annotation\Options({
|
||||
* "label": "Labels"
|
||||
* })
|
||||
* @var array
|
||||
*/
|
||||
private $labels;
|
||||
|
||||
/**
|
||||
* This is a dummy field, not a text actually. Only used to filter the input
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Options({
|
||||
* "label": "Active"
|
||||
* })
|
||||
* @Annotation\Validator({
|
||||
* "name":"NotEmpty",
|
||||
* "options": {"type": Zend\Validator\NotEmpty::NULL}
|
||||
* })
|
||||
* @Annotation\Required(false)
|
||||
|
||||
* @var bool
|
||||
*/
|
||||
private $kanbanEnabled;
|
||||
|
||||
/**
|
||||
* This is a dummy field, not a text actually. Only used to filter the input
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Options({
|
||||
* "label": "Active"
|
||||
* })
|
||||
* @Annotation\Validator({
|
||||
* "name":"NotEmpty",
|
||||
* "options": {"type": Zend\Validator\NotEmpty::NULL}
|
||||
* })
|
||||
* @Annotation\Required(false)
|
||||
|
||||
* @var bool
|
||||
*/
|
||||
private $commitTrackerEnabled;
|
||||
|
||||
/**
|
||||
* This is a dummy field, not a text actually. Only used to filter the input
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Options({
|
||||
* "label": "Active"
|
||||
* })
|
||||
* @Annotation\Validator({
|
||||
* "name":"NotEmpty",
|
||||
* "options": {"type": Zend\Validator\NotEmpty::NULL}
|
||||
* })
|
||||
* @Annotation\Required(false)
|
||||
|
||||
* @var bool
|
||||
*/
|
||||
private $watchedEnabled;
|
||||
|
||||
/**
|
||||
* @Annotation\Type("Zend\Form\Element\Number")
|
||||
* @Annotation\Required(true)
|
||||
* @Annotation\Options({
|
||||
* "label": "Jira filter id"
|
||||
* })
|
||||
* @var int
|
||||
*/
|
||||
private $filterId;
|
||||
|
||||
/**
|
||||
* Also a dummy field, a
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Options({
|
||||
* "label": "Enabled"
|
||||
* })
|
||||
* @Annotation\Validator({
|
||||
* "name":"NotEmpty",
|
||||
* "options": {"type": Zend\Validator\NotEmpty::NULL}
|
||||
* })
|
||||
* @Annotation\Required(false)
|
||||
|
||||
* @var bool
|
||||
*/
|
||||
private $dailyLockEnabled;
|
||||
|
||||
/**
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Required(false)
|
||||
* @Annotation\Options({
|
||||
* "label": "Start time"
|
||||
* })
|
||||
* @var array
|
||||
*/
|
||||
private $dailyStartTime;
|
||||
|
||||
/**
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Required(false)
|
||||
* @Annotation\Options({
|
||||
* "label": "End time"
|
||||
* })
|
||||
* @var array
|
||||
*/
|
||||
private $dailyEndTime;
|
||||
|
||||
/**
|
||||
* This is a dummy field, not a text actually. Only used to filter the input
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Required(true)
|
||||
* @Annotation\Options({
|
||||
* "label": "1st column"
|
||||
* })
|
||||
* @var KanbanColumn
|
||||
*/
|
||||
private $backlogColumn;
|
||||
|
||||
/**
|
||||
* This is a dummy field, not a text actually. Only used to filter the input
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Required(true)
|
||||
* @Annotation\Options({
|
||||
* "label": "1st column"
|
||||
* })
|
||||
* @var KanbanColumn
|
||||
*/
|
||||
private $inprogressColumn;
|
||||
|
||||
/**
|
||||
* This is a dummy field, not a text actually. Only used to filter the input
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Required(true)
|
||||
* @Annotation\Options({
|
||||
* "label": "1st column"
|
||||
* })
|
||||
* @var KanbanColumn
|
||||
*/
|
||||
private $verificationColumn;
|
||||
|
||||
/**
|
||||
* This is a dummy field, not a text actually. Only used to filter the input
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
* @Annotation\Required(true)
|
||||
* @Annotation\Options({
|
||||
* "label": "1st column"
|
||||
* })
|
||||
* @var KanbanColumn
|
||||
*/
|
||||
private $doneColumn;
|
||||
|
||||
/**
|
||||
* Also a dummy field, a
|
||||
* @Annotation\Type("Zend\Form\Element\Text")
|
||||
|
||||
5
src/App/Handler/AvatarHandler.php
Normal file → Executable file
5
src/App/Handler/AvatarHandler.php
Normal file → Executable file
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Handler;
|
||||
|
||||
use App\Service\AvatarService;
|
||||
use finfo;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
@ -36,8 +37,10 @@ class AvatarHandler implements RequestHandlerInterface
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
return new TextResponse("Avatar not found", 404);
|
||||
}
|
||||
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
||||
$contentType = $finfo->buffer($avatarImageData);
|
||||
return (new TextResponse($avatarImageData, 200, [
|
||||
'content-type' => 'image/png',
|
||||
'content-type' => $contentType,
|
||||
]))->withHeader('Expires', '0')
|
||||
->withHeader('Cache-Control', 'must-revalidate');
|
||||
}
|
||||
|
||||
4
src/App/Handler/KanbanHandler.php
Normal file → Executable file
4
src/App/Handler/KanbanHandler.php
Normal file → Executable file
@ -32,9 +32,9 @@ class KanbanHandler implements RequestHandlerInterface
|
||||
*/
|
||||
public function handle(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$filterId = $request->getAttribute('filterId');
|
||||
$teamId = (int)$request->getAttribute('teamId');
|
||||
/** @var KanbanBoard $kanbanResult */
|
||||
$kanbanResult = $this->dataCollector->getKanbanBoard($filterId);
|
||||
$kanbanResult = $this->dataCollector->getKanbanBoard($teamId);
|
||||
return new JsonResponse($kanbanResult);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
310
src/App/Service/JiraCollectorService.php
Normal file → Executable file
310
src/App/Service/JiraCollectorService.php
Normal file → Executable file
@ -9,6 +9,9 @@ use App\Entity\JiraIssueType;
|
||||
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;
|
||||
@ -18,8 +21,26 @@ use Zend\Json\Json;
|
||||
|
||||
class JiraCollectorService
|
||||
{
|
||||
|
||||
const CACHE_KEY_KANBANBOARD = 'kanbanBoard';
|
||||
const CACHE_KEY_WATCHED = 'watchedIssues';
|
||||
const BACKLOG_FIELD_DELIMITER = ';';
|
||||
|
||||
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;
|
||||
@ -33,39 +54,55 @@ class JiraCollectorService
|
||||
/** @var RouterInterface */
|
||||
private $router;
|
||||
|
||||
/** @var TeamService */
|
||||
private $teamService;
|
||||
|
||||
/** @var array */
|
||||
private $cachedEpics = [];
|
||||
|
||||
/**
|
||||
* JiraClientService constructor.
|
||||
* @param StorageInterface $cache
|
||||
* @param Client $client
|
||||
* @param Config $config
|
||||
* @param RouterInterface $router
|
||||
* @param TeamService $teamService
|
||||
*/
|
||||
public function __construct(StorageInterface $cache, Client $client, Config $config, RouterInterface $router)
|
||||
public function __construct(StorageInterface $cache,
|
||||
Client $client,
|
||||
Config $config,
|
||||
RouterInterface $router,
|
||||
TeamService $teamService)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
$this->router = $router;
|
||||
$this->httpClient = $client;
|
||||
$this->config = $config;
|
||||
$this->teamService = $teamService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $filterId
|
||||
* @param int $teamId
|
||||
* @param bool $forceReload
|
||||
* @return KanbanBoard
|
||||
*/
|
||||
public function getKanbanBoard(int $filterId = null, bool $forceReload = false): KanbanBoard
|
||||
public function getKanbanBoard(int $teamId, bool $forceReload = false): KanbanBoard
|
||||
{
|
||||
$kanbanBoard = $this->cache->getItem('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 */
|
||||
$kanbanBoardUriParams = $this->config->get('url.jiraKanbanBoard');
|
||||
$kanbanBoardUri = $this->config->get('url.jiraKanbanBoard');
|
||||
$kanbanBoardFilter = $this->config->get('jira.filterFields')->toArray();
|
||||
|
||||
$kanbanBoardUri = sprintf(
|
||||
$kanbanBoardUriParams['baseUrl'],
|
||||
isset($filterId) ? $filterId : $kanbanBoardUriParams['filterId'],
|
||||
implode(",", $kanbanBoardUriParams['fields']->toArray())
|
||||
$kanbanBoardUri,
|
||||
$team->getFilterId(),
|
||||
implode(",", $kanbanBoardFilter)
|
||||
);
|
||||
|
||||
$response = $this->httpClient
|
||||
@ -74,37 +111,196 @@ class JiraCollectorService
|
||||
->send();
|
||||
|
||||
if (!$response->isSuccess()) {
|
||||
throw new \UnexpectedValueException("Bad JIRA result", $response->getStatusCode());
|
||||
throw new \UnexpectedValueException(sprintf(
|
||||
"Bad JIRA result for URL:\n%s",
|
||||
$kanbanBoardUri
|
||||
), $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);
|
||||
$kanbanBoard = $this->hydrateKanbanBoard($team, $parsedJsonData);
|
||||
$this->cache->setItem(sprintf("%s-%s", self::CACHE_KEY_KANBANBOARD, $teamName), serialize($kanbanBoard));
|
||||
return $kanbanBoard;
|
||||
}
|
||||
|
||||
return $kanbanBoard;
|
||||
return unserialize($kanbanBoard);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $teamId
|
||||
* @param bool $forceReload
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getTeamWatchedIssues(int $teamId, bool $forceReload = false)
|
||||
{
|
||||
$team = $this->teamService->getTeam($teamId);
|
||||
$teamName = $team->getName();
|
||||
$watchedIssues = $this->cache->getItem(sprintf("%s-%s", self::CACHE_KEY_WATCHED, $teamName));
|
||||
if ($forceReload || null === $watchedIssues) {
|
||||
$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());
|
||||
}
|
||||
|
||||
$watchedIssues = $this->hydrateWatchedIssues(Decoder::decode($response->getBody(), Json::TYPE_ARRAY), $members);
|
||||
$this->cache->setItem(sprintf("%s-%s", self::CACHE_KEY_WATCHED, $teamName), serialize($watchedIssues));
|
||||
return $watchedIssues;
|
||||
}
|
||||
return unserialize($watchedIssues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 null !== $issue->getComment()
|
||||
? !in_array($issue->getComment()->getSignum(), $members)
|
||||
: false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @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($parsedJsonData): KanbanBoard
|
||||
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'])
|
||||
->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'])
|
||||
@ -143,48 +339,13 @@ class JiraCollectorService
|
||||
}
|
||||
}
|
||||
|
||||
// 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']);
|
||||
// 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
|
||||
@ -222,20 +383,17 @@ class JiraCollectorService
|
||||
}
|
||||
|
||||
$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;
|
||||
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);
|
||||
}
|
||||
|
||||
3
src/App/Service/JiraCollectorServiceFactory.php
Normal file → Executable file
3
src/App/Service/JiraCollectorServiceFactory.php
Normal file → Executable file
@ -18,6 +18,7 @@ class JiraCollectorServiceFactory
|
||||
$httpClient = $container->get(Client::class);
|
||||
$config = new Config($configArray['app.config']);
|
||||
$router = $container->get(RouterInterface::class);
|
||||
return new JiraCollectorService($cache,$httpClient, $config, $router);
|
||||
$teamService = $container->get(TeamService::class);
|
||||
return new JiraCollectorService($cache,$httpClient, $config, $router, $teamService);
|
||||
}
|
||||
}
|
||||
|
||||
0
src/App/Service/SlideManager.php
Normal file → Executable file
0
src/App/Service/SlideManager.php
Normal file → Executable file
3
src/App/Service/TeamService.php
Normal file → Executable file
3
src/App/Service/TeamService.php
Normal file → Executable file
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\Team;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityNotFoundException;
|
||||
use Zend\Form\Form;
|
||||
@ -26,7 +27,7 @@ class TeamService
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @return Team[]|ArrayCollection
|
||||
*/
|
||||
public function listTeams(): array
|
||||
{
|
||||
|
||||
23
src/DoctrineExpressiveModule/Hydrator/DoctrineObject.php
Normal file → Executable file
23
src/DoctrineExpressiveModule/Hydrator/DoctrineObject.php
Normal file → Executable file
@ -20,6 +20,7 @@
|
||||
namespace DoctrineExpressiveModule\Hydrator;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\Common\Persistence\ObjectManager;
|
||||
use Doctrine\Common\Util\Inflector;
|
||||
@ -484,9 +485,10 @@ class DoctrineObject extends AbstractHydrator
|
||||
/**
|
||||
* Handle various type conversions that should be supported natively by Doctrine (like DateTime)
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param mixed $value
|
||||
* @param string $typeOfField
|
||||
* @return DateTime
|
||||
* @return DateTime|DateTimeImmutable
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function handleTypeConversions($value, $typeOfField)
|
||||
{
|
||||
@ -507,6 +509,23 @@ class DoctrineObject extends AbstractHydrator
|
||||
$value = new DateTime($value);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'datetimetz_immutable':
|
||||
case 'datetime_immutable':
|
||||
case 'time_immutable':
|
||||
case 'date_immutable':
|
||||
if ('' === $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_int($value)) {
|
||||
$dateTime = new DateTimeImmutable();
|
||||
$dateTime->setTimestamp($value);
|
||||
$value = $dateTime;
|
||||
} elseif (is_string($value)) {
|
||||
$value = new DateTimeImmutable($value);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user