From 374f94848d1ea78f30111876060f5eeda4685d04 Mon Sep 17 00:00:00 2001 From: Orka Arnest CRUZE Date: Tue, 21 Nov 2023 17:37:33 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20publication=20du=20flux=20WMS-VECTOR=20?= =?UTF-8?q?avec=20t=C3=A9l=C3=A9versement=20de=20fichier=20de=20style=20SL?= =?UTF-8?q?D=20en=20static=20refs=20#23?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wms-vector/WmsVectorServiceNew.tsx | 14 +- config/parameters.yaml | 1 + src/Constants/EntrepotApi/StaticFileTypes.php | 11 ++ src/Controller/Api/WmsVectorController.php | 170 ++++++++++++++++++ src/Dto/WmsVector/WmsVectorAddDTO.php | 76 ++++++++ 5 files changed, 265 insertions(+), 7 deletions(-) create mode 100644 src/Constants/EntrepotApi/StaticFileTypes.php create mode 100644 src/Controller/Api/WmsVectorController.php create mode 100644 src/Dto/WmsVector/WmsVectorAddDTO.php diff --git a/assets/pages/service/wms-vector/WmsVectorServiceNew.tsx b/assets/pages/service/wms-vector/WmsVectorServiceNew.tsx index ea1f9e9d..f49a2b96 100644 --- a/assets/pages/service/wms-vector/WmsVectorServiceNew.tsx +++ b/assets/pages/service/wms-vector/WmsVectorServiceNew.tsx @@ -33,7 +33,7 @@ import "../../../sass/components/spinner.scss"; const createFormData = (formValues: object) => { const fd = new FormData(); - fd.set("category", formValues["category"]); + fd.set("category", JSON.stringify(formValues["category"])); fd.set("charset", formValues["charset"]); fd.set("creation_date", formValues["creation_date"]); fd.set("description", formValues["description"]); @@ -84,7 +84,7 @@ const WmsVectorServiceNew: FC = ({ datastoreId, vector const offeringsQuery = useQuery({ queryKey: RQKeys.datastore_offering_list(datastoreId), queryFn: () => api.service.getOfferings(datastoreId), - refetchInterval: 10000, + refetchInterval: 30000, }); const commonValidation = useMemo(() => new CommonSchemasValidation(offeringsQuery.data), [offeringsQuery.data]); @@ -178,11 +178,11 @@ const WmsVectorServiceNew: FC = ({ datastoreId, vector onSuccess(response) { console.log(response); - // if (vectorDbQuery.data?.tags?.datasheet_name) { - // routes.datastore_datasheet_view({ datastoreId, datasheetName: vectorDbQuery.data?.tags.datasheet_name, activeTab: "services" }).push(); - // } else { - // routes.datasheet_list({ datastoreId }).push(); - // } + if (vectorDbQuery.data?.tags?.datasheet_name) { + routes.datastore_datasheet_view({ datastoreId, datasheetName: vectorDbQuery.data?.tags.datasheet_name, activeTab: "services" }).push(); + } else { + routes.datasheet_list({ datastoreId }).push(); + } }, }); diff --git a/config/parameters.yaml b/config/parameters.yaml index 9b7bdb7f..41af35e5 100644 --- a/config/parameters.yaml +++ b/config/parameters.yaml @@ -14,6 +14,7 @@ parameters: http_proxy: "%env(resolve:HTTP_PROXY)%" upload_path: "%kernel.project_dir%/var/data/uploaded_files" + style_files_upload_path: "%kernel.project_dir%/var/data/uploaded_style_files" support_contact_mail: "%env(resolve:SUPPORT_CONTACT_EMAIL)%" mailer_sender_address: "%env(resolve:MAILER_SENDER_ADDRESS)%" diff --git a/src/Constants/EntrepotApi/StaticFileTypes.php b/src/Constants/EntrepotApi/StaticFileTypes.php new file mode 100644 index 00000000..5884ff0e --- /dev/null +++ b/src/Constants/EntrepotApi/StaticFileTypes.php @@ -0,0 +1,11 @@ + true], + condition: 'request.isXmlHttpRequest()' // TODO : à remettre après les tests +)] +class WmsVectorController extends AbstractController implements ApiControllerInterface +{ + public function __construct( + private EntrepotApiService $entrepotApiService, + protected Filesystem $filesystem, + ) { + } + + #[Route('', name: 'add', methods: ['POST'])] + public function add( + string $datastoreId, + string $storedDataId, + // #[MapRequestPayload] WmsVectorAddDTO $dto, // TODO : MapRequestPayload ne marche pas avec FormData (envoyé par js), essayer de trouver une solution + Request $request + ): JsonResponse { + $data = $request->request->all(); + $files = $request->files->all(); // les fichiers de style .sld + + $tablesNamesList = isset($data['selected_tables']) ? json_decode($data['selected_tables'], true) : []; + + try { + // ajout ou mise à jour des fichiers de styles SLD + $tables = $this->sendStyleFiles($datastoreId, $storedDataId, $tablesNamesList, $files); + + // // création de configuration + $relations = []; + + foreach ($tablesNamesList as $tableName) { + $relations[] = [ + 'name' => $tableName, + 'style' => $tables[$tableName], + ]; + } + + $body = [ + 'type' => 'WMS-VECTOR', + 'name' => $data['public_name'], + 'layer_name' => $data['technical_name'], + 'type_infos' => [ + 'title' => $data['public_name'], + 'abstract' => $data['description'], + 'keywords' => json_decode($data['category'], true), + 'used_data' => [ + [ + 'relations' => $relations, + 'stored_data' => $storedDataId, + ], + ], + ], + ]; + + $storedData = $this->entrepotApiService->storedData->get($datastoreId, $storedDataId); + $endpoints = []; + $isOfferingOpen = true; + + // TODO : implémentation partielle, tous les partages ne sont pas couverts + if ('all_public' === $data['share_with']) { + $endpoints = $this->entrepotApiService->datastore->getEndpoints($datastoreId, [ + 'type' => 'WMS-VECTOR', + 'open' => true, + ]); + $isOfferingOpen = true; + } elseif ('your_community' === $data['share_with']) { + $endpoints = $this->entrepotApiService->datastore->getEndpoints($datastoreId, [ + 'type' => 'WMS-VECTOR', + 'open' => false, + ]); + $isOfferingOpen = false; + } else { + throw new CartesApiException('Valeur du champ [share_with] est invalide', Response::HTTP_BAD_REQUEST, ['share_with' => $data['share_with']]); + } + + if (0 === count($endpoints)) { + throw new CartesApiException("Aucun point d'accès (endpoint) du datastore ne peut convenir à la demande", Response::HTTP_BAD_REQUEST, ['share_with' => $data['share_with']]); + } + + $endpointId = $endpoints[0]['endpoint']['_id']; + + // Ajout de la configuration + $configuration = $this->entrepotApiService->configuration->add($datastoreId, $body); + $configuration = $this->entrepotApiService->configuration->addTags($datastoreId, $configuration['_id'], [ + StoredDataTags::DATASHEET_NAME => $storedData['tags'][StoredDataTags::DATASHEET_NAME], + ]); + + // Creation d'une offering + $offering = $this->entrepotApiService->configuration->addOffering($datastoreId, $configuration['_id'], $endpointId, $isOfferingOpen); + + return $this->json([ + 'configuration' => $configuration, + 'offering' => $offering, + ]); + } catch (EntrepotApiException $ex) { + throw new CartesApiException($ex->getMessage(), $ex->getStatusCode(), $ex->getDetails(), $ex); + } + } + + /** + * Publie les fichiers de styles SLD en tant que fichiers statiques. Un fichier de style par table. En cas de succès, retourne un tableau avec le nom des tables avec l'ID du fichier statique correspondant. + * + * @param array $tablesNamesList + * @param array $files + * + * @return array + */ + public function sendStyleFiles(string $datastoreId, string $storedDataId, array $tablesNamesList, array $files): array + { + /** @var array nom table, identifiant fichier statique */ + $tables = []; + + // récup tous les fichiers statiques liés à la stored_data + $staticFiles = $this->entrepotApiService->static->getAll($datastoreId, [ + 'type' => StaticFileTypes::GEOSERVER_STYLE, + 'name' => sprintf('storeddata_%s_style_wmsv_%%', $storedDataId), + ]); + + foreach ($tablesNamesList as $tableName) { + /** @var UploadedFile */ + $file = $files["style_{$tableName}"]; + + $styleFileName = sprintf('storeddata_%s_style_wmsv_%s', $storedDataId, $tableName); + + $filteredStyles = array_filter($staticFiles, function ($style) use ($styleFileName) { + return $style['name'] === $styleFileName; + }); + $filteredStyles = array_values($filteredStyles); + + $directory = $this->getParameter('style_files_upload_path'); + $file->move($directory, $file->getClientOriginalName()); + + // aucun fichier de style n'existe pas pour la combo de $storedDataId, 'wmsv' et $tableName + if (0 === count($filteredStyles)) { + $staticStyleFile = $this->entrepotApiService->static->add($datastoreId, $directory.'/'.$file->getClientOriginalName(), $styleFileName, StaticFileTypes::GEOSERVER_STYLE); + } + // un fichier de style existe déjà + else { + $staticStyleFile = $filteredStyles[0]; + $staticStyleFile = $this->entrepotApiService->static->replaceFile($datastoreId, $staticStyleFile['_id'], $directory.'/'.$file->getClientOriginalName()); + } + + $tables[$tableName] = $staticStyleFile['_id']; + } + + return $tables; + } +} diff --git a/src/Dto/WmsVector/WmsVectorAddDTO.php b/src/Dto/WmsVector/WmsVectorAddDTO.php new file mode 100644 index 00000000..5526f19b --- /dev/null +++ b/src/Dto/WmsVector/WmsVectorAddDTO.php @@ -0,0 +1,76 @@ + 'common.technical_name_error'])] + #[Assert\Regex(['pattern' => '/^[\w\-\.]+$/', 'message' => 'common.technical_name_regex'])] + public readonly string $technical_name, + + #[Assert\NotBlank(['message' => 'common.public_name_error'])] + public readonly string $public_name, + + #[Assert\NotBlank(['message' => 'common.description_error'])] + public readonly string $description, + + #[Assert\NotBlank(['message' => 'common.identifier_error'])] + #[Assert\Regex(['pattern' => '/^[\w\-\.]+$/', 'message' => 'common.identifier_regex'])] + public readonly string $identifier, + + /** @var array */ + #[Assert\Count(min: 1, minMessage: 'common.category_min_error')] + public readonly array $category, + + #[Assert\NotBlank(['message' => 'common.email_contact_error'])] + #[Assert\Email(message: 'email_contact {{ value }} n\'est pas une adresse email valide')] + public readonly string $email_contact, + + #[Assert\Date(message: 'creation_date {{ value }} n\'est pas une date valide')] + public readonly string $creation_date, + + #[Assert\NotBlank(['message' => 'common.organization_error'])] + public readonly string $organization, + + #[Assert\NotBlank(['message' => 'common.organization_email_error'])] + #[Assert\Email(message: 'organization_email {{ value }} n\est pas une adresse email valide')] + public readonly string $organization_email, + + #[Assert\NotBlank(['message' => 'common.projection_error'])] + public readonly string $projection, + + /** @var array */ + #[Assert\Count(min: 1, minMessage: 'common.language_min_error')] + public readonly array $languages, + + #[Assert\NotBlank(['message' => 'common.charset_error'])] + public readonly string $charset, + + #[Assert\NotBlank(['message' => 'common.encoding_error'])] + public readonly string $encoding, + + #[Assert\Choice([ + 'choices' => ['', '25000', '75000', '100000', '150000', '200000', '250000', '1000000'], // TODO NON EXHAUSTIF + 'message' => 'common.resolution_error', + ])] + public readonly ?string $resolution, + + #[Assert\Choice([ + 'choices' => ['', 'dataset', 'series'], + 'message' => 'common.resource_genealogy_error', + ])] + public readonly ?string $resource_genealogy, + + #[Assert\Choice([ + 'choices' => ['all_public', 'your_community'], // TODO NON EXHAUSTIF + 'message' => 'common.share_with_error', + ])] + public readonly string $share_with, + + public readonly string $selected_tables, + ) { + } +}