Skip to content
This repository has been archived by the owner on Jan 4, 2023. It is now read-only.

Commit

Permalink
Show errors 500 and 400 on preview (#639)
Browse files Browse the repository at this point in the history
* feat(river): use HAPI FHIR $validate in preview

* clean(settings): remove PYROG_API_URL

* clean(docker): use env variables in docker-compose

* fix(preview): update preview serializer

* feat(app): use OperationOutcomeIssue in preview errors

* deps: bump "requests" package to 2.26.0

* deps: add test dependency to "responses"

* fix(fhir_api): validate() does not raise on non-2XX

* tests(adapters): add tests for fhir_api adapter

* test(preview): test preview with validation errors

* refactor(tests): clean fhir_api adapter tests

* fix(tests): scope of api_client is function

* fix(fhir_api): differentiate _get and _post

* fix(tests): scope api_client for "module"

* chore(tests): add fhir_api marker

* fix(tests): remove unsued snapshots

* fix[app]: handle api errors in preview with notistack and not only alert component

* refactor(app): creation of an handling error function

* fix(app): add handling of server errors

* fix(app): merging v4 in branch

* fix(app): corrections from reviews and add functions for different error types

* fix(app): fix error display on validation

* fix(app): river schema reset

* fix(app): resolve reviews, change api generated to what's in v4

* fix(app): delete cast for color

* fix(app): reset to v4

* fix(app): reset ambr

Co-authored-by: simonvadee <simon.vadee@gmail.com>
  • Loading branch information
Klochette and simonvadee authored Oct 12, 2021
1 parent f5cb0b8 commit 39476cc
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 50 deletions.
157 changes: 110 additions & 47 deletions app/src/features/Preview/Preview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useCallback } from "react";

import { IResource } from "@ahryman40k/ts-fhir-types/lib/R4";
import { Icon } from "@blueprintjs/core";
Expand All @@ -17,7 +17,9 @@ import {
useMediaQuery,
} from "@material-ui/core";
import Alert, { Color } from "@material-ui/lab/Alert";
import { FetchBaseQueryError } from "@reduxjs/toolkit/dist/query";
import clsx from "clsx";
import { useSnackbar } from "notistack";
import { useTranslation } from "react-i18next";
import ReactJson from "react-json-view";
import { useParams } from "react-router-dom";
Expand All @@ -29,6 +31,10 @@ import {
useApiOwnersRetrieveQuery,
useApiPreviewCreateMutation,
} from "services/api/endpoints";
import {
apiValidationErrorFromResponse,
ApiValidationError,
} from "services/api/errors";
import {
ExplorationResponse,
OperationOutcomeIssue,
Expand Down Expand Up @@ -136,6 +142,7 @@ const getAlertSeverityFromOperationOutcomeIssue = (
const Preview = (): JSX.Element => {
const classes = useStyles();
const { t } = useTranslation();
const { enqueueSnackbar } = useSnackbar();
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
const { mappingId } = useParams<{
mappingId?: string;
Expand All @@ -144,20 +151,19 @@ const Preview = (): JSX.Element => {
const [exploration, setExploration] = useState<
ExplorationResponse | null | undefined
>(undefined);

const [alerts, setAlerts] = useState<
AlertOperationOutcomeIssue[] | undefined
>(undefined);

const [preview, setPreview] = useState<IResource | undefined>(undefined);

const handleAlertClose = (index: number) => {
if (alerts) {
const newAlerts = [...alerts];
newAlerts.splice(index, 1);
setAlerts(newAlerts);
}
};

const [preview, setPreview] = useState<IResource | undefined>(undefined);

const { data: mapping } = useApiResourcesRetrieveQuery(
{ id: mappingId ?? "" },
{ skip: !mappingId }
Expand All @@ -172,39 +178,71 @@ const Preview = (): JSX.Element => {

const [apiExploreCreate] = useApiExploreCreateMutation();

useEffect(() => {
if (mappingId && owner && exploration === undefined) {
setExploration(null);
const handleValidationError = useCallback(
({
errors,
errorStatus,
errorField,
}: {
errors?: ApiValidationError<unknown>;
errorStatus: number | "FETCH_ERROR" | "PARSING_ERROR" | "CUSTOM_ERROR";
errorField: string;
}) => {
if (errors) {
const errorEntries = Object.entries(errors);
errorEntries.forEach(([key, text]) => {
for (const error in text) {
enqueueSnackbar(
t<string>("catchValidationErrorPrompt", {
query: errorField,
errorStatus: errorStatus,
errorKey: key,
errorText: text[error],
}),
{
variant: "error",
}
);
}
});
}
},
[enqueueSnackbar, t]
);

const explore = async () => {
try {
const exploration = await apiExploreCreate({
explorationRequestRequest: {
owner: owner?.name ?? "",
table: mapping?.primary_key_table ?? "",
resource_id: mappingId,
},
}).unwrap();
setExploration(exploration);
} catch (e) {
setAlerts([
{
severity: "error",
diagnostics: e.error,
code: "internal",
},
]);
}
};
explore();
}
}, [
apiExploreCreate,
exploration,
mapping?.primary_key_table,
mappingId,
owner,
]);
const handleError = useCallback(
(error: FetchBaseQueryError) => {
const validationError = apiValidationErrorFromResponse(error);
switch (error.status) {
case "PARSING_ERROR":
enqueueSnackbar(
t<string>("catchValidationErrorPrompt", {
query: error.status,
errorStatus: error.originalStatus.toString(),
errorText: error.error,
}),
{ variant: "error" }
);
break;
case "FETCH_ERROR":
case "CUSTOM_ERROR":
error.data &&
enqueueSnackbar(`${error.status} : ${error.data as string}`, {
variant: "error",
});
break;
default:
validationError &&
handleValidationError({
errors: validationError,
errorStatus: error.status,
errorField: "Preview",
});
break;
}
},
[enqueueSnackbar, handleValidationError, t]
);

const [apiPreviewCreate] = useApiPreviewCreateMutation();

Expand All @@ -230,21 +268,46 @@ const Preview = (): JSX.Element => {
severity: getAlertSeverityFromOperationOutcomeIssue(error),
}))
);
} catch (e) {
setAlerts([
{
severity: "error",
diagnostics: e.error,
code: "internal",
},
]);
} catch (error) {
handleError(error as FetchBaseQueryError);
}
};
previewCreate();
}
}
};

