Skip to content

Commit

Permalink
feat: publication du flux WMS-VECTOR avec téléversement de fichier de…
Browse files Browse the repository at this point in the history
… style SLD en static refs #23
  • Loading branch information
ocruze committed Nov 21, 2023
1 parent 0f2ebba commit 374f948
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 7 deletions.
14 changes: 7 additions & 7 deletions assets/pages/service/wms-vector/WmsVectorServiceNew.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"]);
Expand Down Expand Up @@ -84,7 +84,7 @@ const WmsVectorServiceNew: FC<WmsVectorServiceNewProps> = ({ 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]);
Expand Down Expand Up @@ -178,11 +178,11 @@ const WmsVectorServiceNew: FC<WmsVectorServiceNewProps> = ({ 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();
}
},
});

Expand Down
1 change: 1 addition & 0 deletions config/parameters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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)%"
Expand Down
11 changes: 11 additions & 0 deletions src/Constants/EntrepotApi/StaticFileTypes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace App\Constants\EntrepotApi;

final class StaticFileTypes
{
public const GEOSERVER_FTL = 'GEOSERVER-FTL';
public const GEOSERVER_STYLE = 'GEOSERVER-STYLE';
public const ROK4_STYLE = 'ROK4-STYLE';
public const DERIVATION_SQL = 'DERIVATION-SQL';
}
170 changes: 170 additions & 0 deletions src/Controller/Api/WmsVectorController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<?php

namespace App\Controller\Api;

use App\Constants\EntrepotApi\StaticFileTypes;
use App\Constants\EntrepotApi\StoredDataTags;
use App\Dto\WmsVector\WmsVectorAddDTO;
use App\Exception\CartesApiException;
use App\Exception\EntrepotApiException;
use App\Services\EntrepotApiService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\Routing\Annotation\Route;

#[Route(
'/api/datastores/{datastoreId}/stored_data/{storedDataId}/wmsvector',
name: 'cartesgouvfr_api_wmsvector_',
options: ['expose' => 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<string> $tablesNamesList
* @param array<UploadedFile> $files
*
* @return array<string,string>
*/
public function sendStyleFiles(string $datastoreId, string $storedDataId, array $tablesNamesList, array $files): array
{
/** @var array<string,string> 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;
}
}
76 changes: 76 additions & 0 deletions src/Dto/WmsVector/WmsVectorAddDTO.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace App\Dto\WmsVector;

use Symfony\Component\Validator\Constraints as Assert;

class WmsVectorAddDTO
{
public function __construct(
#[Assert\NotBlank(['message' => '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<string> */
#[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<string> */
#[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,
) {
}
}

0 comments on commit 374f948

Please sign in to comment.