-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
666 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
<?php | ||
|
||
namespace App\Controller\Api; | ||
|
||
use App\Dto\Api\Request\FileRequest; | ||
use App\Entity\Enum\DocumentType; | ||
use App\Entity\File; | ||
use App\Factory\Api\FileFactory; | ||
use Doctrine\ORM\EntityManagerInterface; | ||
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; | ||
|
||
#[When('dev')] | ||
#[When('test')] | ||
#[Route('/api')] | ||
class SignalementFileUpdateController extends AbstractController | ||
{ | ||
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly FileFactory $fileFactory) | ||
{ | ||
} | ||
|
||
#[Route('/files/{uuid:file}', name: 'api_signalements_files_patch', methods: ['PATCH'])] | ||
#[OA\Patch( | ||
path: '/api/files/{uuid}', | ||
description: 'Edite le type de document ainsi que la description pour un fichier.', | ||
summary: 'Edition d\'un fichier', | ||
security: [['Bearer' => []]], | ||
requestBody: new OA\RequestBody( | ||
description: 'Mettre à jour le type de document et la description.', | ||
content: new OA\JsonContent(ref: '#/components/schemas/FileRequest') | ||
), | ||
tags: ['Fichiers'], | ||
responses: [ | ||
new OA\Response( | ||
response: 200, | ||
description: 'Fichier édité avec succès', | ||
content: new OA\JsonContent( | ||
properties: [ | ||
new OA\Property(property: 'uuid', type: 'string', example: '123'), | ||
new OA\Property(property: 'documentType', type: 'string', example: 'BAILLEUR_REPONSE_BAILLEUR'), | ||
new OA\Property(property: 'description', type: 'string', example: 'lorem ipsum dolor sit amet'), | ||
] | ||
) | ||
), | ||
new OA\Response( | ||
response: 400, | ||
description: 'Erreur de validation ou autres erreurs liées à la requête.', | ||
content: new OA\JsonContent( | ||
properties: [ | ||
new OA\Property(property: 'status', type: 'integer', example: 400), | ||
new OA\Property(property: 'message', type: 'string', example: 'Validation Failed'), | ||
new OA\Property(property: 'errors', type: 'array', items: new OA\Items( | ||
type: 'object' | ||
)), | ||
] | ||
) | ||
), | ||
new OA\Response( | ||
response: 404, | ||
description: 'Fichier non trouvé.', | ||
content: new OA\JsonContent( | ||
properties: [ | ||
new OA\Property(property: 'status', type: 'integer', example: 404), | ||
new OA\Property(property: 'message', type: 'string', example: 'Resource Not Found'), | ||
] | ||
) | ||
), | ||
new OA\Response( | ||
response: 401, | ||
description: 'Accès non autorisé.', | ||
content: new OA\JsonContent( | ||
properties: [ | ||
new OA\Property(property: 'status', type: 'integer', example: 401), | ||
new OA\Property(property: 'message', type: 'string', example: 'Unauthorized'), | ||
] | ||
) | ||
), | ||
] | ||
)] | ||
public function __invoke( | ||
#[MapRequestPayload] | ||
FileRequest $fileRequest, | ||
?File $file = null, | ||
): JsonResponse { | ||
if (null === $file) { | ||
return $this->json( | ||
['message' => 'Fichier introuvable', 'status' => Response::HTTP_NOT_FOUND], | ||
Response::HTTP_NOT_FOUND | ||
); | ||
} | ||
$this->denyAccessUnlessGranted('FILE_EDIT', $file); | ||
|
||
$file->setDocumentType(DocumentType::tryFrom($fileRequest->documentType)); | ||
if (File::FILE_TYPE_PHOTO === $file->getFileType()) { | ||
$file->setDescription($fileRequest->description); | ||
} | ||
$this->entityManager->flush(); | ||
|
||
return $this->json($this->fileFactory->createFrom($file)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
<?php | ||
|
||
namespace App\Controller\Api; | ||
|
||
use App\Dto\Api\Model\File as FileResponse; | ||
use App\Dto\Api\Request\FilesUploadRequest; | ||
use App\Entity\File; | ||
use App\Entity\Signalement; | ||
use App\Entity\User; | ||
use App\Event\FileUploadedEvent; | ||
use App\Factory\Api\FileFactory; | ||
use App\Service\Signalement\SignalementFileProcessor; | ||
use Doctrine\ORM\EntityManagerInterface; | ||
use Nelmio\ApiDocBundle\Annotation\Model; | ||
use OpenApi\Attributes as OA; | ||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | ||
use Symfony\Component\DependencyInjection\Attribute\When; | ||
use Symfony\Component\EventDispatcher\EventDispatcherInterface; | ||
use Symfony\Component\HttpFoundation\File\UploadedFile; | ||
use Symfony\Component\HttpFoundation\JsonResponse; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\Routing\Attribute\Route; | ||
use Symfony\Component\Serializer\Exception\ExceptionInterface; | ||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; | ||
use Symfony\Component\Validator\Exception\ValidationFailedException; | ||
use Symfony\Component\Validator\Validator\ValidatorInterface; | ||
|
||
#[When('dev')] | ||
#[When('test')] | ||
#[Route('/api')] | ||
class SignalementFileUploadController extends AbstractController | ||
{ | ||
public function __construct( | ||
private readonly DenormalizerInterface $normalizer, | ||
private readonly ValidatorInterface $validator, | ||
private readonly SignalementFileProcessor $signalementFileProcessor, | ||
private readonly EntityManagerInterface $entityManager, | ||
private readonly EventDispatcherInterface $eventDispatcher, | ||
private readonly FileFactory $fileFactory, | ||
) { | ||
} | ||
|
||
/** | ||
* @throws ExceptionInterface | ||
*/ | ||
#[Route('/signalements/{uuid:signalement}/files', name: 'api_signalements_files_post', methods: ['POST'])] | ||
#[OA\Post( | ||
path: '/api/signalements/{uuid}/files', | ||
description: 'Retourne les informations du fichier téléversé pour un signalement.', | ||
summary: 'Téléversement d\'un fichier', | ||
security: [['Bearer' => []]], | ||
requestBody: new OA\RequestBody( | ||
description: 'Données de téléversement du fichier', | ||
required: true, | ||
content: new OA\MediaType( | ||
mediaType: 'multipart/form-data', | ||
schema: new OA\Schema( | ||
properties: [ | ||
'files' => new OA\Property( | ||
description: 'Liste des fichiers téléversés', | ||
type: 'array', | ||
items: new OA\Items(type: 'string', format: 'binary'), | ||
), | ||
], | ||
type: 'object' | ||
), | ||
examples: [ | ||
new OA\Examples( | ||
example: "Exemple d'envoi d'un fichier", | ||
summary: "Exemple d'envoi d'un fichier", | ||
value: [ | ||
'files' => ['file1.jpg', 'file2.pdf'], | ||
] | ||
), | ||
] | ||
) | ||
), | ||
tags: ['Fichiers'], | ||
)] | ||
#[OA\Response( | ||
response: Response::HTTP_OK, | ||
description: 'Un document', | ||
content: new OA\JsonContent( | ||
type: 'array', | ||
items: new OA\Items(ref: new Model(type: FileResponse::class)) | ||
) | ||
)] | ||
#[OA\Response( | ||
response: 400, | ||
description: 'Erreur liée à la validation de la requête.', | ||
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: 'files'), | ||
new OA\Property(property: 'message', type: 'string', example: 'Vous devez téléverser au moins un fichier.'), | ||
new OA\Property(property: 'invalidValue', type: 'array', items: new OA\Items(type: 'string'), example: []), | ||
] | ||
) | ||
), | ||
] | ||
) | ||
)] | ||
#[OA\Response( | ||
response: 401, | ||
description: 'Accès non autorisé à la ressource.', | ||
content: new OA\JsonContent( | ||
properties: [ | ||
new OA\Property(property: 'status', type: 'integer', example: 401), | ||
new OA\Property(property: 'message', type: 'string', example: 'Unauthorized'), | ||
] | ||
) | ||
)] | ||
#[OA\Response( | ||
response: 404, | ||
description: 'Signalement non trouvé.', | ||
content: new OA\JsonContent( | ||
properties: [ | ||
new OA\Property(property: 'status', type: 'integer', example: 404), | ||
new OA\Property(property: 'message', type: 'string', example: 'Resource Not Found'), | ||
] | ||
) | ||
)] | ||
public function __invoke( | ||
Request $request, | ||
?Signalement $signalement = null, | ||
): JsonResponse { | ||
if (null === $signalement) { | ||
return $this->json( | ||
['message' => 'Signalement introuvable', 'status' => Response::HTTP_NOT_FOUND], | ||
Response::HTTP_NOT_FOUND | ||
); | ||
} | ||
$this->denyAccessUnlessGranted('SIGN_EDIT', $signalement); | ||
$data['files'] = $request->files->all(); | ||
$fileRequest = $this->normalizer->denormalize($data['files'], FilesUploadRequest::class, 'json'); | ||
$errors = $this->validator->validate($fileRequest); | ||
if (count($errors) > 0) { | ||
throw new ValidationFailedException($fileRequest, $errors); | ||
} | ||
|
||
/** @var User $user */ | ||
$user = $this->getUser(); | ||
$fileList = $this->processFiles($fileRequest); | ||
$this->signalementFileProcessor->addFilesToSignalement( | ||
fileList: $fileList, | ||
signalement: $signalement, | ||
user: $user | ||
); | ||
|
||
$this->entityManager->persist($signalement); | ||
$this->entityManager->flush(); | ||
|
||
$fileUploadedEvent = $this->eventDispatcher->dispatch( | ||
new FileUploadedEvent($signalement, $user, $fileList), | ||
FileUploadedEvent::NAME | ||
); | ||
|
||
$response = $this->fileFactory->createFromArray($fileUploadedEvent->getFilesPushed()); | ||
|
||
return $this->json($response, Response::HTTP_CREATED); | ||
} | ||
|
||
private function processFiles(FilesUploadRequest $fileRequest): array | ||
{ | ||
$files = $fileList = []; | ||
foreach ($fileRequest->files as $file) { | ||
/** @var UploadedFile $file */ | ||
if (in_array($file->getMimeType(), File::IMAGE_MIME_TYPES)) { | ||
$files['photos'][] = $file; | ||
} else { | ||
$files['documents'][] = $file; | ||
} | ||
} | ||
if (isset($files['documents'])) { | ||
$documentList = $this->signalementFileProcessor->process($files, 'documents'); | ||
$fileList = [...$fileList, ...$documentList]; | ||
} | ||
if (isset($files['photos'])) { | ||
$imageList = $this->signalementFileProcessor->process($files, 'photos'); | ||
$fileList = [...$fileList, ...$imageList]; | ||
} | ||
|
||
return $fileList; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
<?php | ||
|
||
namespace App\Dto\Api\Request; | ||
|
||
use OpenApi\Attributes as OA; | ||
use Symfony\Component\Validator\Constraints as Assert; | ||
|
||
class FileRequest implements RequestInterface | ||
{ | ||
#[OA\Property( | ||
description: 'Type de document<br> | ||
<ul> | ||
<li>`AUTRE_PROCEDURE`</li> | ||
<li>`AUTRE`</li> | ||
<li>`SITUATION_FOYER_DPE`</li> | ||
<li>`SITUATION_FOYER_ETAT_DES_LIEUX`</li> | ||
<li>`PROCEDURE_RAPPORT_DE_VISITE`</li> | ||
<li>`SITUATION_FOYER_BAIL`</li> | ||
<li>`PHOTO_SITUATION`</li> | ||
<li>`PHOTO_VISITE`</li> | ||
<li>`SITUATION_DIAGNOSTIC_PLOMB_AMIANTE`</li> | ||
<li>`PROCEDURE_MISE_EN_DEMEURE`</li> | ||
<li>`PROCEDURE_ARRETE_PREFECTORAL`</li> | ||
<li>`BAILLEUR_REPONSE_BAILLEUR`</li> | ||
<li>`PROCEDURE_ARRETE_MUNICIPAL`</li> | ||
<li>`PROCEDURE_SAISINE`</li> | ||
<li>`BAILLEUR_DEVIS_POUR_TRAVAUX`</li> | ||
<li>`EXPORT`</li> | ||
</ul> | ||
', | ||
example: 'BAILLEUR_REPONSE_BAILLEUR' | ||
)] | ||
#[Assert\NotBlank()] | ||
#[Assert\Choice( | ||
choices: [ | ||
'AUTRE_PROCEDURE', | ||
'AUTRE', | ||
'SITUATION_FOYER_DPE', | ||
'SITUATION_FOYER_ETAT_DES_LIEUX', | ||
'PROCEDURE_RAPPORT_DE_VISITE', | ||
'SITUATION_FOYER_BAIL', | ||
'PHOTO_SITUATION', | ||
'PHOTO_VISITE', | ||
'SITUATION_DIAGNOSTIC_PLOMB_AMIANTE', | ||
'PROCEDURE_MISE_EN_DEMEURE', | ||
'PROCEDURE_ARRETE_PREFECTORAL', | ||
'BAILLEUR_REPONSE_BAILLEUR', | ||
'PROCEDURE_ARRETE_MUNICIPAL', | ||
'PROCEDURE_SAISINE', | ||
'BAILLEUR_DEVIS_POUR_TRAVAUX', | ||
'EXPORT', | ||
], | ||
message: 'Veuillez choisir une valeur valide pour le type de document. {{ choices }}' | ||
)] | ||
public ?string $documentType = null; | ||
#[OA\Property( | ||
description: 'La description d\'une photo, elle sera ignoré pour un document', | ||
example: 'lorem ipsum dolor sit amet' | ||
)] | ||
#[Assert\Length(max: 255)] | ||
public ?string $description = null; | ||
} |
Oops, something went wrong.