* (#5442) fetch should also resolve canonical URL references * (#5442) - added test * (#5442) - cleanup? * (#5442) - changelog and reorganize test * (#5442) PR feedback * (#5442) PR feedback * (#5442) cleared ValidatorResourceFetcher linter warnings * (#5442) - new error code * (#5442) caught additional error * (#5442) spotless apply * (#5442) spotless apply --------- Co-authored-by: taha.attari@smilecdr.com <taha.attari@smilecdr.com>
This commit is contained in:
parent
83bfa817ae
commit
63eed3936b
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 5442
|
||||||
|
title: "The ValidatorResourceFetcher will now resolve canonical URL references as well as simple local references."
|
|
@ -327,18 +327,29 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link #search(SearchParameterMap, RequestDetails)} instead
|
* @deprecated Use {@link #search(SearchParameterMap, RequestDetails)} instead
|
||||||
|
* @throws InvalidRequestException If a SearchParameter is not known to the server
|
||||||
*/
|
*/
|
||||||
IBundleProvider search(SearchParameterMap theParams);
|
IBundleProvider search(SearchParameterMap theParams) throws InvalidRequestException;
|
||||||
|
|
||||||
IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails);
|
/**
|
||||||
|
* *
|
||||||
|
* @throws InvalidRequestException If a SearchParameter is not known to the server
|
||||||
|
*/
|
||||||
|
IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails)
|
||||||
|
throws InvalidRequestException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *
|
||||||
|
* @throws InvalidRequestException If a SearchParameter is not known to the server
|
||||||
|
*/
|
||||||
IBundleProvider search(
|
IBundleProvider search(
|
||||||
SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse);
|
SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse)
|
||||||
|
throws InvalidRequestException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search for IDs for processing a match URLs, etc.
|
* Search for IDs for processing a match URLs, etc.
|
||||||
*/
|
*/
|
||||||
default <T extends IResourcePersistentId> List<T> searchForIds(
|
default <PT extends IResourcePersistentId> List<PT> searchForIds(
|
||||||
SearchParameterMap theParams, RequestDetails theRequest) {
|
SearchParameterMap theParams, RequestDetails theRequest) {
|
||||||
return searchForIds(theParams, theRequest, null);
|
return searchForIds(theParams, theRequest, null);
|
||||||
}
|
}
|
||||||
|
@ -350,7 +361,7 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
||||||
* create/update, this is the resource being searched for
|
* create/update, this is the resource being searched for
|
||||||
* @since 5.5.0
|
* @since 5.5.0
|
||||||
*/
|
*/
|
||||||
default <T extends IResourcePersistentId> List<T> searchForIds(
|
default <PT extends IResourcePersistentId> List<PT> searchForIds(
|
||||||
SearchParameterMap theParams,
|
SearchParameterMap theParams,
|
||||||
RequestDetails theRequest,
|
RequestDetails theRequest,
|
||||||
@Nullable IBaseResource theConditionalOperationTargetOrNull) {
|
@Nullable IBaseResource theConditionalOperationTargetOrNull) {
|
||||||
|
|
|
@ -24,12 +24,14 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import ca.uhn.fhir.rest.param.UriParam;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper;
|
import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper;
|
||||||
import org.hl7.fhir.exceptions.DefinitionException;
|
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.model.IdType;
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
import org.hl7.fhir.r5.elementmodel.Element;
|
import org.hl7.fhir.r5.elementmodel.Element;
|
||||||
|
@ -37,12 +39,11 @@ import org.hl7.fhir.r5.elementmodel.JsonParser;
|
||||||
import org.hl7.fhir.r5.model.CanonicalResource;
|
import org.hl7.fhir.r5.model.CanonicalResource;
|
||||||
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
|
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
|
||||||
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
|
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
|
||||||
|
import org.hl7.fhir.utilities.CanonicalPair;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.util.List;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public class ValidatorResourceFetcher implements IValidatorResourceFetcher {
|
public class ValidatorResourceFetcher implements IValidatorResourceFetcher {
|
||||||
|
@ -50,22 +51,19 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(ValidatorResourceFetcher.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(ValidatorResourceFetcher.class);
|
||||||
|
|
||||||
private final FhirContext myFhirContext;
|
private final FhirContext myFhirContext;
|
||||||
private final IValidationSupport myValidationSupport;
|
|
||||||
private final DaoRegistry myDaoRegistry;
|
private final DaoRegistry myDaoRegistry;
|
||||||
private final VersionSpecificWorkerContextWrapper myVersionSpecificContextWrapper;
|
private final VersionSpecificWorkerContextWrapper myVersionSpecificContextWrapper;
|
||||||
|
|
||||||
public ValidatorResourceFetcher(
|
public ValidatorResourceFetcher(
|
||||||
FhirContext theFhirContext, IValidationSupport theValidationSupport, DaoRegistry theDaoRegistry) {
|
FhirContext theFhirContext, IValidationSupport theValidationSupport, DaoRegistry theDaoRegistry) {
|
||||||
myFhirContext = theFhirContext;
|
myFhirContext = theFhirContext;
|
||||||
myValidationSupport = theValidationSupport;
|
|
||||||
myDaoRegistry = theDaoRegistry;
|
myDaoRegistry = theDaoRegistry;
|
||||||
myVersionSpecificContextWrapper =
|
myVersionSpecificContextWrapper =
|
||||||
VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(myValidationSupport);
|
VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(theValidationSupport);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Element fetch(IResourceValidator iResourceValidator, Object appContext, String theUrl)
|
public Element fetch(IResourceValidator iResourceValidator, Object appContext, String theUrl) throws FHIRException {
|
||||||
throws FHIRFormatError, DefinitionException, FHIRException, IOException {
|
|
||||||
IdType id = new IdType(theUrl);
|
IdType id = new IdType(theUrl);
|
||||||
String resourceType = id.getResourceType();
|
String resourceType = id.getResourceType();
|
||||||
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(resourceType);
|
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(resourceType);
|
||||||
|
@ -74,9 +72,13 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher {
|
||||||
target = dao.read(id, (RequestDetails) appContext);
|
target = dao.read(id, (RequestDetails) appContext);
|
||||||
} catch (ResourceNotFoundException e) {
|
} catch (ResourceNotFoundException e) {
|
||||||
ourLog.info("Failed to resolve local reference: {}", theUrl);
|
ourLog.info("Failed to resolve local reference: {}", theUrl);
|
||||||
|
try {
|
||||||
|
target = fetchByUrl(theUrl, dao, (RequestDetails) appContext);
|
||||||
|
} catch (ResourceNotFoundException e2) {
|
||||||
|
ourLog.info("Failed to find resource by URL: {}", theUrl);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return new JsonParser(myVersionSpecificContextWrapper)
|
return new JsonParser(myVersionSpecificContextWrapper)
|
||||||
.parse(myFhirContext.newJsonParser().encodeResourceToString(target), resourceType);
|
.parse(myFhirContext.newJsonParser().encodeResourceToString(target), resourceType);
|
||||||
|
@ -85,15 +87,40 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IBaseResource fetchByUrl(String url, IFhirResourceDao<?> dao, RequestDetails requestDetails)
|
||||||
|
throws ResourceNotFoundException {
|
||||||
|
CanonicalPair pair = new CanonicalPair(url);
|
||||||
|
SearchParameterMap searchParameterMap = new SearchParameterMap();
|
||||||
|
searchParameterMap.add("url", new UriParam(pair.getUrl()));
|
||||||
|
String version = pair.getVersion();
|
||||||
|
if (version != null && !version.isEmpty()) {
|
||||||
|
searchParameterMap.add("version", new TokenParam(version));
|
||||||
|
}
|
||||||
|
List<IBaseResource> results = null;
|
||||||
|
try {
|
||||||
|
results = dao.search(searchParameterMap, requestDetails).getAllResources();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
ourLog.info("Resource does not support 'url' or 'version' Search Parameters");
|
||||||
|
}
|
||||||
|
if (results != null && results.size() > 0) {
|
||||||
|
if (results.size() > 1) {
|
||||||
|
ourLog.warn(
|
||||||
|
String.format("Multiple results found for URL '%s', only the first will be considered.", url));
|
||||||
|
}
|
||||||
|
return results.get(0);
|
||||||
|
} else {
|
||||||
|
throw new ResourceNotFoundException(Msg.code(2444) + "Failed to find resource by URL: " + url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean resolveURL(
|
public boolean resolveURL(
|
||||||
IResourceValidator iResourceValidator, Object o, String s, String s1, String s2, boolean isCanonical)
|
IResourceValidator iResourceValidator, Object o, String s, String s1, String s2, boolean isCanonical) {
|
||||||
throws IOException, FHIRException {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] fetchRaw(IResourceValidator iResourceValidator, String s) throws MalformedURLException, IOException {
|
public byte[] fetchRaw(IResourceValidator iResourceValidator, String s) throws UnsupportedOperationException {
|
||||||
throw new UnsupportedOperationException(Msg.code(577));
|
throw new UnsupportedOperationException(Msg.code(577));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,8 +131,7 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CanonicalResource fetchCanonicalResource(IResourceValidator iResourceValidator, String s)
|
public CanonicalResource fetchCanonicalResource(IResourceValidator iResourceValidator, String s) {
|
||||||
throws URISyntaxException {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
package ca.uhn.fhir.jpa.validation;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
import ca.uhn.fhir.test.BaseTest;
|
||||||
|
import ca.uhn.fhir.util.ClasspathUtil;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.r5.elementmodel.Element;
|
||||||
|
import org.hl7.fhir.r5.utils.XVerExtensionManager;
|
||||||
|
import org.hl7.fhir.validation.instance.InstanceValidator;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
|
||||||
|
public class ValidatorResourceFetcherTest extends BaseTest {
|
||||||
|
private static final FhirContext ourCtx = FhirContext.forR4();
|
||||||
|
private static final DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport(ourCtx);
|
||||||
|
private static ValidatorResourceFetcher fetcher;
|
||||||
|
private static DaoRegistry mockDaoRegistry;
|
||||||
|
private static IFhirResourceDao<IBaseResource> mockResourceDao;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
mockDaoRegistry = mock(DaoRegistry.class);
|
||||||
|
mockResourceDao = mock(IFhirResourceDao.class);
|
||||||
|
fetcher = new ValidatorResourceFetcher(ourCtx, myDefaultValidationSupport, mockDaoRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkFetchByUrl() {
|
||||||
|
// setup mocks
|
||||||
|
String resource = ClasspathUtil.loadResource("/q_jon_with_url_version.json");
|
||||||
|
doReturn(mockResourceDao).when(mockDaoRegistry).getResourceDao("Questionnaire");
|
||||||
|
doThrow(new ResourceNotFoundException("Not Found")).when(mockResourceDao).read(any(),any());
|
||||||
|
doReturn(new SimpleBundleProvider(List.of(
|
||||||
|
ourCtx.newJsonParser().parseResource(resource)
|
||||||
|
))).when(mockResourceDao).search(any(),any());
|
||||||
|
VersionSpecificWorkerContextWrapper wrappedWorkerContext = VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(myDefaultValidationSupport);
|
||||||
|
InstanceValidator v = new InstanceValidator(
|
||||||
|
wrappedWorkerContext,
|
||||||
|
new FhirInstanceValidator.NullEvaluationContext(),
|
||||||
|
new XVerExtensionManager(null));
|
||||||
|
RequestDetails r = new SystemRequestDetails();
|
||||||
|
// test
|
||||||
|
Element returnedResource = fetcher.fetch(v, r,"http://www.test-url-for-questionnaire.com/Questionnaire/test-id|1.0.0");
|
||||||
|
assertNotNull(returnedResource);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue