Compare commits

...

7 Commits

Author SHA1 Message Date
Mangala Ekanayake cfdcac44c7
Merge a43d12e042 into 77fa7f7819 2024-11-26 11:06:33 -05:00
mangala.ekanayake a43d12e042 [6463] Fix ValidateWithRemoteTerminologyTest 2024-11-25 21:05:13 +05:30
mangala.ekanayake 0c656f072a [6463] re-add removed comments 2024-11-25 19:38:06 +05:30
mangala.ekanayake 7d4a243c5e [6463] unit tests for JpaPersistedResourceValidationSupport when fetch StructureDefinitions 2024-11-25 19:38:06 +05:30
mangala.ekanayake 883281afee [6463] resource url can have only one | character 2024-11-25 19:38:05 +05:30
Mangala Ekanayake 1d0b18d989 Update VersionSpecificWorkerContextWrapper.java
[6463] version is required in url of resource to identify it
2024-11-25 19:38:05 +05:30
Mangala Ekanayake 674f4637a9 [6463] use version in canonical url of StructureDefinition to identify it 2024-11-25 19:38:05 +05:30
7 changed files with 132 additions and 17 deletions

View File

@ -273,7 +273,13 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
}
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
int versionSeparator = theUri.lastIndexOf('|');
if (versionSeparator != -1) {
params.add(StructureDefinition.SP_VERSION, new TokenParam(theUri.substring(versionSeparator + 1)));
params.add(StructureDefinition.SP_URL, new UriParam(theUri.substring(0, versionSeparator)));
} else {
params.add(StructureDefinition.SP_URL, new UriParam(theUri));
}
search = myDaoRegistry.getResourceDao("StructureDefinition").search(params);
break;
}

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.jpa.dao;
import static org.junit.jupiter.api.Assertions.assertNull;
/*-
* #%L
* HAPI FHIR JPA Server
@ -25,15 +24,23 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.sl.cache.Cache;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.StructureDefinition;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ -41,13 +48,18 @@ import org.springframework.test.util.ReflectionTestUtils;
import java.util.function.Function;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_LOW;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class JpaPersistedResourceValidationSupportTest {
@ -115,5 +127,68 @@ class JpaPersistedResourceValidationSupportTest {
}
@Nested
class FetchStructureDefinitionTests {
@Mock
private DaoRegistry myDaoRegistry;
@InjectMocks
private final JpaPersistedResourceValidationSupport testClass = new JpaPersistedResourceValidationSupport(theFhirContext);
@Captor
ArgumentCaptor<SearchParameterMap> searchParameterMapCaptor;
@Test
@DisplayName("fetch StructureDefinition by version less url")
void fetchStructureDefinitionForUrl() {
final String profileUrl = "http://example.com/fhir/StructureDefinition/exampleProfile";
IFhirResourceDao mockDao = mock(IFhirResourceDao.class);
when(mockDao.search(any())).thenReturn(mock(IBundleProvider.class));
when(myDaoRegistry.getResourceDao(anyString())).thenReturn(mockDao);
testClass.fetchResource(StructureDefinition.class, profileUrl);
verify(mockDao).search(searchParameterMapCaptor.capture());
SearchParameterMap searchParams = searchParameterMapCaptor.getValue();
String uriParam = searchParams.get(StructureDefinition.SP_URL)
.get(0)
.stream()
.map(UriParam.class::cast)
.map(UriParam::getValue)
.findFirst()
.orElse(null);
assertThat(uriParam).isEqualTo(profileUrl);
}
@Test
@DisplayName("fetch StructureDefinition by versioned url")
void fetchStructureDefinitionForVersionedUrl() {
final String profileUrl = "http://example.com/fhir/StructureDefinition/exampleProfile|1.1.0";
IFhirResourceDao mockDao = mock(IFhirResourceDao.class);
when(mockDao.search(any())).thenReturn(mock(IBundleProvider.class));
when(myDaoRegistry.getResourceDao(anyString())).thenReturn(mockDao);
testClass.fetchResource(StructureDefinition.class, profileUrl);
verify(mockDao).search(searchParameterMapCaptor.capture());
SearchParameterMap searchParams = searchParameterMapCaptor.getValue();
String uriParam = searchParams.get(StructureDefinition.SP_URL)
.get(0)
.stream()
.map(UriParam.class::cast)
.map(UriParam::getValue)
.findFirst()
.orElse(null);
assertThat(uriParam).isEqualTo("http://example.com/fhir/StructureDefinition/exampleProfile");
String versionParam = searchParams.get(StructureDefinition.SP_VERSION)
.get(0)
.stream()
.map(TokenParam.class::cast)
.map(TokenParam::getValue)
.findFirst()
.orElse(null);
assertThat(versionParam).isEqualTo("1.1.0");
}
}
}

View File

@ -94,7 +94,7 @@ public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Tes
final String classSystem = "http://terminology.hl7.org/CodeSystem/v3-ActCode";
final String identifierTypeSystem = "http://terminology.hl7.org/CodeSystem/v2-0203";
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/encounter-status", "http://hl7.org/fhir/encounter-status", statusCode, "validation/encounter/validateCode-ValueSet-encounter-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/encounter-status", "4.0.1", "http://hl7.org/fhir/encounter-status", statusCode, "validation/encounter/validateCode-ValueSet-encounter-status.json");
setupValueSetValidateCode("http://terminology.hl7.org/ValueSet/v3-ActEncounterCode", "http://terminology.hl7.org/CodeSystem/v3-ActCode", classCode, "validation/encounter/validateCode-ValueSet-v3-ActEncounterCode.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/identifier-type", "http://hl7.org/fhir/identifier-type", identifierTypeCode, "validation/encounter/validateCode-ValueSet-identifier-type.json");
@ -138,7 +138,7 @@ public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Tes
final String loincSystem = "http://loinc.org";
final String system = "http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM";
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-status", statusSystem, statusCode, "validation/observation/validateCode-ValueSet-observation-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-status", "4.0.1", statusSystem, statusCode, "validation/observation/validateCode-ValueSet-observation-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-codes", loincSystem, statusCode, "validation/observation/validateCode-ValueSet-codes.json");
setupCodeSystemValidateCode(statusSystem, statusCode, "validation/observation/validateCode-CodeSystem-observation-status.json");
@ -171,7 +171,7 @@ public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Tes
final String statusSystem = "http://hl7.org/fhir/event-status";
final String snomedSystem = "http://snomed.info/sct";
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", "4.0.1", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode1, "validation/procedure/validateCode-ValueSet-procedure-code-valid.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode2, "validation/procedure/validateCode-ValueSet-procedure-code-invalid.json");
@ -213,7 +213,7 @@ public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Tes
final String snomedSystem = "http://snomed.info/sct";
final String absentUnknownSystem = "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips";
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", "4.0.1", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json");
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode, "validation/procedure/validateCode-ValueSet-procedure-code-invalid-slice.json");
setupValueSetValidateCode("http://hl7.org/fhir/uv/ips/ValueSet/absent-or-unknown-procedures-uv-ips", absentUnknownSystem, procedureCode, "validation/procedure/validateCode-ValueSet-absent-or-unknown-procedure.json");
@ -245,6 +245,15 @@ public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Tes
// which also attempts a validateCode against the CodeSystem after the validateCode against the ValueSet
}
private void setupValueSetValidateCode(String theUrl, String theVersion, String theSystem, String theCode, String theTerminologyResponseFile) {
ValueSet valueSet = myValueSetProvider.addTerminologyResource(theUrl, theVersion);
myValueSetProvider.addTerminologyResource(theSystem, theVersion);
myValueSetProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, valueSet.getUrl(), theCode, ourCtx, theTerminologyResponseFile);
valueSet.getCompose().addInclude().setSystem(theSystem);
}
private void setupCodeSystemValidateCode(String theUrl, String theCode, String theTerminologyResponseFile) {
CodeSystem codeSystem = myCodeSystemProvider.addTerminologyResource(theUrl);
myCodeSystemProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, codeSystem.getUrl(), theCode, ourCtx, theTerminologyResponseFile);

View File

@ -84,9 +84,14 @@ public interface IValidationProviders {
protected void addTerminologyResource(String theUrl, T theResource) {
myTerminologyResourceMap.put(theUrl, theResource);
}
protected void addVersionedTerminologyResource(String theUrl, String theVersion, T theResource) {
myTerminologyResourceMap.put(theUrl + "|" + theVersion, theResource);
}
public abstract T addTerminologyResource(String theUrl);
public abstract T addTerminologyResource(String theUrl, String theVersion);
protected IBaseParameters getTerminologyResponse(String theOperation, String theUrl, String theCode) throws Exception {
String inputKey = getInputKey(theOperation, theUrl, theCode);
if (myExceptionMap.containsKey(inputKey)) {

View File

@ -95,6 +95,11 @@ public interface IValidationProvidersDstu3 {
addTerminologyResource(theUrl, codeSystem);
return codeSystem;
}
@Override
public CodeSystem addTerminologyResource(String theUrl, String theVersion) {
return addTerminologyResource(theUrl);
}
}
@SuppressWarnings("unused")
@ -133,5 +138,10 @@ public interface IValidationProvidersDstu3 {
addTerminologyResource(theUrl, valueSet);
return valueSet;
}
@Override
public ValueSet addTerminologyResource(String theUrl, String theVersion) {
return addTerminologyResource(theUrl);
}
}
}

View File

@ -99,6 +99,14 @@ public interface IValidationProvidersR4 {
addTerminologyResource(theUrl, codeSystem);
return codeSystem;
}
@Override
public CodeSystem addTerminologyResource(String theUrl, String theVersion) {
CodeSystem codeSystem = addTerminologyResource(theUrl);
codeSystem.setVersion(theVersion);
addVersionedTerminologyResource(theUrl, theVersion, codeSystem);
return codeSystem;
}
}
@SuppressWarnings("unused")
@ -130,6 +138,7 @@ public interface IValidationProvidersR4 {
Class<Parameters> getParameterType() {
return Parameters.class;
}
@Override
public ValueSet addTerminologyResource(String theUrl) {
ValueSet valueSet = new ValueSet();
@ -138,5 +147,12 @@ public interface IValidationProvidersR4 {
addTerminologyResource(theUrl, valueSet);
return valueSet;
}
@Override
public ValueSet addTerminologyResource(String theUrl, String theVersion) {
ValueSet valueSet = addTerminologyResource(theUrl);
addVersionedTerminologyResource(theUrl, theVersion, valueSet);
return valueSet;
}
}
}

View File

@ -12,6 +12,7 @@ import ca.uhn.fhir.system.HapiSystemProperties;
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
@ -466,18 +467,11 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
return null;
}
String uri = theUri;
// handle profile version, if present
if (theUri.contains("|")) {
String[] parts = theUri.split("\\|");
if (parts.length == 2) {
uri = parts[0];
} else {
if (StringUtils.countMatches(theUri, "|") > 1) {
ourLog.warn("Unrecognized profile uri: {}", theUri);
}
}
ResourceKey key = new ResourceKey(class_.getSimpleName(), uri);
ResourceKey key = new ResourceKey(class_.getSimpleName(), theUri);
@SuppressWarnings("unchecked")
T retVal = (T) myFetchResourceCache.get(key);