feat(integration) : migrate Gitea/BookStack/Zimbra/Share into module (back)

LST-68 (2.6) backend. Behaviour-preserving move of the external integrations
into src/Module/Integration/. All 26 routes and securities unchanged.

- 5 entities (4 *Configuration singletons + TaskBookStackLink) + 5 repositories
  (Domain interfaces + Doctrine impls, bound). TaskBookStackLink.task now
  references TaskInterface (contract).
- Domain (FileSource interface, SharePathResolver, share DTOs + exceptions);
  Infrastructure (GiteaApiService, BookStackApiService, SmbFileSource, 15
  ApiResources, 21 State, 4 Share controllers).
- Cross-module couplings via abstractions: CalDavService (PM) injects
  ZimbraConfigurationRepositoryInterface; PM TaskDocument consumers repointed
  to the module's FileSource/SharePathResolver; Gitea/BookStack State load
  tasks via TaskRepositoryInterface (concrete Project read for integration
  fields — documented). ZimbraTestConnection keeps CalDavService (no build
  cycle). TokenEncryptor stays shared.
- IntegrationModule registered; doctrine mapping added.
- #[Auditable] + Timestampable on the 4 Configuration entities (additive
  migration on the 4 *_configuration tables).

163 tests green, container compiles (no cycle), no route regression, cs-fixer clean.
This commit is contained in:
Matthieu
2026-06-20 20:16:20 +02:00
parent bb7d7e7953
commit 90682e809c
79 changed files with 589 additions and 284 deletions
+1 -1
View File
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace App\DataFixtures;
use App\Entity\ZimbraConfiguration;
use App\Enum\ContractType;
use App\Module\Absence\Domain\Entity\AbsenceBalance;
use App\Module\Absence\Domain\Entity\AbsencePolicy;
@@ -16,6 +15,7 @@ use App\Module\Core\Domain\Entity\User;
use App\Module\Directory\Domain\Entity\Client;
use App\Module\Directory\Domain\Entity\Prospect;
use App\Module\Directory\Domain\Enum\ProspectStatus;
use App\Module\Integration\Domain\Entity\ZimbraConfiguration;
use App\Module\Mail\Domain\Entity\MailConfiguration;
use App\Module\ProjectManagement\Domain\Entity\Project;
use App\Module\ProjectManagement\Domain\Entity\Task;
@@ -2,15 +2,22 @@
declare(strict_types=1);
namespace App\Entity;
namespace App\Module\Integration\Domain\Entity;
use App\Repository\BookStackConfigurationRepository;
use App\Module\Integration\Infrastructure\Doctrine\DoctrineBookStackConfigurationRepository;
use App\Shared\Domain\Attribute\Auditable;
use App\Shared\Domain\Contract\BlamableInterface;
use App\Shared\Domain\Contract\TimestampableInterface;
use App\Shared\Domain\Trait\TimestampableBlamableTrait;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: BookStackConfigurationRepository::class)]
class BookStackConfiguration
#[Auditable]
#[ORM\Entity(repositoryClass: DoctrineBookStackConfigurationRepository::class)]
class BookStackConfiguration implements TimestampableInterface, BlamableInterface
{
use TimestampableBlamableTrait;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
@@ -2,15 +2,22 @@
declare(strict_types=1);
namespace App\Entity;
namespace App\Module\Integration\Domain\Entity;
use App\Repository\GiteaConfigurationRepository;
use App\Module\Integration\Infrastructure\Doctrine\DoctrineGiteaConfigurationRepository;
use App\Shared\Domain\Attribute\Auditable;
use App\Shared\Domain\Contract\BlamableInterface;
use App\Shared\Domain\Contract\TimestampableInterface;
use App\Shared\Domain\Trait\TimestampableBlamableTrait;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: GiteaConfigurationRepository::class)]
class GiteaConfiguration
#[Auditable]
#[ORM\Entity(repositoryClass: DoctrineGiteaConfigurationRepository::class)]
class GiteaConfiguration implements TimestampableInterface, BlamableInterface
{
use TimestampableBlamableTrait;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
@@ -2,14 +2,21 @@
declare(strict_types=1);
namespace App\Entity;
namespace App\Module\Integration\Domain\Entity;
use App\Repository\ShareConfigurationRepository;
use App\Module\Integration\Infrastructure\Doctrine\DoctrineShareConfigurationRepository;
use App\Shared\Domain\Attribute\Auditable;
use App\Shared\Domain\Contract\BlamableInterface;
use App\Shared\Domain\Contract\TimestampableInterface;
use App\Shared\Domain\Trait\TimestampableBlamableTrait;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ShareConfigurationRepository::class)]
class ShareConfiguration
#[Auditable]
#[ORM\Entity(repositoryClass: DoctrineShareConfigurationRepository::class)]
class ShareConfiguration implements TimestampableInterface, BlamableInterface
{
use TimestampableBlamableTrait;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
@@ -2,15 +2,15 @@
declare(strict_types=1);
namespace App\Entity;
namespace App\Module\Integration\Domain\Entity;
use App\Module\ProjectManagement\Domain\Entity\Task;
use App\Repository\TaskBookStackLinkRepository;
use App\Module\Integration\Infrastructure\Doctrine\DoctrineTaskBookStackLinkRepository;
use App\Shared\Domain\Contract\TaskInterface;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: TaskBookStackLinkRepository::class)]
#[ORM\Entity(repositoryClass: DoctrineTaskBookStackLinkRepository::class)]
#[ORM\UniqueConstraint(name: 'UNIQ_task_bookstack_link', columns: ['task_id', 'bookstack_id', 'bookstack_type'])]
class TaskBookStackLink
{
@@ -19,9 +19,9 @@ class TaskBookStackLink
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne(targetEntity: Task::class)]
#[ORM\ManyToOne(targetEntity: TaskInterface::class)]
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
private Task $task;
private TaskInterface $task;
#[ORM\Column]
private int $bookstackId;
@@ -49,12 +49,12 @@ class TaskBookStackLink
return $this->id;
}
public function getTask(): Task
public function getTask(): TaskInterface
{
return $this->task;
}
public function setTask(Task $task): static
public function setTask(TaskInterface $task): static
{
$this->task = $task;
@@ -2,15 +2,22 @@
declare(strict_types=1);
namespace App\Entity;
namespace App\Module\Integration\Domain\Entity;
use App\Repository\ZimbraConfigurationRepository;
use App\Module\Integration\Infrastructure\Doctrine\DoctrineZimbraConfigurationRepository;
use App\Shared\Domain\Attribute\Auditable;
use App\Shared\Domain\Contract\BlamableInterface;
use App\Shared\Domain\Contract\TimestampableInterface;
use App\Shared\Domain\Trait\TimestampableBlamableTrait;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: ZimbraConfigurationRepository::class)]
class ZimbraConfiguration
#[Auditable]
#[ORM\Entity(repositoryClass: DoctrineZimbraConfigurationRepository::class)]
class ZimbraConfiguration implements TimestampableInterface, BlamableInterface
{
use TimestampableBlamableTrait;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Exception;
namespace App\Module\Integration\Domain\Exception;
use RuntimeException;
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Exception;
namespace App\Module\Integration\Domain\Exception;
use RuntimeException;
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Service\Share\Exception;
namespace App\Module\Integration\Domain\Exception;
use RuntimeException;
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Service\Share\Exception;
namespace App\Module\Integration\Domain\Exception;
use RuntimeException;
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Service\Share\Exception;
namespace App\Module\Integration\Domain\Exception;
use RuntimeException;
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Module\Integration\Domain\Repository;
use App\Module\Integration\Domain\Entity\BookStackConfiguration;
interface BookStackConfigurationRepositoryInterface
{
public function findSingleton(): ?BookStackConfiguration;
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Module\Integration\Domain\Repository;
use App\Module\Integration\Domain\Entity\GiteaConfiguration;
interface GiteaConfigurationRepositoryInterface
{
public function findSingleton(): ?GiteaConfiguration;
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Module\Integration\Domain\Repository;
use App\Module\Integration\Domain\Entity\ShareConfiguration;
interface ShareConfigurationRepositoryInterface
{
public function findSingleton(): ?ShareConfiguration;
}
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace App\Module\Integration\Domain\Repository;
use App\Module\Integration\Domain\Entity\TaskBookStackLink;
interface TaskBookStackLinkRepositoryInterface
{
public function findById(int $id): ?TaskBookStackLink;
/**
* @return TaskBookStackLink[]
*/
public function findByTaskId(int $taskId): array;
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Module\Integration\Domain\Repository;
use App\Module\Integration\Domain\Entity\ZimbraConfiguration;
interface ZimbraConfigurationRepositoryInterface
{
public function findSingleton(): ?ZimbraConfiguration;
}
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Service\Share;
namespace App\Module\Integration\Domain\Service;
final readonly class FileEntry
{
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Service\Share;
namespace App\Module\Integration\Domain\Service;
interface FileSource
{
@@ -2,9 +2,9 @@
declare(strict_types=1);
namespace App\Service\Share;
namespace App\Module\Integration\Domain\Service;
use App\Service\Share\Exception\InvalidPathException;
use App\Module\Integration\Domain\Exception\InvalidPathException;
final class SharePathResolver
{
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Service\Share;
namespace App\Module\Integration\Domain\Service;
final readonly class ShareTestResult
{
@@ -2,17 +2,17 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Post;
use App\Entity\TaskBookStackLink;
use App\Module\Integration\Domain\Entity\TaskBookStackLink;
use App\Module\Integration\Infrastructure\ApiPlatform\State\BookStackLinkProcessor;
use App\Module\Integration\Infrastructure\ApiPlatform\State\BookStackLinkProvider;
use App\Module\ProjectManagement\Domain\Entity\Task;
use App\State\BookStackLinkProcessor;
use App\State\BookStackLinkProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use App\Module\Integration\Infrastructure\ApiPlatform\State\BookStackSearchResultProvider;
use App\Module\ProjectManagement\Domain\Entity\Task;
use App\State\BookStackSearchResultProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Put;
use App\State\BookStackSettingsProcessor;
use App\State\BookStackSettingsProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\BookStackSettingsProcessor;
use App\Module\Integration\Infrastructure\ApiPlatform\State\BookStackSettingsProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,11 +2,11 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
use App\State\BookStackShelfProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\BookStackShelfProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,11 +2,11 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
use App\State\BookStackTestConnectionProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\BookStackTestConnectionProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use App\State\GiteaBranchProcessor;
use App\State\GiteaBranchProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\GiteaBranchProcessor;
use App\Module\Integration\Infrastructure\ApiPlatform\State\GiteaBranchProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,11 +2,11 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use App\State\GiteaBranchNameProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\GiteaBranchNameProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,11 +2,11 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
use App\State\GiteaPullRequestProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\GiteaPullRequestProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,11 +2,11 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
use App\State\GiteaRepositoryProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\GiteaRepositoryProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Put;
use App\State\GiteaSettingsProcessor;
use App\State\GiteaSettingsProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\GiteaSettingsProcessor;
use App\Module\Integration\Infrastructure\ApiPlatform\State\GiteaSettingsProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,11 +2,11 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
use App\State\GiteaTestConnectionProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\GiteaTestConnectionProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Put;
use App\State\ShareSettingsProcessor;
use App\State\ShareSettingsProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\ShareSettingsProcessor;
use App\Module\Integration\Infrastructure\ApiPlatform\State\ShareSettingsProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,11 +2,11 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
use App\State\ShareTestConnectionProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\ShareTestConnectionProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Put;
use App\State\ZimbraSettingsProcessor;
use App\State\ZimbraSettingsProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\ZimbraSettingsProcessor;
use App\Module\Integration\Infrastructure\ApiPlatform\State\ZimbraSettingsProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,11 +2,11 @@
declare(strict_types=1);
namespace App\ApiResource;
namespace App\Module\Integration\Infrastructure\ApiPlatform\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
use App\State\ZimbraTestConnectionProvider;
use App\Module\Integration\Infrastructure\ApiPlatform\State\ZimbraTestConnectionProvider;
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
@@ -2,15 +2,15 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\ApiResource\BookStackLink;
use App\Entity\TaskBookStackLink;
use App\Module\ProjectManagement\Domain\Entity\Task;
use App\Repository\TaskBookStackLinkRepository;
use App\Module\Integration\Domain\Entity\TaskBookStackLink;
use App\Module\Integration\Domain\Repository\TaskBookStackLinkRepositoryInterface;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\BookStackLink;
use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -18,7 +18,8 @@ final readonly class BookStackLinkProcessor implements ProcessorInterface
{
public function __construct(
private EntityManagerInterface $em,
private TaskBookStackLinkRepository $linkRepository,
private TaskBookStackLinkRepositoryInterface $linkRepository,
private TaskRepositoryInterface $taskRepository,
) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): ?BookStackLink
@@ -35,7 +36,7 @@ final readonly class BookStackLinkProcessor implements ProcessorInterface
assert($data instanceof BookStackLink);
$taskId = $uriVariables['taskId'] ?? 0;
$task = $this->em->getRepository(Task::class)->find($taskId);
$task = $this->taskRepository->findById((int) $taskId);
if (null === $task) {
throw new NotFoundHttpException('Task not found.');
@@ -65,7 +66,7 @@ final readonly class BookStackLinkProcessor implements ProcessorInterface
private function handleDelete(array $uriVariables): null
{
$linkId = $uriVariables['id'] ?? 0;
$link = $this->linkRepository->find($linkId);
$link = $this->linkRepository->findById((int) $linkId);
if (null === $link) {
throw new NotFoundHttpException('Link not found.');
@@ -2,21 +2,21 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Post;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\BookStackLink;
use App\Entity\TaskBookStackLink;
use App\Repository\TaskBookStackLinkRepository;
use App\Module\Integration\Domain\Entity\TaskBookStackLink;
use App\Module\Integration\Domain\Repository\TaskBookStackLinkRepositoryInterface;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\BookStackLink;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
final readonly class BookStackLinkProvider implements ProviderInterface
{
public function __construct(
private TaskBookStackLinkRepository $linkRepository,
private TaskBookStackLinkRepositoryInterface $linkRepository,
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): array|BookStackLink
@@ -2,15 +2,14 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\BookStackSearchResult;
use App\Exception\BookStackApiException;
use App\Module\ProjectManagement\Domain\Entity\Task;
use App\Service\BookStackApiService;
use Doctrine\ORM\EntityManagerInterface;
use App\Module\Integration\Domain\Exception\BookStackApiException;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\BookStackSearchResult;
use App\Module\Integration\Infrastructure\Service\BookStackApiService;
use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
@@ -18,14 +17,14 @@ final readonly class BookStackSearchResultProvider implements ProviderInterface
{
public function __construct(
private BookStackApiService $bookStackApiService,
private EntityManagerInterface $em,
private TaskRepositoryInterface $taskRepository,
private RequestStack $requestStack,
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): array
{
$taskId = $uriVariables['taskId'] ?? 0;
$task = $this->em->getRepository(Task::class)->find($taskId);
$task = $this->taskRepository->findById((int) $taskId);
if (null === $task || null === $task->getProject()) {
return [];
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\ApiResource\BookStackSettings;
use App\Entity\BookStackConfiguration;
use App\Repository\BookStackConfigurationRepository;
use App\Module\Integration\Domain\Entity\BookStackConfiguration;
use App\Module\Integration\Domain\Repository\BookStackConfigurationRepositoryInterface;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\BookStackSettings;
use App\Service\TokenEncryptor;
use Doctrine\ORM\EntityManagerInterface;
@@ -16,7 +16,7 @@ final readonly class BookStackSettingsProcessor implements ProcessorInterface
{
public function __construct(
private EntityManagerInterface $em,
private BookStackConfigurationRepository $configRepository,
private BookStackConfigurationRepositoryInterface $configRepository,
private TokenEncryptor $tokenEncryptor,
) {}
@@ -2,17 +2,17 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\BookStackSettings;
use App\Repository\BookStackConfigurationRepository;
use App\Module\Integration\Domain\Repository\BookStackConfigurationRepositoryInterface;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\BookStackSettings;
final readonly class BookStackSettingsProvider implements ProviderInterface
{
public function __construct(
private BookStackConfigurationRepository $configRepository,
private BookStackConfigurationRepositoryInterface $configRepository,
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): BookStackSettings
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\BookStackShelf;
use App\Exception\BookStackApiException;
use App\Service\BookStackApiService;
use App\Module\Integration\Domain\Exception\BookStackApiException;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\BookStackShelf;
use App\Module\Integration\Infrastructure\Service\BookStackApiService;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
final readonly class BookStackShelfProvider implements ProviderInterface
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\BookStackTestConnection;
use App\Service\BookStackApiService;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\BookStackTestConnection;
use App\Module\Integration\Infrastructure\Service\BookStackApiService;
final readonly class BookStackTestConnectionProvider implements ProviderInterface, ProcessorInterface
{
@@ -2,14 +2,13 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\GiteaBranchName;
use App\Module\ProjectManagement\Domain\Entity\Task;
use App\Service\GiteaApiService;
use Doctrine\ORM\EntityManagerInterface;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\GiteaBranchName;
use App\Module\Integration\Infrastructure\Service\GiteaApiService;
use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -20,12 +19,12 @@ final readonly class GiteaBranchNameProvider implements ProviderInterface
public function __construct(
private GiteaApiService $giteaApiService,
private EntityManagerInterface $em,
private TaskRepositoryInterface $taskRepository,
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): GiteaBranchName
{
$task = $this->em->getRepository(Task::class)->find($uriVariables['taskId'] ?? 0);
$task = $this->taskRepository->findById((int) ($uriVariables['taskId'] ?? 0));
if (null === $task) {
throw new NotFoundHttpException('Task not found.');
}
@@ -2,15 +2,14 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\ApiResource\GiteaBranch;
use App\Exception\GiteaApiException;
use App\Module\ProjectManagement\Domain\Entity\Task;
use App\Service\GiteaApiService;
use Doctrine\ORM\EntityManagerInterface;
use App\Module\Integration\Domain\Exception\GiteaApiException;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\GiteaBranch;
use App\Module\Integration\Infrastructure\Service\GiteaApiService;
use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -20,14 +19,14 @@ final readonly class GiteaBranchProcessor implements ProcessorInterface
public function __construct(
private GiteaApiService $giteaApiService,
private EntityManagerInterface $em,
private TaskRepositoryInterface $taskRepository,
) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): GiteaBranch
{
assert($data instanceof GiteaBranch);
$task = $this->em->getRepository(Task::class)->find($uriVariables['taskId'] ?? 0);
$task = $this->taskRepository->findById((int) ($uriVariables['taskId'] ?? 0));
if (null === $task || null === $task->getProject()) {
throw new NotFoundHttpException('Task not found.');
}
@@ -2,23 +2,22 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Post;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\GiteaBranch;
use App\Exception\GiteaApiException;
use App\Module\ProjectManagement\Domain\Entity\Task;
use App\Service\GiteaApiService;
use Doctrine\ORM\EntityManagerInterface;
use App\Module\Integration\Domain\Exception\GiteaApiException;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\GiteaBranch;
use App\Module\Integration\Infrastructure\Service\GiteaApiService;
use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
final readonly class GiteaBranchProvider implements ProviderInterface
{
public function __construct(
private GiteaApiService $giteaApiService,
private EntityManagerInterface $em,
private TaskRepositoryInterface $taskRepository,
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): array|GiteaBranch
@@ -27,7 +26,7 @@ final readonly class GiteaBranchProvider implements ProviderInterface
return new GiteaBranch();
}
$task = $this->em->getRepository(Task::class)->find($uriVariables['taskId'] ?? 0);
$task = $this->taskRepository->findById((int) ($uriVariables['taskId'] ?? 0));
if (null === $task || null === $task->getProject()) {
return [];
}
@@ -2,27 +2,26 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\GiteaPullRequest;
use App\Exception\GiteaApiException;
use App\Module\ProjectManagement\Domain\Entity\Task;
use App\Service\GiteaApiService;
use Doctrine\ORM\EntityManagerInterface;
use App\Module\Integration\Domain\Exception\GiteaApiException;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\GiteaPullRequest;
use App\Module\Integration\Infrastructure\Service\GiteaApiService;
use App\Module\ProjectManagement\Domain\Repository\TaskRepositoryInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
final readonly class GiteaPullRequestProvider implements ProviderInterface
{
public function __construct(
private GiteaApiService $giteaApiService,
private EntityManagerInterface $em,
private TaskRepositoryInterface $taskRepository,
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): array
{
$task = $this->em->getRepository(Task::class)->find($uriVariables['taskId'] ?? 0);
$task = $this->taskRepository->findById((int) ($uriVariables['taskId'] ?? 0));
if (null === $task || null === $task->getProject()) {
return [];
}
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\GiteaRepository;
use App\Exception\GiteaApiException;
use App\Service\GiteaApiService;
use App\Module\Integration\Domain\Exception\GiteaApiException;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\GiteaRepository;
use App\Module\Integration\Infrastructure\Service\GiteaApiService;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
final readonly class GiteaRepositoryProvider implements ProviderInterface
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\ApiResource\GiteaSettings;
use App\Entity\GiteaConfiguration;
use App\Repository\GiteaConfigurationRepository;
use App\Module\Integration\Domain\Entity\GiteaConfiguration;
use App\Module\Integration\Domain\Repository\GiteaConfigurationRepositoryInterface;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\GiteaSettings;
use App\Service\TokenEncryptor;
use Doctrine\ORM\EntityManagerInterface;
@@ -16,7 +16,7 @@ final readonly class GiteaSettingsProcessor implements ProcessorInterface
{
public function __construct(
private EntityManagerInterface $em,
private GiteaConfigurationRepository $configRepository,
private GiteaConfigurationRepositoryInterface $configRepository,
private TokenEncryptor $tokenEncryptor,
) {}
@@ -2,17 +2,17 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\GiteaSettings;
use App\Repository\GiteaConfigurationRepository;
use App\Module\Integration\Domain\Repository\GiteaConfigurationRepositoryInterface;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\GiteaSettings;
final readonly class GiteaSettingsProvider implements ProviderInterface
{
public function __construct(
private GiteaConfigurationRepository $configRepository,
private GiteaConfigurationRepositoryInterface $configRepository,
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): GiteaSettings
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\GiteaTestConnection;
use App\Service\GiteaApiService;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\GiteaTestConnection;
use App\Module\Integration\Infrastructure\Service\GiteaApiService;
final readonly class GiteaTestConnectionProvider implements ProviderInterface, ProcessorInterface
{
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\ApiResource\ShareSettings;
use App\Entity\ShareConfiguration;
use App\Repository\ShareConfigurationRepository;
use App\Module\Integration\Domain\Entity\ShareConfiguration;
use App\Module\Integration\Domain\Repository\ShareConfigurationRepositoryInterface;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\ShareSettings;
use App\Service\TokenEncryptor;
use Doctrine\ORM\EntityManagerInterface;
@@ -16,7 +16,7 @@ final readonly class ShareSettingsProcessor implements ProcessorInterface
{
public function __construct(
private EntityManagerInterface $em,
private ShareConfigurationRepository $configRepository,
private ShareConfigurationRepositoryInterface $configRepository,
private TokenEncryptor $tokenEncryptor,
) {}
@@ -2,17 +2,17 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\ShareSettings;
use App\Repository\ShareConfigurationRepository;
use App\Module\Integration\Domain\Repository\ShareConfigurationRepositoryInterface;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\ShareSettings;
final readonly class ShareSettingsProvider implements ProviderInterface
{
public function __construct(
private ShareConfigurationRepository $configRepository,
private ShareConfigurationRepositoryInterface $configRepository,
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ShareSettings
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\ShareTestConnection;
use App\Service\Share\FileSource;
use App\Module\Integration\Domain\Service\FileSource;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\ShareTestConnection;
final readonly class ShareTestConnectionProvider implements ProviderInterface, ProcessorInterface
{
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\ApiResource\ZimbraSettings;
use App\Entity\ZimbraConfiguration;
use App\Repository\ZimbraConfigurationRepository;
use App\Module\Integration\Domain\Entity\ZimbraConfiguration;
use App\Module\Integration\Domain\Repository\ZimbraConfigurationRepositoryInterface;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\ZimbraSettings;
use App\Service\TokenEncryptor;
use Doctrine\ORM\EntityManagerInterface;
@@ -16,7 +16,7 @@ final readonly class ZimbraSettingsProcessor implements ProcessorInterface
{
public function __construct(
private EntityManagerInterface $em,
private ZimbraConfigurationRepository $configRepository,
private ZimbraConfigurationRepositoryInterface $configRepository,
private TokenEncryptor $tokenEncryptor,
) {}
@@ -2,17 +2,17 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\ZimbraSettings;
use App\Repository\ZimbraConfigurationRepository;
use App\Module\Integration\Domain\Repository\ZimbraConfigurationRepositoryInterface;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\ZimbraSettings;
final readonly class ZimbraSettingsProvider implements ProviderInterface
{
public function __construct(
private ZimbraConfigurationRepository $configRepository,
private ZimbraConfigurationRepositoryInterface $configRepository,
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ZimbraSettings
@@ -2,12 +2,12 @@
declare(strict_types=1);
namespace App\State;
namespace App\Module\Integration\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\ZimbraTestConnection;
use App\Module\Integration\Infrastructure\ApiPlatform\Resource\ZimbraTestConnection;
use App\Module\ProjectManagement\Infrastructure\Service\CalDavService;
use Throwable;
@@ -2,14 +2,14 @@
declare(strict_types=1);
namespace App\Controller\Share;
namespace App\Module\Integration\Infrastructure\Controller;
use App\Service\Share\Exception\InvalidPathException;
use App\Service\Share\Exception\ShareConnectionException;
use App\Service\Share\Exception\ShareNotConfiguredException;
use App\Service\Share\FileEntry;
use App\Service\Share\FileSource;
use App\Service\Share\SharePathResolver;
use App\Module\Integration\Domain\Exception\InvalidPathException;
use App\Module\Integration\Domain\Exception\ShareConnectionException;
use App\Module\Integration\Domain\Exception\ShareNotConfiguredException;
use App\Module\Integration\Domain\Service\FileEntry;
use App\Module\Integration\Domain\Service\FileSource;
use App\Module\Integration\Domain\Service\SharePathResolver;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
@@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\Controller\Share;
namespace App\Module\Integration\Infrastructure\Controller;
use App\Service\Share\Exception\InvalidPathException;
use App\Service\Share\Exception\ShareConnectionException;
use App\Service\Share\Exception\ShareNotConfiguredException;
use App\Service\Share\FileSource;
use App\Service\Share\SharePathResolver;
use App\Module\Integration\Domain\Exception\InvalidPathException;
use App\Module\Integration\Domain\Exception\ShareConnectionException;
use App\Module\Integration\Domain\Exception\ShareNotConfiguredException;
use App\Module\Integration\Domain\Service\FileSource;
use App\Module\Integration\Domain\Service\SharePathResolver;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\HttpFoundation\Request;
@@ -2,12 +2,12 @@
declare(strict_types=1);
namespace App\Controller\Share;
namespace App\Module\Integration\Infrastructure\Controller;
use App\Service\Share\Exception\ShareConnectionException;
use App\Service\Share\Exception\ShareNotConfiguredException;
use App\Service\Share\FileEntry;
use App\Service\Share\FileSource;
use App\Module\Integration\Domain\Exception\ShareConnectionException;
use App\Module\Integration\Domain\Exception\ShareNotConfiguredException;
use App\Module\Integration\Domain\Service\FileEntry;
use App\Module\Integration\Domain\Service\FileSource;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
@@ -2,9 +2,9 @@
declare(strict_types=1);
namespace App\Controller\Share;
namespace App\Module\Integration\Infrastructure\Controller;
use App\Repository\ShareConfigurationRepository;
use App\Module\Integration\Domain\Repository\ShareConfigurationRepositoryInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;
@@ -13,7 +13,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
class ShareStatusController extends AbstractController
{
public function __construct(
private readonly ShareConfigurationRepository $configRepository,
private readonly ShareConfigurationRepositoryInterface $configRepository,
) {}
#[Route('/api/share/status', name: 'share_status', methods: ['GET'], priority: 1)]
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace App\Module\Integration\Infrastructure\Doctrine;
use App\Module\Integration\Domain\Entity\BookStackConfiguration;
use App\Module\Integration\Domain\Repository\BookStackConfigurationRepositoryInterface;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<BookStackConfiguration>
*/
class DoctrineBookStackConfigurationRepository extends ServiceEntityRepository implements BookStackConfigurationRepositoryInterface
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, BookStackConfiguration::class);
}
public function findSingleton(): ?BookStackConfiguration
{
return $this->findOneBy([]);
}
}
@@ -2,13 +2,17 @@
declare(strict_types=1);
namespace App\Repository;
namespace App\Module\Integration\Infrastructure\Doctrine;
use App\Entity\GiteaConfiguration;
use App\Module\Integration\Domain\Entity\GiteaConfiguration;
use App\Module\Integration\Domain\Repository\GiteaConfigurationRepositoryInterface;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
class GiteaConfigurationRepository extends ServiceEntityRepository
/**
* @extends ServiceEntityRepository<GiteaConfiguration>
*/
class DoctrineGiteaConfigurationRepository extends ServiceEntityRepository implements GiteaConfigurationRepositoryInterface
{
public function __construct(ManagerRegistry $registry)
{
@@ -2,13 +2,17 @@
declare(strict_types=1);
namespace App\Repository;
namespace App\Module\Integration\Infrastructure\Doctrine;
use App\Entity\ShareConfiguration;
use App\Module\Integration\Domain\Entity\ShareConfiguration;
use App\Module\Integration\Domain\Repository\ShareConfigurationRepositoryInterface;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
class ShareConfigurationRepository extends ServiceEntityRepository
/**
* @extends ServiceEntityRepository<ShareConfiguration>
*/
class DoctrineShareConfigurationRepository extends ServiceEntityRepository implements ShareConfigurationRepositoryInterface
{
public function __construct(ManagerRegistry $registry)
{
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace App\Module\Integration\Infrastructure\Doctrine;
use App\Module\Integration\Domain\Entity\TaskBookStackLink;
use App\Module\Integration\Domain\Repository\TaskBookStackLinkRepositoryInterface;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<TaskBookStackLink>
*/
class DoctrineTaskBookStackLinkRepository extends ServiceEntityRepository implements TaskBookStackLinkRepositoryInterface
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, TaskBookStackLink::class);
}
public function findById(int $id): ?TaskBookStackLink
{
return $this->find($id);
}
/**
* @return TaskBookStackLink[]
*/
public function findByTaskId(int $taskId): array
{
return $this->findBy(['task' => $taskId], ['createdAt' => 'DESC']);
}
}
@@ -2,13 +2,17 @@
declare(strict_types=1);
namespace App\Repository;
namespace App\Module\Integration\Infrastructure\Doctrine;
use App\Entity\ZimbraConfiguration;
use App\Module\Integration\Domain\Entity\ZimbraConfiguration;
use App\Module\Integration\Domain\Repository\ZimbraConfigurationRepositoryInterface;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
class ZimbraConfigurationRepository extends ServiceEntityRepository
/**
* @extends ServiceEntityRepository<ZimbraConfiguration>
*/
class DoctrineZimbraConfigurationRepository extends ServiceEntityRepository implements ZimbraConfigurationRepositoryInterface
{
public function __construct(ManagerRegistry $registry)
{
@@ -2,11 +2,12 @@
declare(strict_types=1);
namespace App\Service;
namespace App\Module\Integration\Infrastructure\Service;
use App\Entity\BookStackConfiguration;
use App\Exception\BookStackApiException;
use App\Repository\BookStackConfigurationRepository;
use App\Module\Integration\Domain\Entity\BookStackConfiguration;
use App\Module\Integration\Domain\Exception\BookStackApiException;
use App\Module\Integration\Domain\Repository\BookStackConfigurationRepositoryInterface;
use App\Service\TokenEncryptor;
use Symfony\Contracts\HttpClient\Exception\ExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -19,7 +20,7 @@ final class BookStackApiService
public function __construct(
private readonly HttpClientInterface $httpClient,
private readonly BookStackConfigurationRepository $configRepository,
private readonly BookStackConfigurationRepositoryInterface $configRepository,
private readonly TokenEncryptor $tokenEncryptor,
) {}
@@ -2,13 +2,14 @@
declare(strict_types=1);
namespace App\Service;
namespace App\Module\Integration\Infrastructure\Service;
use App\Entity\GiteaConfiguration;
use App\Exception\GiteaApiException;
use App\Module\Integration\Domain\Entity\GiteaConfiguration;
use App\Module\Integration\Domain\Exception\GiteaApiException;
use App\Module\Integration\Domain\Repository\GiteaConfigurationRepositoryInterface;
use App\Module\ProjectManagement\Domain\Entity\Project;
use App\Module\ProjectManagement\Domain\Entity\Task;
use App\Repository\GiteaConfigurationRepository;
use App\Service\TokenEncryptor;
use Symfony\Component\String\Slugger\AsciiSlugger;
use Symfony\Component\String\Slugger\SluggerInterface;
use Symfony\Contracts\HttpClient\Exception\ExceptionInterface;
@@ -22,7 +23,7 @@ final readonly class GiteaApiService
public function __construct(
private HttpClientInterface $httpClient,
private GiteaConfigurationRepository $configRepository,
private GiteaConfigurationRepositoryInterface $configRepository,
private TokenEncryptor $tokenEncryptor,
) {
$this->slugger = new AsciiSlugger('fr');
@@ -2,12 +2,16 @@
declare(strict_types=1);
namespace App\Service\Share;
namespace App\Module\Integration\Infrastructure\Service;
use App\Entity\ShareConfiguration;
use App\Repository\ShareConfigurationRepository;
use App\Service\Share\Exception\ShareConnectionException;
use App\Service\Share\Exception\ShareNotConfiguredException;
use App\Module\Integration\Domain\Entity\ShareConfiguration;
use App\Module\Integration\Domain\Exception\ShareConnectionException;
use App\Module\Integration\Domain\Exception\ShareNotConfiguredException;
use App\Module\Integration\Domain\Repository\ShareConfigurationRepositoryInterface;
use App\Module\Integration\Domain\Service\FileEntry;
use App\Module\Integration\Domain\Service\FileSource;
use App\Module\Integration\Domain\Service\SharePathResolver;
use App\Module\Integration\Domain\Service\ShareTestResult;
use App\Service\TokenEncryptor;
use Icewind\SMB\BasicAuth;
use Icewind\SMB\IFileInfo;
@@ -24,7 +28,7 @@ final class SmbFileSource implements FileSource
private const int SEARCH_MAX_DIRS = 2000;
public function __construct(
private readonly ShareConfigurationRepository $configRepository,
private readonly ShareConfigurationRepositoryInterface $configRepository,
private readonly TokenEncryptor $tokenEncryptor,
private readonly SharePathResolver $pathResolver,
) {}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace App\Module\Integration;
use App\Shared\Domain\Module\ModuleInterface;
final class IntegrationModule implements ModuleInterface
{
public static function id(): string
{
return 'integration';
}
public static function label(): string
{
return 'Intégrations';
}
public static function isRequired(): bool
{
return false;
}
/**
* Permissions RBAC fin du Module Integration (Gitea, BookStack, Zimbra, Share).
*
* Additif : alimente le catalogue RBAC. La sécurité des opérations API
* reste en ROLE_USER/ROLE_ADMIN (non recâblée ici).
*
* @return list<array{code: string, label: string}>
*/
public static function permissions(): array
{
return [
['code' => 'integration.settings.manage', 'label' => 'Gérer les intégrations externes'],
['code' => 'integration.share.access', 'label' => 'Accéder au partage de fichiers'],
];
}
}
@@ -6,14 +6,14 @@ namespace App\Module\ProjectManagement\Infrastructure\ApiPlatform\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Module\Integration\Domain\Exception\InvalidPathException;
use App\Module\Integration\Domain\Exception\ShareConnectionException;
use App\Module\Integration\Domain\Exception\ShareNotConfiguredException;
use App\Module\Integration\Domain\Service\FileEntry;
use App\Module\Integration\Domain\Service\FileSource;
use App\Module\Integration\Domain\Service\SharePathResolver;
use App\Module\ProjectManagement\Domain\Entity\Task;
use App\Module\ProjectManagement\Domain\Entity\TaskDocument;
use App\Service\Share\Exception\InvalidPathException;
use App\Service\Share\Exception\ShareConnectionException;
use App\Service\Share\Exception\ShareNotConfiguredException;
use App\Service\Share\FileEntry;
use App\Service\Share\FileSource;
use App\Service\Share\SharePathResolver;
use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\SecurityBundle\Security;
@@ -4,10 +4,10 @@ declare(strict_types=1);
namespace App\Module\ProjectManagement\Infrastructure\Controller;
use App\Module\Integration\Domain\Exception\ShareConnectionException;
use App\Module\Integration\Domain\Exception\ShareNotConfiguredException;
use App\Module\Integration\Domain\Service\FileSource;
use App\Module\ProjectManagement\Domain\Entity\TaskDocument;
use App\Service\Share\Exception\ShareConnectionException;
use App\Service\Share\Exception\ShareNotConfiguredException;
use App\Service\Share\FileSource;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
@@ -4,10 +4,10 @@ declare(strict_types=1);
namespace App\Module\ProjectManagement\Infrastructure\Service;
use App\Module\Integration\Domain\Repository\ZimbraConfigurationRepositoryInterface;
use App\Module\ProjectManagement\Domain\Entity\Task;
use App\Module\ProjectManagement\Domain\Entity\TaskRecurrence;
use App\Module\ProjectManagement\Domain\Enum\RecurrenceType;
use App\Repository\ZimbraConfigurationRepository;
use App\Service\TokenEncryptor;
use DateTimeZone;
use Psr\Log\LoggerInterface;
@@ -21,7 +21,7 @@ use const ENT_QUOTES;
final class CalDavService
{
public function __construct(
private readonly ZimbraConfigurationRepository $configRepository,
private readonly ZimbraConfigurationRepositoryInterface $configRepository,
private readonly TokenEncryptor $tokenEncryptor,
private readonly HttpClientInterface $httpClient,
private readonly LoggerInterface $logger,
@@ -1,22 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Repository;
use App\Entity\BookStackConfiguration;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
class BookStackConfigurationRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, BookStackConfiguration::class);
}
public function findSingleton(): ?BookStackConfiguration
{
return $this->findOneBy([]);
}
}
@@ -1,23 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Repository;
use App\Entity\TaskBookStackLink;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
class TaskBookStackLinkRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, TaskBookStackLink::class);
}
/** @return TaskBookStackLink[] */
public function findByTaskId(int $taskId): array
{
return $this->findBy(['task' => $taskId], ['createdAt' => 'DESC']);
}
}