feat : generalize TaskDocumentProcessor for client tickets
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,7 @@ use Symfony\Component\Serializer\Attribute\Groups;
|
|||||||
new GetCollection(paginationEnabled: false, security: "is_granted('ROLE_USER')"),
|
new GetCollection(paginationEnabled: false, security: "is_granted('ROLE_USER')"),
|
||||||
new Get(security: "is_granted('ROLE_USER')"),
|
new Get(security: "is_granted('ROLE_USER')"),
|
||||||
new Post(
|
new Post(
|
||||||
security: "is_granted('ROLE_ADMIN')",
|
security: "is_granted('ROLE_ADMIN') or is_granted('ROLE_CLIENT')",
|
||||||
processor: TaskDocumentProcessor::class,
|
processor: TaskDocumentProcessor::class,
|
||||||
deserialize: false,
|
deserialize: false,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ namespace App\State;
|
|||||||
|
|
||||||
use ApiPlatform\Metadata\Operation;
|
use ApiPlatform\Metadata\Operation;
|
||||||
use ApiPlatform\State\ProcessorInterface;
|
use ApiPlatform\State\ProcessorInterface;
|
||||||
|
use App\Entity\ClientTicket;
|
||||||
use App\Entity\Task;
|
use App\Entity\Task;
|
||||||
use App\Entity\TaskDocument;
|
use App\Entity\TaskDocument;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\SecurityBundle\Security;
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use Symfony\Component\HttpFoundation\RequestStack;
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
use Symfony\Component\Uid\Uuid;
|
use Symfony\Component\Uid\Uuid;
|
||||||
|
|
||||||
@@ -50,18 +52,41 @@ final readonly class TaskDocumentProcessor implements ProcessorInterface
|
|||||||
throw new BadRequestHttpException('File size exceeds 50 MB limit.');
|
throw new BadRequestHttpException('File size exceeds 50 MB limit.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$taskIri = $request->request->get('task');
|
$taskIri = $request->request->get('task');
|
||||||
|
$clientTicketIri = $request->request->get('clientTicket');
|
||||||
|
|
||||||
if (null === $taskIri || '' === $taskIri) {
|
if ((null === $taskIri || '' === $taskIri) && (null === $clientTicketIri || '' === $clientTicketIri)) {
|
||||||
throw new BadRequestHttpException('Task IRI is required.');
|
throw new BadRequestHttpException('Either task or clientTicket IRI is required.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract task ID from IRI (e.g., "/api/tasks/42" -> 42)
|
$task = null;
|
||||||
$taskId = (int) basename((string) $taskIri);
|
$clientTicket = null;
|
||||||
$task = $this->entityManager->getRepository(Task::class)->find($taskId);
|
|
||||||
|
|
||||||
if (null === $task) {
|
if (null !== $taskIri && '' !== $taskIri) {
|
||||||
throw new BadRequestHttpException('Task not found.');
|
// Extract task ID from IRI (e.g., "/api/tasks/42" -> 42)
|
||||||
|
$taskId = (int) basename((string) $taskIri);
|
||||||
|
$task = $this->entityManager->getRepository(Task::class)->find($taskId);
|
||||||
|
|
||||||
|
if (null === $task) {
|
||||||
|
throw new BadRequestHttpException('Task not found.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $clientTicketIri && '' !== $clientTicketIri) {
|
||||||
|
$clientTicketId = (int) basename((string) $clientTicketIri);
|
||||||
|
$clientTicket = $this->entityManager->getRepository(ClientTicket::class)->find($clientTicketId);
|
||||||
|
|
||||||
|
if (null === $clientTicket) {
|
||||||
|
throw new BadRequestHttpException('Client ticket not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ownership validation for ROLE_CLIENT
|
||||||
|
if (!$this->security->isGranted('ROLE_ADMIN')) {
|
||||||
|
$currentUser = $this->security->getUser();
|
||||||
|
if ($clientTicket->getSubmittedBy() !== $currentUser) {
|
||||||
|
throw new AccessDeniedHttpException('You can only upload documents to your own tickets.');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture file metadata BEFORE move() — move invalidates the temp file
|
// Capture file metadata BEFORE move() — move invalidates the temp file
|
||||||
@@ -80,6 +105,7 @@ final readonly class TaskDocumentProcessor implements ProcessorInterface
|
|||||||
|
|
||||||
$document = new TaskDocument();
|
$document = new TaskDocument();
|
||||||
$document->setTask($task);
|
$document->setTask($task);
|
||||||
|
$document->setClientTicket($clientTicket);
|
||||||
$document->setOriginalName($originalName);
|
$document->setOriginalName($originalName);
|
||||||
$document->setFileName($fileName);
|
$document->setFileName($fileName);
|
||||||
$document->setMimeType($mimeType);
|
$document->setMimeType($mimeType);
|
||||||
|
|||||||
Reference in New Issue
Block a user