first commit

This commit is contained in:
2025-07-06 16:29:23 +02:00
commit 77160b1905
122 changed files with 18906 additions and 0 deletions

View File

@ -0,0 +1,298 @@
<?php
namespace App\Service;
use App\Entity\Issue;
use App\Entity\Project;
use App\Repository\IssueRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class RedmineService
{
private string $baseUrl;
private HttpClientInterface $client;
private IssueRepository $issueRepository;
private EntityManagerInterface $em;
public function __construct(HttpClientInterface $client, ParameterBagInterface $params, IssueRepository $issueRepository, EntityManagerInterface $em)
{
$this->client = $client;
$this->baseUrl = rtrim($params->get('redmineUrl'), '/');
$this->issueRepository = $issueRepository;
$this->em = $em;
}
/**
* Récupère les projets accessibles via l'API key.
*/
public function getProjects(string $apiKey): array
{
try {
$response = $this->client->request('GET', $this->baseUrl.'/projects.json', [
'headers' => [
'X-Redmine-API-Key' => $apiKey,
],
]);
if (200 !== $response->getStatusCode()) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$response->getStatusCode());
}
$data = $response->toArray();
return $data['projects'] ?? [];
} catch (TransportExceptionInterface $e) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$e->getMessage());
}
}
public function getProject(int|string $id, string $apiKey): ?array
{
try {
$response = $this->client->request('GET', $this->baseUrl.'/projects/'.$id.'.json?include=trackers,issue_categories,issue_custom_fields', [
'headers' => [
'X-Redmine-API-Key' => $apiKey,
'Accept' => 'application/json',
],
]);
$status = $response->getStatusCode();
if (200 !== $response->getStatusCode()) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$response->getStatusCode());
}
$data = $response->toArray();
$data['project']['issue_statuses'] = $this->getIssueStatuses($apiKey);
$data['project']['issue_priorities'] = $this->getIssuePriorities($apiKey);
$data['project']['versions'] = $this->getProjectVersions($id, $apiKey);
if (empty($data['project']['versions']) && array_key_exists('parent', $data['project'])) {
$data['project']['versions'] = $this->getProjectSprints($data['project']['parent']['id'], $apiKey);
}
$data['project']['sprints'] = $this->getProjectSprints($id, $apiKey);
if (empty($data['project']['sprints']) && array_key_exists('parent', $data['project'])) {
$data['project']['sprints'] = $this->getProjectSprints($data['project']['parent']['id'], $apiKey);
}
return $data['project'] ?? null;
} catch (TransportExceptionInterface $e) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$e->getMessage());
}
}
public function getIssueStatuses(string $apiKey): array
{
try {
$response = $this->client->request('GET', $this->baseUrl.'/issue_statuses.json', [
'headers' => [
'X-Redmine-API-Key' => $apiKey,
'Accept' => 'application/json',
],
]);
if (200 !== $response->getStatusCode()) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$response->getStatusCode());
}
return $response->toArray()['issue_statuses'] ?? [];
} catch (TransportExceptionInterface $e) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$e->getMessage());
}
}
public function getIssuePriorities(string $apiKey): array
{
try {
$response = $this->client->request('GET', $this->baseUrl.'/enumerations/issue_priorities.json', [
'headers' => [
'X-Redmine-API-Key' => $apiKey,
'Accept' => 'application/json',
],
]);
if (200 !== $response->getStatusCode()) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$response->getStatusCode());
}
$data = $response->toArray();
return $data['issue_priorities'] ?? [];
} catch (TransportExceptionInterface $e) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$e->getMessage());
}
}
public function getProjectVersions(int $id, string $apiKey): array
{
try {
$response = $this->client->request('GET', $this->baseUrl.'/projects/'.$id.'/versions.json', [
'headers' => [
'X-Redmine-API-Key' => $apiKey,
'Accept' => 'application/json',
],
]);
if (200 !== $response->getStatusCode()) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$response->getStatusCode());
}
$data = $response->toArray();
return $data['versions'];
} catch (TransportExceptionInterface $e) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$e->getMessage());
}
}
public function getProjectSprints(int $projectId, string $apiKey): array
{
try {
$url = "{$this->baseUrl}/projects/{$projectId}/agile_sprints.json";
$response = $this->client->request('GET', $url, [
'headers' => [
'X-Redmine-API-Key' => $apiKey,
'Accept' => 'application/json',
],
]);
if (200 !== $response->getStatusCode()) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$response->getStatusCode());
}
$data = $response->toArray();
return $data['sprints'] ?? [];
} catch (TransportExceptionInterface $e) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$e->getMessage());
}
}
public function majProjectIssues(Project $project, string $apiKey, bool $force = false)
{
$rissues = $this->getProjectIssues($project->getId(), $apiKey, $force ? null : $project->getUpdateAt());
foreach ($rissues as $rissue) {
$issue = $this->issueRepository->find($rissue['id']);
if (!$issue) {
$issue = new Issue();
$issue->setId($rissue['id']);
}
$issue->setRedmine($rissue);
$issue->setProject($project);
// Calcul de la position du status
$issueStatusId = $rissue['status']['id'];
$statusPosition = 0;
foreach ($project->getRedmine()['issue_statuses'] as $index => $status) {
if ($status['id'] === $issueStatusId) {
$statusPosition = $index; // ou $index + 1 si tu veux position humaine
break;
}
}
$issue->setRowstatus($statusPosition);
// Calcul de la position version
if (isset($rissue['fixed_version'])) {
$issue->setRowversion($rissue['fixed_version']['name']);
} else {
$issue->setRowversion('');
}
// Calcul de la position sprint
$issueSprintId = $rissue['sprint']['agile_sprint_id'];
$sprintPosition = '';
foreach ($project->getRedmine()['sprints'] as $sprint) {
if ($sprint['id'] === $issueSprintId) {
$sprintPosition = $sprint['id']; // ou $index + 1 si tu veux position humaine
break;
}
}
$issue->setRowsprint($sprintPosition);
// Calcul position issue
if (isset($rissue['sprint'])) {
$issue->setRowissue($rissue['sprint']['position'] ?? 1000000);
} else {
$issue->setRowissue(1000000);
}
$this->em->persist($issue);
$project->setUpdateAt(new \DateTime());
$this->em->flush();
}
}
public function getProjectIssues(int $projectId, string $apiKey, ?\DateTimeInterface $updatedSince = null): array
{
$limit = 100;
$offset = 0;
$allIssues = [];
do {
$queryParams = [
'project_id' => $projectId,
'limit' => $limit,
'offset' => $offset,
'status_id' => '*', // Inclure toutes les issues
];
if (null !== $updatedSince) {
$queryParams['updated_on'] = $updatedSince->format('Y-m-d\TH:i:s\Z');
}
$url = $this->baseUrl.'/issues.json?'.http_build_query($queryParams);
$response = $this->client->request('GET', $url, [
'headers' => [
'X-Redmine-API-Key' => $apiKey,
],
]);
if (200 !== $response->getStatusCode()) {
throw new \RuntimeException('Erreur API Redmine : '.$response->getStatusCode());
}
$data = $response->toArray();
$issues = $data['issues'] ?? [];
foreach ($issues as $key => $issue) {
$issues[$key]['sprint'] = $this->getIssueAgile($issue['id'], $apiKey);
}
$allIssues = array_merge($allIssues, $issues);
$offset += $limit;
$totalCount = $data['total_count'] ?? 0;
} while ($offset < $totalCount);
return $allIssues;
}
public function getIssueAgile(int $issueId, string $apiKey): array
{
try {
$url = "{$this->baseUrl}/issues/{$issueId}/agile_data.json";
$response = $this->client->request('GET', $url, [
'headers' => [
'X-Redmine-API-Key' => $apiKey,
'Accept' => 'application/json',
],
]);
dump($response);
if (200 !== $response->getStatusCode()) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$response->getStatusCode());
}
$data = $response->toArray();
return $data['agile_data'] ?? [];
} catch (TransportExceptionInterface $e) {
throw new \RuntimeException('Erreur de communication avec Redmine : '.$e->getMessage());
}
}
}