Skip to content

Commit

Permalink
update artifact types to include publishable fields
Browse files Browse the repository at this point in the history
  • Loading branch information
lmd59 committed Oct 15, 2024
1 parent 9e6e8a2 commit 5dd85e8
Show file tree
Hide file tree
Showing 17 changed files with 148 additions and 122 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Measure Repository

A prototype implementation of a [FHIR Measure Repository Service](http://hl7.org/fhir/us/cqfmeasures/measure-repository-service.html) and associated frontend application. The Measure Repository Service is a specific case of the more general [Artifact Repository Service](https://hl7.org/fhir/uv/crmi/artifact-repository-service.html#artifact-repository-service) that supports measures and libraries that exist in the CRMI Artifact Lifecycle as [CRMIShareableMeasure](https://hl7.org/fhir/uv/crmi/StructureDefinition-crmi-shareablemeasure.html) and [CRMIShareableLibrary](https://hl7.org/fhir/uv/crmi/StructureDefinition-crmi-shareablelibrary.html) artifacts. This repository is a monorepo that consists of:
A prototype implementation of a [FHIR Measure Repository Service](http://hl7.org/fhir/us/cqfmeasures/measure-repository-service.html) and associated frontend application. The Measure Repository Service is a specific case of the more general [Artifact Repository Service](https://hl7.org/fhir/uv/crmi/artifact-repository-service.html#artifact-repository-service) that supports measures and libraries that exist in the CRMI Artifact Lifecycle as artifacts implementing [CRMIShareableMeasure](https://hl7.org/fhir/uv/crmi/StructureDefinition-crmi-shareablemeasure.html)/[CRMIPublishableMeasure](https://hl7.org/fhir/uv/crmi/StructureDefinition-crmi-publishablemeasure.html) and [CRMIShareableLibrary](https://hl7.org/fhir/uv/crmi/StructureDefinition-crmi-shareablelibrary.html)/[CRMIPublishableLibrary](https://hl7.org/fhir/uv/crmi/StructureDefinition-crmi-publishablelibrary.html) . This repository is a monorepo that consists of:

- [Measure Repository Service](https://github.com/projecttacoma/measure-repository/blob/main/service/README.md)
- Implements portions of the [FHIR Measure Repository Service](http://hl7.org/fhir/us/cqfmeasures/measure-repository-service.html) specification
Expand Down
4 changes: 2 additions & 2 deletions app/src/pages/[resourceType]/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import { notifications } from '@mantine/notifications';
import React, { useEffect, useMemo, useState } from 'react';
import { GetServerSideProps, InferGetServerSidePropsType } from 'next';
import { CRMIShareableLibrary, FhirArtifact } from '@/util/types/fhir';
import { CRMIRepositoryLibrary, FhirArtifact } from '@/util/types/fhir';
import CQLRegex from '../../util/prismCQL';
import { Prism as PrismRenderer } from 'prism-react-renderer';
import parse from 'html-react-parser';
Expand Down Expand Up @@ -299,7 +299,7 @@ export default function ResourceIDPage({ jsonData }: InferGetServerSidePropsType
*/
function decode(link: string, jsonData: FhirArtifact) {
if (jsonData.resourceType === 'Measure') return null;
const encodedLanguage = (jsonData as CRMIShareableLibrary).content?.find(e => e.contentType === link)?.data;
const encodedLanguage = (jsonData as CRMIRepositoryLibrary).content?.find(e => e.contentType === link)?.data;
return encodedLanguage ? Buffer.from(encodedLanguage, 'base64').toString() : null;
}

Expand Down
4 changes: 2 additions & 2 deletions app/src/server/trpc/routers/draft.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CRMIShareableMeasure, FhirArtifact } from '@/util/types/fhir';
import { CRMIRepositoryMeasure, FhirArtifact } from '@/util/types/fhir';
import { z } from 'zod';
import { publicProcedure, router } from '../trpc';
import { Bundle, OperationOutcome } from 'fhir/r4';
Expand Down Expand Up @@ -71,7 +71,7 @@ export const draftRouter = router({
resource.title = input.values.title;
resource.description = input.values.description;
if (input.resourceType === 'Measure') {
(resource as CRMIShareableMeasure).library = input.values.library;
(resource as CRMIRepositoryMeasure).library = input.values.library;
}
const res = await fetch(`${process.env.MRS_SERVER}/${input.resourceType}/${input.id}`, {
method: 'PUT',
Expand Down
4 changes: 2 additions & 2 deletions app/src/server/trpc/routers/service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { publicProcedure, router } from '../trpc';
import { CRMIShareableLibrary, FhirArtifact } from '@/util/types/fhir';
import { CRMIRepositoryLibrary, FhirArtifact } from '@/util/types/fhir';
import { z } from 'zod';
import { TRPCError } from '@trpc/server';
import { Bundle, OperationOutcome } from 'fhir/r4';
Expand Down Expand Up @@ -58,7 +58,7 @@ export const serviceRouter = router({
message: resource?.issue[0]?.details?.text
});
}
return resource as CRMIShareableLibrary;
return resource as CRMIRepositoryLibrary;
}),

getArtifactById: publicProcedure
Expand Down
12 changes: 7 additions & 5 deletions app/src/util/authoringFixtures.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { CRMIShareableLibrary, CRMIShareableMeasure } from './types/fhir';
import { CRMIRepositoryLibrary, CRMIRepositoryMeasure } from './types/fhir';

export const MeasureSkeleton: CRMIShareableMeasure = {
export const MeasureSkeleton: CRMIRepositoryMeasure = {
id: 'tempMeasureId',
resourceType: 'Measure',
status: 'draft',
version: '0.0.1',
url: 'http://example.com',
title: 'Sample title',
description: 'Sample description'
description: 'Sample description',
date: '2025-01-01T00:00:00.000Z'
};

export const LibrarySkeleton: CRMIShareableLibrary = {
export const LibrarySkeleton: CRMIRepositoryLibrary = {
id: 'tempLibraryId',
resourceType: 'Library',
status: 'draft',
Expand All @@ -24,5 +25,6 @@ export const LibrarySkeleton: CRMIShareableLibrary = {
},
url: 'http://example.com',
title: 'Sample title',
description: 'Sample description'
description: 'Sample description',
date: '2025-01-01T00:00:00.000Z'
};
10 changes: 7 additions & 3 deletions app/src/util/types/fhir.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
export interface CRMIShareableMeasure extends fhir4.Measure {
// CRMIRepository* types implement both CRMIShareable and CRMIPublishable required fields
export interface CRMIRepositoryMeasure extends fhir4.Measure {
id: string;
url: string;
version: string;
title: string;
description: string;
date: string;
}

export interface CRMIShareableLibrary extends fhir4.Library {
export interface CRMIRepositoryLibrary extends fhir4.Library {
id: string;
url: string;
version: string;
title: string;
description: string;
date: string;
type: fhir4.CodeableConcept;
}

// type representing the resource types that are relevant to the Measure Repository Service
export type FhirArtifact = CRMIShareableMeasure | CRMIShareableLibrary;
export type FhirArtifact = CRMIRepositoryMeasure | CRMIRepositoryLibrary;
export type ArtifactResourceType = FhirArtifact['resourceType'];

/**
Expand Down
6 changes: 3 additions & 3 deletions service/scripts/dbSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { MongoError } from 'mongodb';
import { v4 as uuidv4 } from 'uuid';
import path from 'path';
import { addIsOwnedExtension, addLibraryIsOwned } from '../src/util/baseUtils';
import { CRMIShareableLibrary, FhirArtifact } from '../src/types/service-types';
import { CRMIRepositoryLibrary, FhirArtifact } from '../src/types/service-types';
dotenv.config();

const DB_URL = process.env.DATABASE_URL || 'mongodb://localhost:27017/measure-repository';
Expand Down Expand Up @@ -351,10 +351,10 @@ export function modifyEntriesForUpload(entries: fhir4.BundleEntry<fhir4.FhirReso
*/
async function insertFHIRModelInfoLibrary() {
const fhirModelInfo = fs.readFileSync('scripts/fixtures/Library-FHIR-ModelInfo.json', 'utf8');
const fhirModelInfoLibrary: CRMIShareableLibrary = JSON.parse(fhirModelInfo);
const fhirModelInfoLibrary: CRMIRepositoryLibrary = JSON.parse(fhirModelInfo);

const qicoreModelInfo = fs.readFileSync('scripts/fixtures/Library-QICore-ModelInfo.json', 'utf8');
const qicoreModelInfoLibrary: CRMIShareableLibrary = JSON.parse(qicoreModelInfo);
const qicoreModelInfoLibrary: CRMIRepositoryLibrary = JSON.parse(qicoreModelInfo);

const collection = Connection.db.collection<FhirArtifact>('Library');
console.log(`Inserting Library/${fhirModelInfoLibrary.id} into database`);
Expand Down
8 changes: 4 additions & 4 deletions service/src/db/dbOperations.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { loggers } from '@projecttacoma/node-fhir-server-core';
import { Filter, MongoServerError } from 'mongodb';
import { Connection } from './Connection';
import { ArtifactResourceType, CRMIShareableLibrary, FhirArtifact } from '../types/service-types';
import { ArtifactResourceType, CRMIRepositoryLibrary, FhirArtifact } from '../types/service-types';
import { BadRequestError } from '../util/errorUtils';

const logger = loggers.get('default');
Expand Down Expand Up @@ -78,8 +78,8 @@ export async function findResourceElementsWithQuery<T extends FhirArtifact>(
// if the resourceType is Library, then we want to include type in the projection
const projection: any =
resourceType === 'Library'
? { id: 1, status: 1, resourceType: 1, type: 1, url: 1, title: 1, description: 1, version: 1 }
: { id: 1, status: 1, resourceType: 1, url: 1, title: 1, description: 1, version: 1 };
? { id: 1, status: 1, resourceType: 1, type: 1, url: 1, title: 1, description: 1, version: 1, date: 1 }
: { id: 1, status: 1, resourceType: 1, url: 1, title: 1, description: 1, version: 1, date: 1 };

(query._elements as string[]).forEach(elem => {
projection[elem] = 1;
Expand Down Expand Up @@ -132,7 +132,7 @@ export async function findResourceCountWithQuery(query: Filter<any>, resourceTyp
/**
* searches the database for the data requirements Library resource of the desired artifact and parameters
*/
export async function findDataRequirementsWithQuery<T extends CRMIShareableLibrary>(query: Filter<any>) {
export async function findDataRequirementsWithQuery<T extends CRMIRepositoryLibrary>(query: Filter<any>) {
const collection = Connection.db.collection('Library');
return collection.findOne<T>({ _dataRequirements: query }, { projection: { _id: 0, _dataRequirements: 0, url: 0 } });
}
Expand Down
26 changes: 13 additions & 13 deletions service/src/services/LibraryService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import { v4 as uuidv4 } from 'uuid';
import { Calculator } from 'fqm-execution';
const logger = loggers.get('default');
import { Filter } from 'mongodb';
import { CRMIShareableLibrary, FhirLibraryWithDR } from '../types/service-types';
import { CRMIRepositoryLibrary, FhirLibraryWithDR } from '../types/service-types';
import {
createArtifactComment,
getChildren,
Expand All @@ -60,7 +60,7 @@ import {
* Implementation of a service for the `Library` resource
* The Service interface contains all possible functions
*/
export class LibraryService implements Service<CRMIShareableLibrary> {
export class LibraryService implements Service<CRMIRepositoryLibrary> {
/**
* result of sending a GET request to {BASE_URL}/4_0_1/Library?{QUERY}
* searches for all libraries that match the included query and returns a FHIR searchset Bundle
Expand All @@ -80,13 +80,13 @@ export class LibraryService implements Service<CRMIShareableLibrary> {
(parsedQuery._count && parsedQuery._count === '0')
) {
const count = await findResourceCountWithQuery(mongoQuery, 'Library');
return createSummarySearchsetBundle<CRMIShareableLibrary>(count);
return createSummarySearchsetBundle<CRMIRepositoryLibrary>(count);
}
// if the _elements parameter with a comma-separated string is included
// then return a searchset bundle that includes only those elements
// on those resource entries
else if (parsedQuery._elements) {
const result = await findResourceElementsWithQuery<CRMIShareableLibrary>(mongoQuery, 'Library');
const result = await findResourceElementsWithQuery<CRMIRepositoryLibrary>(mongoQuery, 'Library');
// add the SUBSETTED tag to the resources returned by the _elements parameter
result.data.map(e => {
if (e.meta) {
Expand Down Expand Up @@ -115,7 +115,7 @@ export class LibraryService implements Service<CRMIShareableLibrary> {
}
return bundle;
} else {
const result = await findResourcesWithQuery<CRMIShareableLibrary>(mongoQuery, 'Library');
const result = await findResourcesWithQuery<CRMIRepositoryLibrary>(mongoQuery, 'Library');
const bundle = createSearchsetBundle(result.data);
if (parsedQuery._count) {
bundle.link = createPaginationLinks(
Expand All @@ -138,7 +138,7 @@ export class LibraryService implements Service<CRMIShareableLibrary> {
*/
async searchById(args: RequestArgs) {
logger.info(`GET /Library/${args.id}`);
const result = await findResourceById<CRMIShareableLibrary>(args.id, 'Library');
const result = await findResourceById<CRMIRepositoryLibrary>(args.id, 'Library');
if (!result) {
throw new ResourceNotFoundError(`No resource found in collection: Library, with id: ${args.id}`);
}
Expand Down Expand Up @@ -178,7 +178,7 @@ export class LibraryService implements Service<CRMIShareableLibrary> {
if (resource.id !== args.id) {
throw new BadRequestError('Argument id must match request body id for PUT request');
}
const oldResource = (await findResourceById(resource.id, resource.resourceType)) as CRMIShareableLibrary | null;
const oldResource = (await findResourceById(resource.id, resource.resourceType)) as CRMIRepositoryLibrary | null;
// note: the distance between this database call and the update resource call, could cause a race condition
if (oldResource) {
checkFieldsForUpdate(resource, oldResource);
Expand Down Expand Up @@ -213,7 +213,7 @@ export class LibraryService implements Service<CRMIShareableLibrary> {
* requires id parameter
*/
async remove(args: RequestArgs) {
const library = await findResourceById<CRMIShareableLibrary>(args.id, 'Library');
const library = await findResourceById<CRMIRepositoryLibrary>(args.id, 'Library');
if (!library) {
throw new ResourceNotFoundError(`No resource found in collection: Library, with id: ${args.id}`);
}
Expand Down Expand Up @@ -259,7 +259,7 @@ export class LibraryService implements Service<CRMIShareableLibrary> {

const parsedParams = parseRequestSchema({ ...params, ...query }, DraftArgs);

const activeLibrary = await findResourceById<CRMIShareableLibrary>(parsedParams.id, 'Library');
const activeLibrary = await findResourceById<CRMIRepositoryLibrary>(parsedParams.id, 'Library');
if (!activeLibrary) {
throw new ResourceNotFoundError(`No resource found in collection: Library, with id: ${args.id}`);
}
Expand Down Expand Up @@ -305,7 +305,7 @@ export class LibraryService implements Service<CRMIShareableLibrary> {

const parsedParams = parseRequestSchema({ ...params, ...query }, CloneArgs);

const library = await findResourceById<CRMIShareableLibrary>(parsedParams.id, 'Library');
const library = await findResourceById<CRMIRepositoryLibrary>(parsedParams.id, 'Library');
if (!library) {
throw new ResourceNotFoundError(`No resource found in collection: Library, with id: ${parsedParams.id}`);
}
Expand Down Expand Up @@ -353,7 +353,7 @@ export class LibraryService implements Service<CRMIShareableLibrary> {

const parsedParams = parseRequestSchema({ ...params, ...query }, ApproveArgs);

const library = await findResourceById<CRMIShareableLibrary>(parsedParams.id, 'Library');
const library = await findResourceById<CRMIRepositoryLibrary>(parsedParams.id, 'Library');
if (!library) {
throw new ResourceNotFoundError(`No resource found in collection: Library, with id: ${parsedParams.id}`);
}
Expand Down Expand Up @@ -429,7 +429,7 @@ export class LibraryService implements Service<CRMIShareableLibrary> {

const parsedParams = parseRequestSchema({ ...params, ...query }, ReviewArgs);

const library = await findResourceById<CRMIShareableLibrary>(parsedParams.id, 'Library');
const library = await findResourceById<CRMIRepositoryLibrary>(parsedParams.id, 'Library');
if (!library) {
throw new ResourceNotFoundError(`No resource found in collection: Library, with id: ${parsedParams.id}`);
}
Expand Down Expand Up @@ -502,7 +502,7 @@ export class LibraryService implements Service<CRMIShareableLibrary> {
const query = extractIdentificationForQuery(args, params);
const parsedParams = parseRequestSchema({ ...params, ...query }, ReleaseArgs);

const library = await findResourceById<CRMIShareableLibrary>(parsedParams.id, 'Library');
const library = await findResourceById<CRMIRepositoryLibrary>(parsedParams.id, 'Library');
if (!library) {
throw new ResourceNotFoundError(`No resource found in collection: Library, with id: ${parsedParams.id}`);
}
Expand Down
Loading

0 comments on commit 5dd85e8

Please sign in to comment.