Add ability for resource source to be specified by header
This commit is contained in:
parent
0d3f7d4a03
commit
385a885abf
|
@ -28,6 +28,7 @@ public class Constants {
|
|||
|
||||
public static final String CT_TEXT_CSV = "text/csv";
|
||||
public static final String HEADER_REQUEST_ID = "X-Request-ID";
|
||||
public static final String HEADER_REQUEST_SOURCE = "X-Request-Source";
|
||||
public static final String CACHE_CONTROL_MAX_RESULTS = "max-results";
|
||||
public static final String CACHE_CONTROL_NO_CACHE = "no-cache";
|
||||
public static final String CACHE_CONTROL_NO_STORE = "no-store";
|
||||
|
@ -242,6 +243,15 @@ public class Constants {
|
|||
* Operation name for the $lastn operation
|
||||
*/
|
||||
public static final String OPERATION_LASTN = "$lastn";
|
||||
/**
|
||||
* <p>
|
||||
* This extension represents the equivalent of the
|
||||
* <code>Resource.meta.source</code> field within R4+ resources, and is for
|
||||
* use in DSTU3 resources. It should contain a value of type <code>uri</code>
|
||||
* and will be located on the Resource.meta
|
||||
* </p>
|
||||
*/
|
||||
public static final String EXT_META_SOURCE = "http://hapifhir.io/fhir/StructureDefinition/resource-meta-source";
|
||||
|
||||
static {
|
||||
CHARSET_UTF8 = StandardCharsets.UTF_8;
|
||||
|
|
|
@ -23,9 +23,9 @@ package ca.uhn.fhir.util;
|
|||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -46,6 +46,30 @@ public class MetaUtil {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for <code>Resource.meta.source</code> for R4+ resources, and places the value in
|
||||
* an extension on <code>Resource.meta</code>
|
||||
* with the URL <code>http://hapifhir.io/fhir/StructureDefinition/resource-meta-source</code> for DSTU3
|
||||
* and below.
|
||||
*
|
||||
* @param theContext The FhirContext object
|
||||
* @param theResource The resource to modify
|
||||
* @param theValue The source URI
|
||||
* @see <a href="http://hl7.org/fhir/resource-definitions.html#Resource.meta">Meta.source</a>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void setSource(FhirContext theContext, IBaseResource theResource, String theValue) {
|
||||
if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
|
||||
MetaUtil.setSource(theContext, theResource.getMeta(), theValue);
|
||||
} else {
|
||||
IBaseExtension<?, ?> sourceExtension = ((IBaseHasExtensions) theResource.getMeta()).addExtension();
|
||||
sourceExtension.setUrl(Constants.EXT_META_SOURCE);
|
||||
IPrimitiveType<String> value = (IPrimitiveType<String>) theContext.getElementDefinition("uri").newInstance();
|
||||
value.setValue(theValue);
|
||||
sourceExtension.setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setSource(FhirContext theContext, IBaseMetaType theMeta, String theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> elementDef = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theMeta.getClass());
|
||||
BaseRuntimeChildDefinition sourceChild = elementDef.getChildByName("source");
|
||||
|
|
|
@ -25,7 +25,6 @@ import ca.uhn.fhir.jpa.searchparam.extractor.LogicalReferenceHelper;
|
|||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.jpa.util.AddRemoveCount;
|
||||
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
|
||||
|
@ -900,15 +899,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
|||
+ (isNotBlank(provenanceRequestId) ? "#" : "")
|
||||
+ defaultString(provenanceRequestId);
|
||||
|
||||
if (myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
|
||||
IBaseExtension<?, ?> sourceExtension = ((IBaseHasExtensions) retVal.getMeta()).addExtension();
|
||||
sourceExtension.setUrl(JpaConstants.EXT_META_SOURCE);
|
||||
IPrimitiveType<String> value = (IPrimitiveType<String>) myContext.getElementDefinition("uri").newInstance();
|
||||
value.setValue(sourceString);
|
||||
sourceExtension.setValue(value);
|
||||
} else if (myContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
|
||||
MetaUtil.setSource(myContext, retVal.getMeta(), sourceString);
|
||||
}
|
||||
MetaUtil.setSource(myContext, retVal, sourceString);
|
||||
|
||||
}
|
||||
|
||||
return retVal;
|
||||
|
@ -1078,7 +1070,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
|||
source = ((IBaseHasExtensions) theResource.getMeta())
|
||||
.getExtension()
|
||||
.stream()
|
||||
.filter(t -> JpaConstants.EXT_META_SOURCE.equals(t.getUrl()))
|
||||
.filter(t -> Constants.EXT_META_SOURCE.equals(t.getUrl()))
|
||||
.filter(t -> t.getValue() instanceof IPrimitiveType)
|
||||
.map(t -> ((IPrimitiveType) t.getValue()).getValueAsString())
|
||||
.findFirst()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.util.TestUtil;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
|
@ -46,12 +45,12 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
|
|||
|
||||
when(mySrd.getRequestId()).thenReturn(requestId);
|
||||
Patient pt0 = new Patient();
|
||||
pt0.getMeta().addExtension(JpaConstants.EXT_META_SOURCE, new StringType("urn:source:0"));
|
||||
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:0"));
|
||||
pt0.setActive(true);
|
||||
IIdType pt0id = myPatientDao.create(pt0, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient pt1 = new Patient();
|
||||
pt1.getMeta().addExtension(JpaConstants.EXT_META_SOURCE, new StringType("urn:source:1"));
|
||||
pt1.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:1"));
|
||||
pt1.setActive(true);
|
||||
IIdType pt1id = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
|
@ -62,7 +61,7 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
|
|||
IBundleProvider result = myPatientDao.search(params);
|
||||
assertThat(toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(pt0id.getValue()));
|
||||
pt0 = (Patient) result.getResources(0, 1).get(0);
|
||||
assertEquals("urn:source:0#a_request_id", pt0.getMeta().getExtensionString(JpaConstants.EXT_META_SOURCE));
|
||||
assertEquals("urn:source:0#a_request_id", pt0.getMeta().getExtensionString(Constants.EXT_META_SOURCE));
|
||||
|
||||
// Search by request ID
|
||||
params = new SearchParameterMap();
|
||||
|
@ -87,17 +86,17 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
|
|||
|
||||
when(mySrd.getRequestId()).thenReturn(requestId);
|
||||
Patient pt0 = new Patient();
|
||||
pt0.getMeta().addExtension(JpaConstants.EXT_META_SOURCE, new StringType("urn:source:0"));
|
||||
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:0"));
|
||||
pt0.setActive(true);
|
||||
IIdType pt0id = myPatientDao.create(pt0, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient pt1 = new Patient();
|
||||
pt1.getMeta().addExtension(JpaConstants.EXT_META_SOURCE, new StringType("urn:source:1"));
|
||||
pt1.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:1"));
|
||||
pt1.setActive(true);
|
||||
IIdType pt1id = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient pt2 = new Patient();
|
||||
pt2.getMeta().addExtension(JpaConstants.EXT_META_SOURCE, new StringType("urn:source:2"));
|
||||
pt2.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:2"));
|
||||
pt2.setActive(true);
|
||||
myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
|
@ -118,17 +117,17 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
|
|||
|
||||
when(mySrd.getRequestId()).thenReturn(requestId);
|
||||
Patient pt0 = new Patient();
|
||||
pt0.getMeta().addExtension(JpaConstants.EXT_META_SOURCE, new StringType("urn:source:0"));
|
||||
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:0"));
|
||||
pt0.setActive(true);
|
||||
IIdType pt0id = myPatientDao.create(pt0, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient pt1 = new Patient();
|
||||
pt1.getMeta().addExtension(JpaConstants.EXT_META_SOURCE, new StringType("urn:source:1"));
|
||||
pt1.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:1"));
|
||||
pt1.setActive(true);
|
||||
IIdType pt1id = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient pt2 = new Patient();
|
||||
pt2.getMeta().addExtension(JpaConstants.EXT_META_SOURCE, new StringType("urn:source:2"));
|
||||
pt2.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:2"));
|
||||
pt2.setActive(true);
|
||||
myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
|
@ -148,7 +147,7 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
|
|||
when(mySrd.getRequestId()).thenReturn(requestId);
|
||||
|
||||
Patient pt0 = new Patient();
|
||||
pt0.getMeta().addExtension(JpaConstants.EXT_META_SOURCE, new StringType("urn:source:0"));
|
||||
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:0"));
|
||||
pt0.setActive(true);
|
||||
IIdType pt0id = myPatientDao.create(pt0, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
|
@ -166,19 +165,19 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
|
|||
public void testSourceNotPreservedAcrossUpdate() {
|
||||
|
||||
Patient pt0 = new Patient();
|
||||
pt0.getMeta().addExtension(JpaConstants.EXT_META_SOURCE, new StringType("urn:source:0"));
|
||||
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:0"));
|
||||
pt0.setActive(true);
|
||||
IIdType pt0id = myPatientDao.create(pt0, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
pt0 = myPatientDao.read(pt0id);
|
||||
assertEquals("urn:source:0", pt0.getMeta().getExtensionString(JpaConstants.EXT_META_SOURCE));
|
||||
assertEquals("urn:source:0", pt0.getMeta().getExtensionString(Constants.EXT_META_SOURCE));
|
||||
|
||||
pt0.getMeta().getExtension().clear();
|
||||
pt0.setActive(false);
|
||||
myPatientDao.update(pt0);
|
||||
|
||||
pt0 = myPatientDao.read(pt0id.withVersion("2"));
|
||||
assertEquals(null, pt0.getMeta().getExtensionString(JpaConstants.EXT_META_SOURCE));
|
||||
assertEquals(null, pt0.getMeta().getExtensionString(Constants.EXT_META_SOURCE));
|
||||
|
||||
}
|
||||
|
||||
|
@ -188,19 +187,19 @@ public class FhirResourceDaoDstu3SourceTest extends BaseJpaDstu3Test {
|
|||
when(mySrd.getRequestId()).thenReturn("0000000000000000");
|
||||
|
||||
Patient pt0 = new Patient();
|
||||
pt0.getMeta().addExtension(JpaConstants.EXT_META_SOURCE, new StringType("urn:source:0"));
|
||||
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:0"));
|
||||
pt0.setActive(true);
|
||||
IIdType pt0id = myPatientDao.create(pt0, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
pt0 = myPatientDao.read(pt0id);
|
||||
assertEquals(null, pt0.getMeta().getExtensionString(JpaConstants.EXT_META_SOURCE));
|
||||
assertEquals(null, pt0.getMeta().getExtensionString(Constants.EXT_META_SOURCE));
|
||||
|
||||
pt0.getMeta().addExtension(JpaConstants.EXT_META_SOURCE, new StringType("urn:source:1"));
|
||||
pt0.getMeta().addExtension(Constants.EXT_META_SOURCE, new StringType("urn:source:1"));
|
||||
pt0.setActive(false);
|
||||
myPatientDao.update(pt0);
|
||||
|
||||
pt0 = myPatientDao.read(pt0id.withVersion("2"));
|
||||
assertEquals(null, pt0.getMeta().getExtensionString(JpaConstants.EXT_META_SOURCE));
|
||||
assertEquals(null, pt0.getMeta().getExtensionString(Constants.EXT_META_SOURCE));
|
||||
|
||||
// Search without source param
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
|
|
|
@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.provider.dstu3;
|
|||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.provider.r4.ResourceProviderR4Test;
|
||||
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
|
@ -3952,7 +3951,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
{
|
||||
Patient readPatient = (Patient) ourClient.read().resource("Patient").withId(patientid).execute();
|
||||
assertThat(readPatient.getMeta().getExtensionString(JpaConstants.EXT_META_SOURCE), matchesPattern("#[a-zA-Z0-9]+"));
|
||||
assertThat(readPatient.getMeta().getExtensionString(Constants.EXT_META_SOURCE), matchesPattern("#[a-zA-Z0-9]+"));
|
||||
}
|
||||
|
||||
patient.setId(patientid);
|
||||
|
@ -3960,12 +3959,12 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
ourClient.update().resource(patient).execute();
|
||||
{
|
||||
Patient readPatient = (Patient) ourClient.read().resource("Patient").withId(patientid).execute();
|
||||
assertThat(readPatient.getMeta().getExtensionString(JpaConstants.EXT_META_SOURCE), matchesPattern("#[a-zA-Z0-9]+"));
|
||||
assertThat(readPatient.getMeta().getExtensionString(Constants.EXT_META_SOURCE), matchesPattern("#[a-zA-Z0-9]+"));
|
||||
|
||||
readPatient.addName().setFamily("testUpdateWithSource");
|
||||
ourClient.update().resource(readPatient).execute();
|
||||
readPatient = (Patient) ourClient.read().resource("Patient").withId(patientid).execute();
|
||||
assertThat(readPatient.getMeta().getExtensionString(JpaConstants.EXT_META_SOURCE), matchesPattern("#[a-zA-Z0-9]+"));
|
||||
assertThat(readPatient.getMeta().getExtensionString(Constants.EXT_META_SOURCE), matchesPattern("#[a-zA-Z0-9]+"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -243,16 +243,6 @@ public class JpaConstants {
|
|||
*/
|
||||
public static final String EXTENSION_EXT_SYSTEMDEFINED = JpaConstants.class.getName() + "_EXTENSION_EXT_SYSTEMDEFINED";
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This extension represents the equivalent of the
|
||||
* <code>Resource.meta.source</code> field within R4+ resources, and is for
|
||||
* use in DSTU3 resources. It should contain a value of type <code>uri</code>
|
||||
* and will be located on the Resource.meta
|
||||
* </p>
|
||||
*/
|
||||
public static final String EXT_META_SOURCE = "http://hapifhir.io/fhir/StructureDefinition/resource-meta-source";
|
||||
|
||||
/**
|
||||
* Parameter for the $export operation
|
||||
*/
|
||||
|
|
|
@ -370,7 +370,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
throw new ConfigurationException("Failure scanning class " + clazz.getSimpleName() + ": " + e.getMessage(), e);
|
||||
}
|
||||
if (count == 0) {
|
||||
throw new ConfigurationException("Did not find any annotated RESTful methods on provider class " + theProvider.getClass().getCanonicalName());
|
||||
throw new ConfigurationException("Did not find any annotated RESTful methods on provider class " + theProvider.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.util.MetaUtil;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
* This interceptor examines a header on the incoming request and places it in
|
||||
* <code>Resource.meta.source</code> (R4 and above) or in an extension on <code>Resource.meta</code>
|
||||
* with the URL <code>http://hapifhir.io/fhir/StructureDefinition/resource-meta-source</code> (DSTU3 and below).
|
||||
* <p>
|
||||
* This interceptor does not support versions of FHIR below DSTU3.
|
||||
* </p>
|
||||
*
|
||||
* @see <a href="http://hl7.org/fhir/resource-definitions.html#Resource.meta">Meta.source</a>
|
||||
*/
|
||||
@Interceptor
|
||||
public class CaptureResourceSourceFromHeaderInterceptor {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(CaptureResourceSourceFromHeaderInterceptor.class);
|
||||
private final FhirContext myFhirContext;
|
||||
private String myHeaderName;
|
||||
|
||||
public CaptureResourceSourceFromHeaderInterceptor(FhirContext theFhirContext) {
|
||||
myFhirContext = theFhirContext;
|
||||
setHeaderName(Constants.HEADER_REQUEST_SOURCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the header name to examine in incoming requests. Default is {@link ca.uhn.fhir.rest.api.Constants#HEADER_REQUEST_SOURCE "X-Request-Source"}.
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public String getHeaderName() {
|
||||
return myHeaderName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the header name to examine in incoming requests. Default is {@link ca.uhn.fhir.rest.api.Constants#HEADER_REQUEST_SOURCE "X-Request-Source"}.
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public void setHeaderName(String theHeaderName) {
|
||||
myHeaderName = theHeaderName;
|
||||
}
|
||||
|
||||
@Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED)
|
||||
public void extractSource(RequestDetails theRequestDetails) {
|
||||
IBaseResource resource = theRequestDetails.getResource();
|
||||
if (resource != null) {
|
||||
String requestSource = theRequestDetails.getHeader(getHeaderName());
|
||||
if (isNotBlank(requestSource)) {
|
||||
ourLog.trace("Setting Meta.source to \"{}\" because of header \"{}\"", requestSource, getHeaderName());
|
||||
MetaUtil.setSource(myFhirContext, resource, requestSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -126,6 +126,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
|
||||
return new MethodOutcome()
|
||||
.setCreated(true)
|
||||
.setResource(theResource)
|
||||
.setId(theResource.getIdElement());
|
||||
}
|
||||
|
||||
|
@ -373,6 +374,7 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
|
||||
return new MethodOutcome()
|
||||
.setCreated(created)
|
||||
.setResource(theResource)
|
||||
.setId(theResource.getIdElement());
|
||||
}
|
||||
|
||||
|
@ -416,6 +418,19 @@ public class HashMapResourceProvider<T extends IBaseResource> implements IResour
|
|||
return theResource.getIdElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable list containing the current version of all resources stored in this provider
|
||||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public List<T> getStoredResources() {
|
||||
List<T> retVal = new ArrayList<>();
|
||||
for (TreeMap<Long, T> next : myIdToVersionToResourceMap.values()) {
|
||||
retVal.add(next.lastEntry().getValue());
|
||||
}
|
||||
return Collections.unmodifiableList(retVal);
|
||||
}
|
||||
|
||||
private static <T extends IBaseResource> T fireInterceptorsAndFilterAsNeeded(T theResource, RequestDetails theRequestDetails) {
|
||||
List<T> output = fireInterceptorsAndFilterAsNeeded(Lists.newArrayList(theResource), theRequestDetails);
|
||||
if (output.size() == 1) {
|
||||
|
|
|
@ -87,9 +87,8 @@ public class SearchR4Test {
|
|||
* A paging request that incorrectly executes at the type level shouldn't be grabbed by the search method binding
|
||||
*/
|
||||
@Test
|
||||
public void tesPageRequestCantTriggerSearchAccidentally() throws Exception {
|
||||
public void testPageRequestCantTriggerSearchAccidentally() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?" + Constants.PARAM_PAGINGACTION + "=12345");
|
||||
Bundle bundle;
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider;
|
||||
import ca.uhn.fhir.test.utilities.server.HashMapResourceProviderRule;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerRule;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.*;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class CaptureResourceSourceFromHeaderInterceptorTest {
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
@ClassRule
|
||||
public static RestfulServerRule ourServerRule = new RestfulServerRule(ourCtx);
|
||||
private CaptureResourceSourceFromHeaderInterceptor myInterceptor;
|
||||
@Rule
|
||||
public HashMapResourceProviderRule<Patient> myPatientProviderRule = new HashMapResourceProviderRule<>(ourServerRule, Patient.class);
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myInterceptor = new CaptureResourceSourceFromHeaderInterceptor(ourCtx);
|
||||
ourServerRule.getRestfulServer().registerInterceptor(myInterceptor);
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
ourServerRule.getRestfulServer().unregisterInterceptor(myInterceptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithoutAnything() {
|
||||
Patient resource = new Patient();
|
||||
resource.setActive(true);
|
||||
|
||||
ourServerRule.getFhirClient().create().resource(resource).execute();
|
||||
|
||||
Patient stored = myPatientProviderRule.getStoredResources().get(0);
|
||||
assertNull(stored.getMeta().getSource());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithSource() {
|
||||
Patient resource = new Patient();
|
||||
resource.setActive(true);
|
||||
resource.getMeta().setSource("http://source");
|
||||
|
||||
ourServerRule.getFhirClient().create().resource(resource).execute();
|
||||
|
||||
Patient stored = myPatientProviderRule.getStoredResources().get(0);
|
||||
assertEquals("http://source", stored.getMeta().getSource());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithHeader() {
|
||||
Patient resource = new Patient();
|
||||
resource.setActive(true);
|
||||
|
||||
ourServerRule
|
||||
.getFhirClient()
|
||||
.create()
|
||||
.resource(resource)
|
||||
.withAdditionalHeader(Constants.HEADER_REQUEST_SOURCE, "http://header")
|
||||
.execute();
|
||||
|
||||
Patient stored = myPatientProviderRule.getStoredResources().get(0);
|
||||
assertEquals("http://header", stored.getMeta().getSource());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithBoth() {
|
||||
Patient resource = new Patient();
|
||||
resource.setActive(true);
|
||||
resource.getMeta().setSource("http://source");
|
||||
|
||||
ourServerRule
|
||||
.getFhirClient()
|
||||
.create()
|
||||
.resource(resource)
|
||||
.withAdditionalHeader(Constants.HEADER_REQUEST_SOURCE, "http://header")
|
||||
.execute();
|
||||
|
||||
Patient stored = myPatientProviderRule.getStoredResources().get(0);
|
||||
assertEquals("http://header", stored.getMeta().getSource());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonCreateShouldntFail() {
|
||||
Bundle bundle = ourServerRule
|
||||
.getFhirClient()
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
assertEquals(0, bundle.getEntry().size());
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,16 @@
|
|||
<artifactId>hapi-fhir-base</artifactId>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-server</artifactId>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-client</artifactId>
|
||||
<version>4.1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- General -->
|
||||
<dependency>
|
||||
|
@ -53,6 +63,16 @@
|
|||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Jetty -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package ca.uhn.fhir.test.utilities.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
public class HashMapResourceProviderRule<T extends IBaseResource> extends HashMapResourceProvider<T> implements TestRule {
|
||||
|
||||
private final RestfulServerRule myRestfulServerRule;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theFhirContext The FHIR context
|
||||
* @param theResourceType The resource type to support
|
||||
*/
|
||||
public HashMapResourceProviderRule(RestfulServerRule theRestfulServerRule, Class<T> theResourceType) {
|
||||
super(theRestfulServerRule.getFhirContext(), theResourceType);
|
||||
|
||||
myRestfulServerRule = theRestfulServerRule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
clear();
|
||||
myRestfulServerRule.getRestfulServer().registerProvider(HashMapResourceProviderRule.this);
|
||||
try {
|
||||
base.evaluate();
|
||||
} finally {
|
||||
myRestfulServerRule.getRestfulServer().unregisterProvider(HashMapResourceProviderRule.this);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package ca.uhn.fhir.test.utilities.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RestfulServerRule implements TestRule {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServerRule.class);
|
||||
|
||||
private final FhirContext myFhirContext;
|
||||
private final Object[] myProviders;
|
||||
private Server myServer;
|
||||
private RestfulServer myServlet;
|
||||
private int myPort;
|
||||
private CloseableHttpClient myHttpClient;
|
||||
private IGenericClient myFhirClient;
|
||||
|
||||
public RestfulServerRule(FhirContext theFhirContext, Object... theProviders) {
|
||||
myFhirContext = theFhirContext;
|
||||
myProviders = theProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement theBase, Description theDescription) {
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
startServer();
|
||||
theBase.evaluate();
|
||||
stopServer();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private void stopServer() throws Exception {
|
||||
JettyUtil.closeServer(myServer);
|
||||
myHttpClient.close();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
private void startServer() throws Exception {
|
||||
myServer = new Server(0);
|
||||
|
||||
ServletHandler servletHandler = new ServletHandler();
|
||||
myServlet = new RestfulServer(myFhirContext);
|
||||
myServlet.setDefaultPrettyPrint(true);
|
||||
myServlet.registerProviders(myProviders);
|
||||
ServletHolder servletHolder = new ServletHolder(myServlet);
|
||||
servletHandler.addServletWithMapping(servletHolder, "/*");
|
||||
|
||||
myServer.setHandler(servletHandler);
|
||||
myServer.start();
|
||||
myPort = JettyUtil.getPortForStartedServer(myServer);
|
||||
ourLog.info("Server has started on port {}", myPort);
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
myHttpClient = builder.build();
|
||||
|
||||
myFhirContext.getRestfulClientFactory().setSocketTimeout((int) (500 * DateUtils.MILLIS_PER_SECOND));
|
||||
myFhirContext.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||
myFhirClient = myFhirContext.newRestfulGenericClient("http://localhost:" + myPort);
|
||||
}
|
||||
|
||||
|
||||
public IGenericClient getFhirClient() {
|
||||
return myFhirClient;
|
||||
}
|
||||
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
}
|
||||
|
||||
public RestfulServer getRestfulServer() {
|
||||
return myServlet;
|
||||
}
|
||||
}
|
|
@ -450,6 +450,14 @@
|
|||
Several issues with HAPI FHIR's annotation scanner that prevented use with Kotlin based
|
||||
resource providers have been corrected. Thanks to Jelmer ter Wal for the pull request!
|
||||
</action>
|
||||
<action type="add">
|
||||
A new built-in server interceptor called
|
||||
<![CDATA[<code>CaptureResourceSourceFromHeaderInterceptor</code>]]>
|
||||
has been added.
|
||||
This interceptor can be used to capture an incoming source system URI in an HTTP Request
|
||||
Header and automatically place it in
|
||||
<![CDATA[<code>Resource.meta.source</code>]]>
|
||||
</action>
|
||||
</release>
|
||||
<release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
|
||||
<action type="fix">
|
||||
|
|
|
@ -313,6 +313,18 @@
|
|||
|
||||
</subsection>
|
||||
|
||||
<subsection name="Capturing Meta.source from an HTTP Header">
|
||||
|
||||
<p>
|
||||
If you wish to override the value of <code>Resource.meta.source</code> using the value
|
||||
supplied in an HTTP header, you can use the
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/server/interceptor/CaptureResourceSourceFromHeaderInterceptor.html">CaptureResourceSourceFromHeaderInterceptor</a>
|
||||
(<a href="./xref/ca/uhn/fhir/rest/server/interceptor/CaptureResourceSourceFromHeaderInterceptor.html">code</a>)
|
||||
to accomplish this.
|
||||
</p>
|
||||
|
||||
</subsection>
|
||||
|
||||
</section>
|
||||
|
||||
<section name="Creating Interceptors">
|
||||
|
|
Loading…
Reference in New Issue