// load the data for the table preview list
useEffect(() => {
if (mappingId && owner && exploration === undefined) {
setExploration(null);

const explore = async () => {
try {
const _exploration = await apiExploreCreate({
explorationRequestRequest: {
owner: owner?.name ?? "",
table: mapping?.primary_key_table ?? "",
resource_id: mappingId,
},
}).unwrap();
setExploration(_exploration);
} catch (error) {
handleError(error as FetchBaseQueryError);
}
};
explore();
}
}, [
apiExploreCreate,
exploration,
mapping?.primary_key_table,
mappingId,
owner,
handleError,
enqueueSnackbar,
]);

return (
<Container className={classes.container} maxWidth="xl">
<BackButton className={classes.button} />
Expand Down Expand Up @@ -318,8 +381,8 @@ const Preview = (): JSX.Element => {
) : (
<div className={classes.texts}>
<Typography>
{t("clickOnAFireIcon")}{" "}
<Icon icon={IconNames.FLAME} className={classes.iconFlame} />{" "}
{t("clickOnAFireIcon")}
<Icon icon={IconNames.FLAME} className={classes.iconFlame} />
{t("inOrderToPreview")}
</Typography>
</div>
Expand Down
7 changes: 4 additions & 3 deletions app/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@
"exportMapping": "Export mapping",
"ETLDashboard": "ETL Dashboard",
"newMapping": "New mapping",
"clickOnAFireIcon": "Click on a",
"inOrderToPreview": "icon in order to preview a line as a FHIR instance",
"clickOnAFireIcon": "Click on a ",
"inOrderToPreview": " icon in order to preview a line as a FHIR instance",
"back": "Back",
"useThisGroupIf": "Use this group if...",
"resources": "Resources",
Expand Down Expand Up @@ -148,5 +148,6 @@
"batchErrors": "⚠️ {{errors}} errors",
"batchSuccess": "✅ Success",
"batchCanceled": "🚫 Canceled",
"noAvailableResource": "No available resource"
"noAvailableResource": "No available resource",
"catchValidationErrorPrompt": "{{ query }} | Error Type : {{ errorStatus }} | {{errorKey}} : {{errorText}}"
}

0 comments on commit 39476cc

Please sign in to comment.