Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: dépôt vecteur - échec à la vérification standard et vecteur #579 #606

Merged
merged 14 commits into from
Jan 8, 2025
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions assets/@types/app.ts
Original file line number Diff line number Diff line change
@@ -259,6 +259,13 @@ export type StoredDataReport = {
processing_executions: StoredDataReportProcessingExecution[];
};

export type DeliveryReport = {
cbrousseau1 marked this conversation as resolved.
Show resolved Hide resolved
input_upload: Upload & {
file_tree: UploadTree;
checks: CheckDetailed[];
};
};

export type StoredDataReportProcessingExecution = ProcessingExecution & {
output: ProcessingExecutionOutputStoredDataDto;
logs?: CheckOrProcessingExecutionLogs;
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 = {}) => {
@@ -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,
@@ -69,6 +77,7 @@ const upload = {
pingIntegrationProgress,
getFileTree,
remove,
getDeliveryReport,
};

export default upload;
Original file line number Diff line number Diff line change
@@ -205,6 +205,24 @@ const DatasheetUploadIntegrationDialog: FC<DatasheetUploadIntegrationDialogProps
</div>
)}

{integrationStatus === "at_least_one_failure" && uploadQuery.data?.tags.datasheet_name !== undefined && (
<div className={fr.cx("fr-grid-row")}>
<ButtonsGroup
buttons={[
{
children: "Consulter la fiche de données",
linkProps: routes.datastore_datasheet_view({
datastoreId,
datasheetName: uploadQuery.data?.tags.datasheet_name,
activeTab: DatasheetViewActiveTabEnum.Dataset,
}).link,
},
]}
inlineLayoutWhen="always"
/>
</div>
)}

