Skip to content

Commit

Permalink
Add functionality to write extracted resource back to source FHIR server
Browse files Browse the repository at this point in the history
  • Loading branch information
fongsean committed Jun 21, 2024
1 parent 75b55a5 commit 7b4a20d
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 11 deletions.
13 changes: 12 additions & 1 deletion apps/smart-forms-app/src/features/playground/api/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/

import { HEADERS } from '../../../api/headers.ts';
import type { Questionnaire, StructureMap } from 'fhir/r4';
import type { Bundle, Questionnaire, StructureMap } from 'fhir/r4';
import * as FHIR from 'fhirclient';
import { FORMS_SERVER_URL } from '../../../globals.ts';

Expand Down Expand Up @@ -56,3 +56,14 @@ export async function fetchTargetStructureMap(

return null;
}

export function extractedResourceIsBatchBundle(
extractedResource: any

Check warning on line 61 in apps/smart-forms-app/src/features/playground/api/extract.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
): extractedResource is Bundle {
return (
!!extractedResource &&
!!extractedResource.resourceType &&
extractedResource.resourceType === 'Bundle' &&
(extractedResource.type === 'transaction' || extractedResource.type === 'batch')
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import React from 'react';
import { CircularProgress, Fade, IconButton, Tooltip } from '@mui/material';
import Typography from '@mui/material/Typography';
import Iconify from '../../../components/Iconify/Iconify.tsx';
import CloudUploadIcon from '@mui/icons-material/CloudUpload';
import { FORMS_SERVER_URL } from '../../../globals.ts';

interface ExtractForPlaygroundProps {
Expand Down Expand Up @@ -48,7 +48,7 @@ function ExtractButtonForPlayground(props: ExtractForPlaygroundProps) {
{isExtracting ? (
<CircularProgress size={20} color="inherit" sx={{ mb: 0.5 }} />
) : (
<Iconify icon="tabler:transform" />
<CloudUploadIcon />
)}
</IconButton>
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,20 @@ interface Props {
jsonString: string;
onJsonStringChange: (jsonString: string) => void;
buildingState: 'idle' | 'building' | 'built';
fhirServerUrl: string;
onBuildForm: (jsonString: string) => unknown;
onDestroyForm: () => unknown;
}

function JsonEditor(props: Props) {
const { jsonString, onJsonStringChange, buildingState, onBuildForm, onDestroyForm } = props;
const {
jsonString,
onJsonStringChange,
buildingState,
fhirServerUrl,
onBuildForm,
onDestroyForm
} = props;

const [view, setView] = useState<'editor' | 'storeState'>('editor');
const [selectedStore, setSelectedStore] = useState<StateStore>('questionnaireResponseStore');
Expand Down Expand Up @@ -132,7 +140,7 @@ function JsonEditor(props: Props) {
/>
) : (
<Box sx={{ height: '100%', overflow: 'auto' }}>
<StoreStateViewer selectedStore={selectedStore} />
<StoreStateViewer selectedStore={selectedStore} fhirServerUrl={fhirServerUrl} />
</Box>
)}
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ function Playground() {
jsonString={jsonString}
onJsonStringChange={(jsonString: string) => setJsonString(jsonString)}
buildingState={buildingState}
fhirServerUrl={fhirServerUrl}
onBuildForm={handleBuildQuestionnaireFromString}
onDestroyForm={handleDestroyForm}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ export type StateStore =

interface StoreStateViewerProps {
selectedStore: StateStore;
fhirServerUrl: string;
}

function StoreStateViewer(props: StoreStateViewerProps) {
const { selectedStore } = props;
const { selectedStore, fhirServerUrl } = props;

if (selectedStore === 'questionnaireStore') {
return <QuestionnaireStoreViewer />;
Expand All @@ -54,7 +55,7 @@ function StoreStateViewer(props: StoreStateViewerProps) {
}

if (selectedStore === 'extractedResource') {
return <ExtractedSectionViewer />;
return <ExtractedSectionViewer fhirServerUrl={fhirServerUrl} />;
}

return <Typography variant="h5">No store selected</Typography>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,58 @@ import { useState } from 'react';
import GenericStatePropertyPicker from './GenericStatePropertyPicker.tsx';
import GenericViewer from './GenericViewer.tsx';
import { useExtractOperationStore } from '../../stores/smartConfigStore.ts';
import { Box, Tooltip } from '@mui/material';
import { LoadingButton } from '@mui/lab';
import { useSnackbar } from 'notistack';
import { extractedResourceIsBatchBundle } from '../../api/extract.ts';
import { HEADERS } from '../../../../api/headers.ts';
import CloseSnackbar from '../../../../components/Snackbar/CloseSnackbar.tsx';

const extractedSectionPropertyNames: string[] = ['extracted'];

function ExtractedSectionViewer() {
interface ExtractedSectionViewerProps {
fhirServerUrl: string;
}

function ExtractedSectionViewer(props: ExtractedSectionViewerProps) {
const { fhirServerUrl } = props;
const [selectedProperty, setSelectedProperty] = useState('extracted');
const [showJsonTree, setShowJsonTree] = useState(false);
const [writingBack, setWritingBack] = useState(false);

const extractedResource = useExtractOperationStore.use.extractedResource();
const writeBackEnabled = extractedResourceIsBatchBundle(extractedResource);

const { enqueueSnackbar } = useSnackbar();

// Write back extracted resource
async function handleExtract() {
if (!writeBackEnabled) {
return;
}
setWritingBack(true);

const response = await fetch(fhirServerUrl, {
method: 'POST',
headers: { ...HEADERS, 'Content-Type': 'application/json;charset=utf-8' },
body: JSON.stringify(extractedResource)
});
setWritingBack(false);

if (!response.ok) {
enqueueSnackbar('Failed to write back resource', {
variant: 'error',
preventDuplicate: true,
action: <CloseSnackbar />
});
} else {
enqueueSnackbar(`Write back to ${fhirServerUrl} successful. See Network tab for response`, {
variant: 'success',
preventDuplicate: true,
action: <CloseSnackbar />
});
}
}

return (
<>
Expand All @@ -22,8 +66,25 @@ function ExtractedSectionViewer() {
propertyName={selectedProperty}
propertyObject={extractedResource}
showJsonTree={showJsonTree}
onToggleShowJsonTree={setShowJsonTree}
/>
onToggleShowJsonTree={setShowJsonTree}>
<Box display="flex" justifyContent="end">
<Tooltip
title={
writeBackEnabled
? `Write to source FHIR server ${fhirServerUrl} `
: 'No extracted resource to write back, or resource is not a batch/tranaction bundle.'
}>
<span>
<LoadingButton
loading={writingBack}
disabled={!writeBackEnabled}
onClick={handleExtract}>
Write back
</LoadingButton>
</span>
</Tooltip>
</Box>
</GenericViewer>
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import NotesIcon from '@mui/icons-material/Notes';
import AccountTreeIcon from '@mui/icons-material/AccountTree';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import DebugResponseView from '../../../renderer/components/RendererDebugFooter/DebugResponseView.tsx';
import { ReactNode } from 'react';

Check failure on line 6 in apps/smart-forms-app/src/features/playground/components/StoreStateViewers/GenericViewer.tsx

View workflow job for this annotation

GitHub Actions / Lint

All imports in the declaration are only used as types. Use `import type`

interface GenericViewerProps {
propertyName: string;
propertyObject: any;
showJsonTree: boolean;
onToggleShowJsonTree: (toggleShowJsonTree: boolean) => void;
children?: ReactNode;
}

function GenericViewer(props: GenericViewerProps) {
const { propertyName, propertyObject, showJsonTree, onToggleShowJsonTree } = props;
const { propertyName, propertyObject, showJsonTree, onToggleShowJsonTree, children } = props;

if (propertyName === null) {
return <Typography variant="h5">No property selected</Typography>;
Expand Down Expand Up @@ -57,6 +59,7 @@ function GenericViewer(props: GenericViewerProps) {
: 'Use text view for fast Ctrl+F debugging.'}
</Typography>
<DebugResponseView displayObject={propertyObject} showJsonTree={showJsonTree} />
{children}
</Box>
</Stack>
);
Expand Down

0 comments on commit 7b4a20d

Please sign in to comment.