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

MAT-7666: return additional information on the ResourceIdentifier for categorization, profile, etc #250

Merged
merged 2 commits into from
Oct 11, 2024
Merged
Changes from all 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
Original file line number Diff line number Diff line change
@@ -10,6 +10,11 @@ public final class UriConstants {
public static final String LIBRARY_SYSTEM_TYPE_URI =
"http://terminology.hl7.org/CodeSystem/library-type";

public static final class FhirStructureDefinitions {
public static final String CATEGORY_URI =
"http://hl7.org/fhir/StructureDefinition/structuredefinition-category";
}

public static final class CqfMeasures {
public static final String EFFECTIVE_DATA_REQUIREMENT_URL =
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-effectiveDataRequirements";
Original file line number Diff line number Diff line change
@@ -12,4 +12,7 @@
public class ResourceIdentifier {
private String id;
private String title;
private String type;
private String category;
private String profile;
}
Original file line number Diff line number Diff line change
@@ -3,12 +3,14 @@
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.StrictErrorHandler;
import gov.cms.madie.madiefhirservice.constants.UriConstants;
import gov.cms.madie.madiefhirservice.dto.ResourceIdentifier;
import gov.cms.madie.madiefhirservice.dto.StructureDefinitionDto;
import gov.cms.madie.madiefhirservice.exceptions.ResourceNotFoundException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.springframework.stereotype.Service;

@@ -52,8 +54,8 @@ public StructureDefinitionDto getStructureDefinitionById(String structureDefinit
}

/**
* Return the ID and title of all structure definitions that start with QICore and have a kind of
* "resource"
* Return the ID, title, profile, category and type of all structure definitions that start with
* QICore and have a kind of "resource"
*
* @return list of ResourceIdentifier, comprised of ID and title of the structure definitions
*/
@@ -65,10 +67,40 @@ public List<ResourceIdentifier> getAllResources() {
"resource".equals(((StructureDefinition) resource).getKind().toCode())
&& resource.getIdElement().getIdPart().startsWith("qicore"))
.map(
(resource) ->
new ResourceIdentifier(
resource.getIdElement().getIdPart(),
((StructureDefinition) resource).getTitle()))
(resource) -> {
StructureDefinition structureDefinition = (StructureDefinition) resource;
return ResourceIdentifier.builder()
.id(resource.getIdElement().getIdPart())
.title(structureDefinition.getTitle())
.type(structureDefinition.getType())
.category(getCategoryByType(structureDefinition.getType()))
// Todo: update profile URL if this method changes to return more than just
// QI-Core resources
.profile(structureDefinition.getUrl())
.build();
})
.toList();
}

