Skip to content

Commit

Permalink
feat: pouvoir accéder aux raisons de l'échec d'une validation #583
Browse files Browse the repository at this point in the history
  • Loading branch information
cbrousseau1 committed Dec 17, 2024
1 parent 57db739 commit ed328c4
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 22 deletions.
7 changes: 7 additions & 0 deletions assets/@types/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,13 @@ export type StoredDataReport = {
processing_executions: StoredDataReportProcessingExecution[];
};

export type DeliveryReport = {
input_upload: Upload & {
file_tree: UploadTree;
checks: CheckDetailed[];
};
};

export type StoredDataReportProcessingExecution = ProcessingExecution & {
output: ProcessingExecutionOutputStoredDataDto;
logs?: CheckOrProcessingExecutionLogs;
Expand Down
9 changes: 9 additions & 0 deletions assets/entrepot/api/upload.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import SymfonyRouting from "../../modules/Routing";
import { jsonFetch } from "../../modules/jsonFetch";
import { DeliveryReport } from "../../@types/app";
import { Upload, UploadTree, UploadTypeEnum } from "../../@types/app";

const getList = (datastoreId: string, type?: UploadTypeEnum, otherOptions: RequestInit = {}) => {
Expand Down Expand Up @@ -61,6 +62,13 @@ const remove = (datastoreId: string, uploadId: string) => {
return jsonFetch<null>(url, { method: "DELETE" });
};

const getDeliveryReport = (datastoreId: string, uploadId: string, otherOptions: RequestInit = {}) => {
const url = SymfonyRouting.generate("cartesgouvfr_api_upload_get_delivery_report", { datastoreId, uploadId });
return jsonFetch<DeliveryReport>(url, {
...otherOptions,
});
};

const upload = {
getList,
add,
Expand All @@ -69,6 +77,7 @@ const upload = {
pingIntegrationProgress,
getFileTree,
remove,
getDeliveryReport,
};

export default upload;
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ const UnfinishedUploadList: FC<UnfinishedUploadListProps> = ({ datastoreId, uplo

<div className={fr.cx("fr-col")}>
<div className={fr.cx("fr-grid-row", "fr-grid-row--right", "fr-grid-row--middle")}>
<Button linkProps={routes.datastore_datasheet_upload_integration({ datastoreId, uploadId: upload._id }).link}>
{"Reprendre l’intégration"}
<Button className={fr.cx("fr-mr-2w")} linkProps={routes.datastore_delivery_details({ datastoreId, uploadDataId: upload._id }).link}>
{"Voir le rapport"}
</Button>
<Button iconId="fr-icon-delete-fill" priority="secondary">
{"Supprimer"}
</Button>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { fr } from "@codegouvfr/react-dsfr";
import Alert from "@codegouvfr/react-dsfr/Alert";
import Button from "@codegouvfr/react-dsfr/Button";
import Tabs from "@codegouvfr/react-dsfr/Tabs";
import { useQuery } from "@tanstack/react-query";
import { FC, useMemo } from "react";

import { DeliveryReport } from "../../../../@types/app";
import DatastoreLayout from "../../../../components/Layout/DatastoreLayout";
import LoadingIcon from "../../../../components/Utils/LoadingIcon";
import RQKeys from "../../../../modules/entrepot/RQKeys";
import { CartesApiException } from "../../../../modules/jsonFetch";
import { routes } from "../../../../router/router";
import api from "../../../api";
import DeliveryPreviewTab from "./PreviewTab/DeliveryPreviewTab";
import ReportTab from "./ReportTab/ReportTab";

type DeliveryDetailsProps = {
datastoreId: string;
uploadDataId: string;
};

const DeliveryDetails: FC<DeliveryDetailsProps> = ({ datastoreId, uploadDataId }) => {
const datastoreQuery = useQuery({
queryKey: RQKeys.datastore(datastoreId),
queryFn: ({ signal }) => api.datastore.get(datastoreId, { signal }),
staleTime: 3600000,
});

const reportQuery = useQuery<DeliveryReport, CartesApiException>({
queryKey: RQKeys.datastore_delivery_report(datastoreId, uploadDataId),
queryFn: ({ signal }) => api.upload.getDeliveryReport(datastoreId, uploadDataId, { signal }),
staleTime: 3600000,
});

const datasheetName = useMemo(() => reportQuery?.data?.input_upload?.tags?.datasheet_name, [reportQuery?.data?.input_upload?.tags?.datasheet_name]);

return (
<DatastoreLayout datastoreId={datastoreId} documentTitle={`Rapport de livraison ${reportQuery?.data?.input_upload?.name ?? ""}`}>
<div className={fr.cx("fr-grid-row", "fr-grid-row--middle")}>
{datasheetName ? (
<Button
iconId="fr-icon-arrow-left-s-line"
priority="tertiary no outline"
linkProps={routes.datastore_datasheet_view({ datastoreId, datasheetName, activeTab: "dataset" }).link}
title="Retour à la fiche de donnée"
size="large"
/>
) : (
<Button
iconId="fr-icon-arrow-left-s-line"
priority="tertiary no outline"
linkProps={routes.datasheet_list({ datastoreId }).link}
title="Retour à mes données"
size="large"
/>
)}
<h1 className={fr.cx("fr-m-0")}>
{"Rapport de livraison"}
{reportQuery.isLoading && <LoadingIcon className={fr.cx("fr-ml-2v")} largeIcon={true} />}
</h1>
</div>

<div className={fr.cx("fr-grid-row", "fr-grid-row--middle", "fr-mb-4w")}>
{reportQuery.isError && <Alert severity="error" closable title={reportQuery.error.message} onClose={reportQuery.refetch} />}
</div>
{reportQuery?.data?.input_upload?.name && (
<div className={fr.cx("fr-grid-row", "fr-grid-row--middle", "fr-mb-4w")}>
<h2>{reportQuery?.data?.input_upload?.name}</h2>
</div>
)}

{reportQuery.data && (
<div className={fr.cx("fr-grid-row")}>
<div className={fr.cx("fr-col")}>
<Tabs
tabs={[
{
label: "Aperçu de la donnée",
content: <DeliveryPreviewTab reportData={reportQuery.data} />,
},
{
label: "Rapport de génération",
content: <ReportTab datastoreName={datastoreQuery.data?.name} reportQuery={reportQuery} />,
},
]}
/>
</div>
</div>
)}
</DatastoreLayout>
);
};

export default DeliveryDetails;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { fr } from "@codegouvfr/react-dsfr";
import Accordion from "@codegouvfr/react-dsfr/Accordion";
import { FC } from "react";

import { DeliveryReport } from "../../../../../@types/app";
import { niceBytes } from "../../../../../utils";
import ReportStatusBadge from "../ReportTab/ReportStatusBadge";

type DeliveryPreviewTabProps = {
reportData: DeliveryReport;
};

const DeliveryPreviewTab: FC<DeliveryPreviewTabProps> = ({ reportData }) => {
const uploadData = reportData.input_upload;

return (
<Accordion titleAs="h2" label="Informations générales" defaultExpanded={true}>
<ul className={fr.cx("fr-raw-list")}>
<li>
<strong>Nom :</strong> {uploadData.name}
</li>
<li>
<strong>Identifiant technique :</strong> {uploadData._id}
</li>
<li>
<strong>Projection :</strong> {uploadData.srs}
</li>
<li>
<strong>Statut :</strong> {<ReportStatusBadge status="FAILURE" />}
</li>
<li>
<strong>Taille :</strong> {uploadData.size && niceBytes(uploadData.size.toString())}
</li>
<li>
<strong>Type :</strong> {uploadData.type}
</li>
</ul>
</Accordion>
);
};

export default DeliveryPreviewTab;
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Accordion from "@codegouvfr/react-dsfr/Accordion";
import { UseQueryResult } from "@tanstack/react-query";
import { FC } from "react";

import { StoredDataReport as ReportTab } from "../../../../../@types/app";
import { DeliveryReport, StoredDataReport as ReportTab } from "../../../../../@types/app";
import { CheckingExecutionDetailResponseDtoStatusEnum, ProcessingExecutionDetailResponseDtoStatusEnum } from "../../../../../@types/entrepot";
import { CartesApiException } from "../../../../../modules/jsonFetch";
import { niceBytes } from "../../../../../utils";
Expand All @@ -14,7 +14,7 @@ import UploadFileTree from "./UploadFileTree";

type ReportTabProps = {
datastoreName?: string;
reportQuery: UseQueryResult<ReportTab, CartesApiException>;
reportQuery: UseQueryResult<ReportTab | DeliveryReport, CartesApiException>;
};

const ReportTab: FC<ReportTabProps> = ({ datastoreName, reportQuery }) => {
Expand Down Expand Up @@ -65,24 +65,25 @@ const ReportTab: FC<ReportTabProps> = ({ datastoreName, reportQuery }) => {
))}
</Accordion>

{reportQuery?.data.processing_executions.map((procExec) => (
<Accordion
key={procExec._id}
titleAs="h3"
label={
<>
<p>{`${step++}. Traitement : ${procExec.processing.name}`}</p>
<ReportStatusBadge status={procExec.status} className={fr.cx("fr-ml-2w")} />
</>
}
defaultExpanded={[
ProcessingExecutionDetailResponseDtoStatusEnum.FAILURE,
ProcessingExecutionDetailResponseDtoStatusEnum.ABORTED,
].includes(procExec.status)}
>
<ProcessingExecutionReport datastoreName={datastoreName} processingExecution={procExec} />
</Accordion>
))}
{"processing_executions" in reportQuery.data &&
reportQuery?.data.processing_executions.map((procExec) => (
<Accordion
key={procExec._id}
titleAs="h3"
label={
<>
<p>{`${step++}. Traitement : ${procExec.processing.name}`}</p>
<ReportStatusBadge status={procExec.status} className={fr.cx("fr-ml-2w")} />
</>
}
defaultExpanded={[
ProcessingExecutionDetailResponseDtoStatusEnum.FAILURE,
ProcessingExecutionDetailResponseDtoStatusEnum.ABORTED,
].includes(procExec.status)}
>
<ProcessingExecutionReport datastoreName={datastoreName} processingExecution={procExec} />
</Accordion>
))}
</>
)
);
Expand Down
1 change: 1 addition & 0 deletions assets/modules/entrepot/RQKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const RQKeys = {
datastore_stored_data: (datastoreId: string, storedDataId: string): string[] => ["datastore", datastoreId, "stored_data", storedDataId],
datastore_stored_data_uses: (datastoreId: string, storedDataId: string): string[] => ["datastore", datastoreId, "stored_data", storedDataId, "uses"],
datastore_stored_data_report: (datastoreId: string, storedDataId: string): string[] => ["datastore", datastoreId, "stored_data", storedDataId, "report"],
datastore_delivery_report: (datastoreId: string, uploadDataId: string): string[] => ["datastore", datastoreId, "upload_data", uploadDataId, "report"],

datastore_datasheet_list: (datastoreId: string): string[] => ["datastore", datastoreId, "datasheet"],
datastore_datasheet: (datastoreId: string, dataName: string): string[] => ["datastore", datastoreId, "datasheet", dataName],
Expand Down
3 changes: 3 additions & 0 deletions assets/router/RouterRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const DatasheetUploadIntegrationPage = lazy(() => import("../entrepot/pages/data
const DatasheetView = lazy(() => import("../entrepot/pages/datasheet/DatasheetView/DatasheetView"));

const StoredDataDetails = lazy(() => import("../entrepot/pages/stored_data/StoredDataDetails/StoredDataDetails"));
const DeliveryReport = lazy(() => import("../entrepot/pages/stored_data/StoredDataDetails/DeliveryDetails"));

const DatastoreCreationForm = lazy(() => import("../entrepot/pages/datastore/DatastoreCreationForm"));
const Confirm = lazy(() => import("../entrepot/pages/datastore/Confirmation"));
Expand Down Expand Up @@ -157,6 +158,8 @@ const RouterRenderer: FC = () => {
return <DatasheetView datastoreId={route.params.datastoreId} datasheetName={route.params.datasheetName} />;
case "datastore_stored_data_details":
return <StoredDataDetails datastoreId={route.params.datastoreId} storedDataId={route.params.storedDataId} />;
case "datastore_delivery_details":
return <DeliveryReport datastoreId={route.params.datastoreId} uploadDataId={route.params.uploadDataId} />;
case "datastore_wfs_service_new":
return <WfsServiceForm datastoreId={route.params.datastoreId} vectorDbId={route.params.vectorDbId} />;
case "datastore_wfs_service_edit":
Expand Down
7 changes: 7 additions & 0 deletions assets/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ const routeDefs = {
},
(p) => `${appRoot}/entrepot/${p.datastoreId}/donnees/${p.storedDataId}/details`
),
datastore_delivery_details: defineRoute(
{
datastoreId: param.path.string,
uploadDataId: param.path.string,
},
(p) => `${appRoot}/entrepot/${p.datastoreId}/donnees/${p.uploadDataId}/rapport`
),

// Creer et publier un service WFS
datastore_wfs_service_new: defineRoute(
Expand Down
29 changes: 29 additions & 0 deletions src/Controller/Entrepot/UploadController.php
Original file line number Diff line number Diff line change
Expand Up @@ -308,4 +308,33 @@ public function delete(string $datastoreId, string $uploadId): JsonResponse
throw new CartesApiException($ex->getMessage(), JsonResponse::HTTP_INTERNAL_SERVER_ERROR);
}
}

#[Route('/{uploadId}/delivery-report', name: 'get_delivery_report', methods: ['GET'])]
public function getDeliveryReport(string $datastoreId, string $uploadId): JsonResponse
{
try {
// Récupération des détails de l'upload ayant échoué
$inputUpload = $this->uploadApiService->get($datastoreId, $uploadId);
$inputUpload['file_tree'] = $this->uploadApiService->getFileTree($datastoreId, $inputUpload['_id']);
$inputUpload['checks'] = [];
$uploadChecks = $this->uploadApiService->getCheckExecutions($datastoreId, $inputUpload['_id']);

foreach ($uploadChecks as &$checkType) {
foreach ($checkType as &$checkExecution) {
$checkExecution = array_merge($checkExecution, $this->uploadApiService->getCheckExecution($datastoreId, $checkExecution['_id']));
try {
$checkExecution['logs'] = $this->uploadApiService->getCheckExecutionLogs($datastoreId, $checkExecution['_id']);
} catch (ApiException $ex) {
}
$inputUpload['checks'][] = $checkExecution;
}
}

return $this->json([
'input_upload' => $inputUpload,
]);
} catch (ApiException $ex) {
throw new CartesApiException($ex->getMessage(), $ex->getStatusCode(), $ex->getDetails());
}
}
}

0 comments on commit ed328c4

Please sign in to comment.