Skip to content

Commit

Permalink
add suivi via api #3621
Browse files Browse the repository at this point in the history
  • Loading branch information
sfinx13 committed Feb 11, 2025
1 parent 5bef770 commit f1c2714
Show file tree
Hide file tree
Showing 15 changed files with 526 additions and 20 deletions.
2 changes: 1 addition & 1 deletion src/Controller/Api/SignalementController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use App\Entity\User;
use App\Factory\Api\SignalementResponseFactory;
use App\Repository\SignalementRepository;
use Nelmio\ApiDocBundle\Annotation\Model;
use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Attributes as OA;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\When;
Expand Down
2 changes: 1 addition & 1 deletion src/Controller/Api/SignalementFileUploadController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use App\Factory\Api\FileFactory;
use App\Service\Signalement\SignalementFileProcessor;
use Doctrine\ORM\EntityManagerInterface;
use Nelmio\ApiDocBundle\Annotation\Model;
use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Attributes as OA;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\When;
Expand Down
180 changes: 180 additions & 0 deletions src/Controller/Api/SuiviCreateController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<?php

namespace App\Controller\Api;

use App\Dto\Api\Request\SuiviRequest;
use App\Dto\Api\Response\SuiviResponse;
use App\Entity\File;
use App\Entity\Signalement;
use App\Entity\Suivi;
use App\Entity\User;
use App\EventListener\SecurityApiExceptionListener;
use App\Manager\SuiviManager;
use App\Service\Sanitizer;
use Nelmio\ApiDocBundle\Attribute\Model;
use OpenApi\Attributes as OA;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\When;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

#[When('dev')]
#[When('test')]
#[Route('/api')]
class SuiviCreateController extends AbstractController
{
public function __construct(
readonly private SuiviManager $suiviManager,
readonly private UrlGeneratorInterface $urlGenerator,
) {
}

#[Route('/signalements/{uuid:signalement}/suivis', name: 'api_signalements_suivis_post', methods: ['POST'])]
#[OA\Post(
path: '/api/signalements/{uuid}/suivis',
description: 'Création d\'un suivi',
summary: 'Création d\'un suivi',
security: [['Bearer' => []]],
tags: ['Suivis']
)]
#[OA\Response(
response: Response::HTTP_CREATED,
description: 'Suivi crée avec succès',
content: new OA\JsonContent(ref: new Model(type: SuiviResponse::class))
)]
#[OA\Response(
response: Response::HTTP_NOT_FOUND,
description: 'Signalement introuvable',
content: new OA\JsonContent(
properties: [
new OA\Property(
property: 'message',
type: 'string',
example: 'Signalement introuvable'
),
new OA\Property(
property: 'statut',
type: 'int',
example: Response::HTTP_NOT_FOUND
),
],
type: 'object'
)
)]
#[OA\Response(
response: Response::HTTP_BAD_REQUEST,
description: 'Mauvaise payload (données invalides).',
content: new OA\JsonContent(
properties: [
new OA\Property(
property: 'message',
type: 'string',
example: 'Valeurs invalides pour les champs suivants :'
),
new OA\Property(
property: 'status',
type: 'integer',
example: 400
),
new OA\Property(
property: 'errors',
type: 'array',
items: new OA\Items(
properties: [
new OA\Property(
property: 'property',
type: 'string',
example: 'description'
),
new OA\Property(
property: 'message',
type: 'string',
example: 'Le contenu du suivi doit faire au moins 10 caractères !'
),
],
type: 'object'
)
),
],
type: 'object'
)
)]
#[OA\Response(
response: Response::HTTP_FORBIDDEN,
description: 'Accès à la ressource non autorisé.',
content: new OA\JsonContent(
properties: [
new OA\Property(
property: 'message',
type: 'string',
example: 'Vous n\'avez pas l\'autorisation d\'accéder à cette ressource.'
),
new OA\Property(
property: 'statut',
type: 'int',
example: Response::HTTP_FORBIDDEN
),
],
type: 'object'
)
)]
public function __invoke(
#[MapRequestPayload]
SuiviRequest $suiviRequest,
?Signalement $signalement = null,
): JsonResponse {
if (null === $signalement) {
return $this->json(
['message' => 'Signalement introuvable', 'status' => Response::HTTP_NOT_FOUND],
Response::HTTP_NOT_FOUND
);
}
$this->denyAccessUnlessGranted('COMMENT_CREATE',
$signalement,
SecurityApiExceptionListener::ACCESS_DENIED
);

/** @var User $user */
$user = $this->getUser();
$suivi = $this->suiviManager->createSuivi(
signalement: $signalement,
description: $this->buildDescription($signalement, $suiviRequest),
type: Suivi::TYPE_PARTNER,
isPublic: $suiviRequest->notifyUsager,
user: $user,
);

return $this->json(new SuiviResponse($suivi), Response::HTTP_CREATED);
}