/**
* Returns the FHIR categorization of the provided type by loading the StructureDefinition with an
* ID matching the provided type, and inspecting the Category extension.
*
* @param type base Type of the resource
* @return FHIR categorization, including top-level and sub-category, of the provided
*/
public String getCategoryByType(String type) {
Extension extension =
Objects.requireNonNull(validationSupportChainQiCore600.fetchAllStructureDefinitions())
.stream()
.filter(resource -> type.equals(resource.getIdElement().getIdPart()))
.map(resource -> (StructureDefinition) resource)
.findFirst()
.map(
structureDefinition ->
structureDefinition.getExtensionByUrl(
UriConstants.FhirStructureDefinitions.CATEGORY_URI))
.orElse(null);
return extension == null ? null : extension.getValueAsPrimitive().getValueAsString();
}
}
Original file line number Diff line number Diff line change
@@ -2,9 +2,12 @@

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import gov.cms.madie.madiefhirservice.constants.UriConstants;
import gov.cms.madie.madiefhirservice.dto.ResourceIdentifier;
import gov.cms.madie.madiefhirservice.dto.StructureDefinitionDto;
import gov.cms.madie.madiefhirservice.exceptions.ResourceNotFoundException;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -16,6 +19,7 @@
import java.util.List;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -131,17 +135,34 @@ void testGetAllResourcesReturnsOnlyQiCoreResources() {
StructureDefinition def1 = new StructureDefinition();
def1.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def1.setTitle("QICore Patient");
def1.setType("Patient");
def1.setId("qicore-patient");
def1.setUrl("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient");
StructureDefinition def2 = new StructureDefinition();
def2.setKind(StructureDefinition.StructureDefinitionKind.COMPLEXTYPE);
def2.setTitle("QI-Core Key Element Extension");
def2.setType("Extension");
def2.setId("qicore-keyelement");
def2.setUrl("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-keyelement");
StructureDefinition def3 = new StructureDefinition();
def3.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def3.setTitle("US Core Practitioner Profile");
def3.setType("Practitioner");
def3.setId("us-core-practitioner");
def3.setUrl("http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner");
StructureDefinition def4 = new StructureDefinition();
def4.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def4.setTitle(null);
def4.setType("Patient");
def4.setId("Patient");
def4.setUrl("http://hl7.org/fhir/StructureDefinition/Patient");
def4.setExtension(
List.of(
new Extension(
UriConstants.FhirStructureDefinitions.CATEGORY_URI,
new StringType("Base.Individuals"))));
when(validationSupportChainQiCore600.fetchAllStructureDefinitions())
.thenReturn(List.of(def1, def2, def3));
.thenReturn(List.of(def1, def2, def3, def4));

// when
List<ResourceIdentifier> output = structureDefinitionService.getAllResources();
@@ -150,5 +171,170 @@ void testGetAllResourcesReturnsOnlyQiCoreResources() {
assertThat(output, is(notNullValue()));
assertThat(output.size(), is(equalTo(1)));
assertThat(output.get(0).getId(), is(equalTo("qicore-patient")));
assertThat(output.get(0).getTitle(), is(equalTo("QICore Patient")));
assertThat(output.get(0).getType(), is(equalTo("Patient")));
assertThat(output.get(0).getCategory(), is(equalTo("Base.Individuals")));
assertThat(
output.get(0).getProfile(),
is(equalTo("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient")));
}

@Test
void testGetCategoryByTypeHandlesUnknownType() {
// given
StructureDefinition def1 = new StructureDefinition();
def1.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def1.setTitle("QICore Patient");
def1.setType("Patient");
def1.setId("qicore-patient");
def1.setUrl("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient");
StructureDefinition def2 = new StructureDefinition();
def2.setKind(StructureDefinition.StructureDefinitionKind.COMPLEXTYPE);
def2.setTitle("QI-Core Key Element Extension");
def2.setType("Extension");
def2.setId("qicore-keyelement");
def2.setUrl("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-keyelement");
StructureDefinition def3 = new StructureDefinition();
def3.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def3.setTitle("US Core Practitioner Profile");
def3.setType("Practitioner");
def3.setId("us-core-practitioner");
def3.setUrl("http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner");
StructureDefinition def4 = new StructureDefinition();
def4.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def4.setTitle(null);
def4.setType("Patient");
def4.setId("Patient");
def4.setUrl("http://hl7.org/fhir/StructureDefinition/Patient");
def4.setExtension(
List.of(
new Extension(
UriConstants.FhirStructureDefinitions.CATEGORY_URI,
new StringType("Base.Individuals"))));
when(validationSupportChainQiCore600.fetchAllStructureDefinitions())
.thenReturn(List.of(def1, def2, def3, def4));

// when
String output = structureDefinitionService.getCategoryByType("BLAHBLAH");

// then
assertThat(output, is(nullValue()));
}

@Test
void testGetCategoryByTypeHandlesTypeWithNoExtensions() {
// given
StructureDefinition def1 = new StructureDefinition();
def1.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def1.setTitle("QICore Patient");
def1.setType("Patient");
def1.setId("qicore-patient");
def1.setUrl("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient");
StructureDefinition def2 = new StructureDefinition();
def2.setKind(StructureDefinition.StructureDefinitionKind.COMPLEXTYPE);
def2.setTitle("QI-Core Key Element Extension");
def2.setType("Extension");
def2.setId("qicore-keyelement");
def2.setUrl("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-keyelement");
StructureDefinition def3 = new StructureDefinition();
def3.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def3.setTitle("US Core Practitioner Profile");
def3.setType("Practitioner");
def3.setId("us-core-practitioner");
def3.setUrl("http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner");
StructureDefinition def4 = new StructureDefinition();
def4.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def4.setTitle(null);
def4.setType("Patient");
def4.setId("Patient");
def4.setUrl("http://hl7.org/fhir/StructureDefinition/Patient");
when(validationSupportChainQiCore600.fetchAllStructureDefinitions())
.thenReturn(List.of(def1, def2, def3, def4));

// when
String output = structureDefinitionService.getCategoryByType("Patient");

// then
assertThat(output, is(nullValue()));
}

@Test
void testGetCategoryByTypeHandlesTypeWithNoCategoryExtension() {
// given
StructureDefinition def1 = new StructureDefinition();
def1.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def1.setTitle("QICore Patient");
def1.setType("Patient");
def1.setId("qicore-patient");
def1.setUrl("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient");
StructureDefinition def2 = new StructureDefinition();
def2.setKind(StructureDefinition.StructureDefinitionKind.COMPLEXTYPE);
def2.setTitle("QI-Core Key Element Extension");
def2.setType("Extension");
def2.setId("qicore-keyelement");
def2.setUrl("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-keyelement");
StructureDefinition def3 = new StructureDefinition();
def3.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def3.setTitle("US Core Practitioner Profile");
def3.setType("Practitioner");
def3.setId("us-core-practitioner");
def3.setUrl("http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner");
StructureDefinition def4 = new StructureDefinition();
def4.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def4.setTitle(null);
def4.setType("Patient");
def4.setId("Patient");
def4.setUrl("http://hl7.org/fhir/StructureDefinition/Patient");
def4.setExtension(List.of(new Extension("RANDOM.URL", new StringType("NOT_A_CATEGORY"))));
when(validationSupportChainQiCore600.fetchAllStructureDefinitions())
.thenReturn(List.of(def1, def2, def3, def4));

// when
String output = structureDefinitionService.getCategoryByType("Patient");

// then
assertThat(output, is(nullValue()));
}

@Test
void testGetCategoryByTypeReturnsCategoryFromExtension() {
// given
StructureDefinition def1 = new StructureDefinition();
def1.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def1.setTitle("QICore Patient");
def1.setType("Patient");
def1.setId("qicore-patient");
def1.setUrl("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient");
StructureDefinition def2 = new StructureDefinition();
def2.setKind(StructureDefinition.StructureDefinitionKind.COMPLEXTYPE);
def2.setTitle("QI-Core Key Element Extension");
def2.setType("Extension");
def2.setId("qicore-keyelement");
def2.setUrl("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-keyelement");
StructureDefinition def3 = new StructureDefinition();
def3.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def3.setTitle("US Core Practitioner Profile");
def3.setType("Practitioner");
def3.setId("us-core-practitioner");
def3.setUrl("http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner");
StructureDefinition def4 = new StructureDefinition();
def4.setKind(StructureDefinition.StructureDefinitionKind.RESOURCE);
def4.setTitle(null);
def4.setType("Patient");
def4.setId("Patient");
def4.setUrl("http://hl7.org/fhir/StructureDefinition/Patient");
def4.setExtension(
List.of(
new Extension(
UriConstants.FhirStructureDefinitions.CATEGORY_URI,
new StringType("Base.Individuals"))));
when(validationSupportChainQiCore600.fetchAllStructureDefinitions())
.thenReturn(List.of(def1, def2, def3, def4));

// when
String output = structureDefinitionService.getCategoryByType("Patient");

// then
assertThat(output, is(equalTo("Base.Individuals")));
}
}