{integrationStatus === "at_least_one_failure" && uploadQuery.data?.tags?.vectordb_id !== undefined && (
<div className={fr.cx("fr-grid-row")}>
<ButtonsGroup
Original file line number Diff line number Diff line change
@@ -39,7 +39,12 @@ const DatasetListTab: FC<DataListTabProps> = ({ datastoreId, datasheet }) => {
{unfinishedUploads && unfinishedUploads.length > 0 && (
<div className={fr.cx("fr-grid-row", "fr-grid-row--center", "fr-grid-row--middle")}>
<div className={fr.cx("fr-col")}>
<UnfinishedUploadList datastoreId={datastoreId} uploadList={unfinishedUploads} />
<UnfinishedUploadList
datastoreId={datastoreId}
uploadList={unfinishedUploads}
nbPublications={datasheet.nb_publications}
datasheet={datasheet}
/>
</div>
</div>
)}
Original file line number Diff line number Diff line change
@@ -2,15 +2,47 @@ import { fr } from "@codegouvfr/react-dsfr";
import Button from "@codegouvfr/react-dsfr/Button";
import { FC, memo } from "react";
import { symToStr } from "tsafe/symToStr";
import { useMutation, useQueryClient } from "@tanstack/react-query";

import RQKeys from "../../../../../modules/entrepot/RQKeys";
import api from "../../../../api";
import { type DatasheetDetailed } from "../../../../../@types/app";
import { routes } from "../../../../../router/router";
import { Upload } from "../../../../../@types/app";
import ReportStatusBadge from "../../../stored_data/StoredDataDetails/ReportTab/ReportStatusBadge";
import { deleteDeliveryConfirmModal } from "../DatasheetView";
import Wait from "../../../../../components/Utils/Wait";
import LoadingIcon from "../../../../../components/Utils/LoadingIcon";
import { useTranslation } from "../../../../../i18n/i18n";

type UnfinishedUploadListProps = {
datastoreId: string;
uploadList?: Upload[];
nbPublications: number;
datasheet: DatasheetDetailed;
};
const UnfinishedUploadList: FC<UnfinishedUploadListProps> = ({ datastoreId, uploadList }) => {

const UnfinishedUploadList: FC<UnfinishedUploadListProps> = ({ datastoreId, uploadList, nbPublications, datasheet }) => {
const { t } = useTranslation("DatastoreManageStorage");

const queryClient = useQueryClient();

const isLastUpload = (uploadList: Upload[]): boolean => {
return uploadList.length === 1 && nbPublications === 0;
};

const deleteUnfinishedUpload = useMutation({
mutationFn: (uploadId: string) => api.upload.remove(datastoreId, uploadId),
onSuccess(uploadId) {
queryClient.setQueryData(RQKeys.datastore_datasheet(datastoreId, datasheet.name), (datasheet: { upload_list: Upload[] }) => {
return {
...datasheet,
upload_list: datasheet.upload_list.filter((upload) => upload._id !== uploadId),
};
});
},
});

return (
<>
<div className={fr.cx("fr-grid-row")}>
@@ -20,21 +52,92 @@ const UnfinishedUploadList: FC<UnfinishedUploadListProps> = ({ datastoreId, uplo
</h5>
</div>

{uploadList?.map((upload) => (
<div key={upload._id} className={fr.cx("fr-grid-row", "fr-grid-row--middle", "fr-mt-2v")}>
<div className={fr.cx("fr-col")}>
<div className={fr.cx("fr-grid-row", "fr-grid-row--middle")}>{upload.name}</div>
</div>
{uploadList?.map((upload) => {
const integrationProgress = JSON.parse(upload.tags.integration_progress || "{}");
const steps = Object.entries(integrationProgress);
const failureCase = steps.some(([, status]) => status === "failed");

return (
<div key={upload._id} className={fr.cx("fr-grid-row", "fr-grid-row--middle", "fr-mt-2v")}>
<div className={fr.cx("fr-col")}>
<div className={fr.cx("fr-grid-row", "fr-grid-row--middle")}>
{upload.name}
{failureCase ? (
<ReportStatusBadge status="FAILURE" className={fr.cx("fr-ml-2w")} />
) : (
<ReportStatusBadge status="WAITING" className={fr.cx("fr-ml-2w")} />
)}
</div>
</div>

<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>
<div className={fr.cx("fr-col")}>
<div className={fr.cx("fr-grid-row", "fr-grid-row--right", "fr-grid-row--middle")}>
{failureCase ? (
<>
<Button
className={fr.cx("fr-mr-2w")}
linkProps={
routes.datastore_delivery_details({
datastoreId,
uploadDataId: upload._id,
datasheetName: upload.tags.datasheet_name,
}).link
}
>
{"Voir le rapport"}
</Button>
<Button
iconId="fr-icon-delete-fill"
priority="secondary"
onClick={() => {
if (isLastUpload(uploadList)) {
deleteDeliveryConfirmModal.open();
} else {
deleteUnfinishedUpload.mutate(upload._id);
}
}}
>
{"Supprimer"}
</Button>
</>
) : (
<>
<Button
className={fr.cx("fr-mr-2w")}
linkProps={routes.datastore_datasheet_upload_integration({ datastoreId, uploadId: upload._id }).link}
>
{"Reprendre l'intégration"}
</Button>
<Button
iconId="fr-icon-delete-fill"
priority="secondary"
onClick={() => {
if (isLastUpload(uploadList)) {
deleteDeliveryConfirmModal.open();
} else {
deleteUnfinishedUpload.mutate(upload._id);
}
}}
>
{"Supprimer"}
</Button>
</>
)}
</div>
</div>
</div>
);
})}
{deleteUnfinishedUpload.isPending && (
<Wait>
<div className={fr.cx("fr-container")}>
<div className={fr.cx("fr-grid-row", "fr-grid-row--middle")}>
<LoadingIcon className={fr.cx("fr-mr-2v")} />
<h6 className={fr.cx("fr-m-0")}>{t("storage.upload.deletion.in_progress")}</h6>
slafayIGN marked this conversation as resolved.
Show resolved Hide resolved
</div>
</div>
</div>
))}
</Wait>
)}
</>
);
};
25 changes: 25 additions & 0 deletions assets/entrepot/pages/datasheet/DatasheetView/DatasheetView.tsx
Original file line number Diff line number Diff line change
@@ -31,6 +31,11 @@ const deleteDataConfirmModal = createModal({
isOpenedByDefault: false,
});

export const deleteDeliveryConfirmModal = createModal({
id: "delete-delivery-confirm-modal",
isOpenedByDefault: false,
});

export enum DatasheetViewActiveTabEnum {
Metadata = "metadata",
Dataset = "dataset",
@@ -260,6 +265,26 @@ const DatasheetView: FC<DatasheetViewProps> = ({ datastoreId, datasheetName }) =
</deleteDataConfirmModal.Component>,
document.body
)}
{createPortal(
<deleteDeliveryConfirmModal.Component
title={`Voulez-vous supprimer la fiche de données ${datasheetName} ?`}
buttons={[
{
children: tCommon("no"),
doClosesModal: true,
priority: "secondary",
},
{
children: tCommon("yes"),
onClick: () => datasheetDeleteMutation.mutate(),
priority: "primary",
},
]}
>
<strong>En supprimant cette livraison, la fiche de données {datasheetName} sera supprimée.</strong>
</deleteDeliveryConfirmModal.Component>,
document.body
)}
</>
</DatastoreLayout>
);
cbrousseau1 marked this conversation as resolved.
Show resolved Hide resolved
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>
{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>
)}

<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 && (
<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
@@ -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";
@@ -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 }) => {
@@ -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>
))}
</>
)
);
3 changes: 3 additions & 0 deletions assets/i18n/Breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ export const { i18n } = declareComponentKeys<
| "upload"
| "datastore_datasheet_upload_integration"
| "datastore_stored_data_details"
| "datastore_delivery_details"
| "datastore_wfs_service_new"
| "datastore_wfs_service_edit"
| "datastore_wms_vector_service_new"
@@ -74,6 +75,7 @@ export const BreadcrumbFrTranslations: Translations<"fr">["Breadcrumb"] = {
upload: "Téléversement",
datastore_datasheet_upload_integration: "Intégration de données",
datastore_stored_data_details: "Détails d'une donnée stockée",
datastore_delivery_details: "Détails d'une livraison",
datastore_wfs_service_new: "Création d'un service WFS",
datastore_wfs_service_edit: "Modification d'un service WFS",
datastore_wms_vector_service_new: "Création d'un service WMS",
@@ -116,6 +118,7 @@ export const BreadcrumbEnTranslations: Translations<"en">["Breadcrumb"] = {
upload: "Upload",
datastore_datasheet_upload_integration: "Data integration",
datastore_stored_data_details: "Details of stored data",
datastore_delivery_details: "Details of delivery",
datastore_wfs_service_new: "Create a WFS service",
datastore_wfs_service_edit: "Modify WFS service",
datastore_wms_vector_service_new: "Create a WMS service",
1 change: 1 addition & 0 deletions assets/modules/entrepot/RQKeys.ts
Original file line number Diff line number Diff line change
@@ -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],
15 changes: 15 additions & 0 deletions assets/modules/entrepot/breadcrumbs.ts
Original file line number Diff line number Diff line change
@@ -161,6 +161,21 @@ const getBreadcrumb = (route: Route<typeof routes>, datastore?: Datastore): Brea
});
}
return { ...defaultProps, currentPageLabel: t("datastore_stored_data_details") };
case "datastore_delivery_details":
defaultProps.segments = [
...defaultProps.segments,
...[
{ label: t("dashboard_pro"), linkProps: routes.dashboard_pro().link },
{ label: datastore?.name, linkProps: routes.datasheet_list({ datastoreId: route.params.datastoreId }).link },
],
];
if ("datasheetName" in route.params && route.params.datasheetName) {
defaultProps.segments.push({
label: route.params.datasheetName,
linkProps: routes.datastore_datasheet_view({ datastoreId: route.params.datastoreId, datasheetName: route.params.datasheetName }).link,
});
}
return { ...defaultProps, currentPageLabel: t("datastore_delivery_details") };

case "datastore_wfs_service_new":
case "datastore_wfs_service_edit":
3 changes: 3 additions & 0 deletions assets/router/RouterRenderer.tsx
Original file line number Diff line number Diff line change
@@ -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"));
@@ -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":
8 changes: 8 additions & 0 deletions assets/router/router.ts
Original file line number Diff line number Diff line change
@@ -141,6 +141,14 @@ const routeDefs = {
},
(p) => `${appRoot}/entrepot/${p.datastoreId}/donnees/${p.storedDataId}/details`
),
datastore_delivery_details: defineRoute(
{
datastoreId: param.path.string,
uploadDataId: param.path.string,
datasheetName: param.query.optional.string,
},
(p) => `${appRoot}/entrepot/${p.datastoreId}/livraisons/${p.uploadDataId}/rapport`
),

// Creer et publier un service WFS
datastore_wfs_service_new: defineRoute(
29 changes: 29 additions & 0 deletions src/Controller/Entrepot/UploadController.php
Original file line number Diff line number Diff line change
@@ -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());
}
}
}