private function buildDescription(Signalement $signalement, SuiviRequest $suiviRequest): string
{
$fileListAsHtml = '';
$description = Sanitizer::sanitize($suiviRequest->description);
$filesFiltered = $signalement->getFiles()->filter(function (File $file) use ($suiviRequest) {
return in_array($file->getUuid(), $suiviRequest->files, true);
});

if ($filesFiltered->count() > 0) {
$fileListAsHtml = '<ul>';
/** @var File $file */
foreach ($filesFiltered as $file) {
$fileUrl = $this->urlGenerator->generate(
'show_file',
['uuid' => $file->getUuid()],
UrlGeneratorInterface::ABSOLUTE_URL
);
$fileListAsHtml .= sprintf("<li><a class='fr-link' target='_blank' rel='noopener' href='%s'>%s</a>",
$fileUrl,
$file->getTitle()
);
}
$fileListAsHtml .= '</ul>';
}

return $description.$fileListAsHtml;
}
}
7 changes: 3 additions & 4 deletions src/Controller/Back/SignalementActionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use App\Service\Mailer\NotificationMail;
use App\Service\Mailer\NotificationMailerRegistry;
use App\Service\Mailer\NotificationMailerType;
use App\Service\Sanitizer;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use Psr\Log\LoggerInterface;
Expand Down Expand Up @@ -109,9 +110,7 @@ public function addSuiviSignalement(
$this->denyAccessUnlessGranted('COMMENT_CREATE', $signalement);
if ($this->isCsrfTokenValid('signalement_add_suivi_'.$signalement->getId(), $request->get('_token'))
&& $form = $request->get('signalement-add-suivi')) {
$content = $form['content'];
$content = preg_replace('/<p[^>]*>/', '', $content); // Remove the start <p> or <p attr="">
$content = str_replace('</p>', '<br />', $content); // Replace the end
$content = Sanitizer::sanitize($form['content']);
if (mb_strlen($content) < 10) {
$this->addFlash('error', 'Le contenu du suivi doit faire au moins 10 caractères !');

Expand All @@ -121,11 +120,11 @@ public function addSuiviSignalement(
/** @var User $user */
$user = $this->getUser();
$suiviManager->createSuivi(
user: $user,
signalement: $signalement,
description: $content,
type: Suivi::TYPE_PARTNER,
isPublic: !empty($form['notifyUsager']),
user: $user,
);
} catch (\Throwable $exception) {
$this->addFlash('error', 'Une erreur est survenue lors de la publication.');
Expand Down
14 changes: 3 additions & 11 deletions src/Dto/Api/Model/Suivi.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@
)]
class Suivi
{
#[OA\Property(
description: 'Identifiant unique du suivi.',
type: 'integer',
example: 1
)]
public int $id;

#[OA\Property(
description: 'Date de création du suivi.<br>Exemple : `2024-11-01T10:00:00+00:00`',
type: 'string',
Expand All @@ -26,9 +19,9 @@ class Suivi
)]
public string $dateCreation;
#[OA\Property(
description: 'Description détaillée du suivi.',
description: 'Description détaillée du suivi, peut contenir des balises HTML.',
type: 'string',
example: 'Premier suivi associé.'
example: 'Premier <em>suivi associé</em>.'
)]
public string $description;

