From 1f0fdbcaedd6072faa36373adcebf913128a400c Mon Sep 17 00:00:00 2001 From: lmd59 Date: Tue, 10 Dec 2024 09:11:25 -0500 Subject: [PATCH] Pub auth frontend (#117) * Adjust capability statement * Disable authoring functionality when using publishable env --- app/src/components/ResourceInfoCard.tsx | 30 +- app/src/components/SearchComponent.tsx | 2 +- app/src/pages/[resourceType]/[id].tsx | 56 +-- app/src/pages/_app.tsx | 27 +- .../pages/authoring/[resourceType]/[id].tsx | 11 +- .../pages/authoring/[resourceType]/index.tsx | 11 +- app/src/pages/authoring/index.tsx | 9 + app/src/pages/index.tsx | 13 +- app/src/server/trpc/routers/service.ts | 10 + ...blishableCapabilityStatementResources.json | 327 ++++++++++++++++++ service/src/config/serverConfig.ts | 10 +- 11 files changed, 449 insertions(+), 57 deletions(-) create mode 100644 service/src/config/publishableCapabilityStatementResources.json diff --git a/app/src/components/ResourceInfoCard.tsx b/app/src/components/ResourceInfoCard.tsx index 2813c6e9..893f99f9 100644 --- a/app/src/components/ResourceInfoCard.tsx +++ b/app/src/components/ResourceInfoCard.tsx @@ -39,6 +39,7 @@ export default function ResourceInfoCard({ resourceInfo, authoring }: ResourceIn const utils = trpc.useUtils(); const [isDeleteConfirmationModalOpen, setIsDeleteConfirmationModalOpen] = useState(false); const [isCloneConfirmationModalOpen, setIsCloneConfirmationModalOpen] = useState(false); + const authoringEnvironment = trpc.service.getAuthoring.useQuery(); const successNotification = (resourceType: string, childArtifact: boolean, action: string, idOrUrl?: string) => { let message; @@ -180,19 +181,24 @@ export default function ResourceInfoCard({ resourceInfo, authoring }: ResourceIn - - - - - - - + {authoringEnvironment.data ? ( + + + + + + + + ) : ( + <> + )} {authoring && + authoringEnvironment && (resourceInfo.isChild ? ( diff --git a/app/src/components/SearchComponent.tsx b/app/src/components/SearchComponent.tsx index fa5f286a..4ff06a7e 100644 --- a/app/src/components/SearchComponent.tsx +++ b/app/src/components/SearchComponent.tsx @@ -115,7 +115,7 @@ export default function SearchComponent({ resourceType }: SearchComponentProps) requestParams.push({ name: 'version', value: version }); } const query = requestParams.filter(si => si.value !== '').map(si => si.name + '=' + si.value); - return query.length !== 0 ? `?${query.join('&')}` : ''; + return query.length !== 0 ? `?status=active&${query.join('&')}` : '?status=active'; }; return ( diff --git a/app/src/pages/[resourceType]/[id].tsx b/app/src/pages/[resourceType]/[id].tsx index 9776dfd3..3898dedf 100644 --- a/app/src/pages/[resourceType]/[id].tsx +++ b/app/src/pages/[resourceType]/[id].tsx @@ -50,6 +50,7 @@ export default function ResourceIDPage({ jsonData }: InferGetServerSidePropsType const ctx = trpc.useContext(); const router = useRouter(); + const authoring = trpc.service.getAuthoring.useQuery(); // Overwrite Prism with our custom Prism that includes CQL as a language useEffect(() => { @@ -150,31 +151,36 @@ export default function ResourceIDPage({ jsonData }: InferGetServerSidePropsType
- - - {jsonData.resourceType}/{jsonData.id} - - {!!jsonData?.extension?.find( - ext => ext.url === 'http://hl7.org/fhir/StructureDefinition/artifact-isOwned' && ext.valueBoolean === true - ) ? ( - - - - - - ) : ( - - )} - + {authoring.data ? ( + + + {jsonData.resourceType}/{jsonData.id} + + {!!jsonData?.extension?.find( + ext => + ext.url === 'http://hl7.org/fhir/StructureDefinition/artifact-isOwned' && ext.valueBoolean === true + ) ? ( + + + + + + ) : ( + + )} + + ) : ( + <> + )}
diff --git a/app/src/pages/_app.tsx b/app/src/pages/_app.tsx index acf0aefb..e3390d57 100644 --- a/app/src/pages/_app.tsx +++ b/app/src/pages/_app.tsx @@ -41,6 +41,7 @@ const useStyles = createStyles(theme => ({ function App({ Component, pageProps }: AppProps) { const router = useRouter(); const { classes } = useStyles(); + const authoring = trpc.service.getAuthoring.useQuery(); return ( <> @@ -139,17 +140,21 @@ function App({ Component, pageProps }: AppProps) { Repository - - - Authoring - - + {authoring.data ? ( + + + Authoring + + + ) : ( + <> + )} } diff --git a/app/src/pages/authoring/[resourceType]/[id].tsx b/app/src/pages/authoring/[resourceType]/[id].tsx index a46813be..4860c0d2 100644 --- a/app/src/pages/authoring/[resourceType]/[id].tsx +++ b/app/src/pages/authoring/[resourceType]/[id].tsx @@ -1,5 +1,5 @@ import { trpc } from '@/util/trpc'; -import { Button, Center, Divider, Grid, Group, Paper, Select, Stack, Text, Tooltip } from '@mantine/core'; +import { Button, Center, Divider, Grid, Group, Paper, Select, Stack, Text, Title, Tooltip } from '@mantine/core'; import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import { Prism } from '@mantine/prism'; @@ -100,6 +100,15 @@ export default function ResourceAuthoringPage() { } }, [resource]); + const authoring = trpc.service.getAuthoring.useQuery(); + if (!authoring.data) { + return ( +
+ Authoring Unavailable +
+ ); + } + const resourceUpdate = trpc.draft.updateDraft.useMutation({ onSuccess: () => { notifications.show({ diff --git a/app/src/pages/authoring/[resourceType]/index.tsx b/app/src/pages/authoring/[resourceType]/index.tsx index 267c42a7..91730ce6 100644 --- a/app/src/pages/authoring/[resourceType]/index.tsx +++ b/app/src/pages/authoring/[resourceType]/index.tsx @@ -1,5 +1,5 @@ import { trpc } from '@/util/trpc'; -import { Center, Text, Divider } from '@mantine/core'; +import { Center, Text, Divider, Title } from '@mantine/core'; import { useRouter } from 'next/router'; import { ArtifactResourceType, ResourceInfo } from '@/util/types/fhir'; import ResourceCards from '@/components/ResourceCards'; @@ -16,6 +16,15 @@ export default function ResourceAuthoringPage() { return (artifacts ?? []).map(a => extractResourceInfo(a)); }, [artifacts]); + const authoring = trpc.service.getAuthoring.useQuery(); + if (!authoring.data) { + return ( +
+ Authoring Unavailable +
+ ); + } + return (
diff --git a/app/src/pages/authoring/index.tsx b/app/src/pages/authoring/index.tsx index 4627118a..1607b123 100644 --- a/app/src/pages/authoring/index.tsx +++ b/app/src/pages/authoring/index.tsx @@ -38,6 +38,15 @@ export default function AuthoringPage() { const { classes } = useStyles(); const router = useRouter(); + const authoring = trpc.service.getAuthoring.useQuery(); + if (!authoring.data) { + return ( +
+ Authoring Unavailable +
+ ); + } + const successNotification = ( resourceType: string, createdFromArtifact: boolean, diff --git a/app/src/pages/index.tsx b/app/src/pages/index.tsx index ddd21902..db890979 100644 --- a/app/src/pages/index.tsx +++ b/app/src/pages/index.tsx @@ -58,7 +58,7 @@ export default function Home({ FHIR Measure Repository Service {' '} - with Measure and Library authoring capabilities. See the{' '} + with Measure and Library management capabilities. See the{' '} Measure Repository README {' '} @@ -70,7 +70,16 @@ export default function Home({ {`${serviceUri}/metadata`}
- Service Capabilities: + + {!capabilityStatement + ? '' + : capabilityStatement.instantiates?.includes( + 'http://hl7.org/fhir/us/cqfmeasures/CapabilityStatement/authoring-measure-repository' + ) + ? 'Authoring ' + : 'Publishable '} + Service Capabilities: +
{renderCapabilityTable()}
diff --git a/app/src/server/trpc/routers/service.ts b/app/src/server/trpc/routers/service.ts index ed1f0bd7..f2cb426d 100644 --- a/app/src/server/trpc/routers/service.ts +++ b/app/src/server/trpc/routers/service.ts @@ -13,6 +13,16 @@ export const serviceRouter = router({ return process.env.PUBLIC_MRS_SERVER; }), + getAuthoring: publicProcedure.query(async () => { + // get authoring environment based on capability statement + const res = await fetch(`${process.env.MRS_SERVER}/metadata`); + const capabilityStatement = res.status === 200 ? ((await res.json()) as fhir4.CapabilityStatement) : null; + //defaults to publishable if capability statement cannot be resolved + return !!capabilityStatement?.instantiates?.includes( + 'http://hl7.org/fhir/us/cqfmeasures/CapabilityStatement/authoring-measure-repository' + ); + }), + getArtifactCounts: publicProcedure.query(async () => { const [measureBundle, libraryBundle] = await Promise.all([ fetch(`${process.env.MRS_SERVER}/Measure?_summary=count&status=active`), diff --git a/service/src/config/publishableCapabilityStatementResources.json b/service/src/config/publishableCapabilityStatementResources.json new file mode 100644 index 00000000..17f6ab96 --- /dev/null +++ b/service/src/config/publishableCapabilityStatementResources.json @@ -0,0 +1,327 @@ +{ + "mode": "server", + "resource": [ + { + "type": "Library", + "profile": "http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-shareablelibrary", + "supportedProfile": [ + "http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-computablelibrary", + "http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-publishablelibrary", + "http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-executablelibrary", + "http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-moduledefinitionlibrary", + "http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-manifestlibrary" + ], + "interaction": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "code": "read", + "documentation": "Read allows clients to get the definitions and details of repository libraries" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "code": "search-type", + "documentation": "Search allows clients to search for repository libraries" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "code": "create", + "documentation": "Create allows authoring workflows to post new libraries in _draft_ (**submit**) or _active_ (**publish**) status." + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "code": "update", + "documentation": "Update allows authoring workflows to update existing libraries in _draft_ (**revise**) status, add comments to existing libraries (**review** and **approve**), and **release** or **retire** a library." + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "code": "delete", + "documentation": "Delete allows authoring workflows to **withdraw** _draft_ libraries or **archive** _retired_ libraries." + } + ], + "searchParam": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "url", + "definition": "http://hl7.org/fhir/SearchParameter/Library-url", + "type": "uri" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "version", + "definition": "http://hl7.org/fhir/SearchParameter/Library-version", + "type": "string" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "identifier", + "definition": "http://hl7.org/fhir/SearchParameter/Library-identifier", + "type": "token" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "name", + "definition": "http://hl7.org/fhir/SearchParameter/Library-name", + "type": "string" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "title", + "definition": "http://hl7.org/fhir/SearchParameter/Library-title", + "type": "string" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "description", + "definition": "http://hl7.org/fhir/SearchParameter/Library-description", + "type": "string" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "status", + "definition": "http://hl7.org/fhir/SearchParameter/Library-status", + "type": "token" + } + ], + "operation": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "cqfm-package", + "definition": "http://hl7.org/fhir/us/cqfmeasures/OperationDefinition/cqfm-package" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "data-requirements", + "definition": "https://hl7.org/fhir/us/cqfmeasures/OperationDefinition/Library-data-requirements" + } + ] + }, + { + "type": "Measure", + "profile": "http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-shareablemeasure", + "supportedProfile": ["http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-publishablemeasure"], + "interaction": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "code": "read", + "documentation": "Read allows clients to get the definitions and details of repository measures" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "code": "search-type", + "documentation": "Search allows clients to search for repository measures" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "code": "create", + "documentation": "Create allows authoring workflows to post new measures in _draft_ (**submit**) or _active_ (**publish**) status." + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "code": "update", + "documentation": "Update allows authoring workflows to update existing measures in _draft_ (**revise**) status, add comments to existing measures (**review** and **approve**), and **release** or **retire** a measure." + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "code": "delete", + "documentation": "Delete allows authoring workflows to **withdraw** _draft_ measures or **archive** _retired_ measures." + } + ], + "searchParam": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "url", + "definition": "http://hl7.org/fhir/SearchParameter/Measure-url", + "type": "uri" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "version", + "definition": "http://hl7.org/fhir/SearchParameter/Measure-version", + "type": "string" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "identifier", + "definition": "http://hl7.org/fhir/SearchParameter/Measure-identifier", + "type": "token" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "name", + "definition": "http://hl7.org/fhir/SearchParameter/Measure-name", + "type": "string" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "title", + "definition": "http://hl7.org/fhir/SearchParameter/Measure-title", + "type": "string" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "description", + "definition": "http://hl7.org/fhir/SearchParameter/Measure-description", + "type": "string" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "status", + "definition": "http://hl7.org/fhir/SearchParameter/Measure-status", + "type": "token" + } + ], + "operation": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "cqfm-package", + "definition": "http://hl7.org/fhir/us/cqfmeasures/OperationDefinition/cqfm-package" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation", + "valueCode": "SHALL" + } + ], + "name": "data-requirements", + "definition": "http://hl7.org/fhir/us/cqfmeasures/OperationDefinition/Measure-data-requirements" + } + ] + } + ] +} diff --git a/service/src/config/serverConfig.ts b/service/src/config/serverConfig.ts index 95fe6507..377b71f0 100644 --- a/service/src/config/serverConfig.ts +++ b/service/src/config/serverConfig.ts @@ -1,6 +1,7 @@ import { constants, ServerConfig, resolveSchema } from '@projecttacoma/node-fhir-server-core'; import { MeasureService, LibraryService } from '../services'; import capabilityStatementResources from './capabilityStatementResources.json'; +import publishableCapabilityStatementResources from './publishableCapabilityStatementResources.json'; const customCapabilityStatement = (): fhir4.CapabilityStatement => { const base_version = constants.VERSIONS['4_0_1']; @@ -12,12 +13,13 @@ const customCapabilityStatement = (): fhir4.CapabilityStatement => { title: 'FHIR Measure Repository Service Capability Statement', status: 'active', // date last modified - date: new Date(2023, 0, 31), + date: new Date(2024, 10, 25), publisher: 'The MITRE Corporation', instantiates: [ 'http://hl7.org/fhir/us/cqfmeasures/CapabilityStatement/shareable-measure-repository', - 'http://hl7.org/fhir/us/cqfmeasures/CapabilityStatement/publishable-measure-repository', - 'http://hl7.org/fhir/us/cqfmeasures/CapabilityStatement/authoring-measure-repository' + process.env.AUTHORING === 'true' + ? 'http://hl7.org/fhir/us/cqfmeasures/CapabilityStatement/authoring-measure-repository' + : 'http://hl7.org/fhir/us/cqfmeasures/CapabilityStatement/publishable-measure-repository' ], kind: 'instance', implementation: { @@ -28,7 +30,7 @@ const customCapabilityStatement = (): fhir4.CapabilityStatement => { // NOTE: the definitions for authoring measure repository operations // are not FHIR OperationDefinitions, and we should update the JSON // when FHIR OperationDefinitions are available - rest: [capabilityStatementResources] + rest: [process.env.AUTHORING === 'true' ? capabilityStatementResources : publishableCapabilityStatementResources] }); };