Expand All @@ -49,9 +42,8 @@ class Suivi
public function __construct(
SuiviEntity $suivi,
) {
$this->id = $suivi->getId();
$this->dateCreation = $suivi->getCreatedAt()->format(\DATE_ATOM);
$this->description = $suivi->getDescription(); // traitement de suppression du html ? comment gérer les bouton/doc qui sont présent en dur dans le contenu ?
$this->description = $suivi->getDescription();
$this->public = $suivi->getIsPublic();
$this->createdBy = $suivi->getCreatedByLabel();
}
Expand Down
37 changes: 37 additions & 0 deletions src/Dto/Api/Request/SuiviRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace App\Dto\Api\Request;

use App\Validator\SanitizedLength;
use App\Validator\ValidFiles;
use OpenApi\Attributes as OA;

#[OA\Schema(
description: 'Payload pour créer un suivi.',
required: ['description'],
)]
class SuiviRequest implements RequestInterface
{
#[OA\Property(
description: 'Un message de 10 caractère minimum est obligatoire.',
example: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
)]
#[SanitizedLength(min: 10)]
public ?string $description = null;

#[OA\Property(
description: 'Permet d\'indiquer si l\'usager doit être notifié.',
default: false,
example: true,
)]
public bool $notifyUsager = false;

#[OA\Property(
description: 'Tableau contenant une liste d\'UUID des fichiers associés au signalement.',
type: 'array',
items: new OA\Items(type: 'string', format: 'uuid'),
example: ['f47ac10b-58cc-4372-a567-0e02b2c3d479', '8d3c7db7-fc90-43f4-8066-7522f0e9b163']
)]
#[ValidFiles]
public array $files = [];
}
2 changes: 0 additions & 2 deletions src/Dto/Api/Response/SignalementResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -755,14 +755,12 @@ enum: [
items: new OA\Items(ref: new Model(type: Suivi::class)),
example: [
[
'id' => 1,
'dateCreation' => '2024-11-01T10:00:00+00:00',
'description' => 'Premier suivi associé.',
'public' => true,
'createdBy' => 'John Doe',
],
[
'id' => 2,
'dateCreation' => '2024-11-02T12:30:00+00:00',
'description' => 'Deuxième suivi, accès limité.',
'public' => false,
Expand Down
38 changes: 38 additions & 0 deletions src/Dto/Api/Response/SuiviResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace App\Dto\Api\Response;

use App\Entity\Suivi;
use OpenApi\Attributes as OA;

class SuiviResponse
{
#[OA\Property(
description: 'Date de création du suivi.<br>Exemple : `2024-11-01T10:00:00+00:00`',
type: 'string',
format: 'date-time',
example: '2024-11-01T10:00:00+00:00'
)]
public string $dateCreation;

#[OA\Property(
description: 'Description détaillée du suivi, peut contenir des balises HTML.',
type: 'string',
example: '<ul><li>lorem</li><li>ipsum</li></ul>'
)]
public string $description;

#[OA\Property(
description: 'Indique si le suivi est visible pour les usagers.',
type: 'boolean',
example: true
)]
public bool $public;

public function __construct(Suivi $suivi)
{
$this->dateCreation = $suivi->getCreatedAt()->format(\DATE_ATOM);
$this->description = $suivi->getDescription();
$this->public = $suivi->getIsPublic();
}
}
1 change: 0 additions & 1 deletion src/EventListener/SecurityApiExceptionListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ class SecurityApiExceptionListener
public function onKernelException(ExceptionEvent $event): void
{
$exception = $event->getThrowable();

if ($this->supports($exception)) {
$previous = $exception->getPrevious();
$affectation = null;
Expand Down
21 changes: 21 additions & 0 deletions src/Validator/SanitizedLength.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace App\Validator;

use Symfony\Component\Validator\Constraint;

#[\Attribute]
class SanitizedLength extends Constraint
{
public string $message = 'Le texte doit contenir au moins {{ limit }} caractères après sanitation.';
public int $min;

public function __construct(int $min, ?string $message = null)
{
parent::__construct([]);
$this->min = $min;
if ($message) {
$this->message = $message;
}
}
}
Loading

0 comments on commit f1c2714

Please sign in to comment.